Vc  1.1.0
SIMD Vector Classes for C++
simdarray.h
1 /* This file is part of the Vc library. {{{
2 Copyright © 2013-2015 Matthias Kretz <kretz@kde.org>
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the names of contributing organizations nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 }}}*/
28 
29 #ifndef VC_COMMON_SIMDARRAY_H_
30 #define VC_COMMON_SIMDARRAY_H_
31 
32 //#define Vc_DEBUG_SIMD_CAST 1
33 //#define Vc_DEBUG_SORTED 1
34 #if defined Vc_DEBUG_SIMD_CAST || defined Vc_DEBUG_SORTED
35 #include <Vc/IO>
36 #endif
37 
38 #include <array>
39 
40 #include "writemaskedvector.h"
41 #include "simdarrayhelper.h"
42 #include "simdmaskarray.h"
43 #include "utility.h"
44 #include "interleave.h"
45 #include "indexsequence.h"
46 #include "transpose.h"
47 #include "macros.h"
48 
49 namespace Vc_VERSIONED_NAMESPACE
50 {
51 // internal namespace (product & sum helper) {{{1
52 namespace internal
53 {
54 template <typename T> T Vc_INTRINSIC Vc_PURE product_helper_(const T &l, const T &r) { return l * r; }
55 template <typename T> T Vc_INTRINSIC Vc_PURE sum_helper_(const T &l, const T &r) { return l + r; }
56 } // namespace internal
57 
58 // min & max declarations {{{1
59 template <typename T, std::size_t N, typename V, std::size_t M>
60 inline SimdArray<T, N, V, M> min(const SimdArray<T, N, V, M> &x,
61  const SimdArray<T, N, V, M> &y);
62 template <typename T, std::size_t N, typename V, std::size_t M>
63 inline SimdArray<T, N, V, M> max(const SimdArray<T, N, V, M> &x,
64  const SimdArray<T, N, V, M> &y);
65 
66 // SimdArray class {{{1
69 
70 // atomic SimdArray {{{1
71 #define Vc_CURRENT_CLASS_NAME SimdArray
72 
81 template <typename T, std::size_t N, typename VectorType_>
82 class alignas(
84  ((Common::nextPowerOfTwo(N) * (sizeof(VectorType_) / VectorType_::size()) - 1) & 127) +
86  1) SimdArray<T, N, VectorType_, N>
87 {
88  static_assert(std::is_same<T, double>::value || std::is_same<T, float>::value ||
89  std::is_same<T, int32_t>::value ||
90  std::is_same<T, uint32_t>::value ||
91  std::is_same<T, int16_t>::value ||
92  std::is_same<T, uint16_t>::value,
93  "SimdArray<T, N> may only be used with T = { double, float, int32_t, uint32_t, "
94  "int16_t, uint16_t }");
95 
96 public:
97  using VectorType = VectorType_;
98  using vector_type = VectorType;
99  using storage_type = vector_type;
100  using vectorentry_type = typename vector_type::VectorEntryType;
101  using value_type = T;
102  using mask_type = SimdMaskArray<T, N, vector_type>;
103  using index_type = SimdArray<int, N>;
104  static constexpr std::size_t size() { return N; }
105  using Mask = mask_type;
106  using MaskType = Mask;
107  using MaskArgument = const MaskType &;
108  using VectorEntryType = vectorentry_type;
109  using EntryType = value_type;
110  using IndexType = index_type;
111  using AsArg = const SimdArray &;
112  static constexpr std::size_t Size = size();
113  static constexpr std::size_t MemoryAlignment = storage_type::MemoryAlignment;
114 
115  // zero init
116  Vc_INTRINSIC SimdArray() = default;
117 
118  // default copy ctor/operator
119  Vc_INTRINSIC SimdArray(const SimdArray &) = default;
120  Vc_INTRINSIC SimdArray(SimdArray &&) = default;
121  Vc_INTRINSIC SimdArray &operator=(const SimdArray &) = default;
122 
123  // broadcast
124  Vc_INTRINSIC SimdArray(const value_type &a) : data(a) {}
125  Vc_INTRINSIC SimdArray(value_type &a) : data(a) {}
126  Vc_INTRINSIC SimdArray(value_type &&a) : data(a) {}
127  template <
128  typename U,
129  typename = enable_if<std::is_same<U, int>::value && !std::is_same<int, value_type>::value>>
130  Vc_INTRINSIC SimdArray(U a)
131  : SimdArray(static_cast<value_type>(a))
132  {
133  }
134 
135  // implicit casts
136  template <typename U, typename V>
137  Vc_INTRINSIC SimdArray(const SimdArray<U, N, V> &x, enable_if<N == V::size()> = nullarg)
138  : data(simd_cast<vector_type>(internal_data(x)))
139  {
140  }
141  template <typename U, typename V>
142  Vc_INTRINSIC SimdArray(const SimdArray<U, N, V> &x,
143  enable_if<(N > V::size() && N <= 2 * V::size())> = nullarg)
144  : data(simd_cast<vector_type>(internal_data(internal_data0(x)), internal_data(internal_data1(x))))
145  {
146  }
147  template <typename U, typename V>
148  Vc_INTRINSIC SimdArray(const SimdArray<U, N, V> &x,
149  enable_if<(N > 2 * V::size() && N <= 4 * V::size())> = nullarg)
150  : data(simd_cast<vector_type>(internal_data(internal_data0(internal_data0(x))),
151  internal_data(internal_data1(internal_data0(x))),
152  internal_data(internal_data0(internal_data1(x))),
153  internal_data(internal_data1(internal_data1(x)))))
154  {
155  }
156 
157  template <typename V, std::size_t Pieces, std::size_t Index>
158  Vc_INTRINSIC SimdArray(Common::Segment<V, Pieces, Index> &&x)
159  : data(simd_cast<vector_type, Index>(x.data))
160  {
161  }
162 
163  Vc_INTRINSIC SimdArray(const std::initializer_list<value_type> &init)
164  : data(init.begin(), Vc::Unaligned)
165  {
166 #if defined Vc_CXX14 && 0 // doesn't compile yet
167  static_assert(init.size() == size(), "The initializer_list argument to "
168  "SimdArray<T, N> must contain exactly N "
169  "values.");
170 #else
171  Vc_ASSERT(init.size() == size());
172 #endif
173  }
174 
175  // implicit conversion from underlying vector_type
176  template <
177  typename V,
178  typename = enable_if<Traits::is_simd_vector<V>::value && !Traits::isSimdArray<V>::value>>
179  explicit Vc_INTRINSIC SimdArray(const V &x)
180  : data(simd_cast<vector_type>(x))
181  {
182  }
183 
184  // implicit conversion to Vector<U, AnyAbi> for if Vector<U, AnyAbi>::size() == N and
185  // T implicitly convertible to U
186  template <typename V,
187  typename = enable_if<
189  std::is_convertible<T, typename V::EntryType>::value && V::size() == N>>
190  Vc_INTRINSIC operator V() const
191  {
192  return simd_cast<V>(*this);
193  }
194 
195 #include "gatherinterface.h"
196 
197  // forward all remaining ctors
198  template <typename... Args,
199  typename = enable_if<!Traits::is_cast_arguments<Args...>::value &&
200  !Traits::is_gather_signature<Args...>::value &&
201  !Traits::is_initializer_list<Args...>::value>>
202  explicit Vc_INTRINSIC SimdArray(Args &&... args)
203  : data(std::forward<Args>(args)...)
204  {
205  }
206 
207  template <std::size_t Offset>
208  explicit Vc_INTRINSIC SimdArray(
209  Common::AddOffset<VectorSpecialInitializerIndexesFromZero, Offset>)
210  : data(Vc::IndexesFromZero)
211  {
212  data += value_type(Offset);
213  }
214 
215  Vc_INTRINSIC void setZero() { data.setZero(); }
216  Vc_INTRINSIC void setZero(mask_type k) { data.setZero(internal_data(k)); }
217  Vc_INTRINSIC void setZeroInverted() { data.setZeroInverted(); }
218  Vc_INTRINSIC void setZeroInverted(mask_type k) { data.setZeroInverted(internal_data(k)); }
219 
220  Vc_INTRINSIC void setQnan() { data.setQnan(); }
221  Vc_INTRINSIC void setQnan(mask_type m) { data.setQnan(internal_data(m)); }
223  // internal: execute specified Operation
224  template <typename Op, typename... Args>
225  static Vc_INTRINSIC SimdArray fromOperation(Op op, Args &&... args)
226  {
227  SimdArray r;
228  Common::unpackArgumentsAuto(op, r.data, std::forward<Args>(args)...);
229  return r;
230  }
231 
232  template <typename Op, typename... Args>
233  static Vc_INTRINSIC void callOperation(Op op, Args &&... args)
234  {
235  Common::unpackArgumentsAuto(op, nullptr, std::forward<Args>(args)...);
236  }
237 
238  static Vc_INTRINSIC SimdArray Zero()
239  {
240  return SimdArray(Vc::Zero);
241  }
242  static Vc_INTRINSIC SimdArray One()
243  {
244  return SimdArray(Vc::One);
245  }
246  static Vc_INTRINSIC SimdArray IndexesFromZero()
247  {
248  return SimdArray(Vc::IndexesFromZero);
249  }
250  static Vc_INTRINSIC SimdArray Random()
251  {
252  return fromOperation(Common::Operations::random());
253  }
255  template <typename... Args> Vc_INTRINSIC void load(Args &&... args)
256  {
257  data.load(std::forward<Args>(args)...);
258  }
259 
260  template <typename... Args> Vc_INTRINSIC void store(Args &&... args) const
261  {
262  data.store(std::forward<Args>(args)...);
263  }
264 
265  Vc_INTRINSIC mask_type operator!() const
266  {
267  return {!data};
268  }
269 
270  Vc_INTRINSIC SimdArray operator-() const
271  {
272  return {-data};
273  }
274 
276  Vc_INTRINSIC SimdArray operator+() const { return *this; }
278  Vc_INTRINSIC SimdArray operator~() const
279  {
280  return {~data};
281  }
282 
283  template <typename U,
284  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
285  Vc_INTRINSIC Vc_CONST SimdArray operator<<(U x) const
286  {
287  return {data << x};
288  }
289  template <typename U,
290  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
291  Vc_INTRINSIC SimdArray &operator<<=(U x)
292  {
293  data <<= x;
294  return *this;
295  }
296  template <typename U,
297  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
298  Vc_INTRINSIC Vc_CONST SimdArray operator>>(U x) const
299  {
300  return {data >> x};
301  }
302  template <typename U,
303  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
304  Vc_INTRINSIC SimdArray &operator>>=(U x)
305  {
306  data >>= x;
307  return *this;
308  }
309 
310 #define Vc_BINARY_OPERATOR_(op) \
311  Vc_INTRINSIC Vc_CONST SimdArray operator op(const SimdArray &rhs) const \
312  { \
313  return {data op rhs.data}; \
314  } \
315  Vc_INTRINSIC SimdArray &operator op##=(const SimdArray &rhs) \
316  { \
317  data op## = rhs.data; \
318  return *this; \
319  }
320  Vc_ALL_ARITHMETICS(Vc_BINARY_OPERATOR_)
321  Vc_ALL_BINARY(Vc_BINARY_OPERATOR_)
322  Vc_ALL_SHIFTS(Vc_BINARY_OPERATOR_)
323 #undef Vc_BINARY_OPERATOR_
325 #define Vc_COMPARES(op) \
326  Vc_INTRINSIC mask_type operator op(const SimdArray &rhs) const \
327  { \
328  return {data op rhs.data}; \
329  }
330  Vc_ALL_COMPARES(Vc_COMPARES)
331 #undef Vc_COMPARES
332 
334  Vc_INTRINSIC Vc_DEPRECATED("use isnegative(x) instead") MaskType isNegative() const
335  {
336  return {isnegative(data)};
337  }
338 
339  Vc_INTRINSIC decltype(std::declval<vector_type &>()[0]) operator[](std::size_t i)
340  {
341  return data[i];
342  }
343  Vc_INTRINSIC value_type operator[](std::size_t i) const { return data[i]; }
344 
345  Vc_INTRINSIC Common::WriteMaskedVector<SimdArray, mask_type> operator()(const mask_type &k)
346  {
347  return {this, k};
348  }
350  Vc_INTRINSIC void assign(const SimdArray &v, const mask_type &k)
351  {
352  data.assign(v.data, internal_data(k));
353  }
354 
355  // reductions ////////////////////////////////////////////////////////
356 #define Vc_REDUCTION_FUNCTION_(name_) \
357  Vc_INTRINSIC Vc_PURE value_type name_() const { return data.name_(); } \
358  Vc_INTRINSIC Vc_PURE value_type name_(mask_type mask) const \
359  { \
360  return data.name_(internal_data(mask)); \
361  }
362  Vc_REDUCTION_FUNCTION_(min)
363  Vc_REDUCTION_FUNCTION_(max)
364  Vc_REDUCTION_FUNCTION_(product)
365  Vc_REDUCTION_FUNCTION_(sum)
366 #undef Vc_REDUCTION_FUNCTION_
367  Vc_INTRINSIC Vc_PURE SimdArray partialSum() const { return data.partialSum(); }
368 
369  Vc_INTRINSIC void fusedMultiplyAdd(const SimdArray &factor, const SimdArray &summand)
370  {
371  data.fusedMultiplyAdd(internal_data(factor), internal_data(summand));
372  }
373 
374  template <typename F> Vc_INTRINSIC SimdArray apply(F &&f) const
375  {
376  return {data.apply(std::forward<F>(f))};
377  }
378  template <typename F> Vc_INTRINSIC SimdArray apply(F &&f, const mask_type &k) const
379  {
380  return {data.apply(std::forward<F>(f), k)};
381  }
382 
383  Vc_INTRINSIC SimdArray shifted(int amount) const
384  {
385  return {data.shifted(amount)};
386  }
387 
388  template <std::size_t NN>
389  Vc_INTRINSIC SimdArray shifted(int amount, const SimdArray<value_type, NN> &shiftIn)
390  const
391  {
392  return {data.shifted(amount, simd_cast<VectorType>(shiftIn))};
393  }
394 
395  Vc_INTRINSIC SimdArray rotated(int amount) const
396  {
397  return {data.rotated(amount)};
398  }
399 
401  Vc_INTRINSIC Vc_DEPRECATED("use exponent(x) instead") SimdArray exponent() const
402  {
403  return {exponent(data)};
404  }
405 
406  Vc_INTRINSIC SimdArray interleaveLow(SimdArray x) const
407  {
408  return {data.interleaveLow(x.data)};
409  }
410  Vc_INTRINSIC SimdArray interleaveHigh(SimdArray x) const
411  {
412  return {data.interleaveHigh(x.data)};
413  }
414 
415  Vc_INTRINSIC SimdArray reversed() const
416  {
417  return {data.reversed()};
418  }
419 
420  Vc_INTRINSIC SimdArray sorted() const
421  {
422  return {data.sorted()};
423  }
424 
425  template <typename G> static Vc_INTRINSIC SimdArray generate(const G &gen)
426  {
427  return {VectorType::generate(gen)};
428  }
429 
430  Vc_INTRINSIC Vc_DEPRECATED("use copysign(x, y) instead") SimdArray
431  copySign(const SimdArray &reference) const
432  {
433  return {Vc::copysign(data, reference.data)};
434  }
435 
436  friend VectorType &internal_data<>(SimdArray &x);
437  friend const VectorType &internal_data<>(const SimdArray &x);
438 
440  Vc_INTRINSIC SimdArray(VectorType &&x) : data(std::move(x)) {}
441 private:
442  storage_type data;
443 };
444 template <typename T, std::size_t N, typename VectorType> constexpr std::size_t SimdArray<T, N, VectorType, N>::Size;
445 template <typename T, std::size_t N, typename VectorType>
447 template <typename T, std::size_t N, typename VectorType>
448 Vc_INTRINSIC VectorType &internal_data(SimdArray<T, N, VectorType, N> &x)
449 {
450  return x.data;
451 }
452 template <typename T, std::size_t N, typename VectorType>
453 Vc_INTRINSIC const VectorType &internal_data(const SimdArray<T, N, VectorType, N> &x)
454 {
455  return x.data;
456 }
457 
458 // gatherImplementation {{{2
459 template <typename T, std::size_t N, typename VectorType>
460 template <typename MT, typename IT>
461 inline void SimdArray<T, N, VectorType, N>::gatherImplementation(const MT *mem,
462  IT &&indexes)
463 {
464  data.gather(mem, std::forward<IT>(indexes));
465 }
466 template <typename T, std::size_t N, typename VectorType>
467 template <typename MT, typename IT>
468 inline void SimdArray<T, N, VectorType, N>::gatherImplementation(const MT *mem,
469  IT &&indexes,
470  MaskArgument mask)
471 {
472  data.gather(mem, std::forward<IT>(indexes), mask);
473 }
474 
475 // generic SimdArray {{{1
480 template <typename T, std::size_t N, typename VectorType, std::size_t>
481 class alignas(
483  ((Common::nextPowerOfTwo(N) * (sizeof(VectorType) / VectorType::size()) - 1) & 127) +
485  1) SimdArray
486 {
487  static_assert(std::is_same<T, double>::value ||
488  std::is_same<T, float>::value ||
489  std::is_same<T, int32_t>::value ||
490  std::is_same<T, uint32_t>::value ||
491  std::is_same<T, int16_t>::value ||
492  std::is_same<T, uint16_t>::value, "SimdArray<T, N> may only be used with T = { double, float, int32_t, uint32_t, int16_t, uint16_t }");
493  static_assert(
494  // either the EntryType and VectorEntryType of the main VectorType are equal
495  std::is_same<typename VectorType::EntryType,
496  typename VectorType::VectorEntryType>::value ||
497  // or N is a multiple of VectorType::size()
498  (N % VectorType::size() == 0),
499  "SimdArray<(un)signed short, N> on MIC only works correctly for N = k * "
500  "MIC::(u)short_v::size(), i.e. k * 16.");
501 
502  using my_traits = SimdArrayTraits<T, N>;
503  static constexpr std::size_t N0 = my_traits::N0;
504  static constexpr std::size_t N1 = my_traits::N1;
505  using Split = Common::Split<N0>;
506  template <typename U, std::size_t K> using CArray = U[K];
507 
508 public:
509  using storage_type0 = typename my_traits::storage_type0;
510  using storage_type1 = typename my_traits::storage_type1;
511  static_assert(storage_type0::size() == N0, "");
512 
513  using vector_type = VectorType;
514  using vectorentry_type = typename storage_type0::vectorentry_type;
515  typedef vectorentry_type alias_type Vc_MAY_ALIAS;
516  using value_type = T;
517  using mask_type = SimdMaskArray<T, N, vector_type>;
519  static constexpr std::size_t size() { return N; }
520  using Mask = mask_type;
521  using MaskType = Mask;
522  using MaskArgument = const MaskType &;
523  using VectorEntryType = vectorentry_type;
524  using EntryType = value_type;
525  using IndexType = index_type;
526  using AsArg = const SimdArray &;
527  static constexpr std::size_t Size = size();
528  static constexpr std::size_t MemoryAlignment =
532 
534 
535  // zero init
536  SimdArray() = default;
537 
538  // default copy ctor/operator
539  SimdArray(const SimdArray &) = default;
540  SimdArray(SimdArray &&) = default;
541  SimdArray &operator=(const SimdArray &) = default;
542 
543  // broadcast
544  Vc_INTRINSIC SimdArray(value_type a) : data0(a), data1(a) {}
545  template <
546  typename U,
547  typename = enable_if<std::is_same<U, int>::value && !std::is_same<int, value_type>::value>>
548  SimdArray(U a)
549  : SimdArray(static_cast<value_type>(a))
550  {
551  }
552 
553  // load ctor
554  template <typename U,
555  typename Flags = DefaultLoadTag,
556  typename = enable_if<Traits::is_load_store_flag<Flags>::value>>
557  explicit Vc_INTRINSIC SimdArray(const U *mem, Flags f = Flags())
558  : data0(mem, f), data1(mem + storage_type0::size(), f)
559  {
560  }
567  template <typename U, std::size_t Extent, typename Flags = DefaultLoadTag,
568  typename = enable_if<Traits::is_load_store_flag<Flags>::value>>
569  explicit Vc_INTRINSIC SimdArray(CArray<U, Extent> &mem, Flags f = Flags())
570  : data0(&mem[0], f), data1(&mem[storage_type0::size()], f)
571  {
572  }
576  template <typename U, std::size_t Extent, typename Flags = DefaultLoadTag,
577  typename = enable_if<Traits::is_load_store_flag<Flags>::value>>
578  explicit Vc_INTRINSIC SimdArray(const CArray<U, Extent> &mem, Flags f = Flags())
579  : data0(&mem[0], f), data1(&mem[storage_type0::size()], f)
580  {
581  }
582 
583  // initializer list
584  Vc_INTRINSIC SimdArray(const std::initializer_list<value_type> &init)
585  : data0(init.begin(), Vc::Unaligned)
586  , data1(init.begin() + storage_type0::size(), Vc::Unaligned)
587  {
588 #if defined Vc_CXX14 && 0 // doesn't compile yet
589  static_assert(init.size() == size(), "The initializer_list argument to "
590  "SimdArray<T, N> must contain exactly N "
591  "values.");
592 #else
593  Vc_ASSERT(init.size() == size());
594 #endif
595  }
596 
597 #include "gatherinterface.h"
598 
599  // forward all remaining ctors
600  template <typename... Args,
601  typename = enable_if<!Traits::is_cast_arguments<Args...>::value &&
602  !Traits::is_initializer_list<Args...>::value &&
603  !Traits::is_gather_signature<Args...>::value &&
604  !Traits::is_load_arguments<Args...>::value>>
605  explicit Vc_INTRINSIC SimdArray(Args &&... args)
606  : data0(Split::lo(args)...) // no forward here - it could move and thus
607  // break the next line
608  , data1(Split::hi(std::forward<Args>(args))...)
609  {
610  }
611 
612  // explicit casts
613  template <typename V>
614  Vc_INTRINSIC explicit SimdArray(
615  V &&x,
617  !(std::is_convertible<Traits::entry_type_of<V>, T>::value &&
618  Traits::isSimdArray<V>::value))> = nullarg)
619  : data0(Split::lo(x)), data1(Split::hi(x))
620  {
621  }
622 
623  // implicit casts
624  template <typename V>
625  Vc_INTRINSIC SimdArray(
626  V &&x,
628  std::is_convertible<Traits::entry_type_of<V>, T>::value)> = nullarg)
629  : data0(Split::lo(x)), data1(Split::hi(x))
630  {
631  }
632 
633  // implicit conversion to Vector<U, AnyAbi> for if Vector<U, AnyAbi>::size() == N and
634  // T implicitly convertible to U
635  template <typename V,
636  typename = enable_if<
638  std::is_convertible<T, typename V::EntryType>::value && V::size() == N>>
639  operator V() const
640  {
641  return simd_cast<V>(*this);
642  }
643 
645 
646  Vc_INTRINSIC void setZero()
647  {
648  data0.setZero();
649  data1.setZero();
650  }
651  Vc_INTRINSIC void setZero(const mask_type &k)
652  {
653  data0.setZero(Split::lo(k));
654  data1.setZero(Split::hi(k));
655  }
656  Vc_INTRINSIC void setZeroInverted()
657  {
658  data0.setZeroInverted();
659  data1.setZeroInverted();
660  }
661  Vc_INTRINSIC void setZeroInverted(const mask_type &k)
662  {
663  data0.setZeroInverted(Split::lo(k));
664  data1.setZeroInverted(Split::hi(k));
665  }
666 
667 
668  Vc_INTRINSIC void setQnan() {
669  data0.setQnan();
670  data1.setQnan();
671  }
672  Vc_INTRINSIC void setQnan(const mask_type &m) {
673  data0.setQnan(Split::lo(m));
674  data1.setQnan(Split::hi(m));
675  }
676 
678  template <typename Op, typename... Args>
679  static Vc_INTRINSIC SimdArray fromOperation(Op op, Args &&... args)
680  {
681  SimdArray r = {
682  storage_type0::fromOperation(op, Split::lo(args)...), // no forward here - it
683  // could move and thus
684  // break the next line
685  storage_type1::fromOperation(op, Split::hi(std::forward<Args>(args))...)};
686  return r;
687  }
688 
690  template <typename Op, typename... Args>
691  static Vc_INTRINSIC void callOperation(Op op, Args &&... args)
692  {
693  storage_type0::callOperation(op, Split::lo(args)...);
694  storage_type1::callOperation(op, Split::hi(std::forward<Args>(args))...);
695  }
698  static Vc_INTRINSIC SimdArray Zero()
699  {
700  return SimdArray(Vc::Zero);
701  }
704  static Vc_INTRINSIC SimdArray One()
705  {
706  return SimdArray(Vc::One);
707  }
710  static Vc_INTRINSIC SimdArray IndexesFromZero()
711  {
712  return SimdArray(Vc::IndexesFromZero);
713  }
714 
716  static Vc_INTRINSIC SimdArray Random()
717  {
718  return fromOperation(Common::Operations::random());
719  }
720 
721  template <typename U, typename... Args> Vc_INTRINSIC void load(const U *mem, Args &&... args)
722  {
723  data0.load(mem, Split::lo(args)...); // no forward here - it could move and thus
724  // break the next line
725  data1.load(mem + storage_type0::size(), Split::hi(std::forward<Args>(args))...);
726  }
727 
728  template <typename U, typename... Args> Vc_INTRINSIC void store(U *mem, Args &&... args) const
729  {
730  data0.store(mem, Split::lo(args)...); // no forward here - it could move and thus
731  // break the next line
732  data1.store(mem + storage_type0::size(), Split::hi(std::forward<Args>(args))...);
733  }
734 
735  Vc_INTRINSIC mask_type operator!() const
736  {
737  return {!data0, !data1};
738  }
739 
740  Vc_INTRINSIC SimdArray operator-() const
741  {
742  return {-data0, -data1};
743  }
744 
746  Vc_INTRINSIC SimdArray operator+() const { return *this; }
747 
748  Vc_INTRINSIC SimdArray operator~() const
749  {
750  return {~data0, ~data1};
751  }
752 
753  // left/right shift operators {{{2
754  template <typename U,
755  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
756  Vc_INTRINSIC Vc_CONST SimdArray operator<<(U x) const
757  {
758  return {data0 << x, data1 << x};
759  }
760  template <typename U,
761  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
762  Vc_INTRINSIC SimdArray &operator<<=(U x)
763  {
764  data0 <<= x;
765  data1 <<= x;
766  return *this;
767  }
768  template <typename U,
769  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
770  Vc_INTRINSIC Vc_CONST SimdArray operator>>(U x) const
771  {
772  return {data0 >> x, data1 >> x};
773  }
774  template <typename U,
775  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
776  Vc_INTRINSIC SimdArray &operator>>=(U x)
777  {
778  data0 >>= x;
779  data1 >>= x;
780  return *this;
781  }
782 
783  // binary operators {{{2
784 #define Vc_BINARY_OPERATOR_(op) \
785  Vc_INTRINSIC Vc_CONST SimdArray operator op(const SimdArray &rhs) const \
786  { \
787  return {data0 op rhs.data0, data1 op rhs.data1}; \
788  } \
789  Vc_INTRINSIC SimdArray &operator op##=(const SimdArray &rhs) \
790  { \
791  data0 op## = rhs.data0; \
792  data1 op## = rhs.data1; \
793  return *this; \
794  }
795  Vc_ALL_ARITHMETICS(Vc_BINARY_OPERATOR_)
796  Vc_ALL_BINARY(Vc_BINARY_OPERATOR_)
797  Vc_ALL_SHIFTS(Vc_BINARY_OPERATOR_)
798 #undef Vc_BINARY_OPERATOR_
799 
800 #define Vc_COMPARES(op) \
801  Vc_INTRINSIC mask_type operator op(const SimdArray &rhs) const \
802  { \
803  return {data0 op rhs.data0, data1 op rhs.data1}; \
804  }
805  Vc_ALL_COMPARES(Vc_COMPARES)
806 #undef Vc_COMPARES
807 
809  Vc_INTRINSIC MaskType isNegative() const
810  {
811  return {isnegative(data0), isnegative(data1)};
812  }
813 
814  // operator[] {{{2
815  Vc_INTRINSIC value_type operator[](std::size_t i) const
816  {
817  const auto tmp = reinterpret_cast<const alias_type *>(this);
818  return tmp[i];
819  }
820 
821  Vc_INTRINSIC alias_type &operator[](std::size_t i)
822  {
823  auto tmp = reinterpret_cast<alias_type *>(this);
824  return tmp[i];
825  }
826 
827  Vc_INTRINSIC Common::WriteMaskedVector<SimdArray, mask_type> operator()(const mask_type &k) //{{{2
828  {
829  return {this, k};
830  }
831 
832  Vc_INTRINSIC void assign(const SimdArray &v, const mask_type &k) //{{{2
833  {
834  data0.assign(v.data0, internal_data0(k));
835  data1.assign(v.data1, internal_data1(k));
836  }
837 
838  // reductions {{{2
839 #define Vc_REDUCTION_FUNCTION_(name_, binary_fun_, scalar_fun_) \
840  template <typename ForSfinae = void> \
841  Vc_INTRINSIC enable_if<std::is_same<ForSfinae, void>::value && \
842  storage_type0::size() == storage_type1::size(), \
843  value_type> \
844  name_() const \
845  { \
846  return binary_fun_(data0, data1).name_(); \
847  } \
848  \
849  template <typename ForSfinae = void> \
850  Vc_INTRINSIC enable_if<std::is_same<ForSfinae, void>::value && \
851  storage_type0::size() != storage_type1::size(), \
852  value_type> \
853  name_() const \
854  { \
855  return scalar_fun_(data0.name_(), data1.name_()); \
856  } \
857  \
858  Vc_INTRINSIC value_type name_(const mask_type &mask) const \
859  { \
860  if (Vc_IS_UNLIKELY(Split::lo(mask).isEmpty())) { \
861  return data1.name_(Split::hi(mask)); \
862  } else if (Vc_IS_UNLIKELY(Split::hi(mask).isEmpty())) { \
863  return data0.name_(Split::lo(mask)); \
864  } else { \
865  return scalar_fun_(data0.name_(Split::lo(mask)), \
866  data1.name_(Split::hi(mask))); \
867  } \
868  }
869  Vc_REDUCTION_FUNCTION_(min, Vc::min, std::min)
870  Vc_REDUCTION_FUNCTION_(max, Vc::max, std::max)
871  Vc_REDUCTION_FUNCTION_(product, internal::product_helper_, internal::product_helper_)
872  Vc_REDUCTION_FUNCTION_(sum, internal::sum_helper_, internal::sum_helper_)
873 #undef Vc_REDUCTION_FUNCTION_
874  Vc_INTRINSIC Vc_PURE SimdArray partialSum() const //{{{2
875  {
876  auto ps0 = data0.partialSum();
877  auto tmp = data1;
878  tmp[0] += ps0[data0.size() - 1];
879  return {std::move(ps0), tmp.partialSum()};
880  }
881 
882  void fusedMultiplyAdd(const SimdArray &factor, const SimdArray &summand) //{{{2
883  {
884  data0.fusedMultiplyAdd(Split::lo(factor), Split::lo(summand));
885  data1.fusedMultiplyAdd(Split::hi(factor), Split::hi(summand));
886  }
887 
888  // apply {{{2
889  template <typename F> Vc_INTRINSIC SimdArray apply(F &&f) const
890  {
891  return {data0.apply(f), data1.apply(f)};
892  }
893  template <typename F> Vc_INTRINSIC SimdArray apply(F &&f, const mask_type &k) const
894  {
895  return {data0.apply(f, Split::lo(k)), data1.apply(f, Split::hi(k))};
896  }
897 
898  // shifted {{{2
899  inline SimdArray shifted(int amount) const
900  {
901  constexpr int SSize = Size;
902  constexpr int SSize0 = storage_type0::Size;
903  constexpr int SSize1 = storage_type1::Size;
904  if (amount == 0) {
905  return *this;
906  }
907  if (amount < 0) {
908  if (amount > -SSize0) {
909  return {data0.shifted(amount), data1.shifted(amount, data0)};
910  }
911  if (amount == -SSize0) {
912  return {storage_type0::Zero(), simd_cast<storage_type1>(data0)};
913  }
914  if (amount < -SSize0) {
915  return {storage_type0::Zero(), simd_cast<storage_type1>(data0.shifted(
916  amount + SSize0))};
917  }
918  return Zero();
919  } else {
920  if (amount >= SSize) {
921  return Zero();
922  } else if (amount >= SSize0) {
923  return {
924  simd_cast<storage_type0>(data1).shifted(amount - SSize0),
926  } else if (amount >= SSize1) {
927  return {data0.shifted(amount, data1), storage_type1::Zero()};
928  } else {
929  return {data0.shifted(amount, data1), data1.shifted(amount)};
930  }
931  }
932  }
933 
934  template <std::size_t NN>
935  inline enable_if<
936  !(std::is_same<storage_type0, storage_type1>::value && // not bisectable
937  N == NN),
938  SimdArray>
939  shifted(int amount, const SimdArray<value_type, NN> &shiftIn) const
940  {
941  constexpr int SSize = Size;
942  if (amount < 0) {
943  return SimdArray::generate([&](int i) -> value_type {
944  i += amount;
945  if (i >= 0) {
946  return operator[](i);
947  } else if (i >= -SSize) {
948  return shiftIn[i + SSize];
949  }
950  return 0;
951  });
952  }
953  return SimdArray::generate([&](int i) -> value_type {
954  i += amount;
955  if (i < SSize) {
956  return operator[](i);
957  } else if (i < 2 * SSize) {
958  return shiftIn[i - SSize];
959  }
960  return 0;
961  });
962  }
963 
964  template <std::size_t NN>
965  inline
966  enable_if<(std::is_same<storage_type0, storage_type1>::value && // bisectable
967  N == NN),
968  SimdArray>
969  shifted(int amount, const SimdArray<value_type, NN> &shiftIn) const
970  {
971  constexpr int SSize = Size;
972  if (amount < 0) {
973  if (amount > -static_cast<int>(storage_type0::Size)) {
974  return {data0.shifted(amount, internal_data1(shiftIn)),
975  data1.shifted(amount, data0)};
976  }
977  if (amount == -static_cast<int>(storage_type0::Size)) {
978  return {storage_type0(internal_data1(shiftIn)), storage_type1(data0)};
979  }
980  if (amount > -SSize) {
981  return {
982  internal_data1(shiftIn)
983  .shifted(amount + static_cast<int>(storage_type0::Size), internal_data0(shiftIn)),
984  data0.shifted(amount + static_cast<int>(storage_type0::Size), internal_data1(shiftIn))};
985  }
986  if (amount == -SSize) {
987  return shiftIn;
988  }
989  if (amount > -2 * SSize) {
990  return shiftIn.shifted(amount + SSize);
991  }
992  }
993  if (amount == 0) {
994  return *this;
995  }
996  if (amount < static_cast<int>(storage_type0::Size)) {
997  return {data0.shifted(amount, data1),
998  data1.shifted(amount, internal_data0(shiftIn))};
999  }
1000  if (amount == static_cast<int>(storage_type0::Size)) {
1001  return {storage_type0(data1), storage_type1(internal_data0(shiftIn))};
1002  }
1003  if (amount < SSize) {
1004  return {data1.shifted(amount - static_cast<int>(storage_type0::Size), internal_data0(shiftIn)),
1005  internal_data0(shiftIn)
1006  .shifted(amount - static_cast<int>(storage_type0::Size), internal_data1(shiftIn))};
1007  }
1008  if (amount == SSize) {
1009  return shiftIn;
1010  }
1011  if (amount < 2 * SSize) {
1012  return shiftIn.shifted(amount - SSize);
1013  }
1014  return Zero();
1015  }
1016 
1017  // rotated {{{2
1018  Vc_INTRINSIC SimdArray rotated(int amount) const
1019  {
1020  amount %= int(size());
1021  if (amount == 0) {
1022  return *this;
1023  } else if (amount < 0) {
1024  amount += size();
1025  }
1026 
1027  auto &&d0cvtd = simd_cast<storage_type1>(data0);
1028  auto &&d1cvtd = simd_cast<storage_type0>(data1);
1029  constexpr int size0 = storage_type0::size();
1030  constexpr int size1 = storage_type1::size();
1031 
1032  if (amount == size0 && std::is_same<storage_type0, storage_type1>::value) {
1033  return {std::move(d1cvtd), std::move(d0cvtd)};
1034  } else if (amount < size1) {
1035  return {data0.shifted(amount, d1cvtd), data1.shifted(amount, d0cvtd)};
1036  } else if (amount == size1) {
1037  return {data0.shifted(amount, d1cvtd), std::move(d0cvtd)};
1038  } else if (int(size()) - amount < size1) {
1039  return {data0.shifted(amount - int(size()), d1cvtd.shifted(size1 - size0)),
1040  data1.shifted(amount - int(size()), data0.shifted(size0 - size1))};
1041  } else if (int(size()) - amount == size1) {
1042  return {data0.shifted(-size1, d1cvtd.shifted(size1 - size0)),
1043  simd_cast<storage_type1>(data0.shifted(size0 - size1))};
1044  } else if (amount <= size0) {
1045  return {data0.shifted(size1, d1cvtd).shifted(amount - size1, data0),
1046  simd_cast<storage_type1>(data0.shifted(amount - size1))};
1047  } else {
1048  return {data0.shifted(size1, d1cvtd).shifted(amount - size1, data0),
1049  simd_cast<storage_type1>(data0.shifted(amount - size1, d1cvtd))};
1050  }
1051  return *this;
1052  }
1053 
1055  Vc_INTRINSIC Vc_DEPRECATED("use exponent(x) instead") SimdArray exponent() const
1056  {
1057  return {exponent(data0), exponent(data1)};
1058  }
1059 
1060  // interleaveLow/-High {{{2
1061  Vc_INTRINSIC SimdArray interleaveLow(const SimdArray &x) const
1062  {
1063  // return data0[0], x.data0[0], data0[1], x.data0[1], ...
1064  return {data0.interleaveLow(x.data0),
1065  simd_cast<storage_type1>(data0.interleaveHigh(x.data0))};
1066  }
1067  Vc_INTRINSIC SimdArray interleaveHigh(const SimdArray &x) const
1068  {
1069  return interleaveHighImpl(
1070  x,
1071  std::integral_constant<bool, storage_type0::Size == storage_type1::Size>());
1072  }
1073 
1074 private:
1075  Vc_INTRINSIC SimdArray interleaveHighImpl(const SimdArray &x, std::true_type) const
1076  {
1077  return {data1.interleaveLow(x.data1), data1.interleaveHigh(x.data1)};
1078  }
1079  inline SimdArray interleaveHighImpl(const SimdArray &x, std::false_type) const
1080  {
1081  return {data0.interleaveHigh(x.data0)
1082  .shifted(storage_type1::Size,
1083  simd_cast<storage_type0>(data1.interleaveLow(x.data1))),
1084  data1.interleaveHigh(x.data1)};
1085  }
1086 
1087 public:
1088  inline SimdArray reversed() const //{{{2
1089  {
1090  if (std::is_same<storage_type0, storage_type1>::value) {
1091  return {simd_cast<storage_type0>(data1).reversed(),
1092  simd_cast<storage_type1>(data0).reversed()};
1093  } else {
1094  return {data0.shifted(storage_type1::Size, data1).reversed(),
1095  simd_cast<storage_type1>(data0.reversed().shifted(
1096  storage_type0::Size - storage_type1::Size))};
1097  }
1098  }
1099  inline SimdArray sorted() const //{{{2
1100  {
1101  return sortedImpl(
1102  std::integral_constant<bool, storage_type0::Size == storage_type1::Size>());
1103  }
1104 
1105  Vc_INTRINSIC SimdArray sortedImpl(std::true_type) const
1106  {
1107 #ifdef Vc_DEBUG_SORTED
1108  std::cerr << "-- " << data0 << data1 << '\n';
1109 #endif
1110  const auto a = data0.sorted();
1111  const auto b = data1.sorted().reversed();
1112  const auto lo = Vc::min(a, b);
1113  const auto hi = Vc::max(a, b);
1114  return {lo.sorted(), hi.sorted()};
1115  }
1116 
1117  Vc_INTRINSIC SimdArray sortedImpl(std::false_type) const
1118  {
1119  using SortableArray = SimdArray<value_type, Common::nextPowerOfTwo(size())>;
1120  auto sortable = simd_cast<SortableArray>(*this);
1121  for (std::size_t i = Size; i < SortableArray::Size; ++i) {
1122  using limits = std::numeric_limits<value_type>;
1123  if (limits::has_infinity) {
1124  sortable[i] = limits::infinity();
1125  } else {
1126  sortable[i] = std::numeric_limits<value_type>::max();
1127  }
1128  }
1129  return simd_cast<SimdArray>(sortable.sorted());
1130 
1131  /* The following implementation appears to be less efficient. But this may need further
1132  * work.
1133  const auto a = data0.sorted();
1134  const auto b = data1.sorted();
1135 #ifdef Vc_DEBUG_SORTED
1136  std::cerr << "== " << a << b << '\n';
1137 #endif
1138  auto aIt = Vc::begin(a);
1139  auto bIt = Vc::begin(b);
1140  const auto aEnd = Vc::end(a);
1141  const auto bEnd = Vc::end(b);
1142  return SimdArray::generate([&](std::size_t) {
1143  if (aIt == aEnd) {
1144  return *(bIt++);
1145  }
1146  if (bIt == bEnd) {
1147  return *(aIt++);
1148  }
1149  if (*aIt < *bIt) {
1150  return *(aIt++);
1151  } else {
1152  return *(bIt++);
1153  }
1154  });
1155  */
1156  }
1157 
1158  template <typename G> static Vc_INTRINSIC SimdArray generate(const G &gen) // {{{2
1159  {
1160  auto tmp = storage_type0::generate(gen); // GCC bug: the order of evaluation in
1161  // an initializer list is well-defined
1162  // (front to back), but GCC 4.8 doesn't
1163  // implement this correctly. Therefore
1164  // we enforce correct order.
1165  return {std::move(tmp),
1166  storage_type1::generate([&](std::size_t i) { return gen(i + N0); })};
1167  }
1168 
1169  Vc_INTRINSIC Vc_DEPRECATED("use copysign(x, y) instead") SimdArray
1170  copySign(const SimdArray &reference) const
1171  {
1172  return {Vc::copysign(data0, reference.data0),
1173  Vc::copysign(data1, reference.data1)};
1174  }
1175 
1176  // internal_data0/1 {{{2
1177  friend storage_type0 &internal_data0<>(SimdArray &x);
1178  friend storage_type1 &internal_data1<>(SimdArray &x);
1179  friend const storage_type0 &internal_data0<>(const SimdArray &x);
1180  friend const storage_type1 &internal_data1<>(const SimdArray &x);
1181 
1183  Vc_INTRINSIC SimdArray(storage_type0 &&x, storage_type1 &&y) //{{{2
1184  : data0(std::move(x)), data1(std::move(y))
1185  {
1186  }
1187 private: //{{{2
1188  storage_type0 data0;
1189  storage_type1 data1;
1190 };
1191 #undef Vc_CURRENT_CLASS_NAME
1192 template <typename T, std::size_t N, typename VectorType, std::size_t M> constexpr std::size_t SimdArray<T, N, VectorType, M>::Size;
1193 template <typename T, std::size_t N, typename VectorType, std::size_t M>
1195 
1196 // gatherImplementation {{{2
1197 template <typename T, std::size_t N, typename VectorType, std::size_t M>
1198 template <typename MT, typename IT>
1199 inline void SimdArray<T, N, VectorType, M>::gatherImplementation(const MT *mem,
1200  IT &&indexes)
1201 {
1202  data0.gather(mem, Split::lo(Common::Operations::gather(),
1203  indexes)); // don't forward indexes - it could move and
1204  // thus break the next line
1205  data1.gather(mem, Split::hi(Common::Operations::gather(), std::forward<IT>(indexes)));
1206 }
1207 template <typename T, std::size_t N, typename VectorType, std::size_t M>
1208 template <typename MT, typename IT>
1209 inline void SimdArray<T, N, VectorType, M>::gatherImplementation(const MT *mem,
1210  IT &&indexes, MaskArgument mask)
1211 {
1212  data0.gather(mem, Split::lo(Common::Operations::gather(), indexes),
1213  Split::lo(mask)); // don't forward indexes - it could move and
1214  // thus break the next line
1215  data1.gather(mem, Split::hi(Common::Operations::gather(), std::forward<IT>(indexes)),
1216  Split::hi(mask));
1217 }
1218 
1219 // internal_data0/1 (SimdArray) {{{1
1220 template <typename T, std::size_t N, typename V, std::size_t M>
1221 Vc_INTRINSIC typename SimdArrayTraits<T, N>::storage_type0 &internal_data0(
1222  SimdArray<T, N, V, M> &x)
1223 {
1224  return x.data0;
1225 }
1226 template <typename T, std::size_t N, typename V, std::size_t M>
1227 Vc_INTRINSIC typename SimdArrayTraits<T, N>::storage_type1 &internal_data1(
1228  SimdArray<T, N, V, M> &x)
1229 {
1230  return x.data1;
1231 }
1232 template <typename T, std::size_t N, typename V, std::size_t M>
1233 Vc_INTRINSIC const typename SimdArrayTraits<T, N>::storage_type0 &internal_data0(
1234  const SimdArray<T, N, V, M> &x)
1235 {
1236  return x.data0;
1237 }
1238 template <typename T, std::size_t N, typename V, std::size_t M>
1239 Vc_INTRINSIC const typename SimdArrayTraits<T, N>::storage_type1 &internal_data1(
1240  const SimdArray<T, N, V, M> &x)
1241 {
1242  return x.data1;
1243 }
1244 
1245 // binary operators {{{1
1246 namespace result_vector_type_internal
1247 {
1248 template <typename T>
1249 using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
1250 
1251 template <typename T>
1252 using is_integer_larger_than_int = std::integral_constant<
1253  bool, std::is_integral<T>::value &&(sizeof(T) > sizeof(int) ||
1254  std::is_same<T, long>::value ||
1255  std::is_same<T, unsigned long>::value)>;
1256 
1257 template <
1258  typename L, typename R, std::size_t N = Traits::isSimdArray<L>::value
1259  ? Traits::simd_vector_size<L>::value
1260  : Traits::simd_vector_size<R>::value,
1261  bool = (Traits::isSimdArray<L>::value ||
1262  Traits::isSimdArray<R>::value) // one of the operands must be a SimdArray
1263  &&
1264  !std::is_same<type<L>, type<R>>::value // if the operands are of the same type
1265  // use the member function
1266  &&
1267  ((std::is_arithmetic<type<L>>::value &&
1268  !is_integer_larger_than_int<type<L>>::value) ||
1269  (std::is_arithmetic<type<R>>::value &&
1270  !is_integer_larger_than_int<
1271  type<R>>::value) // one of the operands is a scalar type
1272  ||
1273  (Traits::is_simd_vector<L>::value && !Traits::isSimdArray<L>::value) ||
1274  (Traits::is_simd_vector<R>::value &&
1275  !Traits::isSimdArray<R>::value) // or one of the operands is Vector<T>
1276  ) > struct evaluate;
1277 
1278 template <typename L, typename R, std::size_t N> struct evaluate<L, R, N, true>
1279 {
1280 private:
1281  using LScalar = Traits::entry_type_of<L>;
1282  using RScalar = Traits::entry_type_of<R>;
1283 
1284  template <bool B, typename True, typename False>
1285  using conditional = typename std::conditional<B, True, False>::type;
1286 
1287 public:
1288  // In principle we want the exact same rules for SimdArray<T> ⨉ SimdArray<U> as the standard
1289  // defines for T ⨉ U. BUT: short ⨉ short returns int (because all integral types smaller than
1290  // int are promoted to int before any operation). This would imply that SIMD types with integral
1291  // types smaller than int are more or less useless - and you could use SimdArray<int> from the
1292  // start. Therefore we special-case those operations where the scalar type of both operands is
1293  // integral and smaller than int.
1294  // In addition to that there is no generic support for 64-bit int SIMD types. Therefore
1295  // promotion to a 64-bit integral type (including `long` because it can potentially have 64
1296  // bits) also is not done. But if one of the operands is a scalar type that is larger than int
1297  // then the operator is disabled altogether. We do not want an implicit demotion.
1298  using type = SimdArray<
1299  conditional<(std::is_integral<LScalar>::value &&std::is_integral<RScalar>::value &&
1300  sizeof(LScalar) < sizeof(int) &&
1301  sizeof(RScalar) < sizeof(int)),
1302  conditional<(sizeof(LScalar) == sizeof(RScalar)),
1303  conditional<std::is_unsigned<LScalar>::value, LScalar, RScalar>,
1304  conditional<(sizeof(LScalar) > sizeof(RScalar)), LScalar, RScalar>>,
1305  decltype(std::declval<LScalar>() + std::declval<RScalar>())>,
1306  N>;
1307 };
1308 
1309 } // namespace result_vector_type_internal
1310 
1311 template <typename L, typename R>
1312 using result_vector_type = typename result_vector_type_internal::evaluate<L, R>::type;
1313 
1314 static_assert(
1315  std::is_same<result_vector_type<short int, Vc::SimdArray<short unsigned int, 32ul>>,
1317  "result_vector_type does not work");
1318 
1319 #define Vc_BINARY_OPERATORS_(op_) \
1320  template <typename L, typename R> \
1321  Vc_INTRINSIC result_vector_type<L, R> operator op_(L &&lhs, R &&rhs) \
1322  { \
1323  using Return = result_vector_type<L, R>; \
1324  return Return(std::forward<L>(lhs)) op_ Return(std::forward<R>(rhs)); \
1325  }
1326 Vc_ALL_ARITHMETICS(Vc_BINARY_OPERATORS_)
1327 Vc_ALL_BINARY(Vc_BINARY_OPERATORS_)
1328 #undef Vc_BINARY_OPERATORS_
1329 #define Vc_BINARY_OPERATORS_(op_) \
1330  template <typename L, typename R> \
1331  Vc_INTRINSIC typename result_vector_type<L, R>::mask_type operator op_(L &&lhs, \
1332  R &&rhs) \
1333  { \
1334  using Promote = result_vector_type<L, R>; \
1335  return Promote(std::forward<L>(lhs)) op_ Promote(std::forward<R>(rhs)); \
1336  }
1337 Vc_ALL_COMPARES(Vc_BINARY_OPERATORS_)
1338 #undef Vc_BINARY_OPERATORS_
1339 
1340 // math functions {{{1
1341 #define Vc_FORWARD_UNARY_OPERATOR(name_) \
1342  template <typename T, std::size_t N, typename V, std::size_t M> \
1343  inline SimdArray<T, N, V, M> name_(const SimdArray<T, N, V, M> &x) \
1344  { \
1345  return SimdArray<T, N, V, M>::fromOperation( \
1346  Common::Operations::Forward_##name_(), x); \
1347  }
1348 
1349 #define Vc_FORWARD_UNARY_BOOL_OPERATOR(name_) \
1350  template <typename T, std::size_t N, typename V, std::size_t M> \
1351  inline SimdMaskArray<T, N, V, M> name_(const SimdArray<T, N, V, M> &x) \
1352  { \
1353  return SimdMaskArray<T, N, V, M>::fromOperation( \
1354  Common::Operations::Forward_##name_(), x); \
1355  }
1356 
1357 #define Vc_FORWARD_BINARY_OPERATOR(name_) \
1358  template <typename T, std::size_t N, typename V, std::size_t M> \
1359  inline SimdArray<T, N, V, M> name_(const SimdArray<T, N, V, M> &x, \
1360  const SimdArray<T, N, V, M> &y) \
1361  { \
1362  return SimdArray<T, N, V, M>::fromOperation( \
1363  Common::Operations::Forward_##name_(), x, y); \
1364  }
1365 
1366 Vc_FORWARD_UNARY_OPERATOR(abs)
1367 Vc_FORWARD_UNARY_OPERATOR(asin)
1368 Vc_FORWARD_UNARY_OPERATOR(atan)
1369 Vc_FORWARD_BINARY_OPERATOR(atan2)
1370 Vc_FORWARD_UNARY_OPERATOR(ceil)
1371 Vc_FORWARD_BINARY_OPERATOR(copysign)
1372 Vc_FORWARD_UNARY_OPERATOR(cos)
1373 Vc_FORWARD_UNARY_OPERATOR(exp)
1374 Vc_FORWARD_UNARY_OPERATOR(exponent)
1375 Vc_FORWARD_UNARY_OPERATOR(floor)
1376 template <typename T, std::size_t N>
1377 SimdArray<T, N> fma(const SimdArray<T, N> &a, const SimdArray<T, N> &b, const SimdArray<T, N> &c)
1378 {
1379  return SimdArray<T, N>::fromOperation(Common::Operations::Forward_fma(), a, b, c);
1380 }
1381 Vc_FORWARD_UNARY_BOOL_OPERATOR(isfinite)
1382 Vc_FORWARD_UNARY_BOOL_OPERATOR(isinf)
1383 Vc_FORWARD_UNARY_BOOL_OPERATOR(isnan)
1384 Vc_FORWARD_UNARY_BOOL_OPERATOR(isnegative)
1385 template <typename T, std::size_t N>
1386 SimdArray<T, N> frexp(const SimdArray<T, N> &x, SimdArray<int, N> *e)
1387 {
1388  return SimdArray<T, N>::fromOperation(Common::Operations::Forward_frexp(), x, e);
1389 }
1390 template <typename T, std::size_t N>
1391 SimdArray<T, N> ldexp(const SimdArray<T, N> &x, const SimdArray<int, N> &e)
1392 {
1393  return SimdArray<T, N>::fromOperation(Common::Operations::Forward_ldexp(), x, e);
1394 }
1395 Vc_FORWARD_UNARY_OPERATOR(log)
1396 Vc_FORWARD_UNARY_OPERATOR(log10)
1397 Vc_FORWARD_UNARY_OPERATOR(log2)
1398 Vc_FORWARD_UNARY_OPERATOR(reciprocal)
1399 Vc_FORWARD_UNARY_OPERATOR(round)
1400 Vc_FORWARD_UNARY_OPERATOR(rsqrt)
1401 Vc_FORWARD_UNARY_OPERATOR(sin)
1402 template <typename T, std::size_t N>
1403 void sincos(const SimdArray<T, N> &x, SimdArray<T, N> *sin, SimdArray<T, N> *cos)
1404 {
1405  SimdArray<T, N>::callOperation(Common::Operations::Forward_sincos(), x, sin, cos);
1406 }
1407 Vc_FORWARD_UNARY_OPERATOR(sqrt)
1408 Vc_FORWARD_UNARY_OPERATOR(trunc)
1409 Vc_FORWARD_BINARY_OPERATOR(min)
1410 Vc_FORWARD_BINARY_OPERATOR(max)
1411 #undef Vc_FORWARD_UNARY_OPERATOR
1412 #undef Vc_FORWARD_UNARY_BOOL_OPERATOR
1413 #undef Vc_FORWARD_BINARY_OPERATOR
1414 
1415 // simd_cast {{{1
1416 // simd_cast_impl_smaller_input {{{2
1417 // The following function can be implemented without the sizeof...(From) overload.
1418 // However, ICC has a bug (Premier Issue #6000116338) which leads to an ICE. Splitting the
1419 // function in two works around the issue.
1420 template <typename Return, std::size_t N, typename T, typename... From>
1421 Vc_INTRINSIC Vc_CONST enable_if<sizeof...(From) != 0, Return>
1422 simd_cast_impl_smaller_input(const From &... xs, const T &last)
1423 {
1424  Return r = simd_cast<Return>(xs...);
1425  for (size_t i = 0; i < N; ++i) {
1426  r[i + N * sizeof...(From)] = static_cast<typename Return::EntryType>(last[i]);
1427  }
1428  return r;
1429 }
1430 template <typename Return, std::size_t N, typename T>
1431 Vc_INTRINSIC Vc_CONST Return simd_cast_impl_smaller_input(const T &last)
1432 {
1433  Return r = Return();
1434  for (size_t i = 0; i < N; ++i) {
1435  r[i] = static_cast<typename Return::EntryType>(last[i]);
1436  }
1437  return r;
1438 }
1439 template <typename Return, std::size_t N, typename T, typename... From>
1440 Vc_INTRINSIC Vc_CONST enable_if<sizeof...(From) != 0, Return> simd_cast_impl_larger_input(
1441  const From &... xs, const T &last)
1442 {
1443  Return r = simd_cast<Return>(xs...);
1444  for (size_t i = N * sizeof...(From); i < Return::Size; ++i) {
1445  r[i] = static_cast<typename Return::EntryType>(last[i - N * sizeof...(From)]);
1446  }
1447  return r;
1448 }
1449 template <typename Return, std::size_t N, typename T>
1450 Vc_INTRINSIC Vc_CONST Return simd_cast_impl_larger_input(const T &last)
1451 {
1452  Return r = Return();
1453  for (size_t i = 0; i < Return::size(); ++i) {
1454  r[i] = static_cast<typename Return::EntryType>(last[i]);
1455  }
1456  return r;
1457 }
1458 
1459 // simd_cast_without_last (declaration) {{{2
1460 template <typename Return, typename T, typename... From>
1461 Vc_INTRINSIC_L Vc_CONST_L Return
1462  simd_cast_without_last(const From &... xs, const T &) Vc_INTRINSIC_R Vc_CONST_R;
1463 
1464 // are_all_types_equal {{{2
1465 template <typename... Ts> struct are_all_types_equal;
1466 template <typename T>
1467 struct are_all_types_equal<T> : public std::integral_constant<bool, true>
1468 {
1469 };
1470 template <typename T0, typename T1, typename... Ts>
1471 struct are_all_types_equal<T0, T1, Ts...>
1472  : public std::integral_constant<
1473  bool, std::is_same<T0, T1>::value && are_all_types_equal<T1, Ts...>::value>
1474 {
1475 };
1476 
1477 // simd_cast_interleaved_argument_order (declarations) {{{2
1497 template <typename Return, typename... Ts>
1498 Vc_INTRINSIC Vc_CONST Return
1499  simd_cast_interleaved_argument_order(const Ts &... a, const Ts &... b);
1500 
1501 // simd_cast_with_offset (declarations and one impl) {{{2
1502 // offset == 0 {{{3
1503 template <typename Return, std::size_t offset, typename From, typename... Froms>
1504 Vc_INTRINSIC Vc_CONST
1505  enable_if<(are_all_types_equal<From, Froms...>::value && offset == 0), Return>
1506  simd_cast_with_offset(const From &x, const Froms &... xs);
1507 // offset > 0 && offset divisible by Return::Size {{{3
1508 template <typename Return, std::size_t offset, typename From>
1509 Vc_INTRINSIC Vc_CONST
1510  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size == 0), Return>
1511  simd_cast_with_offset(const From &x);
1512 // offset > 0 && offset NOT divisible && Return is non-atomic simd(mask)array {{{3
1513 template <typename Return, std::size_t offset, typename From>
1514 Vc_INTRINSIC Vc_CONST
1515  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size != 0 &&
1516  ((Traits::isSimdArray<Return>::value &&
1517  !Traits::isAtomicSimdArray<Return>::value) ||
1518  (Traits::isSimdMaskArray<Return>::value &&
1519  !Traits::isAtomicSimdMaskArray<Return>::value))),
1520  Return>
1521  simd_cast_with_offset(const From &x);
1522 // offset > 0 && offset NOT divisible && Return is atomic simd(mask)array {{{3
1523 template <typename Return, std::size_t offset, typename From>
1524 Vc_INTRINSIC Vc_CONST
1525  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size != 0 &&
1526  ((Traits::isSimdArray<Return>::value &&
1527  Traits::isAtomicSimdArray<Return>::value) ||
1528  (Traits::isSimdMaskArray<Return>::value &&
1529  Traits::isAtomicSimdMaskArray<Return>::value))),
1530  Return>
1531  simd_cast_with_offset(const From &x);
1532 // offset > first argument (drops first arg) {{{3
1533 template <typename Return, std::size_t offset, typename From, typename... Froms>
1534 Vc_INTRINSIC Vc_CONST enable_if<
1535  (are_all_types_equal<From, Froms...>::value && From::Size <= offset), Return>
1536  simd_cast_with_offset(const From &, const Froms &... xs)
1537 {
1538  return simd_cast_with_offset<Return, offset - From::Size>(xs...);
1539 }
1540 
1541 // offset > first and only argument (returns Zero) {{{3
1542 template <typename Return, std::size_t offset, typename From>
1543 Vc_INTRINSIC Vc_CONST enable_if<(From::Size <= offset), Return> simd_cast_with_offset(
1544  const From &)
1545 {
1546  return Return::Zero();
1547 }
1548 
1549 // first_type_of {{{2
1550 template <typename T, typename... Ts> struct first_type_of_impl
1551 {
1552  using type = T;
1553 };
1554 template <typename... Ts> using first_type_of = typename first_type_of_impl<Ts...>::type;
1555 
1556 // simd_cast_drop_arguments (declarations) {{{2
1557 template <typename Return, typename From>
1558 Vc_INTRINSIC Vc_CONST Return simd_cast_drop_arguments(From x);
1559 template <typename Return, typename... Froms>
1560 Vc_INTRINSIC Vc_CONST
1561  enable_if<(are_all_types_equal<Froms...>::value &&
1562  sizeof...(Froms) * first_type_of<Froms...>::Size < Return::Size),
1563  Return>
1564  simd_cast_drop_arguments(Froms... xs, first_type_of<Froms...> x);
1565 // The following function can be implemented without the sizeof...(From) overload.
1566 // However, ICC has a bug (Premier Issue #6000116338) which leads to an ICE. Splitting the
1567 // function in two works around the issue.
1568 template <typename Return, typename From, typename... Froms>
1569 Vc_INTRINSIC Vc_CONST enable_if<
1570  (are_all_types_equal<From, Froms...>::value &&
1571  (1 + sizeof...(Froms)) * From::Size >= Return::Size && sizeof...(Froms) != 0),
1572  Return>
1573 simd_cast_drop_arguments(Froms... xs, From x, From);
1574 template <typename Return, typename From>
1575 Vc_INTRINSIC Vc_CONST
1576  enable_if<(are_all_types_equal<From>::value && From::Size >= Return::Size), Return>
1577  simd_cast_drop_arguments(From x, From);
1578 
1579 namespace
1580 {
1581 #ifdef Vc_DEBUG_SIMD_CAST
1582 void debugDoNothing(const std::initializer_list<void *> &) {}
1583 template <typename T0, typename... Ts>
1584 inline void vc_debug_(const char *prefix, const char *suffix, const T0 &arg0,
1585  const Ts &... args)
1586 {
1587  std::cerr << prefix << arg0;
1588  debugDoNothing({&(std::cerr << ", " << args)...});
1589  std::cerr << suffix;
1590 }
1591 #else
1592 template <typename T0, typename... Ts>
1593 Vc_INTRINSIC void vc_debug_(const char *, const char *, const T0 &, const Ts &...)
1594 {
1595 }
1596 #endif
1597 } // unnamed namespace
1598 
1599 // simd_cast<T>(xs...) to SimdArray/-mask {{{2
1600 #define Vc_SIMDARRAY_CASTS(SimdArrayType_, trait_name_) \
1601  template <typename Return, typename From, typename... Froms> \
1602  Vc_INTRINSIC Vc_CONST enable_if<(Traits::isAtomic##SimdArrayType_<Return>::value && \
1603  !Traits::is##SimdArrayType_<From>::value && \
1604  Traits::is_simd_##trait_name_<From>::value && \
1605  From::Size * sizeof...(Froms) < Return::Size && \
1606  are_all_types_equal<From, Froms...>::value), \
1607  Return> \
1608  simd_cast(From x, Froms... xs) \
1609  { \
1610  vc_debug_("simd_cast{1}(", ")\n", x, xs...); \
1611  return {simd_cast<typename Return::storage_type>(x, xs...)}; \
1612  } \
1613  template <typename Return, typename From, typename... Froms> \
1614  Vc_INTRINSIC Vc_CONST enable_if<(Traits::isAtomic##SimdArrayType_<Return>::value && \
1615  !Traits::is##SimdArrayType_<From>::value && \
1616  Traits::is_simd_##trait_name_<From>::value && \
1617  From::Size * sizeof...(Froms) >= Return::Size && \
1618  are_all_types_equal<From, Froms...>::value), \
1619  Return> \
1620  simd_cast(From x, Froms... xs) \
1621  { \
1622  vc_debug_("simd_cast{2}(", ")\n", x, xs...); \
1623  return {simd_cast_without_last<Return, From, Froms...>(x, xs...)}; \
1624  } \
1625  template <typename Return, typename From, typename... Froms> \
1626  Vc_INTRINSIC Vc_CONST enable_if<(Traits::is##SimdArrayType_<Return>::value && \
1627  !Traits::isAtomic##SimdArrayType_<Return>::value && \
1628  !Traits::is##SimdArrayType_<From>::value && \
1629  Traits::is_simd_##trait_name_<From>::value && \
1630  Common::left_size(Return::Size) < \
1631  From::Size * (1 + sizeof...(Froms)) && \
1632  are_all_types_equal<From, Froms...>::value), \
1633  Return> \
1634  simd_cast(From x, Froms... xs) \
1635  { \
1636  vc_debug_("simd_cast{3}(", ")\n", x, xs...); \
1637  using R0 = typename Return::storage_type0; \
1638  using R1 = typename Return::storage_type1; \
1639  return {simd_cast_drop_arguments<R0, Froms...>(x, xs...), \
1640  simd_cast_with_offset<R1, R0::Size>(x, xs...)}; \
1641  } \
1642  template <typename Return, typename From, typename... Froms> \
1643  Vc_INTRINSIC Vc_CONST enable_if<(Traits::is##SimdArrayType_<Return>::value && \
1644  !Traits::isAtomic##SimdArrayType_<Return>::value && \
1645  !Traits::is##SimdArrayType_<From>::value && \
1646  Traits::is_simd_##trait_name_<From>::value && \
1647  Common::left_size(Return::Size) >= \
1648  From::Size * (1 + sizeof...(Froms)) && \
1649  are_all_types_equal<From, Froms...>::value), \
1650  Return> \
1651  simd_cast(From x, Froms... xs) \
1652  { \
1653  vc_debug_("simd_cast{4}(", ")\n", x, xs...); \
1654  using R0 = typename Return::storage_type0; \
1655  using R1 = typename Return::storage_type1; \
1656  return {simd_cast<R0>(x, xs...), R1::Zero()}; \
1657  }
1658 Vc_SIMDARRAY_CASTS(SimdArray, vector)
1659 Vc_SIMDARRAY_CASTS(SimdMaskArray, mask)
1660 #undef Vc_SIMDARRAY_CASTS
1661 
1662 // simd_cast<SimdArray/-mask, offset>(V) {{{2
1663 #define Vc_SIMDARRAY_CASTS(SimdArrayType_, trait_name_) \
1664  /* SIMD Vector/Mask to atomic SimdArray/simdmaskarray */ \
1665  template <typename Return, int offset, typename From> \
1666  Vc_INTRINSIC Vc_CONST enable_if<(Traits::isAtomic##SimdArrayType_<Return>::value && \
1667  !Traits::is##SimdArrayType_<From>::value && \
1668  Traits::is_simd_##trait_name_<From>::value), \
1669  Return> \
1670  simd_cast(From x) \
1671  { \
1672  vc_debug_("simd_cast{offset, atomic}(", ")\n", offset, x); \
1673  return {simd_cast<typename Return::storage_type, offset>(x)}; \
1674  } \
1675  /* both halves of Return array are extracted from argument */ \
1676  template <typename Return, int offset, typename From> \
1677  Vc_INTRINSIC Vc_CONST enable_if< \
1678  (Traits::is##SimdArrayType_<Return>::value && \
1679  !Traits::isAtomic##SimdArrayType_<Return>::value && \
1680  !Traits::is##SimdArrayType_<From>::value && \
1681  Traits::is_simd_##trait_name_<From>::value && \
1682  Return::Size * offset + Common::left_size(Return::Size) < From::Size), \
1683  Return> \
1684  simd_cast(From x) \
1685  { \
1686  vc_debug_("simd_cast{offset, split Return}(", ")\n", offset, x); \
1687  using R0 = typename Return::storage_type0; \
1688  constexpr int entries_offset = offset * Return::Size; \
1689  constexpr int entries_offset_right = entries_offset + R0::Size; \
1690  return { \
1691  simd_cast_with_offset<typename Return::storage_type0, entries_offset>(x), \
1692  simd_cast_with_offset<typename Return::storage_type1, entries_offset_right>( \
1693  x)}; \
1694  } \
1695  /* SIMD Vector/Mask to non-atomic SimdArray/simdmaskarray */ \
1696  /* right half of Return array is zero */ \
1697  template <typename Return, int offset, typename From> \
1698  Vc_INTRINSIC Vc_CONST enable_if< \
1699  (Traits::is##SimdArrayType_<Return>::value && \
1700  !Traits::isAtomic##SimdArrayType_<Return>::value && \
1701  !Traits::is##SimdArrayType_<From>::value && \
1702  Traits::is_simd_##trait_name_<From>::value && \
1703  Return::Size * offset + Common::left_size(Return::Size) >= From::Size), \
1704  Return> \
1705  simd_cast(From x) \
1706  { \
1707  vc_debug_("simd_cast{offset, R1::Zero}(", ")\n", offset, x); \
1708  using R0 = typename Return::storage_type0; \
1709  using R1 = typename Return::storage_type1; \
1710  constexpr int entries_offset = offset * Return::Size; \
1711  return {simd_cast_with_offset<R0, entries_offset>(x), R1::Zero()}; \
1712  }
1713 Vc_SIMDARRAY_CASTS(SimdArray, vector)
1714 Vc_SIMDARRAY_CASTS(SimdMaskArray, mask)
1715 #undef Vc_SIMDARRAY_CASTS
1716 
1717 // simd_cast<T>(xs...) from SimdArray/-mask {{{2
1718 #define Vc_SIMDARRAY_CASTS(SimdArrayType_) \
1719  /* indivisible SimdArrayType_ */ \
1720  template <typename Return, typename T, std::size_t N, typename V, typename... From> \
1721  Vc_INTRINSIC Vc_CONST \
1722  enable_if<(are_all_types_equal<SimdArrayType_<T, N, V, N>, From...>::value && \
1723  (sizeof...(From) == 0 || N * sizeof...(From) < Return::Size) && \
1724  !std::is_same<Return, SimdArrayType_<T, N, V, N>>::value), \
1725  Return> \
1726  simd_cast(const SimdArrayType_<T, N, V, N> &x0, const From &... xs) \
1727  { \
1728  vc_debug_("simd_cast{indivisible}(", ")\n", x0, xs...); \
1729  return simd_cast<Return>(internal_data(x0), internal_data(xs)...); \
1730  } \
1731  /* indivisible SimdArrayType_ && can drop arguments from the end */ \
1732  template <typename Return, typename T, std::size_t N, typename V, typename... From> \
1733  Vc_INTRINSIC Vc_CONST \
1734  enable_if<(are_all_types_equal<SimdArrayType_<T, N, V, N>, From...>::value && \
1735  (sizeof...(From) > 0 && (N * sizeof...(From) >= Return::Size)) && \
1736  !std::is_same<Return, SimdArrayType_<T, N, V, N>>::value), \
1737  Return> \
1738  simd_cast(const SimdArrayType_<T, N, V, N> &x0, const From &... xs) \
1739  { \
1740  vc_debug_("simd_cast{indivisible2}(", ")\n", x0, xs...); \
1741  return simd_cast_without_last<Return, \
1742  typename SimdArrayType_<T, N, V, N>::storage_type, \
1743  typename From::storage_type...>( \
1744  internal_data(x0), internal_data(xs)...); \
1745  } \
1746  /* bisectable SimdArrayType_ (N = 2^n) && never too large */ \
1747  template <typename Return, typename T, std::size_t N, typename V, std::size_t M, \
1748  typename... From> \
1749  Vc_INTRINSIC Vc_CONST enable_if< \
1750  (N != M && are_all_types_equal<SimdArrayType_<T, N, V, M>, From...>::value && \
1751  N * sizeof...(From) < Return::Size && ((N - 1) & N) == 0), \
1752  Return> \
1753  simd_cast(const SimdArrayType_<T, N, V, M> &x0, const From &... xs) \
1754  { \
1755  vc_debug_("simd_cast{bisectable}(", ")\n", x0, xs...); \
1756  return simd_cast_interleaved_argument_order< \
1757  Return, typename SimdArrayType_<T, N, V, M>::storage_type0, \
1758  typename From::storage_type0...>(internal_data0(x0), internal_data0(xs)..., \
1759  internal_data1(x0), internal_data1(xs)...); \
1760  } \
1761  /* bisectable SimdArrayType_ (N = 2^n) && input so large that at least the last \
1762  * input can be dropped */ \
1763  template <typename Return, typename T, std::size_t N, typename V, std::size_t M, \
1764  typename... From> \
1765  Vc_INTRINSIC Vc_CONST enable_if< \
1766  (N != M && are_all_types_equal<SimdArrayType_<T, N, V, M>, From...>::value && \
1767  N * sizeof...(From) >= Return::Size && ((N - 1) & N) == 0), \
1768  Return> \
1769  simd_cast(const SimdArrayType_<T, N, V, M> &x0, const From &... xs) \
1770  { \
1771  vc_debug_("simd_cast{bisectable2}(", ")\n", x0, xs...); \
1772  return simd_cast_without_last<Return, SimdArrayType_<T, N, V, M>, From...>( \
1773  x0, xs...); \
1774  } \
1775  /* remaining SimdArrayType_ input never larger (N != 2^n) */ \
1776  template <typename Return, typename T, std::size_t N, typename V, std::size_t M, \
1777  typename... From> \
1778  Vc_INTRINSIC Vc_CONST enable_if< \
1779  (N != M && are_all_types_equal<SimdArrayType_<T, N, V, M>, From...>::value && \
1780  N * (1 + sizeof...(From)) <= Return::Size && ((N - 1) & N) != 0), \
1781  Return> \
1782  simd_cast(const SimdArrayType_<T, N, V, M> &x0, const From &... xs) \
1783  { \
1784  vc_debug_("simd_cast{remaining}(", ")\n", x0, xs...); \
1785  return simd_cast_impl_smaller_input<Return, N, SimdArrayType_<T, N, V, M>, \
1786  From...>(x0, xs...); \
1787  } \
1788  /* remaining SimdArrayType_ input larger (N != 2^n) */ \
1789  template <typename Return, typename T, std::size_t N, typename V, std::size_t M, \
1790  typename... From> \
1791  Vc_INTRINSIC Vc_CONST enable_if< \
1792  (N != M && are_all_types_equal<SimdArrayType_<T, N, V, M>, From...>::value && \
1793  N * (1 + sizeof...(From)) > Return::Size && ((N - 1) & N) != 0), \
1794  Return> \
1795  simd_cast(const SimdArrayType_<T, N, V, M> &x0, const From &... xs) \
1796  { \
1797  vc_debug_("simd_cast{remaining2}(", ")\n", x0, xs...); \
1798  return simd_cast_impl_larger_input<Return, N, SimdArrayType_<T, N, V, M>, \
1799  From...>(x0, xs...); \
1800  } \
1801  /* a single bisectable SimdArrayType_ (N = 2^n) too large */ \
1802  template <typename Return, typename T, std::size_t N, typename V, std::size_t M> \
1803  Vc_INTRINSIC Vc_CONST \
1804  enable_if<(N != M && N >= 2 * Return::Size && ((N - 1) & N) == 0), Return> \
1805  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
1806  { \
1807  vc_debug_("simd_cast{single bisectable}(", ")\n", x); \
1808  return simd_cast<Return>(internal_data0(x)); \
1809  } \
1810  template <typename Return, typename T, std::size_t N, typename V, std::size_t M> \
1811  Vc_INTRINSIC Vc_CONST enable_if<(N != M && N > Return::Size && \
1812  N < 2 * Return::Size && ((N - 1) & N) == 0), \
1813  Return> \
1814  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
1815  { \
1816  vc_debug_("simd_cast{single bisectable2}(", ")\n", x); \
1817  return simd_cast<Return>(internal_data0(x), internal_data1(x)); \
1818  }
1819 Vc_SIMDARRAY_CASTS(SimdArray)
1820 Vc_SIMDARRAY_CASTS(SimdMaskArray)
1821 #undef Vc_SIMDARRAY_CASTS
1822 
1823 // simd_cast<T, offset>(SimdArray/-mask) {{{2
1824 #define Vc_SIMDARRAY_CASTS(SimdArrayType_) \
1825  /* offset == 0 is like without offset */ \
1826  template <typename Return, int offset, typename T, std::size_t N, typename V, \
1827  std::size_t M> \
1828  Vc_INTRINSIC Vc_CONST enable_if<(offset == 0), Return> simd_cast( \
1829  const SimdArrayType_<T, N, V, M> &x) \
1830  { \
1831  vc_debug_("simd_cast{offset == 0}(", ")\n", offset, x); \
1832  return simd_cast<Return>(x); \
1833  } \
1834  /* forward to V */ \
1835  template <typename Return, int offset, typename T, std::size_t N, typename V> \
1836  Vc_INTRINSIC Vc_CONST enable_if<(offset != 0), Return> simd_cast( \
1837  const SimdArrayType_<T, N, V, N> &x) \
1838  { \
1839  vc_debug_("simd_cast{offset, forward}(", ")\n", offset, x); \
1840  return simd_cast<Return, offset>(internal_data(x)); \
1841  } \
1842  /* convert from right member of SimdArray */ \
1843  template <typename Return, int offset, typename T, std::size_t N, typename V, \
1844  std::size_t M> \
1845  Vc_INTRINSIC Vc_CONST \
1846  enable_if<(N != M && offset * Return::Size >= Common::left_size(N) && \
1847  offset != 0 && Common::left_size(N) % Return::Size == 0), \
1848  Return> \
1849  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
1850  { \
1851  vc_debug_("simd_cast{offset, right}(", ")\n", offset, x); \
1852  return simd_cast<Return, offset - Common::left_size(N) / Return::Size>( \
1853  internal_data1(x)); \
1854  } \
1855  /* same as above except for odd cases where offset * Return::Size doesn't fit the \
1856  * left side of the SimdArray */ \
1857  template <typename Return, int offset, typename T, std::size_t N, typename V, \
1858  std::size_t M> \
1859  Vc_INTRINSIC Vc_CONST \
1860  enable_if<(N != M && offset * Return::Size >= Common::left_size(N) && \
1861  offset != 0 && Common::left_size(N) % Return::Size != 0), \
1862  Return> \
1863  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
1864  { \
1865  vc_debug_("simd_cast{offset, right, nofit}(", ")\n", offset, x); \
1866  return simd_cast_with_offset<Return, \
1867  offset * Return::Size - Common::left_size(N)>( \
1868  internal_data1(x)); \
1869  } \
1870  /* convert from left member of SimdArray */ \
1871  template <typename Return, int offset, typename T, std::size_t N, typename V, \
1872  std::size_t M> \
1873  Vc_INTRINSIC Vc_CONST \
1874  enable_if<(N != M && /*offset * Return::Size < Common::left_size(N) &&*/ \
1875  offset != 0 && (offset + 1) * Return::Size <= Common::left_size(N)), \
1876  Return> \
1877  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
1878  { \
1879  vc_debug_("simd_cast{offset, left}(", ")\n", offset, x); \
1880  return simd_cast<Return, offset>(internal_data0(x)); \
1881  } \
1882  /* fallback to copying scalars */ \
1883  template <typename Return, int offset, typename T, std::size_t N, typename V, \
1884  std::size_t M> \
1885  Vc_INTRINSIC Vc_CONST \
1886  enable_if<(N != M && (offset * Return::Size < Common::left_size(N)) && \
1887  offset != 0 && (offset + 1) * Return::Size > Common::left_size(N)), \
1888  Return> \
1889  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
1890  { \
1891  vc_debug_("simd_cast{offset, copy scalars}(", ")\n", offset, x); \
1892  using R = typename Return::EntryType; \
1893  Return r = Return::Zero(); \
1894  for (std::size_t i = offset * Return::Size; \
1895  i < std::min(N, (offset + 1) * Return::Size); ++i) { \
1896  r[i - offset * Return::Size] = static_cast<R>(x[i]); \
1897  } \
1898  return r; \
1899  }
1900 Vc_SIMDARRAY_CASTS(SimdArray)
1901 Vc_SIMDARRAY_CASTS(SimdMaskArray)
1902 #undef Vc_SIMDARRAY_CASTS
1903 // simd_cast_drop_arguments (definitions) {{{2
1904 template <typename Return, typename From>
1905 Vc_INTRINSIC Vc_CONST Return simd_cast_drop_arguments(From x)
1906 {
1907  return simd_cast<Return>(x);
1908 }
1909 template <typename Return, typename... Froms>
1910 Vc_INTRINSIC Vc_CONST
1911  enable_if<(are_all_types_equal<Froms...>::value &&
1912  sizeof...(Froms) * first_type_of<Froms...>::Size < Return::Size),
1913  Return>
1914  simd_cast_drop_arguments(Froms... xs, first_type_of<Froms...> x)
1915 {
1916  return simd_cast<Return>(xs..., x);
1917 }
1918 // The following function can be implemented without the sizeof...(From) overload.
1919 // However, ICC has a bug (Premier Issue #6000116338) which leads to an ICE. Splitting the
1920 // function in two works around the issue.
1921 template <typename Return, typename From, typename... Froms>
1922 Vc_INTRINSIC Vc_CONST enable_if<
1923  (are_all_types_equal<From, Froms...>::value &&
1924  (1 + sizeof...(Froms)) * From::Size >= Return::Size && sizeof...(Froms) != 0),
1925  Return>
1926 simd_cast_drop_arguments(Froms... xs, From x, From)
1927 {
1928  return simd_cast_drop_arguments<Return, Froms...>(xs..., x);
1929 }
1930 template <typename Return, typename From>
1931 Vc_INTRINSIC Vc_CONST
1932  enable_if<(are_all_types_equal<From>::value && From::Size >= Return::Size), Return>
1933  simd_cast_drop_arguments(From x, From)
1934 {
1935  return simd_cast_drop_arguments<Return>(x);
1936 }
1937 
1938 // simd_cast_with_offset (definitions) {{{2
1939  template <typename Return, std::size_t offset, typename From>
1940  Vc_INTRINSIC Vc_CONST
1941  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size == 0),
1942  Return> simd_cast_with_offset(const From &x)
1943 {
1944  return simd_cast<Return, offset / Return::Size>(x);
1945 }
1946 template <typename Return, std::size_t offset, typename From>
1947 Vc_INTRINSIC Vc_CONST
1948  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size != 0 &&
1949  ((Traits::isSimdArray<Return>::value &&
1950  !Traits::isAtomicSimdArray<Return>::value) ||
1951  (Traits::isSimdMaskArray<Return>::value &&
1952  !Traits::isAtomicSimdMaskArray<Return>::value))),
1953  Return>
1954  simd_cast_with_offset(const From &x)
1955 {
1956  using R0 = typename Return::storage_type0;
1957  using R1 = typename Return::storage_type1;
1958  return {simd_cast_with_offset<R0, offset>(x),
1959  simd_cast_with_offset<R1, offset + R0::Size>(x)};
1960 }
1961 template <typename Return, std::size_t offset, typename From>
1962 Vc_INTRINSIC Vc_CONST
1963  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size != 0 &&
1964  ((Traits::isSimdArray<Return>::value &&
1965  Traits::isAtomicSimdArray<Return>::value) ||
1966  (Traits::isSimdMaskArray<Return>::value &&
1967  Traits::isAtomicSimdMaskArray<Return>::value))),
1968  Return>
1969  simd_cast_with_offset(const From &x)
1970 {
1971  return simd_cast<Return, offset / Return::Size>(x.shifted(offset % Return::Size));
1972 }
1973 template <typename Return, std::size_t offset, typename From, typename... Froms>
1974 Vc_INTRINSIC Vc_CONST
1975  enable_if<(are_all_types_equal<From, Froms...>::value && offset == 0), Return>
1976  simd_cast_with_offset(const From &x, const Froms &... xs)
1977 {
1978  return simd_cast<Return>(x, xs...);
1979 }
1980 
1981 // simd_cast_without_last (definition) {{{2
1982 template <typename Return, typename T, typename... From>
1983 Vc_INTRINSIC Vc_CONST Return simd_cast_without_last(const From &... xs, const T &)
1984 {
1985  return simd_cast<Return>(xs...);
1986 }
1987 
1988 // simd_cast_interleaved_argument_order (definitions) {{{2
1989 
1991 template <std::size_t I, typename T0, typename... Ts>
1992 Vc_INTRINSIC Vc_CONST enable_if<(I == 0), T0> extract_interleaved(const T0 &a0,
1993  const Ts &...,
1994  const T0 &,
1995  const Ts &...)
1996 {
1997  return a0;
1998 }
2000 template <std::size_t I, typename T0, typename... Ts>
2001 Vc_INTRINSIC Vc_CONST enable_if<(I == 1), T0> extract_interleaved(const T0 &,
2002  const Ts &...,
2003  const T0 &b0,
2004  const Ts &...)
2005 {
2006  return b0;
2007 }
2009 template <std::size_t I, typename T0, typename... Ts>
2010 Vc_INTRINSIC Vc_CONST enable_if<(I > 1), T0> extract_interleaved(const T0 &,
2011  const Ts &... a,
2012  const T0 &,
2013  const Ts &... b)
2014 {
2015  return extract_interleaved<I - 2, Ts...>(a..., b...);
2016 }
2019 template <typename Return, typename... Ts, std::size_t... Indexes>
2020 Vc_INTRINSIC Vc_CONST Return
2021  simd_cast_interleaved_argument_order_1(index_sequence<Indexes...>, const Ts &... a,
2022  const Ts &... b)
2023 {
2024  return simd_cast<Return>(extract_interleaved<Indexes, Ts...>(a..., b...)...);
2025 }
2028 template <typename Return, typename... Ts>
2029 Vc_INTRINSIC Vc_CONST Return
2030  simd_cast_interleaved_argument_order(const Ts &... a, const Ts &... b)
2031 {
2032  using seq = make_index_sequence<sizeof...(Ts)*2>;
2033  return simd_cast_interleaved_argument_order_1<Return, Ts...>(seq(), a..., b...);
2034 }
2035 
2036 // conditional_assign {{{1
2037 #define Vc_CONDITIONAL_ASSIGN(name_, op_) \
2038  template <Operator O, typename T, std::size_t N, typename V, size_t VN, typename M, \
2039  typename U> \
2040  Vc_INTRINSIC enable_if<O == Operator::name_, void> conditional_assign( \
2041  SimdArray<T, N, V, VN> &lhs, M &&mask, U &&rhs) \
2042  { \
2043  lhs(mask) op_ rhs; \
2044  }
2045 Vc_CONDITIONAL_ASSIGN( Assign, =)
2046 Vc_CONDITIONAL_ASSIGN( PlusAssign, +=)
2047 Vc_CONDITIONAL_ASSIGN( MinusAssign, -=)
2048 Vc_CONDITIONAL_ASSIGN( MultiplyAssign, *=)
2049 Vc_CONDITIONAL_ASSIGN( DivideAssign, /=)
2050 Vc_CONDITIONAL_ASSIGN( RemainderAssign, %=)
2051 Vc_CONDITIONAL_ASSIGN( XorAssign, ^=)
2052 Vc_CONDITIONAL_ASSIGN( AndAssign, &=)
2053 Vc_CONDITIONAL_ASSIGN( OrAssign, |=)
2054 Vc_CONDITIONAL_ASSIGN( LeftShiftAssign,<<=)
2055 Vc_CONDITIONAL_ASSIGN(RightShiftAssign,>>=)
2056 #undef Vc_CONDITIONAL_ASSIGN
2057 
2058 #define Vc_CONDITIONAL_ASSIGN(name_, expr_) \
2059  template <Operator O, typename T, std::size_t N, typename V, size_t VN, typename M> \
2060  Vc_INTRINSIC enable_if<O == Operator::name_, SimdArray<T, N, V, VN>> \
2061  conditional_assign(SimdArray<T, N, V, VN> &lhs, M &&mask) \
2062  { \
2063  return expr_; \
2064  }
2065 Vc_CONDITIONAL_ASSIGN(PostIncrement, lhs(mask)++)
2066 Vc_CONDITIONAL_ASSIGN( PreIncrement, ++lhs(mask))
2067 Vc_CONDITIONAL_ASSIGN(PostDecrement, lhs(mask)--)
2068 Vc_CONDITIONAL_ASSIGN( PreDecrement, --lhs(mask))
2069 #undef Vc_CONDITIONAL_ASSIGN
2070 // transpose_impl {{{1
2071 namespace Common
2072 {
2073  template <int L, typename T, std::size_t N, typename V>
2074  inline enable_if<L == 4, void> transpose_impl(
2075  SimdArray<T, N, V, N> * Vc_RESTRICT r[],
2076  const TransposeProxy<SimdArray<T, N, V, N>, SimdArray<T, N, V, N>,
2077  SimdArray<T, N, V, N>, SimdArray<T, N, V, N>> &proxy)
2078  {
2079  V *Vc_RESTRICT r2[L] = {&internal_data(*r[0]), &internal_data(*r[1]),
2080  &internal_data(*r[2]), &internal_data(*r[3])};
2081  transpose_impl<L>(
2082  &r2[0], TransposeProxy<V, V, V, V>{internal_data(std::get<0>(proxy.in)),
2083  internal_data(std::get<1>(proxy.in)),
2084  internal_data(std::get<2>(proxy.in)),
2085  internal_data(std::get<3>(proxy.in))});
2086  }
2087  template <int L, typename T, typename V>
2088  inline enable_if<(L == 2), void> transpose_impl(
2089  SimdArray<T, 4, V, 1> *Vc_RESTRICT r[],
2090  const TransposeProxy<SimdArray<T, 2, V, 1>, SimdArray<T, 2, V, 1>,
2091  SimdArray<T, 2, V, 1>, SimdArray<T, 2, V, 1>> &proxy)
2092  {
2093  auto &lo = *r[0];
2094  auto &hi = *r[1];
2095  internal_data0(internal_data0(lo)) = internal_data0(std::get<0>(proxy.in));
2096  internal_data1(internal_data0(lo)) = internal_data0(std::get<1>(proxy.in));
2097  internal_data0(internal_data1(lo)) = internal_data0(std::get<2>(proxy.in));
2098  internal_data1(internal_data1(lo)) = internal_data0(std::get<3>(proxy.in));
2099  internal_data0(internal_data0(hi)) = internal_data1(std::get<0>(proxy.in));
2100  internal_data1(internal_data0(hi)) = internal_data1(std::get<1>(proxy.in));
2101  internal_data0(internal_data1(hi)) = internal_data1(std::get<2>(proxy.in));
2102  internal_data1(internal_data1(hi)) = internal_data1(std::get<3>(proxy.in));
2103  }
2104  template <int L, typename T, std::size_t N, typename V>
2105  inline enable_if<(L == 4 && N > 1), void> transpose_impl(
2106  SimdArray<T, N, V, 1> *Vc_RESTRICT r[],
2107  const TransposeProxy<SimdArray<T, N, V, 1>, SimdArray<T, N, V, 1>,
2108  SimdArray<T, N, V, 1>, SimdArray<T, N, V, 1>> &proxy)
2109  {
2110  SimdArray<T, N, V, 1> *Vc_RESTRICT r0[L / 2] = {r[0], r[1]};
2111  SimdArray<T, N, V, 1> *Vc_RESTRICT r1[L / 2] = {r[2], r[3]};
2112  using H = SimdArray<T, 2>;
2113  transpose_impl<2>(
2114  &r0[0], TransposeProxy<H, H, H, H>{internal_data0(std::get<0>(proxy.in)),
2115  internal_data0(std::get<1>(proxy.in)),
2116  internal_data0(std::get<2>(proxy.in)),
2117  internal_data0(std::get<3>(proxy.in))});
2118  transpose_impl<2>(
2119  &r1[0], TransposeProxy<H, H, H, H>{internal_data1(std::get<0>(proxy.in)),
2120  internal_data1(std::get<1>(proxy.in)),
2121  internal_data1(std::get<2>(proxy.in)),
2122  internal_data1(std::get<3>(proxy.in))});
2123  }
2124  /* TODO:
2125  template <typename T, std::size_t N, typename V, std::size_t VSize>
2126  inline enable_if<(N > VSize), void> transpose_impl(
2127  std::array<SimdArray<T, N, V, VSize> * Vc_RESTRICT, 4> & r,
2128  const TransposeProxy<SimdArray<T, N, V, VSize>, SimdArray<T, N, V, VSize>,
2129  SimdArray<T, N, V, VSize>, SimdArray<T, N, V, VSize>> &proxy)
2130  {
2131  typedef SimdArray<T, N, V, VSize> SA;
2132  std::array<typename SA::storage_type0 * Vc_RESTRICT, 4> r0 = {
2133  {&internal_data0(*r[0]), &internal_data0(*r[1]), &internal_data0(*r[2]),
2134  &internal_data0(*r[3])}};
2135  transpose_impl(
2136  r0, TransposeProxy<typename SA::storage_type0, typename SA::storage_type0,
2137  typename SA::storage_type0, typename SA::storage_type0>{
2138  internal_data0(std::get<0>(proxy.in)),
2139  internal_data0(std::get<1>(proxy.in)),
2140  internal_data0(std::get<2>(proxy.in)),
2141  internal_data0(std::get<3>(proxy.in))});
2142 
2143  std::array<typename SA::storage_type1 * Vc_RESTRICT, 4> r1 = {
2144  {&internal_data1(*r[0]), &internal_data1(*r[1]), &internal_data1(*r[2]),
2145  &internal_data1(*r[3])}};
2146  transpose_impl(
2147  r1, TransposeProxy<typename SA::storage_type1, typename SA::storage_type1,
2148  typename SA::storage_type1, typename SA::storage_type1>{
2149  internal_data1(std::get<0>(proxy.in)),
2150  internal_data1(std::get<1>(proxy.in)),
2151  internal_data1(std::get<2>(proxy.in)),
2152  internal_data1(std::get<3>(proxy.in))});
2153  }
2154  */
2155 } // namespace Common
2156 
2157 // Traits static assertions {{{1
2158 static_assert(Traits::has_no_allocated_data<const volatile Vc::SimdArray<int, 4> &>::value, "");
2159 static_assert(Traits::has_no_allocated_data<const volatile Vc::SimdArray<int, 4>>::value, "");
2160 static_assert(Traits::has_no_allocated_data<volatile Vc::SimdArray<int, 4> &>::value, "");
2161 static_assert(Traits::has_no_allocated_data<volatile Vc::SimdArray<int, 4>>::value, "");
2162 static_assert(Traits::has_no_allocated_data<const Vc::SimdArray<int, 4> &>::value, "");
2163 static_assert(Traits::has_no_allocated_data<const Vc::SimdArray<int, 4>>::value, "");
2164 static_assert(Traits::has_no_allocated_data<Vc::SimdArray<int, 4>>::value, "");
2165 static_assert(Traits::has_no_allocated_data<Vc::SimdArray<int, 4> &&>::value, "");
2166 // }}}1
2168 
2169 } // namespace Vc_VERSIONED_NAMESPACE
2170 
2171 // numeric_limits {{{1
2172 namespace std
2173 {
2174 template <typename T, size_t N, typename V, size_t VN>
2175 struct numeric_limits<Vc::SimdArray<T, N, V, VN>> : public numeric_limits<T> {
2176 private:
2177  using R = Vc::SimdArray<T, N, V, VN>;
2178 
2179 public:
2180  static Vc_ALWAYS_INLINE Vc_CONST R max() noexcept { return numeric_limits<T>::max(); }
2181  static Vc_ALWAYS_INLINE Vc_CONST R min() noexcept { return numeric_limits<T>::min(); }
2182  static Vc_ALWAYS_INLINE Vc_CONST R lowest() noexcept
2183  {
2184  return numeric_limits<T>::lowest();
2185  }
2186  static Vc_ALWAYS_INLINE Vc_CONST R epsilon() noexcept
2187  {
2188  return numeric_limits<T>::epsilon();
2189  }
2190  static Vc_ALWAYS_INLINE Vc_CONST R round_error() noexcept
2191  {
2192  return numeric_limits<T>::round_error();
2193  }
2194  static Vc_ALWAYS_INLINE Vc_CONST R infinity() noexcept
2195  {
2196  return numeric_limits<T>::infinity();
2197  }
2198  static Vc_ALWAYS_INLINE Vc_CONST R quiet_NaN() noexcept
2199  {
2200  return numeric_limits<T>::quiet_NaN();
2201  }
2202  static Vc_ALWAYS_INLINE Vc_CONST R signaling_NaN() noexcept
2203  {
2204  return numeric_limits<T>::signaling_NaN();
2205  }
2206  static Vc_ALWAYS_INLINE Vc_CONST R denorm_min() noexcept
2207  {
2208  return numeric_limits<T>::denorm_min();
2209  }
2210 };
2211 } // namespace std
2212 //}}}1
#endif // VC_COMMON_SIMDARRAY_H_
// vim: foldmethod=marker
Vc::IndexesFromZero
constexpr VectorSpecialInitializerIndexesFromZero IndexesFromZero
The special object Vc::IndexesFromZero can be used to construct Vector objects initialized to values ...
Definition: types.h:95
Vc::min
Vc::Vector< T > min(const Vc::Vector< T > &x, const Vc::Vector< T > &y)
Vc::ldexp
Vc::Vector< T > ldexp(Vc::Vector< T > x, Vc::SimdArray< int, size()> e)
Multiply floating-point number by integral power of 2.
Vc::operator<<
std::ostream & operator<<(std::ostream &out, const Vc::Vector< T, Abi > &v)
Prints the contents of a vector into a stream object.
Definition: IO:118
Vc::DefaultLoadTag
UnalignedTag DefaultLoadTag
The default load tag type uses unaligned (non-streaming) loads.
Definition: loadstoreflags.h:167
Vc::max
Vc::Vector< T > max(const Vc::Vector< T > &x, const Vc::Vector< T > &y)
std
Definition: vector.h:258
Vc::Traits::isSimdArray
Identifies any possible SimdArray type (independent of const/volatile or reference) ...
Definition: type_traits.h:143
Vc::Common::unpackArgumentsAuto
void unpackArgumentsAuto(Op op, R &&r, Args &&...args)
The interface to start the machinery.
Definition: simdarrayhelper.h:525
Vc::SimdArray
Data-parallel type with (somewhat) arbitrary number of components.
Definition: simdarray.h:475
Vc::Traits::simd_vector_size
The value member will either be the number of SIMD vector entries or 0 if T is not a SIMD type...
Definition: type_traits.h:172
Vc::simd_cast
enable_if< std::is_same< To, Traits::decay< From > >::value, To > simd_cast(From &&x)
Casts the argument x from type From to type To.
Definition: simd_cast.h:49
Vc::vector
Common::AdaptSubscriptOperator< std::vector< T, Allocator >> vector
An adapted std::vector container with an additional subscript operator which implements gather and sc...
Definition: vector:51
Vc::Traits::is_simd_vector
Identifies any SIMD vector type (independent of implementation or whether it's SimdArray).
Definition: type_traits.h:134
Vc::copysign
Vector< T, Abi > copysign(Vector< T, Abi > magnitude, Vector< T, Abi > sign)
Copies the sign(s) of sign to the value(s) in magnitude and returns the resulting vector...
Vc::SimdizeDetail::assign
void assign(Adapter< S, T, N > &a, size_t i, const S &x)
Assigns one scalar object x to a SIMD slot at offset i in the simdized object a.
Definition: simdize.h:938
Vc::Zero
constexpr VectorSpecialInitializerZero Zero
The special object Vc::Zero can be used to construct Vector and Mask objects initialized to zero/fals...
Definition: types.h:85
Vc::SimdizeDetail::shifted
Adapter< S, T, N > shifted(const Adapter< S, T, N > &a, int shift)
Returns a new vectorized object where each entry is shifted by shift.
Definition: simdize.h:999
Vc
Vector Classes Namespace.
Definition: cpuid.h:33
Vc::One
constexpr VectorSpecialInitializerOne One
The special object Vc::One can be used to construct Vector and Mask objects initialized to one/true...
Definition: types.h:90
Vc::MemoryAlignment
constexpr std::size_t MemoryAlignment
Specifies the most conservative memory alignment necessary for aligned loads and stores of Vector typ...
Definition: vector.h:219
Vc::Unaligned
constexpr UnalignedTag Unaligned
Use this object for a flags parameter to request unaligned loads and stores.
Definition: loadstoreflags.h:197