Danny Robson
fdaa5e1392
LIMIT hid an off-by-one bug when tests used end iterators. We rename the assertion to uncover all uses of the flawed implementation, and split it into an identical assertion, and one intended to protect against iterator ends.
1622 lines
45 KiB
C++
1622 lines
45 KiB
C++
/*
|
|
* 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/.
|
|
*
|
|
* Copyright 2012-2019 Danny Robson <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "fwd.hpp"
|
|
#include "traits.hpp"
|
|
#include "../array/varray.hpp"
|
|
|
|
// we specifically rely on vector<bool> to compute a few logical operations
|
|
#include "../vector.hpp"
|
|
#include "../tuple/value.hpp"
|
|
#include "../debug/assert.hpp"
|
|
#include "../maths.hpp"
|
|
#include "../types/bits.hpp"
|
|
|
|
#include <cruft/util/preprocessor.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <iterator>
|
|
#include <functional>
|
|
|
|
|
|
namespace cruft {
|
|
/// returns the data at a templated index in a coordinate.
|
|
///
|
|
/// specifically required for structured bindings support.
|
|
///
|
|
/// \tparam I index of the requested data
|
|
/// \tparam S dimensionality of the coordinate
|
|
/// \tparam T underlying data type of the coordinate
|
|
/// \tparam K coordinate data type to operate on
|
|
template <
|
|
std::size_t I,
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
const auto&
|
|
get (const K &k)
|
|
{
|
|
static_assert (I < K::elements);
|
|
return k[I];
|
|
};
|
|
|
|
|
|
/// returns the data at a templated index in a coordinate.
|
|
///
|
|
/// specifically required for structured bindings support.
|
|
///
|
|
/// \tparam I index of the requested data
|
|
/// \tparam S dimensionality of the coordinate
|
|
/// \tparam T underlying data type of the coordinate
|
|
/// \tparam K coordinate data type to operate on
|
|
template <
|
|
std::size_t I,
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K> && I < K::elements, void
|
|
>
|
|
>
|
|
auto &
|
|
get (K &k)
|
|
{
|
|
static_assert (I < K::elements);
|
|
return k[I];
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// a templated functor that exposes arithmetic and assignment maths
|
|
// functions for vector-vector or vector-scalar operations.
|
|
//
|
|
// we implement the operations this way because it (somewhat) simplifies
|
|
// ambiguity resolution in the various operators we need to provide.
|
|
// eg, operator+(vec,vec) vs operator+(vec,int).
|
|
//
|
|
// it used to be directly implemented with a series of templated free
|
|
// functions when we could restrict the arguments more easily with quite
|
|
// specific template template parameters. but the introduction of
|
|
// coordinate types that do not expose size or type information as template
|
|
// parameters we can't rely on this mechanism anymore.
|
|
|
|
template <typename, typename, typename=void>
|
|
struct assignment {};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <typename CoordA, typename CoordB>
|
|
struct assignment<
|
|
CoordA,
|
|
CoordB,
|
|
std::enable_if_t<
|
|
is_coord_v<CoordA> &&
|
|
is_coord_v<CoordB> &&
|
|
arity_v<CoordA> == arity_v<CoordB> &&
|
|
std::is_same_v<
|
|
typename CoordA::value_type,
|
|
std::common_type_t<
|
|
typename CoordA::value_type,
|
|
typename CoordB::value_type
|
|
>
|
|
>
|
|
,
|
|
void
|
|
>
|
|
> {
|
|
template <typename OperationT>
|
|
static constexpr CoordA&
|
|
eval (OperationT &&op, CoordA &a, const CoordB &b)
|
|
{
|
|
for (std::size_t i = 0; i < CoordA::elements; ++i)
|
|
a[i] = op (a[i], b[i]);
|
|
return a;
|
|
}
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// vector-scalar operations
|
|
template <
|
|
typename CoordT,
|
|
typename ScalarT
|
|
>
|
|
struct assignment<
|
|
CoordT,
|
|
ScalarT,
|
|
std::enable_if_t<
|
|
is_coord_v<CoordT> &&
|
|
!is_coord_v<ScalarT> &&
|
|
has_scalar_op_v<CoordT> &&
|
|
std::is_same_v<
|
|
typename CoordT::value_type,
|
|
std::common_type_t<
|
|
typename CoordT::value_type,
|
|
ScalarT
|
|
>
|
|
>
|
|
,
|
|
void
|
|
>
|
|
> {
|
|
// we allow scalar types which can be naturally promoted to the vector's
|
|
// value_type
|
|
template <typename OperationT>
|
|
static constexpr CoordT&
|
|
eval (OperationT &&op, CoordT &coord, const ScalarT scalar)
|
|
{
|
|
for (size_t i = 0; i < CoordT::elements; ++i)
|
|
coord[i] = op (coord[i], scalar);
|
|
return coord;
|
|
}
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
/// create a coord from supplied arguments, optionally specifying the
|
|
/// underlying type.
|
|
///
|
|
/// much like experimental::make_array we use a void type to signal we
|
|
/// need to deduce the underlying type.
|
|
#define MAKE_COORD(KLASS) \
|
|
template < \
|
|
typename _T = void, \
|
|
typename ...Args \
|
|
> \
|
|
constexpr auto \
|
|
make_##KLASS (Args &&...args) \
|
|
{ \
|
|
using T = std::conditional_t< \
|
|
std::is_void_v<_T>, \
|
|
std::common_type_t<Args...>, \
|
|
_T \
|
|
>; \
|
|
\
|
|
return KLASS<sizeof...(Args),T> { \
|
|
std::forward<Args> (args)... \
|
|
}; \
|
|
}
|
|
|
|
MAKE_COORD(extent)
|
|
MAKE_COORD(point)
|
|
MAKE_COORD(vector)
|
|
|
|
#undef MAKE_COORD
|
|
|
|
template <
|
|
template <std::size_t,typename> class K,
|
|
typename ...Args
|
|
>
|
|
constexpr auto
|
|
make_coord (Args &&...args)
|
|
{
|
|
using T = std::common_type_t<Args...>;
|
|
return K<sizeof...(Args),T> { std::forward<Args> (args)... };
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <typename ValueA, typename ValueB, typename=void>
|
|
struct arithmetic {};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <typename CoordA, typename CoordB>
|
|
struct arithmetic<
|
|
CoordA,
|
|
CoordB,
|
|
std::enable_if_t<
|
|
is_coord_v<CoordA> &&
|
|
is_coord_v<CoordB> &&
|
|
arity_v<CoordA> == arity_v<CoordB> &&
|
|
has_result_v<CoordA,CoordB>
|
|
,
|
|
void
|
|
>
|
|
> {
|
|
template <typename OperationT>
|
|
static constexpr auto
|
|
eval (OperationT &&op, const CoordA &a, const CoordB &b)
|
|
{
|
|
using common_t = std::common_type_t<
|
|
typename CoordA::value_type,
|
|
typename CoordB::value_type
|
|
>;
|
|
|
|
revalue_t<result_t<CoordA,CoordB>,common_t> out {};
|
|
for (size_t i = 0; i < CoordA::elements; ++i)
|
|
out[i] = op (a[i], b[i]);
|
|
return out;
|
|
}
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <typename CoordT, typename ScalarT>
|
|
struct arithmetic<
|
|
CoordT,
|
|
ScalarT,
|
|
std::enable_if_t<
|
|
is_coord_v<CoordT> && std::is_arithmetic_v<ScalarT> && has_scalar_op_v<CoordT>,
|
|
void
|
|
>
|
|
> {
|
|
template <typename OperationT>
|
|
static constexpr auto
|
|
eval (OperationT &&op, const CoordT &coord, const ScalarT &scalar)
|
|
{
|
|
using common_t = std::common_type_t<typename CoordT::value_type, ScalarT>;
|
|
revalue_t<CoordT,common_t> out {};
|
|
for (size_t i = 0; i < CoordT::elements; ++i)
|
|
out[i] = op (coord[i], scalar);
|
|
return out;
|
|
}
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <typename ScalarT, typename CoordT>
|
|
struct arithmetic<
|
|
ScalarT,
|
|
CoordT,
|
|
std::enable_if_t<
|
|
is_coord_v<CoordT> && std::is_arithmetic_v<ScalarT> && has_scalar_op_v<CoordT>,
|
|
void
|
|
>
|
|
> {
|
|
template <typename OperationT>
|
|
static constexpr auto
|
|
eval (OperationT &&op, const ScalarT &scalar, const CoordT &coord)
|
|
{
|
|
using common_t = std::common_type_t<typename CoordT::value_type, ScalarT>;
|
|
revalue_t<CoordT,common_t> out {};
|
|
for (size_t i = 0; i < CoordT::elements; ++i)
|
|
out[i] = op (scalar, coord[i]);
|
|
return out;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
|
|
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
|
|
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
|
|
>
|
|
>
|
|
constexpr auto
|
|
operator+ (A &&a, B &&b)
|
|
{
|
|
return arithmetic<std::decay_t<A>, std::decay_t<B>>::template eval (std::plus{}, a, b);
|
|
}
|
|
|
|
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
|
|
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
|
|
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
|
|
>
|
|
>
|
|
constexpr auto
|
|
operator- (A &&a, B &&b)
|
|
{
|
|
return arithmetic<std::decay_t<A>, std::decay_t<B>>::template eval (std::minus{}, a, b);
|
|
}
|
|
|
|
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
|
|
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
|
|
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
|
|
>
|
|
>
|
|
constexpr auto
|
|
operator* (A &&a, B &&b)
|
|
{
|
|
return arithmetic<
|
|
std::decay_t<A>,
|
|
std::decay_t<B>
|
|
>::template eval (std::multiplies{}, a, b);
|
|
}
|
|
|
|
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
|
|
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
|
|
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
|
|
>
|
|
>
|
|
constexpr auto
|
|
operator/ (A &&a, B &&b)
|
|
{
|
|
return arithmetic<std::decay_t<A>, std::decay_t<B>>::template eval (std::divides{}, a, b);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
|
|
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
|
|
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
|
|
>
|
|
>
|
|
constexpr auto
|
|
operator += (A &&a, B &&b)
|
|
{
|
|
return assignment<
|
|
std::decay_t<A>,
|
|
std::decay_t<B>
|
|
>::eval (std::plus{}, a, b);
|
|
}
|
|
|
|
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
|
|
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
|
|
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
|
|
>
|
|
>
|
|
constexpr auto
|
|
operator -= (A &&a, B &&b)
|
|
{
|
|
return assignment<
|
|
std::decay_t<A>,
|
|
std::decay_t<B>
|
|
>::eval (std::minus{}, a, b);
|
|
}
|
|
|
|
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
|
|
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
|
|
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
|
|
>
|
|
>
|
|
constexpr auto
|
|
operator *= (A &&a, B &&b)
|
|
{
|
|
return assignment<
|
|
std::decay_t<A>,
|
|
std::decay_t<B>
|
|
>::eval (std::multiplies{}, a, b);
|
|
}
|
|
|
|
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
|
|
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
|
|
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
|
|
>
|
|
>
|
|
constexpr auto
|
|
operator /= (A &&a, B &&b)
|
|
{
|
|
return assignment<
|
|
std::decay_t<A>,
|
|
std::decay_t<B>
|
|
>::eval (std::divides{}, a, b);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// unary operators
|
|
#define UNARY_OP(OP) \
|
|
template < \
|
|
typename K, \
|
|
typename = std::enable_if_t< \
|
|
is_coord_v<K>, void \
|
|
> \
|
|
> \
|
|
constexpr \
|
|
auto \
|
|
operator OP (K k) \
|
|
{ \
|
|
using value_type = decltype( \
|
|
OP std::declval<typename K::value_type> () \
|
|
); \
|
|
\
|
|
revalue_t<K,value_type> out {}; \
|
|
\
|
|
for (std::size_t i = 0; i < K::elements; ++i) \
|
|
out[i] = OP k[i]; \
|
|
\
|
|
return out; \
|
|
}
|
|
|
|
UNARY_OP(!)
|
|
UNARY_OP(~)
|
|
UNARY_OP(+)
|
|
UNARY_OP(-)
|
|
|
|
#undef UNARY_OP
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
namespace detail {
|
|
/// invoke a function elementwise to the arguments elementwise.
|
|
///
|
|
/// \tparam ArgsT a tuple containing the (coord) arguments for the func
|
|
template <
|
|
typename RetT,
|
|
typename FuncT,
|
|
typename ArgsT,
|
|
std::size_t ...Indices
|
|
>
|
|
constexpr auto
|
|
apply (const std::index_sequence<Indices...>,
|
|
FuncT &&func,
|
|
ArgsT args) noexcept
|
|
{
|
|
using part_t = std::tuple_element_t<0,ArgsT>;
|
|
using value_t = typename part_t::value_type;
|
|
|
|
return RetT {
|
|
std::apply (
|
|
func,
|
|
::cruft::tuple::value::map (
|
|
static_cast<
|
|
const value_t& (&)(const part_t&)
|
|
> (
|
|
get<Indices,RetT>
|
|
), args
|
|
)
|
|
)...
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// invokes a function elementwise using elementwise parameters from the
|
|
// supplied arguments.
|
|
//
|
|
// equivalent to this pseduocode:
|
|
// for (int i: indices (ReturnT))
|
|
// res[i] = func (args[i]...);
|
|
// return res;
|
|
//
|
|
// forwards the arguments as a tuple to a helper function that has access
|
|
// to indices as a template parameter.
|
|
template <
|
|
typename ReturnT,
|
|
typename FuncT,
|
|
typename ...ArgT,
|
|
typename = std::enable_if_t<
|
|
// return type and arguments must be coordinates
|
|
(is_coord_v<ReturnT> && ... && is_coord_v<std::decay_t<ArgT>>) &&
|
|
// all types must be the same arity
|
|
((ReturnT::elements == std::decay_t<ArgT>::elements) && ...) &&
|
|
// all the arguments must be the same type
|
|
(std::is_same_v<
|
|
std::tuple_element_t<0,std::tuple<std::decay_t<ArgT>...>>,
|
|
std::decay_t<ArgT>
|
|
> && ...),
|
|
void
|
|
>,
|
|
typename Indices = std::make_index_sequence<ReturnT::elements>
|
|
>
|
|
constexpr auto
|
|
invoke (FuncT &&func, ArgT &&...args) noexcept
|
|
{
|
|
return detail::apply<ReturnT> (
|
|
Indices{},
|
|
std::forward<FuncT> (func),
|
|
std::tuple (std::forward<ArgT> (args)...)
|
|
);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// logic operators
|
|
namespace detail {
|
|
template <
|
|
typename K,
|
|
typename FuncT,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>,
|
|
std::size_t ...Indices
|
|
>
|
|
constexpr auto
|
|
compare (FuncT &&func, std::index_sequence<Indices...>, const K a, const K b)
|
|
{
|
|
return vector<K::elements,bool> {
|
|
std::invoke (func, a[Indices], b[Indices])...
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename FuncT,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>,
|
|
typename Indices = std::make_index_sequence<K::elements>
|
|
>
|
|
constexpr auto
|
|
compare (const K a, const K b, FuncT &&func)
|
|
{
|
|
return detail::compare (std::forward<FuncT> (func), Indices{}, a, b);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
compare (const K a, const K b)
|
|
{
|
|
return compare (a, b, std::equal_to<typename K::value_type> {});
|
|
}
|
|
|
|
|
|
/// elementwise equality operator
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr bool
|
|
operator== (const K a, const K b)
|
|
{
|
|
return all (compare (a, b, std::equal_to<typename K::value_type> {}));
|
|
}
|
|
|
|
///------------------------------------------------------------------------
|
|
/// elementwise inquality operator
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr bool
|
|
operator!= (const K a, const K b)
|
|
{
|
|
return any (compare (a, b, std::not_equal_to<typename K::value_type> {}));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr
|
|
bool
|
|
almost_zero (const K &k)
|
|
{
|
|
return std::all_of (
|
|
std::cbegin (k),
|
|
std::cend (k),
|
|
[] (auto t) { return almost_zero (t); }
|
|
);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// special operators
|
|
|
|
/// point-point subtraction giving a vector difference
|
|
template <
|
|
std::size_t S,
|
|
typename T,
|
|
typename U
|
|
>
|
|
constexpr
|
|
vector<S,std::common_type_t<T,U>>
|
|
operator- (point<S,T> a, point<S,U> b)
|
|
{
|
|
vector<S,std::common_type_t<T,U>> out {};
|
|
for (std::size_t i = 0; i < S; ++i)
|
|
out[i] = a[i] - b[i];
|
|
return out;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
std::size_t S,
|
|
typename T,
|
|
typename U,
|
|
typename = std::enable_if_t<
|
|
std::is_arithmetic<T>::value && std::is_arithmetic<U>::value,
|
|
void
|
|
>
|
|
>
|
|
constexpr
|
|
vector<S,std::common_type_t<T,U>>
|
|
operator- (U u, point<S,T> p)
|
|
{
|
|
return point<S,U> {u} - p;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <std::size_t S, typename T>
|
|
constexpr T
|
|
dot (
|
|
cruft::varray<S,T> const &a,
|
|
cruft::varray<S,T> const &b
|
|
) {
|
|
T sum = 0;
|
|
for (std::size_t i = 0; i < S; ++i)
|
|
sum += a[i] * b[i];
|
|
return sum;
|
|
}
|
|
|
|
|
|
template <
|
|
std::size_t S,
|
|
typename T,
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K> && std::is_same_v<typename K::value_type, T> && K::elements == S,
|
|
void
|
|
>
|
|
>
|
|
constexpr
|
|
T
|
|
dot (const T (&a)[S], K k)
|
|
{
|
|
return dot (a, k.data);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<A> && is_coord_v<B>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
dot (A a, B b)
|
|
{
|
|
return dot (varray (a.data), varray (b.data));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename T,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K> && std::is_same_v<T,typename K::value_type>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
dot (K a, const T (&b)[K::elements])
|
|
{
|
|
return dot (a.data, b);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
dot (const typename K::value_type (&a)[K::elements], K b)
|
|
{
|
|
return dot (a, b.data);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<has_norm_v<K>,void>
|
|
>
|
|
constexpr
|
|
auto
|
|
norm2 (const K &k)
|
|
{
|
|
return dot (k, k);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
has_norm_v<K>,
|
|
void
|
|
>
|
|
>
|
|
constexpr
|
|
auto
|
|
norm (const K &k)
|
|
{
|
|
return std::sqrt (norm2 (k));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
has_norm_v<K>,
|
|
void
|
|
>
|
|
>
|
|
constexpr
|
|
auto
|
|
normalised (const K &k)
|
|
{
|
|
CHECK_NEZ (norm (k));
|
|
return k / norm (k);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
has_norm_v<K>,
|
|
void
|
|
>
|
|
>
|
|
constexpr
|
|
bool
|
|
is_normalised (const K &k)
|
|
{
|
|
using value_type = typename K::value_type;
|
|
constexpr auto hi = value_type (1.00001);
|
|
constexpr auto lo = value_type (0.99999);
|
|
|
|
const auto n2 = norm2 (k);
|
|
return n2 < hi && n2 > lo;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr
|
|
K
|
|
abs (K k)
|
|
{
|
|
for (auto &v: k)
|
|
v = std::abs (v);
|
|
return k;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <
|
|
typename BaseT,
|
|
typename = std::enable_if_t<is_coord_v<BaseT>>
|
|
>
|
|
constexpr
|
|
auto
|
|
pow (BaseT k, float p)
|
|
{
|
|
for (auto &v: k)
|
|
v = std::pow (v, p);
|
|
return k;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// root of sum of squares
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr typename K::value_type
|
|
hypot (K k)
|
|
{
|
|
return std::sqrt (sum (k * k));
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <
|
|
typename K,
|
|
typename T,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K> && std::is_same_v<T, typename K::value_type>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
mod (K k, T t)
|
|
{
|
|
std::transform (
|
|
std::cbegin (k),
|
|
std::cend (k),
|
|
std::begin (k),
|
|
[t] (auto v) { return mod (v, t);
|
|
});
|
|
|
|
return k;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename A,
|
|
typename B,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<std::decay_t<A>> && is_coord_v<std::decay_t<B>>
|
|
>
|
|
>
|
|
constexpr auto
|
|
mod (A &&a, B &&b)
|
|
{
|
|
return arithmetic<
|
|
std::decay_t<A>,
|
|
std::decay_t<B>
|
|
>::template eval (
|
|
std::modulus{},
|
|
std::forward<A> (a),
|
|
std::forward<B> (b)
|
|
);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// trigonometric functions
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<is_coord_v<K>,void>
|
|
>
|
|
constexpr auto
|
|
sin (K k)
|
|
{
|
|
std::transform (
|
|
std::cbegin (k),
|
|
std::cend (k),
|
|
std::begin (k),
|
|
[] (auto v) { return std::sin (v); }
|
|
);
|
|
|
|
return k;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<is_coord_v<K>,void>
|
|
>
|
|
constexpr auto
|
|
cos (K k)
|
|
{
|
|
std::transform (
|
|
std::cbegin (k),
|
|
std::cend (k),
|
|
std::begin (k),
|
|
[] (auto v) { return std::cos (v); }
|
|
);
|
|
|
|
return k;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// logical element operators
|
|
|
|
/// return a coord type containing the max element at each offset
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>,
|
|
typename ...Args
|
|
>
|
|
constexpr auto
|
|
min (K a, K b, Args &&...args)
|
|
{
|
|
// the varargs must be the same types as the first two arguments
|
|
static_assert ((
|
|
... && std::is_same_v<
|
|
K,
|
|
std::decay_t<Args>
|
|
>
|
|
));
|
|
|
|
K out {};
|
|
for (std::size_t i = 0; i < K::elements; ++i)
|
|
out[i] = min (a[i], b[i], args[i]...);
|
|
return out;
|
|
}
|
|
|
|
|
|
///------------------------------------------------------------------------
|
|
// /return a coord type containing the max element at each offset
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>,
|
|
typename ...Args
|
|
>
|
|
constexpr auto
|
|
max (K a, K b, Args &&...args)
|
|
{
|
|
static_assert ((
|
|
... && std::is_same_v<
|
|
K,
|
|
std::decay_t<Args>
|
|
>
|
|
));
|
|
|
|
K out {};
|
|
for (std::size_t i = 0; i < K::elements; ++i)
|
|
out[i] = max (a[i], b[i], args[i]...);
|
|
return out;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
/// returns a coordinate type where each element has been clamped to the
|
|
/// range [lo,hi].
|
|
///
|
|
/// we specifically do not allow different coordinate types for val, lo,
|
|
/// and hi because the min and max calls are ill definied for varying
|
|
/// types (not because varying types would not be useful).
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
clamp (K k, K lo, K hi)
|
|
{
|
|
assert (all (lo <= hi));
|
|
return max (min (k, hi), lo);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
clamp (K k, typename K::value_type lo, K hi)
|
|
{
|
|
return clamp (k, K {lo}, hi);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
clamp (K k, K lo, typename K::value_type hi)
|
|
{
|
|
return clamp (k, lo, K {hi});
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
clamp (K k, typename K::value_type lo, typename K::value_type hi)
|
|
{
|
|
return clamp (k, K {lo}, K {hi});
|
|
}
|
|
|
|
|
|
///------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
min (const K &k)
|
|
{
|
|
return *std::min_element (std::cbegin (k), std::cend (k));
|
|
}
|
|
|
|
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
max (const K &k)
|
|
{
|
|
return *std::max_element (std::cbegin (k), std::cend (k));
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
sum (const K &k)
|
|
{
|
|
// DO NOT USE cruft::sum(begin, end) from maths.hpp
|
|
//
|
|
// It would be nice to use kahan summation from maths.hpp but speed
|
|
// and simplicity is more important for these fixed sized
|
|
// coordinates. Infinities tend to crop up using these classes and
|
|
// they cause a few headaches in the kahan code.
|
|
//
|
|
// So, if the user wants kahan summation they can request it
|
|
// explicitly.
|
|
|
|
return std::accumulate (std::cbegin (k), std::cend (k), typename K::value_type{0});
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<is_coord_v<K>>
|
|
>
|
|
auto
|
|
product (K const& k)
|
|
{
|
|
typename K::value_type accum = 1;
|
|
for (auto i: k)
|
|
accum *= i;
|
|
return accum;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
#define VECTOR_OP(OP) \
|
|
template < \
|
|
typename A, \
|
|
typename B, \
|
|
typename = std::enable_if_t< \
|
|
is_coord_v<A> && \
|
|
is_coord_v<B> && \
|
|
A::elements == B::elements && \
|
|
std::is_same_v< \
|
|
typename A::value_type, \
|
|
typename B::value_type \
|
|
>, \
|
|
void \
|
|
> \
|
|
> \
|
|
constexpr auto \
|
|
operator OP (const A a, const B b) \
|
|
{ \
|
|
vector<A::elements,bool> out {}; \
|
|
for (std::size_t i = 0; i < A::elements; ++i) \
|
|
out[i] = a[i] OP b[i]; \
|
|
return out; \
|
|
}
|
|
|
|
VECTOR_OP(<)
|
|
VECTOR_OP(>)
|
|
VECTOR_OP(<=)
|
|
VECTOR_OP(>=)
|
|
VECTOR_OP(&&)
|
|
VECTOR_OP(||)
|
|
|
|
#undef VECTOR_OP
|
|
|
|
|
|
#define SCALAR_OP(OP) \
|
|
template < \
|
|
typename K, \
|
|
typename U, \
|
|
typename = std::enable_if_t< \
|
|
is_coord_v<K> && \
|
|
std::is_arithmetic_v<U>, \
|
|
void \
|
|
> \
|
|
> \
|
|
constexpr auto \
|
|
operator OP (const K &k, const U u) \
|
|
{ \
|
|
vector<K::elements,bool> out {}; \
|
|
for (std::size_t i = 0; i < K::elements; ++i) \
|
|
out[i] = k[i] OP u; \
|
|
return out; \
|
|
} \
|
|
\
|
|
template < \
|
|
typename K, \
|
|
typename U, \
|
|
typename = std::enable_if_t< \
|
|
is_coord_v<K> && \
|
|
std::is_arithmetic_v<U>, \
|
|
void \
|
|
> \
|
|
> \
|
|
constexpr auto \
|
|
operator OP (const U u, const K &k) \
|
|
{ \
|
|
vector<K::elements,bool> out {}; \
|
|
for (std::size_t i = 0; i < K::elements; ++i) \
|
|
out[i] = u OP k[i]; \
|
|
return out; \
|
|
}
|
|
|
|
SCALAR_OP(<)
|
|
SCALAR_OP(>)
|
|
SCALAR_OP(<=)
|
|
SCALAR_OP(>=)
|
|
SCALAR_OP(==)
|
|
SCALAR_OP(&&)
|
|
SCALAR_OP(||)
|
|
|
|
#undef SCALAR_OP
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
namespace detail {
|
|
template <
|
|
std::size_t S,
|
|
template <std::size_t,typename> class K,
|
|
std::size_t ...I,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K<S,bool>>,
|
|
void
|
|
>
|
|
>
|
|
constexpr bool
|
|
any (const K<S,bool> k, std::index_sequence<I...>)
|
|
{
|
|
return (k[I] || ...);
|
|
}
|
|
};
|
|
|
|
|
|
///---------------------------------------------------------------------------
|
|
/// returns true if any element is true.
|
|
///
|
|
/// this function must be suitable for use in static_assert, so it must remain
|
|
/// constexpr.
|
|
///
|
|
/// we would ideally use std::any_of, but it is not constexpr.
|
|
/// we would ideally use range-for, but cbegin is not constexpr.
|
|
/// so... moar templates.
|
|
template <
|
|
std::size_t S,
|
|
template <std::size_t,typename> class K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K<S,bool>>, void
|
|
>,
|
|
typename Indices = std::make_index_sequence<S>
|
|
>
|
|
constexpr
|
|
bool
|
|
any (const K<S,bool> k)
|
|
{
|
|
return detail::any (k, Indices{});
|
|
}
|
|
|
|
|
|
///------------------------------------------------------------------------
|
|
/// returns true if the value is true.
|
|
///
|
|
/// provided so that templates may operate with the same calls for vectors
|
|
/// and scalars. eg, any (t >= 0 && t <= 1) should work for arbitrary
|
|
/// types.
|
|
constexpr
|
|
bool any (const bool val)
|
|
{
|
|
return val;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
namespace detail {
|
|
template <
|
|
std::size_t S,
|
|
template <std::size_t,typename> class K,
|
|
std::size_t ...I,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K<S,bool>>,
|
|
void
|
|
>
|
|
>
|
|
constexpr bool
|
|
all (const K<S,bool> k, std::index_sequence<I...>)
|
|
{
|
|
return (k[I] && ...);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
/// returns true if all elements are true.
|
|
///
|
|
/// this function must be suitable for use in static_assert, so it must be
|
|
/// constexpr.
|
|
///
|
|
/// we would ideally use std::all_of, but it is not constexpr.
|
|
/// we would ideally use range-for, but cbegin is not constexpr.
|
|
/// so... moar templates.
|
|
template <
|
|
std::size_t S,
|
|
template <std::size_t,typename> class K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K<S,bool>>, void
|
|
>,
|
|
typename Indices = std::make_index_sequence<S>
|
|
>
|
|
constexpr
|
|
bool
|
|
all (const K<S,bool> k)
|
|
{
|
|
return detail::all (k, Indices {});
|
|
}
|
|
|
|
|
|
///------------------------------------------------------------------------
|
|
/// returns true if the value is true.
|
|
///
|
|
/// provided so that templates may operate with the same calls for vectors
|
|
/// and scalars. eg, all (t >= 0 && t <= 1) should work for either type.
|
|
constexpr bool
|
|
all (bool val)
|
|
{
|
|
return val;
|
|
}
|
|
|
|
|
|
///------------------------------------------------------------------------
|
|
/// returns an instance of K elementwise using a when s is true, and b
|
|
/// otherwise. ie, k[i] = s[i] ? a[i] : b[i];
|
|
///
|
|
/// corresponds to the function `select' from OpenCL.
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
select (vector<K::elements,bool> s, K a, K b)
|
|
{
|
|
K k {};
|
|
for (std::size_t i = 0; i < K::elements; ++i)
|
|
k[i] = s[i] ? a[i] : b[i];
|
|
return k;
|
|
}
|
|
|
|
|
|
template <
|
|
size_t S,
|
|
typename T,
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K> && !is_coord_v<T>
|
|
>
|
|
>
|
|
constexpr auto
|
|
select (vector<S,bool> s, K a, T b)
|
|
{
|
|
return select (s, a, K {b});
|
|
}
|
|
|
|
|
|
template <
|
|
size_t S,
|
|
typename T,
|
|
typename = std::enable_if_t<!is_coord_v<T>>
|
|
>
|
|
constexpr auto
|
|
select (vector<S,bool> s, T a, T b)
|
|
{
|
|
return select (s, vector<S,T> {a}, vector<S,T> {b});
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// return the componentwise floor of the coordinate type
|
|
//
|
|
// don't use std::floor, it's _really_ slow in comparison.
|
|
//
|
|
// ideally we'd use SIMD or other more useful instructions here.
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K> && std::is_floating_point_v<typename K::value_type>
|
|
>
|
|
>
|
|
constexpr auto
|
|
floor (const K &k)
|
|
{
|
|
K out {};
|
|
std::transform (
|
|
std::cbegin (k),
|
|
std::cend (k),
|
|
std::begin (out),
|
|
[] (auto i) {
|
|
return i >= 0 ? static_cast<intmax_t> (i) : static_cast<intmax_t> (i) - 1;
|
|
}
|
|
);
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K> && std::is_floating_point_v<typename K::value_type>
|
|
>
|
|
> constexpr auto
|
|
ceil (K const &k)
|
|
{
|
|
K res {};
|
|
for (std::size_t i = 0; i < K::elements; ++i)
|
|
res[i] = std::ceil (k[i]);
|
|
return res;
|
|
}
|
|
|
|
|
|
///------------------------------------------------------------------------
|
|
/// Return the fractional part of a real value.
|
|
///
|
|
/// This is an extraordinarily naive implementation. We avoid doing
|
|
/// explicit casts here in the hope that floor and sub is more efficient
|
|
/// (ie, keeps floats as floats in registers).
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K> &&
|
|
std::is_floating_point_v<
|
|
typename K::value_type
|
|
>
|
|
>
|
|
>
|
|
constexpr auto
|
|
frac (const K k)
|
|
{
|
|
return k - floor (k);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
/// shifts all elements `num' indices to the right, setting the left-most
|
|
/// `num' indices to the value `fill'.
|
|
///
|
|
/// num must be between 0 and S. when 0 it is equivalent to an ordinary
|
|
/// fill, when S it is equivalent to a noop.
|
|
template<
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
rshift (const K k, const int num, const K fill)
|
|
{
|
|
CHECK_INCLUSIVE (num, 0, int (K::elements));
|
|
|
|
K res {};
|
|
|
|
std::copy_n (std::cbegin (k), K::elements - num, std::begin (res) + num);
|
|
std::copy_n (std::cbegin (fill), num, std::begin (res));
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template<
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>, void
|
|
>
|
|
>
|
|
constexpr auto
|
|
rshift (const K k, const int num, typename K::value_type fill)
|
|
{
|
|
return rshift (k, num, K {fill});
|
|
}
|
|
|
|
|
|
template <
|
|
typename K,
|
|
typename = std::enable_if_t<
|
|
is_coord_v<K>
|
|
>
|
|
>
|
|
constexpr auto
|
|
lshift (const K k, const int places, typename K::value_type fill)
|
|
{
|
|
K res {};
|
|
std::copy_n (std::cbegin (k) + places, K::elements - places, std::begin (res));
|
|
std::fill_n (std::begin (res) + K::elements - places, places, fill);
|
|
return res;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
std::size_t S,
|
|
typename T
|
|
>
|
|
vector<S,T>
|
|
to_radians (vector<S,T> const &val)
|
|
{
|
|
return ::cruft::invoke<vector<S,T>> (
|
|
::cruft::sin<T>,
|
|
val
|
|
);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
std::size_t S,
|
|
typename T,
|
|
typename = std::enable_if_t<std::is_floating_point_v<T>>
|
|
>
|
|
vector<S,bool>
|
|
isfinite (vector<S,T> const &val)
|
|
{
|
|
vector<S,bool> res;
|
|
for (std::size_t i = 0; i < S; ++i)
|
|
res[i] = std::isfinite (val[i]);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include <tuple>
|
|
|
|
namespace std {
|
|
/// returns the dimensions of a coordinate type.
|
|
///
|
|
/// specifically required for structured bindings support.
|
|
///
|
|
/// \tparam S dimensions
|
|
/// \tparam T data type
|
|
/// \tparam K coordinate class
|
|
template <
|
|
std::size_t S,
|
|
typename T,
|
|
template <std::size_t,typename> typename K
|
|
>
|
|
class tuple_size<K<S,T>> : public std::enable_if_t<
|
|
::cruft::is_coord_v<K<S,T>>,
|
|
std::integral_constant<std::size_t, S>
|
|
> { };
|
|
|
|
|
|
/// indicates the type at a given index of a coordinate type
|
|
///
|
|
/// specifically required for structured bindings support.
|
|
///
|
|
/// \tparam I data index
|
|
/// \tparam S dimensionality of the coordinate
|
|
/// \tparam T data type for the coordinate
|
|
/// \tparam K the underlying coordinate class
|
|
template <
|
|
std::size_t I,
|
|
std::size_t S,
|
|
typename T,
|
|
template <std::size_t,typename> typename K
|
|
>
|
|
class tuple_element<I,K<S,T>> : public std::enable_if<
|
|
::cruft::is_coord_v<K<S,T>>,
|
|
T
|
|
> {};
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "../hash.hpp"
|
|
|
|
|
|
namespace std {
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
std::size_t S,
|
|
typename T,
|
|
template <std::size_t,typename> typename K
|
|
>
|
|
struct hash<K<S,T>> : enable_if<
|
|
::cruft::is_coord_v<K<S,T>>
|
|
> {
|
|
constexpr std::size_t
|
|
operator() (K<S,T> k) const noexcept
|
|
{
|
|
std::size_t v = 0xdeadbeef;
|
|
for (T const &t: k)
|
|
v = ::cruft::hash::mix (t, v);
|
|
return v;
|
|
}
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename CoordT,
|
|
typename = std::enable_if_t<
|
|
::cruft::is_coord_v<CoordT>, void
|
|
>
|
|
>
|
|
auto cos (CoordT val)
|
|
{
|
|
return ::cruft::invoke<CoordT> (::cruft::cos<typename CoordT::value_type>, val);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <
|
|
typename CoordT,
|
|
typename = std::enable_if_t<
|
|
::cruft::is_coord_v<CoordT>, void
|
|
>
|
|
>
|
|
auto sin (CoordT val)
|
|
{
|
|
return ::cruft::invoke<CoordT> (::cruft::sin<typename CoordT::value_type>, val);
|
|
}
|
|
};
|