Vc  1.2.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)); }
222 
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  }
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  }
254 
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; }
277 
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_
324 
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  }
349 
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_NOTHING_EXPECTING_SEMICOLON
363  Vc_REDUCTION_FUNCTION_(min);
364  Vc_REDUCTION_FUNCTION_(max);
365  Vc_REDUCTION_FUNCTION_(product);
366  Vc_REDUCTION_FUNCTION_(sum);
367 #undef Vc_REDUCTION_FUNCTION_
368  Vc_INTRINSIC Vc_PURE SimdArray partialSum() const { return data.partialSum(); }
369 
370  template <typename F> Vc_INTRINSIC SimdArray apply(F &&f) const
371  {
372  return {data.apply(std::forward<F>(f))};
373  }
374  template <typename F> Vc_INTRINSIC SimdArray apply(F &&f, const mask_type &k) const
375  {
376  return {data.apply(std::forward<F>(f), k)};
377  }
378 
379  Vc_INTRINSIC SimdArray shifted(int amount) const
380  {
381  return {data.shifted(amount)};
382  }
383 
384  template <std::size_t NN>
385  Vc_INTRINSIC SimdArray shifted(int amount, const SimdArray<value_type, NN> &shiftIn)
386  const
387  {
388  return {data.shifted(amount, simd_cast<VectorType>(shiftIn))};
389  }
390 
391  Vc_INTRINSIC SimdArray rotated(int amount) const
392  {
393  return {data.rotated(amount)};
394  }
395 
397  Vc_INTRINSIC Vc_DEPRECATED("use exponent(x) instead") SimdArray exponent() const
398  {
399  return {exponent(data)};
400  }
401 
402  Vc_INTRINSIC SimdArray interleaveLow(SimdArray x) const
403  {
404  return {data.interleaveLow(x.data)};
405  }
406  Vc_INTRINSIC SimdArray interleaveHigh(SimdArray x) const
407  {
408  return {data.interleaveHigh(x.data)};
409  }
410 
411  Vc_INTRINSIC SimdArray reversed() const
412  {
413  return {data.reversed()};
414  }
415 
416  Vc_INTRINSIC SimdArray sorted() const
417  {
418  return {data.sorted()};
419  }
420 
421  template <typename G> static Vc_INTRINSIC SimdArray generate(const G &gen)
422  {
423  return {VectorType::generate(gen)};
424  }
425 
426  Vc_INTRINSIC Vc_DEPRECATED("use copysign(x, y) instead") SimdArray
427  copySign(const SimdArray &reference) const
428  {
429  return {Vc::copysign(data, reference.data)};
430  }
431 
432  friend VectorType &internal_data<>(SimdArray &x);
433  friend const VectorType &internal_data<>(const SimdArray &x);
434 
436  Vc_INTRINSIC SimdArray(VectorType &&x) : data(std::move(x)) {}
437 
438  Vc_FREE_STORE_OPERATORS_ALIGNED(alignof(storage_type));
439 
440 private:
441  storage_type data;
442 };
443 template <typename T, std::size_t N, typename VectorType> constexpr std::size_t SimdArray<T, N, VectorType, N>::Size;
444 template <typename T, std::size_t N, typename VectorType>
446 template <typename T, std::size_t N, typename VectorType>
447 Vc_INTRINSIC VectorType &internal_data(SimdArray<T, N, VectorType, N> &x)
448 {
449  return x.data;
450 }
451 template <typename T, std::size_t N, typename VectorType>
452 Vc_INTRINSIC const VectorType &internal_data(const SimdArray<T, N, VectorType, N> &x)
453 {
454  return x.data;
455 }
456 
457 // gatherImplementation {{{2
458 template <typename T, std::size_t N, typename VectorType>
459 template <typename MT, typename IT>
461  IT &&indexes)
462 {
463  data.gather(mem, std::forward<IT>(indexes));
464 }
465 template <typename T, std::size_t N, typename VectorType>
466 template <typename MT, typename IT>
468  IT &&indexes,
469  MaskArgument mask)
470 {
471  data.gather(mem, std::forward<IT>(indexes), mask);
472 }
473 
474 // generic SimdArray {{{1
507 template <typename T, size_t N, typename V, size_t Wt>
508 class alignas(((Common::nextPowerOfTwo(N) * (sizeof(V) / V::size()) - 1) & 127) +
510  1) SimdArray
511 {
512  static_assert(std::is_same<T, double>::value ||
513  std::is_same<T, float>::value ||
514  std::is_same<T, int32_t>::value ||
515  std::is_same<T, uint32_t>::value ||
516  std::is_same<T, int16_t>::value ||
517  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 }");
518  static_assert(
519  // either the EntryType and VectorEntryType of the main V are equal
520  std::is_same<typename V::EntryType, typename V::VectorEntryType>::value ||
521  // or N is a multiple of V::size()
522  (N % V::size() == 0),
523  "SimdArray<(un)signed short, N> on MIC only works correctly for N = k * "
524  "MIC::(u)short_v::size(), i.e. k * 16.");
525 
526  using my_traits = SimdArrayTraits<T, N>;
527  static constexpr std::size_t N0 = my_traits::N0;
528  static constexpr std::size_t N1 = my_traits::N1;
529  using Split = Common::Split<N0>;
530  template <typename U, std::size_t K> using CArray = U[K];
531 
532 public:
533  using storage_type0 = typename my_traits::storage_type0;
534  using storage_type1 = typename my_traits::storage_type1;
535  static_assert(storage_type0::size() == N0, "");
536 
540  using vector_type = V;
541  using vectorentry_type = typename storage_type0::vectorentry_type;
542  typedef vectorentry_type alias_type Vc_MAY_ALIAS;
543 
545  using value_type = T;
546 
549 
552 
563  static constexpr std::size_t size() { return N; }
564 
566  using Mask = mask_type;
568  using MaskType = Mask;
569  using MaskArgument = const MaskType &;
570  using VectorEntryType = vectorentry_type;
572  using EntryType = value_type;
574  using IndexType = index_type;
575  using AsArg = const SimdArray &;
576 
578  static constexpr std::size_t MemoryAlignment =
582 
585 
587  static Vc_INTRINSIC SimdArray Zero()
588  {
589  return SimdArray(Vc::Zero);
590  }
591 
593  static Vc_INTRINSIC SimdArray One()
594  {
595  return SimdArray(Vc::One);
596  }
597 
599  static Vc_INTRINSIC SimdArray IndexesFromZero()
600  {
601  return SimdArray(Vc::IndexesFromZero);
602  }
603 
605  static Vc_INTRINSIC SimdArray Random()
606  {
607  return fromOperation(Common::Operations::random());
608  }
609 
611  template <typename G> static Vc_INTRINSIC SimdArray generate(const G &gen) // {{{2
612  {
613  auto tmp = storage_type0::generate(gen); // GCC bug: the order of evaluation in
614  // an initializer list is well-defined
615  // (front to back), but GCC 4.8 doesn't
616  // implement this correctly. Therefore
617  // we enforce correct order.
618  return {std::move(tmp),
619  storage_type1::generate([&](std::size_t i) { return gen(i + N0); })};
620  }
622 
625 
627  SimdArray() = default;
629 
632 
634  Vc_INTRINSIC SimdArray(value_type a) : data0(a), data1(a) {}
635  template <
636  typename U,
637  typename = enable_if<std::is_same<U, int>::value && !std::is_same<int, value_type>::value>>
638  SimdArray(U a)
639  : SimdArray(static_cast<value_type>(a))
640  {
641  }
643 
644  // default copy ctor/operator
645  SimdArray(const SimdArray &) = default;
646  SimdArray(SimdArray &&) = default;
647  SimdArray &operator=(const SimdArray &) = default;
648 
649  // load ctor
650  template <typename U,
651  typename Flags = DefaultLoadTag,
652  typename = enable_if<Traits::is_load_store_flag<Flags>::value>>
653  explicit Vc_INTRINSIC SimdArray(const U *mem, Flags f = Flags())
654  : data0(mem, f), data1(mem + storage_type0::size(), f)
655  {
656  }
663  template <typename U, std::size_t Extent, typename Flags = DefaultLoadTag,
664  typename = enable_if<Traits::is_load_store_flag<Flags>::value>>
665  explicit Vc_INTRINSIC SimdArray(CArray<U, Extent> &mem, Flags f = Flags())
666  : data0(&mem[0], f), data1(&mem[storage_type0::size()], f)
667  {
668  }
672  template <typename U, std::size_t Extent, typename Flags = DefaultLoadTag,
673  typename = enable_if<Traits::is_load_store_flag<Flags>::value>>
674  explicit Vc_INTRINSIC SimdArray(const CArray<U, Extent> &mem, Flags f = Flags())
675  : data0(&mem[0], f), data1(&mem[storage_type0::size()], f)
676  {
677  }
678 
679  // initializer list
680  Vc_INTRINSIC SimdArray(const std::initializer_list<value_type> &init)
681  : data0(init.begin(), Vc::Unaligned)
682  , data1(init.begin() + storage_type0::size(), Vc::Unaligned)
683  {
684 #if defined Vc_CXX14 && 0 // doesn't compile yet
685  static_assert(init.size() == size(), "The initializer_list argument to "
686  "SimdArray<T, N> must contain exactly N "
687  "values.");
688 #else
689  Vc_ASSERT(init.size() == size());
690 #endif
691  }
692 
693 #include "gatherinterface.h"
694 
695  // forward all remaining ctors
696  template <typename... Args,
697  typename = enable_if<!Traits::is_cast_arguments<Args...>::value &&
698  !Traits::is_initializer_list<Args...>::value &&
699  !Traits::is_gather_signature<Args...>::value &&
700  !Traits::is_load_arguments<Args...>::value>>
701  explicit Vc_INTRINSIC SimdArray(Args &&... args)
702  : data0(Split::lo(args)...) // no forward here - it could move and thus
703  // break the next line
704  , data1(Split::hi(std::forward<Args>(args))...)
705  {
706  }
707 
708  // explicit casts
709  template <typename W>
710  Vc_INTRINSIC explicit SimdArray(
711  W &&x,
712  enable_if<(Traits::is_simd_vector<W>::value && Traits::simd_vector_size<W>::value == N &&
713  !(std::is_convertible<Traits::entry_type_of<W>, T>::value &&
714  Traits::isSimdArray<W>::value))> = nullarg)
715  : data0(Split::lo(x)), data1(Split::hi(x))
716  {
717  }
718 
719  // implicit casts
720  template <typename W>
721  Vc_INTRINSIC SimdArray(
722  W &&x,
723  enable_if<(Traits::isSimdArray<W>::value && Traits::simd_vector_size<W>::value == N &&
724  std::is_convertible<Traits::entry_type_of<W>, T>::value)> = nullarg)
725  : data0(Split::lo(x)), data1(Split::hi(x))
726  {
727  }
728 
729  // implicit conversion to Vector<U, AnyAbi> for if Vector<U, AnyAbi>::size() == N and
730  // T implicitly convertible to U
731  template <typename W,
732  typename = enable_if<
733  Traits::is_simd_vector<W>::value && !Traits::isSimdArray<W>::value &&
734  std::is_convertible<T, typename W::EntryType>::value && W::size() == N>>
735  operator W() const
736  {
737  return simd_cast<W>(*this);
738  }
739 
741 
742  Vc_INTRINSIC void setZero()
743  {
744  data0.setZero();
745  data1.setZero();
746  }
747  Vc_INTRINSIC void setZero(const mask_type &k)
748  {
749  data0.setZero(Split::lo(k));
750  data1.setZero(Split::hi(k));
751  }
752  Vc_INTRINSIC void setZeroInverted()
753  {
754  data0.setZeroInverted();
755  data1.setZeroInverted();
756  }
757  Vc_INTRINSIC void setZeroInverted(const mask_type &k)
758  {
759  data0.setZeroInverted(Split::lo(k));
760  data1.setZeroInverted(Split::hi(k));
761  }
762 
763 
764  Vc_INTRINSIC void setQnan() {
765  data0.setQnan();
766  data1.setQnan();
767  }
768  Vc_INTRINSIC void setQnan(const mask_type &m) {
769  data0.setQnan(Split::lo(m));
770  data1.setQnan(Split::hi(m));
771  }
772 
774  template <typename Op, typename... Args>
775  static Vc_INTRINSIC SimdArray fromOperation(Op op, Args &&... args)
776  {
777  SimdArray r = {
778  storage_type0::fromOperation(op, Split::lo(args)...), // no forward here - it
779  // could move and thus
780  // break the next line
781  storage_type1::fromOperation(op, Split::hi(std::forward<Args>(args))...)};
782  return r;
783  }
784 
786  template <typename Op, typename... Args>
787  static Vc_INTRINSIC void callOperation(Op op, Args &&... args)
788  {
789  storage_type0::callOperation(op, Split::lo(args)...);
790  storage_type1::callOperation(op, Split::hi(std::forward<Args>(args))...);
791  }
792 
793 
794  template <typename U, typename... Args> Vc_INTRINSIC void load(const U *mem, Args &&... args)
795  {
796  data0.load(mem, Split::lo(args)...); // no forward here - it could move and thus
797  // break the next line
798  data1.load(mem + storage_type0::size(), Split::hi(std::forward<Args>(args))...);
799  }
800 
801  template <typename U, typename... Args> Vc_INTRINSIC void store(U *mem, Args &&... args) const
802  {
803  data0.store(mem, Split::lo(args)...); // no forward here - it could move and thus
804  // break the next line
805  data1.store(mem + storage_type0::size(), Split::hi(std::forward<Args>(args))...);
806  }
807 
808  Vc_INTRINSIC mask_type operator!() const
809  {
810  return {!data0, !data1};
811  }
812 
813  Vc_INTRINSIC SimdArray operator-() const
814  {
815  return {-data0, -data1};
816  }
817 
819  Vc_INTRINSIC SimdArray operator+() const { return *this; }
820 
821  Vc_INTRINSIC SimdArray operator~() const
822  {
823  return {~data0, ~data1};
824  }
825 
826  // left/right shift operators {{{2
827  template <typename U,
828  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
829  Vc_INTRINSIC Vc_CONST SimdArray operator<<(U x) const
830  {
831  return {data0 << x, data1 << x};
832  }
833  template <typename U,
834  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
835  Vc_INTRINSIC SimdArray &operator<<=(U x)
836  {
837  data0 <<= x;
838  data1 <<= x;
839  return *this;
840  }
841  template <typename U,
842  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
843  Vc_INTRINSIC Vc_CONST SimdArray operator>>(U x) const
844  {
845  return {data0 >> x, data1 >> x};
846  }
847  template <typename U,
848  typename = enable_if<std::is_integral<T>::value && std::is_integral<U>::value>>
849  Vc_INTRINSIC SimdArray &operator>>=(U x)
850  {
851  data0 >>= x;
852  data1 >>= x;
853  return *this;
854  }
855 
856  // binary operators {{{2
857 #define Vc_BINARY_OPERATOR_(op) \
858  Vc_INTRINSIC Vc_CONST SimdArray operator op(const SimdArray &rhs) const \
859  { \
860  return {data0 op rhs.data0, data1 op rhs.data1}; \
861  } \
862  Vc_INTRINSIC SimdArray &operator op##=(const SimdArray &rhs) \
863  { \
864  data0 op## = rhs.data0; \
865  data1 op## = rhs.data1; \
866  return *this; \
867  }
868  Vc_ALL_ARITHMETICS(Vc_BINARY_OPERATOR_);
869  Vc_ALL_BINARY(Vc_BINARY_OPERATOR_);
870  Vc_ALL_SHIFTS(Vc_BINARY_OPERATOR_);
871 #undef Vc_BINARY_OPERATOR_
872 
873 #define Vc_COMPARES(op) \
874  Vc_INTRINSIC mask_type operator op(const SimdArray &rhs) const \
875  { \
876  return {data0 op rhs.data0, data1 op rhs.data1}; \
877  }
878  Vc_ALL_COMPARES(Vc_COMPARES);
879 #undef Vc_COMPARES
880 
881  // operator[] {{{2
884 
886  Vc_INTRINSIC alias_type &operator[](std::size_t index)
887  {
888  auto tmp = reinterpret_cast<alias_type *>(this);
889  return tmp[index];
890  }
891 
893  Vc_INTRINSIC value_type operator[](std::size_t index) const
894  {
895  const auto tmp = reinterpret_cast<const alias_type *>(this);
896  return tmp[index];
897  }
900  // operator(){{{2
902  Vc_INTRINSIC Common::WriteMaskedVector<SimdArray, mask_type> operator()(
903  const mask_type &mask)
904  {
905  return {this, mask};
906  }
907 
909  Vc_INTRINSIC void assign(const SimdArray &v, const mask_type &k) //{{{2
910  {
911  data0.assign(v.data0, internal_data0(k));
912  data1.assign(v.data1, internal_data1(k));
913  }
914 
915  // reductions {{{2
916 #define Vc_REDUCTION_FUNCTION_(name_, binary_fun_, scalar_fun_) \
917 private: \
918  template <typename ForSfinae = void> \
919  Vc_INTRINSIC enable_if<std::is_same<ForSfinae, void>::value && \
920  storage_type0::size() == storage_type1::size(), \
921  value_type> name_##_impl() const \
922  { \
923  return binary_fun_(data0, data1).name_(); \
924  } \
925  \
926  template <typename ForSfinae = void> \
927  Vc_INTRINSIC enable_if<std::is_same<ForSfinae, void>::value && \
928  storage_type0::size() != storage_type1::size(), \
929  value_type> name_##_impl() const \
930  { \
931  return scalar_fun_(data0.name_(), data1.name_()); \
932  } \
933  \
934 public: \
935  \
936  Vc_INTRINSIC value_type name_() const { return name_##_impl(); } \
937  \
938  Vc_INTRINSIC value_type name_(const mask_type &mask) const \
939  { \
940  if (Vc_IS_UNLIKELY(Split::lo(mask).isEmpty())) { \
941  return data1.name_(Split::hi(mask)); \
942  } else if (Vc_IS_UNLIKELY(Split::hi(mask).isEmpty())) { \
943  return data0.name_(Split::lo(mask)); \
944  } else { \
945  return scalar_fun_(data0.name_(Split::lo(mask)), \
946  data1.name_(Split::hi(mask))); \
947  } \
948  } \
949  Vc_NOTHING_EXPECTING_SEMICOLON
950  Vc_REDUCTION_FUNCTION_(min, Vc::min, std::min);
951  Vc_REDUCTION_FUNCTION_(max, Vc::max, std::max);
952  Vc_REDUCTION_FUNCTION_(product, internal::product_helper_, internal::product_helper_);
953  Vc_REDUCTION_FUNCTION_(sum, internal::sum_helper_, internal::sum_helper_);
954 #undef Vc_REDUCTION_FUNCTION_
955  Vc_INTRINSIC Vc_PURE SimdArray partialSum() const //{{{2
957  {
958  auto ps0 = data0.partialSum();
959  auto tmp = data1;
960  tmp[0] += ps0[data0.size() - 1];
961  return {std::move(ps0), tmp.partialSum()};
962  }
964  // apply {{{2
966  template <typename F> Vc_INTRINSIC SimdArray apply(F &&f) const
967  {
968  return {data0.apply(f), data1.apply(f)};
969  }
971  template <typename F> Vc_INTRINSIC SimdArray apply(F &&f, const mask_type &k) const
972  {
973  return {data0.apply(f, Split::lo(k)), data1.apply(f, Split::hi(k))};
974  }
976  // shifted {{{2
978  inline SimdArray shifted(int amount) const
979  {
980  constexpr int SSize = Size;
981  constexpr int SSize0 = storage_type0::Size;
982  constexpr int SSize1 = storage_type1::Size;
983  if (amount == 0) {
984  return *this;
985  }
986  if (amount < 0) {
987  if (amount > -SSize0) {
988  return {data0.shifted(amount), data1.shifted(amount, data0)};
989  }
990  if (amount == -SSize0) {
991  return {storage_type0::Zero(), simd_cast<storage_type1>(data0)};
992  }
993  if (amount < -SSize0) {
994  return {storage_type0::Zero(), simd_cast<storage_type1>(data0.shifted(
995  amount + SSize0))};
996  }
997  return Zero();
998  } else {
999  if (amount >= SSize) {
1000  return Zero();
1001  } else if (amount >= SSize0) {
1002  return {
1003  simd_cast<storage_type0>(data1).shifted(amount - SSize0),
1005  } else if (amount >= SSize1) {
1006  return {data0.shifted(amount, data1), storage_type1::Zero()};
1007  } else {
1008  return {data0.shifted(amount, data1), data1.shifted(amount)};
1009  }
1010  }
1011  }
1012 
1013  template <std::size_t NN>
1014  inline enable_if<
1015  !(std::is_same<storage_type0, storage_type1>::value && // not bisectable
1016  N == NN),
1017  SimdArray>
1018  shifted(int amount, const SimdArray<value_type, NN> &shiftIn) const
1019  {
1020  constexpr int SSize = Size;
1021  if (amount < 0) {
1022  return SimdArray::generate([&](int i) -> value_type {
1023  i += amount;
1024  if (i >= 0) {
1025  return operator[](i);
1026  } else if (i >= -SSize) {
1027  return shiftIn[i + SSize];
1028  }
1029  return 0;
1030  });
1031  }
1032  return SimdArray::generate([&](int i) -> value_type {
1033  i += amount;
1034  if (i < SSize) {
1035  return operator[](i);
1036  } else if (i < 2 * SSize) {
1037  return shiftIn[i - SSize];
1038  }
1039  return 0;
1040  });
1041  }
1042 
1043  template <std::size_t NN>
1044  inline
1045  enable_if<(std::is_same<storage_type0, storage_type1>::value && // bisectable
1046  N == NN),
1047  SimdArray>
1048  shifted(int amount, const SimdArray<value_type, NN> &shiftIn) const
1049  {
1050  constexpr int SSize = Size;
1051  if (amount < 0) {
1052  if (amount > -static_cast<int>(storage_type0::Size)) {
1053  return {data0.shifted(amount, internal_data1(shiftIn)),
1054  data1.shifted(amount, data0)};
1055  }
1056  if (amount == -static_cast<int>(storage_type0::Size)) {
1057  return {storage_type0(internal_data1(shiftIn)), storage_type1(data0)};
1058  }
1059  if (amount > -SSize) {
1060  return {
1061  internal_data1(shiftIn)
1062  .shifted(amount + static_cast<int>(storage_type0::Size), internal_data0(shiftIn)),
1063  data0.shifted(amount + static_cast<int>(storage_type0::Size), internal_data1(shiftIn))};
1064  }
1065  if (amount == -SSize) {
1066  return shiftIn;
1067  }
1068  if (amount > -2 * SSize) {
1069  return shiftIn.shifted(amount + SSize);
1070  }
1071  }
1072  if (amount == 0) {
1073  return *this;
1074  }
1075  if (amount < static_cast<int>(storage_type0::Size)) {
1076  return {data0.shifted(amount, data1),
1077  data1.shifted(amount, internal_data0(shiftIn))};
1078  }
1079  if (amount == static_cast<int>(storage_type0::Size)) {
1080  return {storage_type0(data1), storage_type1(internal_data0(shiftIn))};
1081  }
1082  if (amount < SSize) {
1083  return {data1.shifted(amount - static_cast<int>(storage_type0::Size), internal_data0(shiftIn)),
1084  internal_data0(shiftIn)
1085  .shifted(amount - static_cast<int>(storage_type0::Size), internal_data1(shiftIn))};
1086  }
1087  if (amount == SSize) {
1088  return shiftIn;
1089  }
1090  if (amount < 2 * SSize) {
1091  return shiftIn.shifted(amount - SSize);
1092  }
1093  return Zero();
1094  }
1096  // rotated {{{2
1098  Vc_INTRINSIC SimdArray rotated(int amount) const
1099  {
1100  amount %= int(size());
1101  if (amount == 0) {
1102  return *this;
1103  } else if (amount < 0) {
1104  amount += size();
1105  }
1106 
1107  auto &&d0cvtd = simd_cast<storage_type1>(data0);
1108  auto &&d1cvtd = simd_cast<storage_type0>(data1);
1109  constexpr int size0 = storage_type0::size();
1110  constexpr int size1 = storage_type1::size();
1111 
1112  if (amount == size0 && std::is_same<storage_type0, storage_type1>::value) {
1113  return {std::move(d1cvtd), std::move(d0cvtd)};
1114  } else if (amount < size1) {
1115  return {data0.shifted(amount, d1cvtd), data1.shifted(amount, d0cvtd)};
1116  } else if (amount == size1) {
1117  return {data0.shifted(amount, d1cvtd), std::move(d0cvtd)};
1118  } else if (int(size()) - amount < size1) {
1119  return {data0.shifted(amount - int(size()), d1cvtd.shifted(size1 - size0)),
1120  data1.shifted(amount - int(size()), data0.shifted(size0 - size1))};
1121  } else if (int(size()) - amount == size1) {
1122  return {data0.shifted(-size1, d1cvtd.shifted(size1 - size0)),
1123  simd_cast<storage_type1>(data0.shifted(size0 - size1))};
1124  } else if (amount <= size0) {
1125  return {data0.shifted(size1, d1cvtd).shifted(amount - size1, data0),
1126  simd_cast<storage_type1>(data0.shifted(amount - size1))};
1127  } else {
1128  return {data0.shifted(size1, d1cvtd).shifted(amount - size1, data0),
1129  simd_cast<storage_type1>(data0.shifted(amount - size1, d1cvtd))};
1130  }
1131  return *this;
1132  }
1133 
1134  // interleaveLow/-High {{{2
1136  Vc_INTRINSIC SimdArray interleaveLow(const SimdArray &x) const
1137  {
1138  // return data0[0], x.data0[0], data0[1], x.data0[1], ...
1139  return {data0.interleaveLow(x.data0),
1140  simd_cast<storage_type1>(data0.interleaveHigh(x.data0))};
1141  }
1143  Vc_INTRINSIC SimdArray interleaveHigh(const SimdArray &x) const
1144  {
1145  return interleaveHighImpl(
1146  x,
1147  std::integral_constant<bool, storage_type0::Size == storage_type1::Size>());
1148  }
1149 
1150 private:
1152  Vc_INTRINSIC SimdArray interleaveHighImpl(const SimdArray &x, std::true_type) const
1153  {
1154  return {data1.interleaveLow(x.data1), data1.interleaveHigh(x.data1)};
1155  }
1157  inline SimdArray interleaveHighImpl(const SimdArray &x, std::false_type) const
1158  {
1159  return {data0.interleaveHigh(x.data0)
1160  .shifted(storage_type1::Size,
1161  simd_cast<storage_type0>(data1.interleaveLow(x.data1))),
1162  data1.interleaveHigh(x.data1)};
1163  }
1165 public:
1167  inline SimdArray reversed() const //{{{2
1168  {
1169  if (std::is_same<storage_type0, storage_type1>::value) {
1170  return {simd_cast<storage_type0>(data1).reversed(),
1171  simd_cast<storage_type1>(data0).reversed()};
1172  } else {
1173  return {data0.shifted(storage_type1::Size, data1).reversed(),
1174  simd_cast<storage_type1>(data0.reversed().shifted(
1175  storage_type0::Size - storage_type1::Size))};
1176  }
1177  }
1179  inline SimdArray sorted() const //{{{2
1180  {
1181  return sortedImpl(
1182  std::integral_constant<bool, storage_type0::Size == storage_type1::Size>());
1183  }
1184 
1186  Vc_INTRINSIC SimdArray sortedImpl(std::true_type) const
1187  {
1188 #ifdef Vc_DEBUG_SORTED
1189  std::cerr << "-- " << data0 << data1 << '\n';
1190 #endif
1191  const auto a = data0.sorted();
1192  const auto b = data1.sorted().reversed();
1193  const auto lo = Vc::min(a, b);
1194  const auto hi = Vc::max(a, b);
1195  return {lo.sorted(), hi.sorted()};
1196  }
1197 
1199  Vc_INTRINSIC SimdArray sortedImpl(std::false_type) const
1200  {
1201  using SortableArray = SimdArray<value_type, Common::nextPowerOfTwo(size())>;
1202  auto sortable = simd_cast<SortableArray>(*this);
1203  for (std::size_t i = Size; i < SortableArray::Size; ++i) {
1204  using limits = std::numeric_limits<value_type>;
1205  if (limits::has_infinity) {
1206  sortable[i] = limits::infinity();
1207  } else {
1208  sortable[i] = std::numeric_limits<value_type>::max();
1209  }
1210  }
1211  return simd_cast<SimdArray>(sortable.sorted());
1212 
1213  /* The following implementation appears to be less efficient. But this may need further
1214  * work.
1215  const auto a = data0.sorted();
1216  const auto b = data1.sorted();
1217 #ifdef Vc_DEBUG_SORTED
1218  std::cerr << "== " << a << b << '\n';
1219 #endif
1220  auto aIt = Vc::begin(a);
1221  auto bIt = Vc::begin(b);
1222  const auto aEnd = Vc::end(a);
1223  const auto bEnd = Vc::end(b);
1224  return SimdArray::generate([&](std::size_t) {
1225  if (aIt == aEnd) {
1226  return *(bIt++);
1227  }
1228  if (bIt == bEnd) {
1229  return *(aIt++);
1230  }
1231  if (*aIt < *bIt) {
1232  return *(aIt++);
1233  } else {
1234  return *(bIt++);
1235  }
1236  });
1237  */
1238  }
1239 
1245  static constexpr std::size_t Size = size();
1246 
1248  Vc_INTRINSIC Vc_DEPRECATED("use exponent(x) instead") SimdArray exponent() const
1249  {
1250  return {exponent(data0), exponent(data1)};
1251  }
1252 
1254  Vc_INTRINSIC Vc_DEPRECATED("use isnegative(x) instead") MaskType isNegative() const
1255  {
1256  return {isnegative(data0), isnegative(data1)};
1257  }
1260  Vc_INTRINSIC Vc_DEPRECATED("use copysign(x, y) instead") SimdArray
1261  copySign(const SimdArray &reference) const
1262  {
1263  return {Vc::copysign(data0, reference.data0),
1264  Vc::copysign(data1, reference.data1)};
1265  }
1267 
1268  // internal_data0/1 {{{2
1269  friend storage_type0 &internal_data0<>(SimdArray &x);
1270  friend storage_type1 &internal_data1<>(SimdArray &x);
1271  friend const storage_type0 &internal_data0<>(const SimdArray &x);
1272  friend const storage_type1 &internal_data1<>(const SimdArray &x);
1273 
1275  Vc_INTRINSIC SimdArray(storage_type0 &&x, storage_type1 &&y) //{{{2
1276  : data0(std::move(x)), data1(std::move(y))
1277  {
1278  }
1279 
1280  Vc_FREE_STORE_OPERATORS_ALIGNED(alignof(storage_type0));
1281 
1282 private: //{{{2
1283  storage_type0 data0;
1284  storage_type1 data1;
1285 };
1286 #undef Vc_CURRENT_CLASS_NAME
1287 template <typename T, std::size_t N, typename V, std::size_t M>
1288 constexpr std::size_t SimdArray<T, N, V, M>::Size;
1289 template <typename T, std::size_t N, typename V, std::size_t M>
1290 constexpr std::size_t SimdArray<T, N, V, M>::MemoryAlignment;
1291 
1292 // gatherImplementation {{{2
1293 template <typename T, std::size_t N, typename VectorType, std::size_t M>
1294 template <typename MT, typename IT>
1295 inline void SimdArray<T, N, VectorType, M>::gatherImplementation(const MT *mem,
1296  IT &&indexes)
1297 {
1298  data0.gather(mem, Split::lo(Common::Operations::gather(),
1299  indexes)); // don't forward indexes - it could move and
1300  // thus break the next line
1301  data1.gather(mem, Split::hi(Common::Operations::gather(), std::forward<IT>(indexes)));
1302 }
1303 template <typename T, std::size_t N, typename VectorType, std::size_t M>
1304 template <typename MT, typename IT>
1305 inline void SimdArray<T, N, VectorType, M>::gatherImplementation(const MT *mem,
1306  IT &&indexes, MaskArgument mask)
1307 {
1308  data0.gather(mem, Split::lo(Common::Operations::gather(), indexes),
1309  Split::lo(mask)); // don't forward indexes - it could move and
1310  // thus break the next line
1311  data1.gather(mem, Split::hi(Common::Operations::gather(), std::forward<IT>(indexes)),
1312  Split::hi(mask));
1313 }
1314 
1315 // internal_data0/1 (SimdArray) {{{1
1317 template <typename T, std::size_t N, typename V, std::size_t M>
1318 Vc_INTRINSIC typename SimdArrayTraits<T, N>::storage_type0 &internal_data0(
1319  SimdArray<T, N, V, M> &x)
1320 {
1321  return x.data0;
1322 }
1324 template <typename T, std::size_t N, typename V, std::size_t M>
1325 Vc_INTRINSIC typename SimdArrayTraits<T, N>::storage_type1 &internal_data1(
1326  SimdArray<T, N, V, M> &x)
1327 {
1328  return x.data1;
1329 }
1331 template <typename T, std::size_t N, typename V, std::size_t M>
1332 Vc_INTRINSIC const typename SimdArrayTraits<T, N>::storage_type0 &internal_data0(
1333  const SimdArray<T, N, V, M> &x)
1334 {
1335  return x.data0;
1336 }
1338 template <typename T, std::size_t N, typename V, std::size_t M>
1339 Vc_INTRINSIC const typename SimdArrayTraits<T, N>::storage_type1 &internal_data1(
1340  const SimdArray<T, N, V, M> &x)
1341 {
1342  return x.data1;
1343 }
1344 
1345 // binary operators {{{1
1346 namespace result_vector_type_internal
1347 {
1348 template <typename T>
1349 using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
1350 
1351 template <typename T>
1352 using is_integer_larger_than_int = std::integral_constant<
1353  bool, std::is_integral<T>::value &&(sizeof(T) > sizeof(int) ||
1354  std::is_same<T, long>::value ||
1355  std::is_same<T, unsigned long>::value)>;
1356 
1357 template <
1358  typename L, typename R,
1359  std::size_t N = Traits::isSimdArray<L>::value ? Traits::simd_vector_size<L>::value
1360  : Traits::simd_vector_size<R>::value,
1361  bool =
1362  (Traits::isSimdArray<L>::value ||
1363  Traits::isSimdArray<R>::value) // one of the operands must be a SimdArray
1364  && !std::is_same<type<L>, type<R>>::value // if the operands are of the same type
1365  // use the member function
1366  &&
1367  ((std::is_arithmetic<type<L>>::value &&
1368  !is_integer_larger_than_int<type<L>>::value) ||
1369  (std::is_arithmetic<type<R>>::value &&
1370  !is_integer_larger_than_int<type<R>>::value) // one of the operands is a scalar
1371  // type
1372  ||
1373  ( // or one of the operands is Vector<T> with Vector<T>::size() ==
1374  // SimdArray::size()
1375  Traits::simd_vector_size<L>::value == Traits::simd_vector_size<R>::value &&
1376  ((Traits::is_simd_vector<L>::value && !Traits::isSimdArray<L>::value) ||
1377  (Traits::is_simd_vector<R>::value && !Traits::isSimdArray<R>::value))))>
1378 struct evaluate;
1379 
1380 template <typename L, typename R, std::size_t N> struct evaluate<L, R, N, true>
1381 {
1382 private:
1383  using LScalar = Traits::entry_type_of<L>;
1384  using RScalar = Traits::entry_type_of<R>;
1385 
1386  template <bool B, typename True, typename False>
1387  using conditional = typename std::conditional<B, True, False>::type;
1388 
1389 public:
1390  // In principle we want the exact same rules for SimdArray<T> ⨉ SimdArray<U> as the standard
1391  // defines for T ⨉ U. BUT: short ⨉ short returns int (because all integral types smaller than
1392  // int are promoted to int before any operation). This would imply that SIMD types with integral
1393  // types smaller than int are more or less useless - and you could use SimdArray<int> from the
1394  // start. Therefore we special-case those operations where the scalar type of both operands is
1395  // integral and smaller than int.
1396  // In addition to that there is no generic support for 64-bit int SIMD types. Therefore
1397  // promotion to a 64-bit integral type (including `long` because it can potentially have 64
1398  // bits) also is not done. But if one of the operands is a scalar type that is larger than int
1399  // then the operator is disabled altogether. We do not want an implicit demotion.
1400  using type = SimdArray<
1401  conditional<(std::is_integral<LScalar>::value &&std::is_integral<RScalar>::value &&
1402  sizeof(LScalar) < sizeof(int) &&
1403  sizeof(RScalar) < sizeof(int)),
1404  conditional<(sizeof(LScalar) == sizeof(RScalar)),
1405  conditional<std::is_unsigned<LScalar>::value, LScalar, RScalar>,
1406  conditional<(sizeof(LScalar) > sizeof(RScalar)), LScalar, RScalar>>,
1407  decltype(std::declval<LScalar>() + std::declval<RScalar>())>,
1408  N>;
1409 };
1410 
1411 } // namespace result_vector_type_internal
1412 
1413 template <typename L, typename R>
1414 using result_vector_type = typename result_vector_type_internal::evaluate<L, R>::type;
1415 
1416 static_assert(
1417  std::is_same<result_vector_type<short int, Vc::SimdArray<short unsigned int, 32ul>>,
1419  "result_vector_type does not work");
1420 
1421 #define Vc_BINARY_OPERATORS_(op_) \
1422  \
1423  template <typename L, typename R> \
1424  Vc_INTRINSIC result_vector_type<L, R> operator op_(L &&lhs, R &&rhs) \
1425  { \
1426  using Return = result_vector_type<L, R>; \
1427  return Return(std::forward<L>(lhs)) op_ Return(std::forward<R>(rhs)); \
1428  }
1429 
1446 Vc_ALL_ARITHMETICS(Vc_BINARY_OPERATORS_);
1448 Vc_ALL_BINARY(Vc_BINARY_OPERATORS_);
1450 #undef Vc_BINARY_OPERATORS_
1451 #define Vc_BINARY_OPERATORS_(op_) \
1452  \
1453  template <typename L, typename R> \
1454  Vc_INTRINSIC typename result_vector_type<L, R>::mask_type operator op_(L &&lhs, \
1455  R &&rhs) \
1456  { \
1457  using Promote = result_vector_type<L, R>; \
1458  return Promote(std::forward<L>(lhs)) op_ Promote(std::forward<R>(rhs)); \
1459  }
1460 
1477 Vc_ALL_COMPARES(Vc_BINARY_OPERATORS_);
1480 #undef Vc_BINARY_OPERATORS_
1481 
1482 // math functions {{{1
1483 #define Vc_FORWARD_UNARY_OPERATOR(name_) \
1484  \
1485  template <typename T, std::size_t N, typename V, std::size_t M> \
1486  inline SimdArray<T, N, V, M> name_(const SimdArray<T, N, V, M> &x) \
1487  { \
1488  return SimdArray<T, N, V, M>::fromOperation( \
1489  Common::Operations::Forward_##name_(), x); \
1490  } \
1491  Vc_NOTHING_EXPECTING_SEMICOLON
1492 
1493 #define Vc_FORWARD_UNARY_BOOL_OPERATOR(name_) \
1494  \
1495  template <typename T, std::size_t N, typename V, std::size_t M> \
1496  inline SimdMaskArray<T, N, V, M> name_(const SimdArray<T, N, V, M> &x) \
1497  { \
1498  return SimdMaskArray<T, N, V, M>::fromOperation( \
1499  Common::Operations::Forward_##name_(), x); \
1500  } \
1501  Vc_NOTHING_EXPECTING_SEMICOLON
1502 
1503 #define Vc_FORWARD_BINARY_OPERATOR(name_) \
1504  \
1505  template <typename T, std::size_t N, typename V, std::size_t M> \
1506  inline SimdArray<T, N, V, M> name_(const SimdArray<T, N, V, M> &x, \
1507  const SimdArray<T, N, V, M> &y) \
1508  { \
1509  return SimdArray<T, N, V, M>::fromOperation( \
1510  Common::Operations::Forward_##name_(), x, y); \
1511  } \
1512  Vc_NOTHING_EXPECTING_SEMICOLON
1513 
1518 Vc_FORWARD_UNARY_OPERATOR(abs);
1520 Vc_FORWARD_UNARY_OPERATOR(asin);
1521 Vc_FORWARD_UNARY_OPERATOR(atan);
1522 Vc_FORWARD_BINARY_OPERATOR(atan2);
1523 Vc_FORWARD_UNARY_OPERATOR(ceil);
1524 Vc_FORWARD_BINARY_OPERATOR(copysign);
1525 Vc_FORWARD_UNARY_OPERATOR(cos);
1526 Vc_FORWARD_UNARY_OPERATOR(exp);
1527 Vc_FORWARD_UNARY_OPERATOR(exponent);
1528 Vc_FORWARD_UNARY_OPERATOR(floor);
1530 template <typename T, std::size_t N>
1532  const SimdArray<T, N> &c)
1534  return SimdArray<T, N>::fromOperation(Common::Operations::Forward_fma(), a, b, c);
1536 Vc_FORWARD_UNARY_BOOL_OPERATOR(isfinite);
1537 Vc_FORWARD_UNARY_BOOL_OPERATOR(isinf);
1538 Vc_FORWARD_UNARY_BOOL_OPERATOR(isnan);
1539 Vc_FORWARD_UNARY_BOOL_OPERATOR(isnegative);
1541 template <typename T, std::size_t N>
1543 {
1544  return SimdArray<T, N>::fromOperation(Common::Operations::Forward_frexp(), x, e);
1547 template <typename T, std::size_t N>
1548 SimdArray<T, N> ldexp(const SimdArray<T, N> &x, const SimdArray<int, N> &e)
1550  return SimdArray<T, N>::fromOperation(Common::Operations::Forward_ldexp(), x, e);
1552 Vc_FORWARD_UNARY_OPERATOR(log);
1553 Vc_FORWARD_UNARY_OPERATOR(log10);
1554 Vc_FORWARD_UNARY_OPERATOR(log2);
1555 Vc_FORWARD_UNARY_OPERATOR(reciprocal);
1556 Vc_FORWARD_UNARY_OPERATOR(round);
1557 Vc_FORWARD_UNARY_OPERATOR(rsqrt);
1558 Vc_FORWARD_UNARY_OPERATOR(sin);
1560 template <typename T, std::size_t N>
1563  SimdArray<T, N>::callOperation(Common::Operations::Forward_sincos(), x, sin, cos);
1565 Vc_FORWARD_UNARY_OPERATOR(sqrt);
1566 Vc_FORWARD_UNARY_OPERATOR(trunc);
1567 Vc_FORWARD_BINARY_OPERATOR(min);
1568 Vc_FORWARD_BINARY_OPERATOR(max);
1570 #undef Vc_FORWARD_UNARY_OPERATOR
1571 #undef Vc_FORWARD_UNARY_BOOL_OPERATOR
1572 #undef Vc_FORWARD_BINARY_OPERATOR
1573 
1574 // simd_cast {{{1
1575 // simd_cast_impl_smaller_input {{{2
1576 // The following function can be implemented without the sizeof...(From) overload.
1577 // However, ICC has a bug (Premier Issue #6000116338) which leads to an ICE. Splitting the
1578 // function in two works around the issue.
1579 template <typename Return, std::size_t N, typename T, typename... From>
1580 Vc_INTRINSIC Vc_CONST enable_if<sizeof...(From) != 0, Return>
1581 simd_cast_impl_smaller_input(const From &... xs, const T &last)
1582 {
1583  Return r = simd_cast<Return>(xs...);
1584  for (size_t i = 0; i < N; ++i) {
1585  r[i + N * sizeof...(From)] = static_cast<typename Return::EntryType>(last[i]);
1586  }
1587  return r;
1588 }
1589 template <typename Return, std::size_t N, typename T>
1590 Vc_INTRINSIC Vc_CONST Return simd_cast_impl_smaller_input(const T &last)
1591 {
1592  Return r = Return();
1593  for (size_t i = 0; i < N; ++i) {
1594  r[i] = static_cast<typename Return::EntryType>(last[i]);
1595  }
1596  return r;
1597 }
1598 template <typename Return, std::size_t N, typename T, typename... From>
1599 Vc_INTRINSIC Vc_CONST enable_if<sizeof...(From) != 0, Return> simd_cast_impl_larger_input(
1600  const From &... xs, const T &last)
1601 {
1602  Return r = simd_cast<Return>(xs...);
1603  for (size_t i = N * sizeof...(From); i < Return::Size; ++i) {
1604  r[i] = static_cast<typename Return::EntryType>(last[i - N * sizeof...(From)]);
1605  }
1606  return r;
1607 }
1608 template <typename Return, std::size_t N, typename T>
1609 Vc_INTRINSIC Vc_CONST Return simd_cast_impl_larger_input(const T &last)
1610 {
1611  Return r = Return();
1612  for (size_t i = 0; i < Return::size(); ++i) {
1613  r[i] = static_cast<typename Return::EntryType>(last[i]);
1614  }
1615  return r;
1616 }
1617 
1618 // simd_cast_without_last (declaration) {{{2
1619 template <typename Return, typename T, typename... From>
1620 Vc_INTRINSIC_L Vc_CONST_L Return
1621  simd_cast_without_last(const From &... xs, const T &) Vc_INTRINSIC_R Vc_CONST_R;
1622 
1623 // are_all_types_equal {{{2
1624 template <typename... Ts> struct are_all_types_equal;
1625 template <typename T>
1626 struct are_all_types_equal<T> : public std::integral_constant<bool, true>
1627 {
1628 };
1629 template <typename T0, typename T1, typename... Ts>
1630 struct are_all_types_equal<T0, T1, Ts...>
1631  : public std::integral_constant<
1632  bool, std::is_same<T0, T1>::value && are_all_types_equal<T1, Ts...>::value>
1633 {
1634 };
1635 
1636 // simd_cast_interleaved_argument_order (declarations) {{{2
1656 template <typename Return, typename... Ts>
1657 Vc_INTRINSIC Vc_CONST Return
1658  simd_cast_interleaved_argument_order(const Ts &... a, const Ts &... b);
1659 
1660 // simd_cast_with_offset (declarations and one impl) {{{2
1661 // offset == 0 {{{3
1662 template <typename Return, std::size_t offset, typename From, typename... Froms>
1663 Vc_INTRINSIC Vc_CONST
1664  enable_if<(are_all_types_equal<From, Froms...>::value && offset == 0), Return>
1665  simd_cast_with_offset(const From &x, const Froms &... xs);
1666 // offset > 0 && offset divisible by Return::Size {{{3
1667 template <typename Return, std::size_t offset, typename From>
1668 Vc_INTRINSIC Vc_CONST
1669  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size == 0), Return>
1670  simd_cast_with_offset(const From &x);
1671 // offset > 0 && offset NOT divisible && Return is non-atomic simd(mask)array {{{3
1672 template <typename Return, std::size_t offset, typename From>
1673 Vc_INTRINSIC Vc_CONST
1674  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size != 0 &&
1675  ((Traits::isSimdArray<Return>::value &&
1676  !Traits::isAtomicSimdArray<Return>::value) ||
1677  (Traits::isSimdMaskArray<Return>::value &&
1678  !Traits::isAtomicSimdMaskArray<Return>::value))),
1679  Return>
1680  simd_cast_with_offset(const From &x);
1681 // offset > 0 && offset NOT divisible && Return is atomic simd(mask)array {{{3
1682 template <typename Return, std::size_t offset, typename From>
1683 Vc_INTRINSIC Vc_CONST
1684  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size != 0 &&
1685  ((Traits::isSimdArray<Return>::value &&
1686  Traits::isAtomicSimdArray<Return>::value) ||
1687  (Traits::isSimdMaskArray<Return>::value &&
1688  Traits::isAtomicSimdMaskArray<Return>::value))),
1689  Return>
1690  simd_cast_with_offset(const From &x);
1691 // offset > first argument (drops first arg) {{{3
1692 template <typename Return, std::size_t offset, typename From, typename... Froms>
1693 Vc_INTRINSIC Vc_CONST enable_if<
1694  (are_all_types_equal<From, Froms...>::value && From::Size <= offset), Return>
1695  simd_cast_with_offset(const From &, const Froms &... xs)
1696 {
1697  return simd_cast_with_offset<Return, offset - From::Size>(xs...);
1698 }
1699 
1700 // offset > first and only argument (returns Zero) {{{3
1701 template <typename Return, std::size_t offset, typename From>
1702 Vc_INTRINSIC Vc_CONST enable_if<(From::Size <= offset), Return> simd_cast_with_offset(
1703  const From &)
1704 {
1705  return Return::Zero();
1706 }
1707 
1708 // first_type_of {{{2
1709 template <typename T, typename... Ts> struct first_type_of_impl
1710 {
1711  using type = T;
1712 };
1713 template <typename... Ts> using first_type_of = typename first_type_of_impl<Ts...>::type;
1714 
1715 // simd_cast_drop_arguments (declarations) {{{2
1716 template <typename Return, typename From>
1717 Vc_INTRINSIC Vc_CONST Return simd_cast_drop_arguments(From x);
1718 template <typename Return, typename... Froms>
1719 Vc_INTRINSIC Vc_CONST
1720  enable_if<(are_all_types_equal<Froms...>::value &&
1721  sizeof...(Froms) * first_type_of<Froms...>::Size < Return::Size),
1722  Return>
1723  simd_cast_drop_arguments(Froms... xs, first_type_of<Froms...> x);
1724 // The following function can be implemented without the sizeof...(From) overload.
1725 // However, ICC has a bug (Premier Issue #6000116338) which leads to an ICE. Splitting the
1726 // function in two works around the issue.
1727 template <typename Return, typename From, typename... Froms>
1728 Vc_INTRINSIC Vc_CONST enable_if<
1729  (are_all_types_equal<From, Froms...>::value &&
1730  (1 + sizeof...(Froms)) * From::Size >= Return::Size && sizeof...(Froms) != 0),
1731  Return>
1732 simd_cast_drop_arguments(Froms... xs, From x, From);
1733 template <typename Return, typename From>
1734 Vc_INTRINSIC Vc_CONST
1735  enable_if<(are_all_types_equal<From>::value && From::Size >= Return::Size), Return>
1736  simd_cast_drop_arguments(From x, From);
1737 
1738 namespace
1739 {
1740 #ifdef Vc_DEBUG_SIMD_CAST
1741 void debugDoNothing(const std::initializer_list<void *> &) {}
1742 template <typename T0, typename... Ts>
1743 inline void vc_debug_(const char *prefix, const char *suffix, const T0 &arg0,
1744  const Ts &... args)
1745 {
1746  std::cerr << prefix << arg0;
1747  debugDoNothing({&(std::cerr << ", " << args)...});
1748  std::cerr << suffix;
1749 }
1750 #else
1751 template <typename T0, typename... Ts>
1752 Vc_INTRINSIC void vc_debug_(const char *, const char *, const T0 &, const Ts &...)
1753 {
1754 }
1755 #endif
1756 } // unnamed namespace
1757 
1758 // simd_cast<T>(xs...) to SimdArray/-mask {{{2
1759 #define Vc_SIMDARRAY_CASTS(SimdArrayType_, trait_name_) \
1760  template <typename Return, typename From, typename... Froms> \
1761  Vc_INTRINSIC Vc_CONST enable_if<(Traits::isAtomic##SimdArrayType_<Return>::value && \
1762  !Traits::is##SimdArrayType_<From>::value && \
1763  Traits::is_simd_##trait_name_<From>::value && \
1764  From::Size * sizeof...(Froms) < Return::Size && \
1765  are_all_types_equal<From, Froms...>::value), \
1766  Return> \
1767  simd_cast(From x, Froms... xs) \
1768  { \
1769  vc_debug_("simd_cast{1}(", ")\n", x, xs...); \
1770  return {simd_cast<typename Return::storage_type>(x, xs...)}; \
1771  } \
1772  template <typename Return, typename From, typename... Froms> \
1773  Vc_INTRINSIC Vc_CONST enable_if<(Traits::isAtomic##SimdArrayType_<Return>::value && \
1774  !Traits::is##SimdArrayType_<From>::value && \
1775  Traits::is_simd_##trait_name_<From>::value && \
1776  From::Size * sizeof...(Froms) >= Return::Size && \
1777  are_all_types_equal<From, Froms...>::value), \
1778  Return> \
1779  simd_cast(From x, Froms... xs) \
1780  { \
1781  vc_debug_("simd_cast{2}(", ")\n", x, xs...); \
1782  return {simd_cast_without_last<Return, From, Froms...>(x, xs...)}; \
1783  } \
1784  template <typename Return, typename From, typename... Froms> \
1785  Vc_INTRINSIC Vc_CONST enable_if<(Traits::is##SimdArrayType_<Return>::value && \
1786  !Traits::isAtomic##SimdArrayType_<Return>::value && \
1787  !Traits::is##SimdArrayType_<From>::value && \
1788  Traits::is_simd_##trait_name_<From>::value && \
1789  Common::left_size(Return::Size) < \
1790  From::Size * (1 + sizeof...(Froms)) && \
1791  are_all_types_equal<From, Froms...>::value), \
1792  Return> \
1793  simd_cast(From x, Froms... xs) \
1794  { \
1795  vc_debug_("simd_cast{3}(", ")\n", x, xs...); \
1796  using R0 = typename Return::storage_type0; \
1797  using R1 = typename Return::storage_type1; \
1798  return {simd_cast_drop_arguments<R0, Froms...>(x, xs...), \
1799  simd_cast_with_offset<R1, R0::Size>(x, xs...)}; \
1800  } \
1801  template <typename Return, typename From, typename... Froms> \
1802  Vc_INTRINSIC Vc_CONST enable_if<(Traits::is##SimdArrayType_<Return>::value && \
1803  !Traits::isAtomic##SimdArrayType_<Return>::value && \
1804  !Traits::is##SimdArrayType_<From>::value && \
1805  Traits::is_simd_##trait_name_<From>::value && \
1806  Common::left_size(Return::Size) >= \
1807  From::Size * (1 + sizeof...(Froms)) && \
1808  are_all_types_equal<From, Froms...>::value), \
1809  Return> \
1810  simd_cast(From x, Froms... xs) \
1811  { \
1812  vc_debug_("simd_cast{4}(", ")\n", x, xs...); \
1813  using R0 = typename Return::storage_type0; \
1814  using R1 = typename Return::storage_type1; \
1815  return {simd_cast<R0>(x, xs...), R1::Zero()}; \
1816  } \
1817  Vc_NOTHING_EXPECTING_SEMICOLON
1818 Vc_SIMDARRAY_CASTS(SimdArray, vector);
1819 Vc_SIMDARRAY_CASTS(SimdMaskArray, mask);
1820 #undef Vc_SIMDARRAY_CASTS
1821 
1822 // simd_cast<SimdArray/-mask, offset>(V) {{{2
1823 #define Vc_SIMDARRAY_CASTS(SimdArrayType_, trait_name_) \
1824  /* SIMD Vector/Mask to atomic SimdArray/simdmaskarray */ \
1825  template <typename Return, int offset, typename From> \
1826  Vc_INTRINSIC Vc_CONST enable_if<(Traits::isAtomic##SimdArrayType_<Return>::value && \
1827  !Traits::is##SimdArrayType_<From>::value && \
1828  Traits::is_simd_##trait_name_<From>::value), \
1829  Return> \
1830  simd_cast(From x) \
1831  { \
1832  vc_debug_("simd_cast{offset, atomic}(", ")\n", offset, x); \
1833  return {simd_cast<typename Return::storage_type, offset>(x)}; \
1834  } \
1835  /* both halves of Return array are extracted from argument */ \
1836  template <typename Return, int offset, typename From> \
1837  Vc_INTRINSIC Vc_CONST enable_if< \
1838  (Traits::is##SimdArrayType_<Return>::value && \
1839  !Traits::isAtomic##SimdArrayType_<Return>::value && \
1840  !Traits::is##SimdArrayType_<From>::value && \
1841  Traits::is_simd_##trait_name_<From>::value && \
1842  Return::Size * offset + Common::left_size(Return::Size) < From::Size), \
1843  Return> \
1844  simd_cast(From x) \
1845  { \
1846  vc_debug_("simd_cast{offset, split Return}(", ")\n", offset, x); \
1847  using R0 = typename Return::storage_type0; \
1848  constexpr int entries_offset = offset * Return::Size; \
1849  constexpr int entries_offset_right = entries_offset + R0::Size; \
1850  return { \
1851  simd_cast_with_offset<typename Return::storage_type0, entries_offset>(x), \
1852  simd_cast_with_offset<typename Return::storage_type1, entries_offset_right>( \
1853  x)}; \
1854  } \
1855  /* SIMD Vector/Mask to non-atomic SimdArray/simdmaskarray */ \
1856  /* right half of Return array is zero */ \
1857  template <typename Return, int offset, typename From> \
1858  Vc_INTRINSIC Vc_CONST enable_if< \
1859  (Traits::is##SimdArrayType_<Return>::value && \
1860  !Traits::isAtomic##SimdArrayType_<Return>::value && \
1861  !Traits::is##SimdArrayType_<From>::value && \
1862  Traits::is_simd_##trait_name_<From>::value && \
1863  Return::Size * offset + Common::left_size(Return::Size) >= From::Size), \
1864  Return> \
1865  simd_cast(From x) \
1866  { \
1867  vc_debug_("simd_cast{offset, R1::Zero}(", ")\n", offset, x); \
1868  using R0 = typename Return::storage_type0; \
1869  using R1 = typename Return::storage_type1; \
1870  constexpr int entries_offset = offset * Return::Size; \
1871  return {simd_cast_with_offset<R0, entries_offset>(x), R1::Zero()}; \
1872  } \
1873  Vc_NOTHING_EXPECTING_SEMICOLON
1874 Vc_SIMDARRAY_CASTS(SimdArray, vector);
1875 Vc_SIMDARRAY_CASTS(SimdMaskArray, mask);
1876 #undef Vc_SIMDARRAY_CASTS
1877 
1878 // simd_cast<T>(xs...) from SimdArray/-mask {{{2
1879 #define Vc_SIMDARRAY_CASTS(SimdArrayType_) \
1880  /* indivisible SimdArrayType_ */ \
1881  template <typename Return, typename T, std::size_t N, typename V, typename... From> \
1882  Vc_INTRINSIC Vc_CONST \
1883  enable_if<(are_all_types_equal<SimdArrayType_<T, N, V, N>, From...>::value && \
1884  (sizeof...(From) == 0 || N * sizeof...(From) < Return::Size) && \
1885  !std::is_same<Return, SimdArrayType_<T, N, V, N>>::value), \
1886  Return> \
1887  simd_cast(const SimdArrayType_<T, N, V, N> &x0, const From &... xs) \
1888  { \
1889  vc_debug_("simd_cast{indivisible}(", ")\n", x0, xs...); \
1890  return simd_cast<Return>(internal_data(x0), internal_data(xs)...); \
1891  } \
1892  /* indivisible SimdArrayType_ && can drop arguments from the end */ \
1893  template <typename Return, typename T, std::size_t N, typename V, typename... From> \
1894  Vc_INTRINSIC Vc_CONST \
1895  enable_if<(are_all_types_equal<SimdArrayType_<T, N, V, N>, From...>::value && \
1896  (sizeof...(From) > 0 && (N * sizeof...(From) >= Return::Size)) && \
1897  !std::is_same<Return, SimdArrayType_<T, N, V, N>>::value), \
1898  Return> \
1899  simd_cast(const SimdArrayType_<T, N, V, N> &x0, const From &... xs) \
1900  { \
1901  vc_debug_("simd_cast{indivisible2}(", ")\n", x0, xs...); \
1902  return simd_cast_without_last<Return, \
1903  typename SimdArrayType_<T, N, V, N>::storage_type, \
1904  typename From::storage_type...>( \
1905  internal_data(x0), internal_data(xs)...); \
1906  } \
1907  /* bisectable SimdArrayType_ (N = 2^n) && never too large */ \
1908  template <typename Return, typename T, std::size_t N, typename V, std::size_t M, \
1909  typename... From> \
1910  Vc_INTRINSIC Vc_CONST enable_if< \
1911  (N != M && are_all_types_equal<SimdArrayType_<T, N, V, M>, From...>::value && \
1912  N * sizeof...(From) < Return::Size && ((N - 1) & N) == 0), \
1913  Return> \
1914  simd_cast(const SimdArrayType_<T, N, V, M> &x0, const From &... xs) \
1915  { \
1916  vc_debug_("simd_cast{bisectable}(", ")\n", x0, xs...); \
1917  return simd_cast_interleaved_argument_order< \
1918  Return, typename SimdArrayType_<T, N, V, M>::storage_type0, \
1919  typename From::storage_type0...>(internal_data0(x0), internal_data0(xs)..., \
1920  internal_data1(x0), internal_data1(xs)...); \
1921  } \
1922  /* bisectable SimdArrayType_ (N = 2^n) && input so large that at least the last \
1923  * input can be dropped */ \
1924  template <typename Return, typename T, std::size_t N, typename V, std::size_t M, \
1925  typename... From> \
1926  Vc_INTRINSIC Vc_CONST enable_if< \
1927  (N != M && are_all_types_equal<SimdArrayType_<T, N, V, M>, From...>::value && \
1928  N * sizeof...(From) >= Return::Size && ((N - 1) & N) == 0), \
1929  Return> \
1930  simd_cast(const SimdArrayType_<T, N, V, M> &x0, const From &... xs) \
1931  { \
1932  vc_debug_("simd_cast{bisectable2}(", ")\n", x0, xs...); \
1933  return simd_cast_without_last<Return, SimdArrayType_<T, N, V, M>, From...>( \
1934  x0, xs...); \
1935  } \
1936  /* remaining SimdArrayType_ input never larger (N != 2^n) */ \
1937  template <typename Return, typename T, std::size_t N, typename V, std::size_t M, \
1938  typename... From> \
1939  Vc_INTRINSIC Vc_CONST enable_if< \
1940  (N != M && are_all_types_equal<SimdArrayType_<T, N, V, M>, From...>::value && \
1941  N * (1 + sizeof...(From)) <= Return::Size && ((N - 1) & N) != 0), \
1942  Return> \
1943  simd_cast(const SimdArrayType_<T, N, V, M> &x0, const From &... xs) \
1944  { \
1945  vc_debug_("simd_cast{remaining}(", ")\n", x0, xs...); \
1946  return simd_cast_impl_smaller_input<Return, N, SimdArrayType_<T, N, V, M>, \
1947  From...>(x0, xs...); \
1948  } \
1949  /* remaining SimdArrayType_ input larger (N != 2^n) */ \
1950  template <typename Return, typename T, std::size_t N, typename V, std::size_t M, \
1951  typename... From> \
1952  Vc_INTRINSIC Vc_CONST enable_if< \
1953  (N != M && are_all_types_equal<SimdArrayType_<T, N, V, M>, From...>::value && \
1954  N * (1 + sizeof...(From)) > Return::Size && ((N - 1) & N) != 0), \
1955  Return> \
1956  simd_cast(const SimdArrayType_<T, N, V, M> &x0, const From &... xs) \
1957  { \
1958  vc_debug_("simd_cast{remaining2}(", ")\n", x0, xs...); \
1959  return simd_cast_impl_larger_input<Return, N, SimdArrayType_<T, N, V, M>, \
1960  From...>(x0, xs...); \
1961  } \
1962  /* a single bisectable SimdArrayType_ (N = 2^n) too large */ \
1963  template <typename Return, typename T, std::size_t N, typename V, std::size_t M> \
1964  Vc_INTRINSIC Vc_CONST \
1965  enable_if<(N != M && N >= 2 * Return::Size && ((N - 1) & N) == 0), Return> \
1966  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
1967  { \
1968  vc_debug_("simd_cast{single bisectable}(", ")\n", x); \
1969  return simd_cast<Return>(internal_data0(x)); \
1970  } \
1971  template <typename Return, typename T, std::size_t N, typename V, std::size_t M> \
1972  Vc_INTRINSIC Vc_CONST enable_if<(N != M && N > Return::Size && \
1973  N < 2 * Return::Size && ((N - 1) & N) == 0), \
1974  Return> \
1975  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
1976  { \
1977  vc_debug_("simd_cast{single bisectable2}(", ")\n", x); \
1978  return simd_cast<Return>(internal_data0(x), internal_data1(x)); \
1979  } \
1980  Vc_NOTHING_EXPECTING_SEMICOLON
1981 Vc_SIMDARRAY_CASTS(SimdArray);
1982 Vc_SIMDARRAY_CASTS(SimdMaskArray);
1983 #undef Vc_SIMDARRAY_CASTS
1984 
1985 // simd_cast<T, offset>(SimdArray/-mask) {{{2
1986 #define Vc_SIMDARRAY_CASTS(SimdArrayType_) \
1987  /* offset == 0 is like without offset */ \
1988  template <typename Return, int offset, typename T, std::size_t N, typename V, \
1989  std::size_t M> \
1990  Vc_INTRINSIC Vc_CONST enable_if<(offset == 0), Return> simd_cast( \
1991  const SimdArrayType_<T, N, V, M> &x) \
1992  { \
1993  vc_debug_("simd_cast{offset == 0}(", ")\n", offset, x); \
1994  return simd_cast<Return>(x); \
1995  } \
1996  /* forward to V */ \
1997  template <typename Return, int offset, typename T, std::size_t N, typename V> \
1998  Vc_INTRINSIC Vc_CONST enable_if<(offset != 0), Return> simd_cast( \
1999  const SimdArrayType_<T, N, V, N> &x) \
2000  { \
2001  vc_debug_("simd_cast{offset, forward}(", ")\n", offset, x); \
2002  return simd_cast<Return, offset>(internal_data(x)); \
2003  } \
2004  /* convert from right member of SimdArray */ \
2005  template <typename Return, int offset, typename T, std::size_t N, typename V, \
2006  std::size_t M> \
2007  Vc_INTRINSIC Vc_CONST \
2008  enable_if<(N != M && offset * Return::Size >= Common::left_size(N) && \
2009  offset != 0 && Common::left_size(N) % Return::Size == 0), \
2010  Return> \
2011  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
2012  { \
2013  vc_debug_("simd_cast{offset, right}(", ")\n", offset, x); \
2014  return simd_cast<Return, offset - Common::left_size(N) / Return::Size>( \
2015  internal_data1(x)); \
2016  } \
2017  /* same as above except for odd cases where offset * Return::Size doesn't fit the \
2018  * left side of the SimdArray */ \
2019  template <typename Return, int offset, typename T, std::size_t N, typename V, \
2020  std::size_t M> \
2021  Vc_INTRINSIC Vc_CONST \
2022  enable_if<(N != M && offset * Return::Size >= Common::left_size(N) && \
2023  offset != 0 && Common::left_size(N) % Return::Size != 0), \
2024  Return> \
2025  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
2026  { \
2027  vc_debug_("simd_cast{offset, right, nofit}(", ")\n", offset, x); \
2028  return simd_cast_with_offset<Return, \
2029  offset * Return::Size - Common::left_size(N)>( \
2030  internal_data1(x)); \
2031  } \
2032  /* convert from left member of SimdArray */ \
2033  template <typename Return, int offset, typename T, std::size_t N, typename V, \
2034  std::size_t M> \
2035  Vc_INTRINSIC Vc_CONST \
2036  enable_if<(N != M && /*offset * Return::Size < Common::left_size(N) &&*/ \
2037  offset != 0 && (offset + 1) * Return::Size <= Common::left_size(N)), \
2038  Return> \
2039  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
2040  { \
2041  vc_debug_("simd_cast{offset, left}(", ")\n", offset, x); \
2042  return simd_cast<Return, offset>(internal_data0(x)); \
2043  } \
2044  /* fallback to copying scalars */ \
2045  template <typename Return, int offset, typename T, std::size_t N, typename V, \
2046  std::size_t M> \
2047  Vc_INTRINSIC Vc_CONST \
2048  enable_if<(N != M && (offset * Return::Size < Common::left_size(N)) && \
2049  offset != 0 && (offset + 1) * Return::Size > Common::left_size(N)), \
2050  Return> \
2051  simd_cast(const SimdArrayType_<T, N, V, M> &x) \
2052  { \
2053  vc_debug_("simd_cast{offset, copy scalars}(", ")\n", offset, x); \
2054  using R = typename Return::EntryType; \
2055  Return r = Return::Zero(); \
2056  for (std::size_t i = offset * Return::Size; \
2057  i < std::min(N, (offset + 1) * Return::Size); ++i) { \
2058  r[i - offset * Return::Size] = static_cast<R>(x[i]); \
2059  } \
2060  return r; \
2061  } \
2062  Vc_NOTHING_EXPECTING_SEMICOLON
2063 Vc_SIMDARRAY_CASTS(SimdArray);
2064 Vc_SIMDARRAY_CASTS(SimdMaskArray);
2065 #undef Vc_SIMDARRAY_CASTS
2066 // simd_cast_drop_arguments (definitions) {{{2
2067 template <typename Return, typename From>
2068 Vc_INTRINSIC Vc_CONST Return simd_cast_drop_arguments(From x)
2069 {
2070  return simd_cast<Return>(x);
2071 }
2072 template <typename Return, typename... Froms>
2073 Vc_INTRINSIC Vc_CONST
2074  enable_if<(are_all_types_equal<Froms...>::value &&
2075  sizeof...(Froms) * first_type_of<Froms...>::Size < Return::Size),
2076  Return>
2077  simd_cast_drop_arguments(Froms... xs, first_type_of<Froms...> x)
2078 {
2079  return simd_cast<Return>(xs..., x);
2080 }
2081 // The following function can be implemented without the sizeof...(From) overload.
2082 // However, ICC has a bug (Premier Issue #6000116338) which leads to an ICE. Splitting the
2083 // function in two works around the issue.
2084 template <typename Return, typename From, typename... Froms>
2085 Vc_INTRINSIC Vc_CONST enable_if<
2086  (are_all_types_equal<From, Froms...>::value &&
2087  (1 + sizeof...(Froms)) * From::Size >= Return::Size && sizeof...(Froms) != 0),
2088  Return>
2089 simd_cast_drop_arguments(Froms... xs, From x, From)
2090 {
2091  return simd_cast_drop_arguments<Return, Froms...>(xs..., x);
2092 }
2093 template <typename Return, typename From>
2094 Vc_INTRINSIC Vc_CONST
2095  enable_if<(are_all_types_equal<From>::value && From::Size >= Return::Size), Return>
2096  simd_cast_drop_arguments(From x, From)
2097 {
2098  return simd_cast_drop_arguments<Return>(x);
2099 }
2100 
2101 // simd_cast_with_offset (definitions) {{{2
2102  template <typename Return, std::size_t offset, typename From>
2103  Vc_INTRINSIC Vc_CONST
2104  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size == 0),
2105  Return> simd_cast_with_offset(const From &x)
2106 {
2107  return simd_cast<Return, offset / Return::Size>(x);
2108 }
2109 template <typename Return, std::size_t offset, typename From>
2110 Vc_INTRINSIC Vc_CONST
2111  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size != 0 &&
2112  ((Traits::isSimdArray<Return>::value &&
2113  !Traits::isAtomicSimdArray<Return>::value) ||
2114  (Traits::isSimdMaskArray<Return>::value &&
2115  !Traits::isAtomicSimdMaskArray<Return>::value))),
2116  Return>
2117  simd_cast_with_offset(const From &x)
2118 {
2119  using R0 = typename Return::storage_type0;
2120  using R1 = typename Return::storage_type1;
2121  return {simd_cast_with_offset<R0, offset>(x),
2122  simd_cast_with_offset<R1, offset + R0::Size>(x)};
2123 }
2124 template <typename Return, std::size_t offset, typename From>
2125 Vc_INTRINSIC Vc_CONST
2126  enable_if<(From::Size > offset && offset > 0 && offset % Return::Size != 0 &&
2127  ((Traits::isSimdArray<Return>::value &&
2128  Traits::isAtomicSimdArray<Return>::value) ||
2129  (Traits::isSimdMaskArray<Return>::value &&
2130  Traits::isAtomicSimdMaskArray<Return>::value))),
2131  Return>
2132  simd_cast_with_offset(const From &x)
2133 {
2134  return simd_cast<Return, offset / Return::Size>(x.shifted(offset % Return::Size));
2135 }
2136 template <typename Return, std::size_t offset, typename From, typename... Froms>
2137 Vc_INTRINSIC Vc_CONST
2138  enable_if<(are_all_types_equal<From, Froms...>::value && offset == 0), Return>
2139  simd_cast_with_offset(const From &x, const Froms &... xs)
2140 {
2141  return simd_cast<Return>(x, xs...);
2142 }
2143 
2144 // simd_cast_without_last (definition) {{{2
2145 template <typename Return, typename T, typename... From>
2146 Vc_INTRINSIC Vc_CONST Return simd_cast_without_last(const From &... xs, const T &)
2147 {
2148  return simd_cast<Return>(xs...);
2149 }
2150 
2151 // simd_cast_interleaved_argument_order (definitions) {{{2
2152 
2154 template <std::size_t I, typename T0, typename... Ts>
2155 Vc_INTRINSIC Vc_CONST enable_if<(I == 0), T0> extract_interleaved(const T0 &a0,
2156  const Ts &...,
2157  const T0 &,
2158  const Ts &...)
2159 {
2160  return a0;
2161 }
2163 template <std::size_t I, typename T0, typename... Ts>
2164 Vc_INTRINSIC Vc_CONST enable_if<(I == 1), T0> extract_interleaved(const T0 &,
2165  const Ts &...,
2166  const T0 &b0,
2167  const Ts &...)
2168 {
2169  return b0;
2170 }
2172 template <std::size_t I, typename T0, typename... Ts>
2173 Vc_INTRINSIC Vc_CONST enable_if<(I > 1), T0> extract_interleaved(const T0 &,
2174  const Ts &... a,
2175  const T0 &,
2176  const Ts &... b)
2177 {
2178  return extract_interleaved<I - 2, Ts...>(a..., b...);
2179 }
2182 template <typename Return, typename... Ts, std::size_t... Indexes>
2183 Vc_INTRINSIC Vc_CONST Return
2184  simd_cast_interleaved_argument_order_1(index_sequence<Indexes...>, const Ts &... a,
2185  const Ts &... b)
2186 {
2187  return simd_cast<Return>(extract_interleaved<Indexes, Ts...>(a..., b...)...);
2188 }
2191 template <typename Return, typename... Ts>
2192 Vc_INTRINSIC Vc_CONST Return
2193  simd_cast_interleaved_argument_order(const Ts &... a, const Ts &... b)
2194 {
2195  using seq = make_index_sequence<sizeof...(Ts)*2>;
2196  return simd_cast_interleaved_argument_order_1<Return, Ts...>(seq(), a..., b...);
2197 }
2198 
2199 // conditional_assign {{{1
2200 #define Vc_CONDITIONAL_ASSIGN(name_, op_) \
2201  template <Operator O, typename T, std::size_t N, typename V, size_t VN, typename M, \
2202  typename U> \
2203  Vc_INTRINSIC enable_if<O == Operator::name_, void> conditional_assign( \
2204  SimdArray<T, N, V, VN> &lhs, M &&mask, U &&rhs) \
2205  { \
2206  lhs(mask) op_ rhs; \
2207  } \
2208  Vc_NOTHING_EXPECTING_SEMICOLON
2209 Vc_CONDITIONAL_ASSIGN( Assign, =);
2210 Vc_CONDITIONAL_ASSIGN( PlusAssign, +=);
2211 Vc_CONDITIONAL_ASSIGN( MinusAssign, -=);
2212 Vc_CONDITIONAL_ASSIGN( MultiplyAssign, *=);
2213 Vc_CONDITIONAL_ASSIGN( DivideAssign, /=);
2214 Vc_CONDITIONAL_ASSIGN( RemainderAssign, %=);
2215 Vc_CONDITIONAL_ASSIGN( XorAssign, ^=);
2216 Vc_CONDITIONAL_ASSIGN( AndAssign, &=);
2217 Vc_CONDITIONAL_ASSIGN( OrAssign, |=);
2218 Vc_CONDITIONAL_ASSIGN( LeftShiftAssign,<<=);
2219 Vc_CONDITIONAL_ASSIGN(RightShiftAssign,>>=);
2220 #undef Vc_CONDITIONAL_ASSIGN
2221 
2222 #define Vc_CONDITIONAL_ASSIGN(name_, expr_) \
2223  template <Operator O, typename T, std::size_t N, typename V, size_t VN, typename M> \
2224  Vc_INTRINSIC enable_if<O == Operator::name_, SimdArray<T, N, V, VN>> \
2225  conditional_assign(SimdArray<T, N, V, VN> &lhs, M &&mask) \
2226  { \
2227  return expr_; \
2228  } \
2229  Vc_NOTHING_EXPECTING_SEMICOLON
2230 Vc_CONDITIONAL_ASSIGN(PostIncrement, lhs(mask)++);
2231 Vc_CONDITIONAL_ASSIGN( PreIncrement, ++lhs(mask));
2232 Vc_CONDITIONAL_ASSIGN(PostDecrement, lhs(mask)--);
2233 Vc_CONDITIONAL_ASSIGN( PreDecrement, --lhs(mask));
2234 #undef Vc_CONDITIONAL_ASSIGN
2235 // transpose_impl {{{1
2236 namespace Common
2237 {
2238  template <int L, typename T, std::size_t N, typename V>
2239  inline enable_if<L == 4, void> transpose_impl(
2240  SimdArray<T, N, V, N> * Vc_RESTRICT r[],
2241  const TransposeProxy<SimdArray<T, N, V, N>, SimdArray<T, N, V, N>,
2242  SimdArray<T, N, V, N>, SimdArray<T, N, V, N>> &proxy)
2243  {
2244  V *Vc_RESTRICT r2[L] = {&internal_data(*r[0]), &internal_data(*r[1]),
2245  &internal_data(*r[2]), &internal_data(*r[3])};
2246  transpose_impl<L>(
2247  &r2[0], TransposeProxy<V, V, V, V>{internal_data(std::get<0>(proxy.in)),
2248  internal_data(std::get<1>(proxy.in)),
2249  internal_data(std::get<2>(proxy.in)),
2250  internal_data(std::get<3>(proxy.in))});
2251  }
2252  template <int L, typename T, typename V>
2253  inline enable_if<(L == 2), void> transpose_impl(
2254  SimdArray<T, 4, V, 1> *Vc_RESTRICT r[],
2255  const TransposeProxy<SimdArray<T, 2, V, 1>, SimdArray<T, 2, V, 1>,
2256  SimdArray<T, 2, V, 1>, SimdArray<T, 2, V, 1>> &proxy)
2257  {
2258  auto &lo = *r[0];
2259  auto &hi = *r[1];
2260  internal_data0(internal_data0(lo)) = internal_data0(std::get<0>(proxy.in));
2261  internal_data1(internal_data0(lo)) = internal_data0(std::get<1>(proxy.in));
2262  internal_data0(internal_data1(lo)) = internal_data0(std::get<2>(proxy.in));
2263  internal_data1(internal_data1(lo)) = internal_data0(std::get<3>(proxy.in));
2264  internal_data0(internal_data0(hi)) = internal_data1(std::get<0>(proxy.in));
2265  internal_data1(internal_data0(hi)) = internal_data1(std::get<1>(proxy.in));
2266  internal_data0(internal_data1(hi)) = internal_data1(std::get<2>(proxy.in));
2267  internal_data1(internal_data1(hi)) = internal_data1(std::get<3>(proxy.in));
2268  }
2269  template <int L, typename T, std::size_t N, typename V>
2270  inline enable_if<(L == 4 && N > 1), void> transpose_impl(
2271  SimdArray<T, N, V, 1> *Vc_RESTRICT r[],
2272  const TransposeProxy<SimdArray<T, N, V, 1>, SimdArray<T, N, V, 1>,
2273  SimdArray<T, N, V, 1>, SimdArray<T, N, V, 1>> &proxy)
2274  {
2275  SimdArray<T, N, V, 1> *Vc_RESTRICT r0[L / 2] = {r[0], r[1]};
2276  SimdArray<T, N, V, 1> *Vc_RESTRICT r1[L / 2] = {r[2], r[3]};
2277  using H = SimdArray<T, 2>;
2278  transpose_impl<2>(
2279  &r0[0], TransposeProxy<H, H, H, H>{internal_data0(std::get<0>(proxy.in)),
2280  internal_data0(std::get<1>(proxy.in)),
2281  internal_data0(std::get<2>(proxy.in)),
2282  internal_data0(std::get<3>(proxy.in))});
2283  transpose_impl<2>(
2284  &r1[0], TransposeProxy<H, H, H, H>{internal_data1(std::get<0>(proxy.in)),
2285  internal_data1(std::get<1>(proxy.in)),
2286  internal_data1(std::get<2>(proxy.in)),
2287  internal_data1(std::get<3>(proxy.in))});
2288  }
2289  /* TODO:
2290  template <typename T, std::size_t N, typename V, std::size_t VSize>
2291  inline enable_if<(N > VSize), void> transpose_impl(
2292  std::array<SimdArray<T, N, V, VSize> * Vc_RESTRICT, 4> & r,
2293  const TransposeProxy<SimdArray<T, N, V, VSize>, SimdArray<T, N, V, VSize>,
2294  SimdArray<T, N, V, VSize>, SimdArray<T, N, V, VSize>> &proxy)
2295  {
2296  typedef SimdArray<T, N, V, VSize> SA;
2297  std::array<typename SA::storage_type0 * Vc_RESTRICT, 4> r0 = {
2298  {&internal_data0(*r[0]), &internal_data0(*r[1]), &internal_data0(*r[2]),
2299  &internal_data0(*r[3])}};
2300  transpose_impl(
2301  r0, TransposeProxy<typename SA::storage_type0, typename SA::storage_type0,
2302  typename SA::storage_type0, typename SA::storage_type0>{
2303  internal_data0(std::get<0>(proxy.in)),
2304  internal_data0(std::get<1>(proxy.in)),
2305  internal_data0(std::get<2>(proxy.in)),
2306  internal_data0(std::get<3>(proxy.in))});
2307 
2308  std::array<typename SA::storage_type1 * Vc_RESTRICT, 4> r1 = {
2309  {&internal_data1(*r[0]), &internal_data1(*r[1]), &internal_data1(*r[2]),
2310  &internal_data1(*r[3])}};
2311  transpose_impl(
2312  r1, TransposeProxy<typename SA::storage_type1, typename SA::storage_type1,
2313  typename SA::storage_type1, typename SA::storage_type1>{
2314  internal_data1(std::get<0>(proxy.in)),
2315  internal_data1(std::get<1>(proxy.in)),
2316  internal_data1(std::get<2>(proxy.in)),
2317  internal_data1(std::get<3>(proxy.in))});
2318  }
2319  */
2320 } // namespace Common
2321 
2322 // Traits static assertions {{{1
2323 static_assert(Traits::has_no_allocated_data<const volatile Vc::SimdArray<int, 4> &>::value, "");
2324 static_assert(Traits::has_no_allocated_data<const volatile Vc::SimdArray<int, 4>>::value, "");
2325 static_assert(Traits::has_no_allocated_data<volatile Vc::SimdArray<int, 4> &>::value, "");
2326 static_assert(Traits::has_no_allocated_data<volatile Vc::SimdArray<int, 4>>::value, "");
2327 static_assert(Traits::has_no_allocated_data<const Vc::SimdArray<int, 4> &>::value, "");
2328 static_assert(Traits::has_no_allocated_data<const Vc::SimdArray<int, 4>>::value, "");
2329 static_assert(Traits::has_no_allocated_data<Vc::SimdArray<int, 4>>::value, "");
2330 static_assert(Traits::has_no_allocated_data<Vc::SimdArray<int, 4> &&>::value, "");
2331 // }}}1
2333 
2334 } // namespace Vc_VERSIONED_NAMESPACE
2335 
2336 // numeric_limits {{{1
2337 namespace std
2338 {
2339 template <typename T, size_t N, typename V, size_t VN>
2340 struct numeric_limits<Vc::SimdArray<T, N, V, VN>> : public numeric_limits<T> {
2341 private:
2342  using R = Vc::SimdArray<T, N, V, VN>;
2343 
2344 public:
2345  static Vc_ALWAYS_INLINE Vc_CONST R max() noexcept { return numeric_limits<T>::max(); }
2346  static Vc_ALWAYS_INLINE Vc_CONST R min() noexcept { return numeric_limits<T>::min(); }
2347  static Vc_ALWAYS_INLINE Vc_CONST R lowest() noexcept
2348  {
2349  return numeric_limits<T>::lowest();
2350  }
2351  static Vc_ALWAYS_INLINE Vc_CONST R epsilon() noexcept
2352  {
2353  return numeric_limits<T>::epsilon();
2354  }
2355  static Vc_ALWAYS_INLINE Vc_CONST R round_error() noexcept
2356  {
2357  return numeric_limits<T>::round_error();
2358  }
2359  static Vc_ALWAYS_INLINE Vc_CONST R infinity() noexcept
2360  {
2361  return numeric_limits<T>::infinity();
2362  }
2363  static Vc_ALWAYS_INLINE Vc_CONST R quiet_NaN() noexcept
2364  {
2365  return numeric_limits<T>::quiet_NaN();
2366  }
2367  static Vc_ALWAYS_INLINE Vc_CONST R signaling_NaN() noexcept
2368  {
2369  return numeric_limits<T>::signaling_NaN();
2370  }
2371  static Vc_ALWAYS_INLINE Vc_CONST R denorm_min() noexcept
2372  {
2373  return numeric_limits<T>::denorm_min();
2374  }
2375 };
2376 } // namespace std
2377 //}}}1
2378 
#endif // VC_COMMON_SIMDARRAY_H_
// vim: foldmethod=marker
Vc::ceil
SimdArray< T, N, V, M > ceil(const SimdArray< T, N, V, M > &x)
Applies the std:: ceil function component-wise and concurrently.
Definition: simdarray.h:1520
Vc::floor
SimdArray< T, N, V, M > floor(const SimdArray< T, N, V, M > &x)
Applies the std:: floor function component-wise and concurrently.
Definition: simdarray.h:1525
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::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::asin
SimdArray< T, N, V, M > asin(const SimdArray< T, N, V, M > &x)
Applies the std:: asin function component-wise and concurrently.
Definition: simdarray.h:1517
Vc::DefaultLoadTag
UnalignedTag DefaultLoadTag
The default load tag type uses unaligned (non-streaming) loads.
Definition: loadstoreflags.h:167
Vc::rsqrt
SimdArray< T, N, V, M > rsqrt(const SimdArray< T, N, V, M > &x)
Applies the std:: rsqrt function component-wise and concurrently.
Definition: simdarray.h:1554
Vc::operator-
result_vector_type< L, R > operator-(L &&lhs, R &&rhs)
Applies - component-wise and concurrently.
Definition: simdarray.h:1444
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::log10
SimdArray< T, N, V, M > log10(const SimdArray< T, N, V, M > &x)
Applies the std:: log10 function component-wise and concurrently.
Definition: simdarray.h:1550
Vc::min
SimdArray< T, N, V, M > min(const SimdArray< T, N, V, M > &x, const SimdArray< T, N, V, M > &y)
Applies the std:: min function component-wise and concurrently.
Definition: simdarray.h:1564
Vc::max
SimdArray< T, N, V, M > max(const SimdArray< T, N, V, M > &x, const SimdArray< T, N, V, M > &y)
Applies the std:: max function component-wise and concurrently.
Definition: simdarray.h:1565
Vc::SimdArray
Data-parallel arithmetic type with user-defined number of elements.
Definition: simdarray.h:506
Vc::sin
SimdArray< T, N, V, M > sin(const SimdArray< T, N, V, M > &x)
Applies the std:: sin function component-wise and concurrently.
Definition: simdarray.h:1555
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::SimdMaskArray
Data-parallel mask type with user-defined number of boolean elements.
Definition: simdarrayfwd.h:126
Vc::exp
SimdArray< T, N, V, M > exp(const SimdArray< T, N, V, M > &x)
Applies the std:: exp function component-wise and concurrently.
Definition: simdarray.h:1523
Vc::sqrt
SimdArray< T, N, V, M > sqrt(const SimdArray< T, N, V, M > &x)
Applies the std:: sqrt function component-wise and concurrently.
Definition: simdarray.h:1562
Vc::reciprocal
SimdArray< T, N, V, M > reciprocal(const SimdArray< T, N, V, M > &x)
Applies the std:: reciprocal function component-wise and concurrently.
Definition: simdarray.h:1552
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::isnegative
SimdMaskArray< T, N, V, M > isnegative(const SimdArray< T, N, V, M > &x)
Applies the std:: isnegative function component-wise and concurrently.
Definition: simdarray.h:1536
Vc::abs
SimdArray< T, N, V, M > abs(const SimdArray< T, N, V, M > &x)
Applies the std:: abs function component-wise and concurrently.
Definition: simdarray.h:1516
Vc::operator+
result_vector_type< L, R > operator+(L &&lhs, R &&rhs)
Applies + component-wise and concurrently.
Definition: simdarray.h:1444
Vc::trunc
SimdArray< T, N, V, M > trunc(const SimdArray< T, N, V, M > &x)
Applies the std:: trunc function component-wise and concurrently.
Definition: simdarray.h:1563
Vc::exponent
SimdArray< T, N, V, M > exponent(const SimdArray< T, N, V, M > &x)
Applies the std:: exponent function component-wise and concurrently.
Definition: simdarray.h:1524
Vc::SimdArray::EntryType
value_type EntryType
The type of the elements (i.e. T)
Definition: simdarray.h:569
Vc::copysign
SimdArray< T, N, V, M > copysign(const SimdArray< T, N, V, M > &x, const SimdArray< T, N, V, M > &y)
Applies the std:: copysign function component-wise and concurrently.
Definition: simdarray.h:1521
Vc::atan
SimdArray< T, N, V, M > atan(const SimdArray< T, N, V, M > &x)
Applies the std:: atan function component-wise and concurrently.
Definition: simdarray.h:1518
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:932
Vc::Mask
The main SIMD mask class.
Definition: mask.h:42
Vc::log2
SimdArray< T, N, V, M > log2(const SimdArray< T, N, V, M > &x)
Applies the std:: log2 function component-wise and concurrently.
Definition: simdarray.h:1551
Vc::isfinite
SimdMaskArray< T, N, V, M > isfinite(const SimdArray< T, N, V, M > &x)
Applies the std:: isfinite function component-wise and concurrently.
Definition: simdarray.h:1533
Vc::isnan
SimdMaskArray< T, N, V, M > isnan(const SimdArray< T, N, V, M > &x)
Applies the std:: isnan function component-wise and concurrently.
Definition: simdarray.h:1535
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:993
Vc::cos
SimdArray< T, N, V, M > cos(const SimdArray< T, N, V, M > &x)
Applies the std:: cos function component-wise and concurrently.
Definition: simdarray.h:1522
Vc::SimdArray::value_type
T value_type
The type of the elements (i.e. T)
Definition: simdarray.h:542
Vc::frexp
SimdArray< T, N > frexp(const SimdArray< T, N > &x, SimdArray< int, N > *e)
Applies the std::frexp function component-wise and concurrently.
Definition: simdarray.h:1539
Vc::log
SimdArray< T, N, V, M > log(const SimdArray< T, N, V, M > &x)
Applies the std:: log function component-wise and concurrently.
Definition: simdarray.h:1549
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::SimdArray::gather
void gather(const MT *mem, IT &&indexes)
Gather function.
Definition: simdarray.h:203
Vc::round
SimdArray< T, N, V, M > round(const SimdArray< T, N, V, M > &x)
Applies the std:: round function component-wise and concurrently.
Definition: simdarray.h:1553
Vc::sincos
void sincos(const SimdArray< T, N > &x, SimdArray< T, N > *sin, SimdArray< T, N > *cos)
Determines sine and cosine concurrently and component-wise on x.
Definition: simdarray.h:1558
Vc::fma
SimdArray< T, N > fma(const SimdArray< T, N > &a, const SimdArray< T, N > &b, const SimdArray< T, N > &c)
Applies the std::fma function component-wise and concurrently.
Definition: simdarray.h:1528
Vc::ldexp
SimdArray< T, N > ldexp(const SimdArray< T, N > &x, const SimdArray< int, N > &e)
Applies the std::ldexp function component-wise and concurrently.
Definition: simdarray.h:1545
Vc::Unaligned
constexpr UnalignedTag Unaligned
Use this object for a flags parameter to request unaligned loads and stores.
Definition: loadstoreflags.h:197
Vc::atan2
SimdArray< T, N, V, M > atan2(const SimdArray< T, N, V, M > &x, const SimdArray< T, N, V, M > &y)
Applies the std:: atan2 function component-wise and concurrently.
Definition: simdarray.h:1519
Vc::isinf
SimdMaskArray< T, N, V, M > isinf(const SimdArray< T, N, V, M > &x)
Applies the std:: isinf function component-wise and concurrently.
Definition: simdarray.h:1534