/* * 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 */ #pragma once #include "fwd.hpp" #include "traits.hpp" #include "../array/varray.hpp" // we specifically rely on vector to compute a few logical operations #include "../vector.hpp" #include "../tuple/value.hpp" #include "../debug/assert.hpp" #include "../maths.hpp" #include #include #include #include #include #include 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, 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 && 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 struct assignment {}; //------------------------------------------------------------------------- template struct assignment< CoordA, CoordB, std::enable_if_t< is_coord_v && is_coord_v && arity_v == arity_v && std::is_same_v< typename CoordA::value_type, std::common_type_t< typename CoordA::value_type, typename CoordB::value_type > > , void > > { template 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 && !is_coord_v && has_scalar_op_v && 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 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, \ _T \ >; \ \ return KLASS { \ std::forward (args)... \ }; \ } MAKE_COORD(extent) MAKE_COORD(point) MAKE_COORD(vector) #undef MAKE_COORD template < template class K, typename ...Args > constexpr auto make_coord (Args &&...args) { using T = std::common_type_t; return K { std::forward (args)... }; } /////////////////////////////////////////////////////////////////////////// template struct arithmetic {}; //------------------------------------------------------------------------- template struct arithmetic< CoordA, CoordB, std::enable_if_t< is_coord_v && is_coord_v && arity_v == arity_v && has_result_v , void > > { template 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,common_t> out {}; for (size_t i = 0; i < CoordA::elements; ++i) out[i] = op (a[i], b[i]); return out; } }; //------------------------------------------------------------------------- template struct arithmetic< CoordT, ScalarT, std::enable_if_t< is_coord_v && std::is_arithmetic_v && has_scalar_op_v, void > > { template static constexpr auto eval (OperationT &&op, const CoordT &coord, const ScalarT &scalar) { using common_t = std::common_type_t; revalue_t out {}; for (size_t i = 0; i < CoordT::elements; ++i) out[i] = op (coord[i], scalar); return out; } }; //------------------------------------------------------------------------- template struct arithmetic< ScalarT, CoordT, std::enable_if_t< is_coord_v && std::is_arithmetic_v && has_scalar_op_v, void > > { template static constexpr auto eval (OperationT &&op, const ScalarT &scalar, const CoordT &coord) { using common_t = std::common_type_t; revalue_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> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator+ (A &&a, B &&b) { return arithmetic, std::decay_t>::eval (std::plus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator- (A &&a, B &&b) { return arithmetic, std::decay_t>::eval (std::minus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator* (A &&a, B &&b) { return arithmetic< std::decay_t, std::decay_t >::eval (std::multiplies{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator/ (A &&a, B &&b) { return arithmetic, std::decay_t>::eval (std::divides{}, a, b); } //------------------------------------------------------------------------- template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator += (A &&a, B &&b) { return assignment< std::decay_t, std::decay_t >::eval (std::plus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator -= (A &&a, B &&b) { return assignment< std::decay_t, std::decay_t >::eval (std::minus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator *= (A &&a, B &&b) { return assignment< std::decay_t, std::decay_t >::eval (std::multiplies{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator /= (A &&a, B &&b) { return assignment< std::decay_t, std::decay_t >::eval (std::divides{}, a, b); } /////////////////////////////////////////////////////////////////////////// // unary operators #define UNARY_OP(OP) \ template < \ typename K, \ typename = std::enable_if_t< \ is_coord_v, void \ > \ > \ constexpr \ auto \ operator OP (K k) \ { \ using value_type = decltype( \ OP std::declval () \ ); \ \ revalue_t 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, 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 ), 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 && ... && is_coord_v>) && // all types must be the same arity ((ReturnT::elements == std::decay_t::elements) && ...) && // all the arguments must be the same type (std::is_same_v< std::tuple_element_t<0,std::tuple...>>, std::decay_t > && ...), void >, typename Indices = std::make_index_sequence > constexpr auto invoke (FuncT &&func, ArgT &&...args) noexcept { return detail::apply ( Indices{}, std::forward (func), std::tuple (std::forward (args)...) ); } /////////////////////////////////////////////////////////////////////////// // logic operators namespace detail { template < typename K, typename FuncT, typename = std::enable_if_t< is_coord_v, void >, std::size_t ...Indices > constexpr auto compare (FuncT &&func, std::index_sequence, const K a, const K b) { return vector { std::invoke (func, a[Indices], b[Indices])... }; } } //------------------------------------------------------------------------- template < typename K, typename FuncT, typename = std::enable_if_t< is_coord_v, void >, typename Indices = std::make_index_sequence > constexpr auto compare (const K a, const K b, FuncT &&func) { return detail::compare (std::forward (func), Indices{}, a, b); } //------------------------------------------------------------------------- template < typename K, typename = std::enable_if_t< is_coord_v, void > > constexpr auto compare (const K a, const K b) { return compare (a, b, std::equal_to {}); } /// elementwise equality operator template < typename K, typename = std::enable_if_t< is_coord_v, void > > constexpr bool operator== (const K a, const K b) { return all (compare (a, b, std::equal_to {})); } ///------------------------------------------------------------------------ /// elementwise inquality operator template < typename K, typename = std::enable_if_t< is_coord_v, void > > constexpr bool operator!= (const K a, const K b) { return any (compare (a, b, std::not_equal_to {})); } //------------------------------------------------------------------------- template < typename K, typename = std::enable_if_t< is_coord_v, 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> operator- (point a, point b) { vector> 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::value && std::is_arithmetic::value, void > > constexpr vector> operator- (U u, point p) { return point {u} - p; } /////////////////////////////////////////////////////////////////////////// template constexpr T dot ( cruft::varray const &a, cruft::varray 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 && std::is_same_v && 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 && is_coord_v, 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 && std::is_same_v, 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, 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,void> > constexpr auto norm2 (const K &k) { return dot (k, k); } //------------------------------------------------------------------------- template < typename K, typename = std::enable_if_t< has_norm_v, void > > constexpr auto norm (const K &k) { return std::sqrt (norm2 (k)); } //------------------------------------------------------------------------- template < typename K, typename = std::enable_if_t< has_norm_v, 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, 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, void > > constexpr K abs (K k) { for (auto &v: k) v = std::abs (v); return k; } /////////////////////////////////////////////////////////////////////////// template < typename BaseT, typename = std::enable_if_t> > 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, 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 && std::is_same_v, 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> && is_coord_v> > > constexpr auto mod (A &&a, B &&b) { return arithmetic< std::decay_t, std::decay_t >::eval ( std::modulus{}, std::forward (a), std::forward (b) ); } /////////////////////////////////////////////////////////////////////////// // trigonometric functions template < typename K, typename = std::enable_if_t,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,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, 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 > )); 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, void >, typename ...Args > constexpr auto max (K a, K b, Args &&...args) { static_assert (( ... && std::is_same_v< K, std::decay_t > )); 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, 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, 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, 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, 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, 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, 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, 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> > 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 && \ is_coord_v && \ 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 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 && \ std::is_arithmetic_v, \ void \ > \ > \ constexpr auto \ operator OP (const K &k, const U u) \ { \ vector 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 && \ std::is_arithmetic_v, \ void \ > \ > \ constexpr auto \ operator OP (const U u, const K &k) \ { \ vector 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 class K, std::size_t ...I, typename = std::enable_if_t< is_coord_v>, void > > constexpr bool any (const K k, std::index_sequence) { 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 class K, typename = std::enable_if_t< is_coord_v>, void >, typename Indices = std::make_index_sequence > constexpr bool any (const K 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 class K, std::size_t ...I, typename = std::enable_if_t< is_coord_v>, void > > constexpr bool all (const K k, std::index_sequence) { 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 class K, typename = std::enable_if_t< is_coord_v>, void >, typename Indices = std::make_index_sequence > constexpr bool all (const K 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, void > > constexpr auto select (vector 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 && !is_coord_v > > constexpr auto select (vector s, K a, T b) { return select (s, a, K {b}); } template < size_t S, typename T, typename = std::enable_if_t> > constexpr auto select (vector s, T a, T b) { return select (s, vector {a}, vector {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 && std::is_floating_point_v > > 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 (i) : static_cast (i) - 1; } ); return out; } //------------------------------------------------------------------------- template < typename K, typename = std::enable_if_t< is_coord_v && std::is_floating_point_v > > 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 && 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, 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, 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 > > 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 to_radians (vector const &val) { return ::cruft::invoke> ( ::cruft::sin, val ); } //------------------------------------------------------------------------- template < std::size_t S, typename T, typename = std::enable_if_t> > vector isfinite (vector const &val) { vector res; for (std::size_t i = 0; i < S; ++i) res[i] = std::isfinite (val[i]); return res; } } /////////////////////////////////////////////////////////////////////////////// #include 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 typename K > class tuple_size> : public std::enable_if_t< ::cruft::is_coord_v>, std::integral_constant > { }; /// 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 typename K > class tuple_element> : public std::enable_if< ::cruft::is_coord_v>, T > {}; } /////////////////////////////////////////////////////////////////////////////// #include "../hash.hpp" namespace std { //------------------------------------------------------------------------- template < std::size_t S, typename T, template typename K > struct hash> : enable_if< ::cruft::is_coord_v> > { constexpr std::size_t operator() (K 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, void > > auto cos (CoordT val) { return ::cruft::invoke (::cruft::cos, val); } //------------------------------------------------------------------------- template < typename CoordT, typename = std::enable_if_t< ::cruft::is_coord_v, void > > auto sin (CoordT val) { return ::cruft::invoke (::cruft::sin, val); } };