vspline 1.1.0
Generic C++11 Code for Uniform B-Splines
vc_simd_type.h
Go to the documentation of this file.
1/************************************************************************/
2/* */
3/* vspline - a set of generic tools for creation and evaluation */
4/* of uniform b-splines */
5/* */
6/* Copyright 2015 - 2023 by Kay F. Jahnke */
7/* */
8/* The git repository for this software is at */
9/* */
10/* https://bitbucket.org/kfj/vspline */
11/* */
12/* Please direct questions, bug reports, and contributions to */
13/* */
14/* kfjahnke+vspline@gmail.com */
15/* */
16/* Permission is hereby granted, free of charge, to any person */
17/* obtaining a copy of this software and associated documentation */
18/* files (the "Software"), to deal in the Software without */
19/* restriction, including without limitation the rights to use, */
20/* copy, modify, merge, publish, distribute, sublicense, and/or */
21/* sell copies of the Software, and to permit persons to whom the */
22/* Software is furnished to do so, subject to the following */
23/* conditions: */
24/* */
25/* The above copyright notice and this permission notice shall be */
26/* included in all copies or substantial portions of the */
27/* Software. */
28/* */
29/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
30/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
31/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
32/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
33/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
34/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
35/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
36/* OTHER DEALINGS IN THE SOFTWARE. */
37/* */
38/************************************************************************/
39
40/*! \file vc_simd_type.h
41
42 \brief SIMD type derived from Vc::SimdArray
43
44 Initially, vspline used Vc::SimdArray directly, but over time
45 I have written interfaces to several SIMD implementations based
46 on vspline's own simd_type, and I now prefer to introduce SIMD
47 capability to my code through a common interface derived from
48 vspline::simd_type, which allows for simple switching from one
49 SIMD implementation to another.
50
51*/
52
53#ifndef VSPLINE_VC_SIMD_TYPE_H
54#define VSPLINE_VC_SIMD_TYPE_H
55
56#include <iostream>
57#include <Vc/Vc>
58
59namespace vspline
60{
61
62/// class template vc_simd_type provides a fixed-size SIMD type.
63/// This implementation of vspline::vc_simd_type uses Vc::SimdArray
64/// The 'acrobatics' may seem futile - why inherit privately from
65/// Vc::SimdArray, then code a class template which does essentially
66/// the same? There are several reasons: first, the wrapper class
67/// results in a common interface shared with the other SIMD
68/// implementations, second, there are some added members which
69/// can't be 'put into' Vc::SimdArray from the outside. And, third,
70/// the template signature is uniform, avoiding Vc::SimdArray's
71/// two additional template arguments.
72
73template < typename _value_type ,
74 std::size_t _vsize >
76: private Vc::SimdArray < _value_type , _vsize >
77{
78 typedef Vc::SimdArray < _value_type , _vsize > base_t ;
79
80 // access the underlying base type
81
83 {
84 return ( reinterpret_cast < base_t & > ( * this ) ) ;
85 }
86
87 const base_t & to_base() const
88 {
89 return ( reinterpret_cast < const base_t & > ( * this ) ) ;
90 }
91
92 // make it easy to convert to Vc::SimdArray
93
94 operator base_t()
95 {
96 return to_base() ;
97 }
98
99 operator base_t() const
100 {
101 return to_base() ;
102 }
103
104 typedef std::size_t size_type ;
105 typedef _value_type value_type ;
106 static const size_type vsize = _vsize ;
107 static const int ivsize = _vsize ; // finessing for g++
108
109 // provide the size as a constexpr
110
111 static constexpr size_type size()
112 {
113 return vsize ;
114 }
115
116 // both styles of the types used for indices and masks
117
118 using typename base_t::index_type ;
119 using typename base_t::mask_type ;
120
121 using typename base_t::IndexType ;
122 using typename base_t::MaskType ;
123
124 // type for an individual index value, while index_type
125 // holds vsize of index_ele_type
126
127 typedef typename index_type::value_type index_ele_type ;
128
129 // operator[] is mapped to SimdArray element access
130
131 using base_t::operator[] ;
132
133 // assignment from a value_type, base_t.
134
136 {
137 to_base() = rhs ;
138 return *this ;
139 }
141 {
142 to_base() = rhs ;
143 return *this ;
144 }
145
147 {
148 to_base() = ini ;
149 return *this ;
150 }
152 {
153 to_base() = ini ;
154 return *this ;
155 }
156
157 vc_simd_type & operator= ( const vc_simd_type & ini ) = default ;
158 vc_simd_type & operator= ( vc_simd_type && ini ) = default ;
159
160 // c'tor from value_type, base_t. We use the assignment operator for
161 // initialization.
162
163 vc_simd_type ( const value_type & ini )
164 : base_t ( ini )
165 { }
166 vc_simd_type ( const value_type && ini )
167 : base_t ( ini )
168 { }
169
170 vc_simd_type ( const base_t & ini )
171 : base_t ( ini )
172 { }
173
174 vc_simd_type ( const base_t && ini )
175 : base_t ( ini )
176 { }
177
178 // these two c'tors are left in default mode
179
180 vc_simd_type() = default ;
181 vc_simd_type ( const vc_simd_type & ) = default ;
182 vc_simd_type ( vc_simd_type && ) = default ;
183
184 // c'tor and copy c'tor from another vc_simd_type of different
185 // value_type, delegating to the base class
186
187 template < typename U >
189 : base_t ( ini.to_base() )
190 { }
191 template < typename U >
193 : base_t ( ini.to_base() )
194 { }
195
196 template < typename U >
198 {
199 to_base() = ini.to_base() ;
200 return *this ;
201 }
202
203 template < typename U >
205 {
206 to_base() = ini.to_base() ;
207 return *this ;
208 }
209
210 // special c'tor for vspline::simd_type of unsigned char as rhs
211
212 vc_simd_type & operator=
214 {
215 to_base().load ( ( unsigned char * ) ( & rhs ) ) ;
216// for ( size_type i = 0 ; i < vsize ; i++ )
217// (*this) [ i ] = rhs [ i ] ;
218 return *this ;
219 }
220
221 vc_simd_type & operator=
223 {
224 to_base().load ( ( unsigned char * ) ( & rhs ) ) ;
225// for ( size_type i = 0 ; i < vsize ; i++ )
226// (*this) [ i ] = rhs [ i ] ;
227 return *this ;
228 }
229
231 {
232 *this = ini ;
233 }
234
236 {
237 *this = ini ;
238 }
239
240 // assignment from equally-sized container. Most containers use std::size_t
241 // for the template argument defining the number of elements they hold,
242 // but some (notably vigra::TinyVector) use int, which is probably a relic
243 // from times when non-type template arguments were of a restricted type
244 // set only. By providing a specialization for SIZE_TYPE int, we make
245 // equally-sized vigra::TinyVectors permitted initializers.
246 // the c'tor from an equally-sized container also uses the corresponding
247 // operator= overload, so we use one macro for both.
248 // we also need two different variants of vsize for g++; clang++ accepts
249 // size_type vsize for both places where VSZ is used, but g++ requires
250 // an integer.
251 // Note that the rhs can use any elementary type which can be legally
252 // assigned to value_type. This allows transport of information from
253 // differently typed objects, but there are no further constraints on
254 // the types involved, which may degrade precision. It's the user's
255 // responsibility to make sure such assignments have the desired effect
256 // and overload them if necessary.
257
258 #define BUILD_FROM_CONTAINER(SIZE_TYPE,VSZ) \
259 template < typename U , template < typename , SIZE_TYPE > class V > \
260 vc_simd_type & operator= ( const V < U , VSZ > & rhs ) \
261 { \
262 static_assert ( vsize == VSZ , "incompatible vector size" ) ; \
263 for ( size_type i = 0 ; i < vsize ; i++ ) \
264 (*this) [ i ] = rhs [ i ] ; \
265 return *this ; \
266 } \
267 template < typename U , template < typename , SIZE_TYPE > class V > \
268 vc_simd_type ( const V < U , VSZ > & ini ) \
269 { \
270 *this = ini ; \
271 }
272
273 BUILD_FROM_CONTAINER(std::size_t,vsize)
275
276 #undef BUILD_FROM_CONTAINER
277
278 // use Vc's IndexesFromZero, Zero and One.
279
280 using base_t::IndexesFromZero ;
281 using base_t::Zero ;
282 using base_t::One ;
283
284 // iota() is a synonym for IndexesFromZero
285
286 static const vc_simd_type iota()
287 {
288 return IndexesFromZero() ;
289 }
290
291 // variant which starts from a different starting point and optionally
292 // uses steps other than one.
293
294 static const index_type IndexesFrom ( const index_ele_type & start ,
295 const index_ele_type & step )
296 {
297 return ( ( IndexesFromZero() * step ) + start ) ;
298 }
299
300 // overload staring from zero, but with steps != 1
301
302 static const index_type IndexesFrom ( const index_ele_type & step )
303 {
304 return ( ( IndexesFromZero() * step ) ) ;
305 }
306
307 // echo the vector to a std::ostream, read it from an istream
308
309 friend std::ostream & operator<< ( std::ostream & osr ,
310 vc_simd_type it )
311 {
312 osr << it.to_base() ;
313 return osr ;
314 }
315
316 friend std::istream & operator>> ( std::istream & isr ,
317 vc_simd_type it )
318 {
319 for ( size_type i = 0 ; i < vsize ; i++ )
320 isr >> it [ i ] ;
321 return isr ;
322 }
323
324 // memory access functions, which load and store vector data.
325 // We start out with functions transporting data from memory into
326 // the vc_simd_type. Some of these operations have corresponding
327 // c'tors which use the member function to initialize to_base().
328
329 using base_t::load ;
330 using base_t::store ;
331
332 // gather/scatter, first with index_type, then with a vc_simd_type
333 // object providing indexes, delegating to Vc for the purpose.
334
335 void gather ( const value_type * const p_src ,
336 const index_type & indexes )
337 {
338 to_base().gather ( p_src , indexes ) ;
339 }
340
341 void scatter ( value_type * const p_trg ,
342 const index_type & indexes ) const
343 {
344 to_base().scatter ( p_trg , indexes ) ;
345 }
346
347 template < typename _index_type >
348 void gather ( const value_type * const p_src ,
349 const _index_type & _indexes )
350 {
351 to_base().gather ( p_src , _indexes.to_base() ) ;
352 }
353
354 template < typename _index_type >
355 void scatter ( value_type * const p_trg ,
356 const _index_type & _indexes ) const
357 {
358 to_base().scatter ( p_trg , _indexes.to_base() ) ;
359 }
360
361 // c'tor from pointer and indexes, uses gather
362
363 template < typename index_type >
364 vc_simd_type ( const value_type * const p_src ,
365 const index_type & indexes )
366 {
367 gather ( p_src , indexes ) ;
368 }
369
370 // 'regular' gather and scatter, accessing strided memory so that the
371 // first address visited is p_src/p_trg, and successive addresses are
372 // 'step' apart - in units of T. Might also be done with goading, the
373 // loop should autovectorize.
374
375 void rgather ( const value_type * const p_src ,
376 const index_ele_type & step )
377 {
378 gather ( p_src , IndexesFrom ( step ) ) ;
379 }
380
381 void rscatter ( value_type * p_trg ,
382 const index_ele_type & step ) const
383 {
384 scatter ( p_trg , IndexesFrom ( step ) ) ;
385 }
386
387 // apply functions from namespace std to each element in a vector,
388 // or to each corresponding set of elements in a set of vectors
389 // - going up to three for fma.
390 // Here we delegate to the Vc functions.
391
392 #define BROADCAST_STD_FUNC(FUNC) \
393 friend vc_simd_type FUNC ( const vc_simd_type & arg ) \
394 { \
395 return FUNC ( arg.to_base() ) ; \
396 }
397
399 BROADCAST_STD_FUNC(trunc)
400 BROADCAST_STD_FUNC(round)
401 BROADCAST_STD_FUNC(floor)
406
409 // BROADCAST_STD_FUNC(tan)
413
414 #undef BROADCAST_STD_FUNC
415
416 // Vc doesn't offer tan(), but it has sincos.
417
418 friend vc_simd_type tan ( const vc_simd_type & arg )
419 {
420 base_t sin , cos ;
421 Vc::sincos ( arg.to_base() , &sin , &cos ) ;
422 auto result = sin / cos ;
423 result ( cos == 0 ) = M_PI_2 ;
424 return result ;
425 }
426
427 #define BROADCAST_STD_FUNC2(FUNC) \
428 friend vc_simd_type FUNC ( const vc_simd_type & arg1 , \
429 const vc_simd_type & arg2 ) \
430 { \
431 return FUNC ( arg1.to_base() , arg2.to_base() ) ; \
432 }
433
435
436 #undef BROADCAST_STD_FUNC2
437
438 // Vc has no pow() function
439
440 friend vc_simd_type pow ( const vc_simd_type & base ,
441 const vc_simd_type & exponent )
442 {
443 return exp ( exponent.to_base() * log ( base.to_base() ) ) ;
444 }
445
446 // macros used for the parameter 'CONSTRAINT' in the definitions
447 // further down. Some operations are only allowed for integral types
448 // or boolans. This might be enforced by enable_if, here we use a
449 // static_assert with a clear error message.
450 // TODO: might relax constraints by using 'std::is_convertible'
451
452 #define INTEGRAL_ONLY \
453 static_assert ( std::is_integral < value_type > :: value , \
454 "this operation is only allowed for integral types" ) ;
455
456 #define BOOL_ONLY \
457 static_assert ( std::is_same < value_type , bool > :: value , \
458 "this operation is only allowed for booleans" ) ;
459
460 // augmented assignment operators. Some operators are only applicable
461 // to specific data types, which is enforced by 'CONSTRAINT'.
462 // One might consider widening the scope by making these operator
463 // functions templates and accepting arbitrary indexable types.
464 // Only value_type and vc_simd_type are taken as rhs arguments.
465
466 #define OPEQ_FUNC(OPFUNC,OPEQ,CONSTRAINT) \
467 vc_simd_type & OPFUNC ( const value_type & rhs ) \
468 { \
469 CONSTRAINT \
470 to_base() OPEQ rhs ; \
471 return *this ; \
472 } \
473 vc_simd_type & OPFUNC ( const vc_simd_type & rhs ) \
474 { \
475 CONSTRAINT \
476 to_base() OPEQ rhs.to_base() ; \
477 return *this ; \
478 }
479
480 OPEQ_FUNC(operator+=,+=,)
481 OPEQ_FUNC(operator-=,-=,)
482 OPEQ_FUNC(operator*=,*=,)
483 OPEQ_FUNC(operator/=,/=,)
484
485 OPEQ_FUNC(operator%=,%=,INTEGRAL_ONLY)
486 OPEQ_FUNC(operator&=,&=,INTEGRAL_ONLY)
487 OPEQ_FUNC(operator|=,|=,INTEGRAL_ONLY)
488 OPEQ_FUNC(operator^=,^=,INTEGRAL_ONLY)
489 OPEQ_FUNC(operator<<=,<<=,INTEGRAL_ONLY)
490 OPEQ_FUNC(operator>>=,>>=,INTEGRAL_ONLY)
491
492 #undef OPEQ_FUNC
493
494// we use a simple scheme for type promotion: the promoted type
495// of two values should be the same as the type we would receive
496// when adding the two values. That's standard C semantics, but
497// it won't widen the result type to avoid overflow or increase
498// precision - such conversions have to be made by user code if
499// necessary.
500
501#define C_PROMOTE(A,B) \
502typename std::conditional \
503 < std::is_same < A , B > :: value , \
504 A , \
505 decltype ( std::declval < A > () \
506 + std::declval < B > () ) \
507 > :: type
508
509
510#define OP_FUNC(OPFUNC,OP,CONSTRAINT) \
511 template < typename RHST , \
512 typename = typename std::enable_if \
513 < std::is_fundamental < RHST > :: value \
514 > :: type \
515 > \
516 vc_simd_type < C_PROMOTE ( value_type , RHST ) , size() > \
517 OPFUNC ( vc_simd_type < RHST , vsize > rhs ) const \
518 { \
519 CONSTRAINT \
520 vc_simd_type < C_PROMOTE ( value_type , RHST ) , vsize > help ( *this ) ; \
521 return help.to_base() OP rhs.to_base() ; \
522 } \
523 template < typename RHST , \
524 typename = typename std::enable_if \
525 < std::is_fundamental < RHST > :: value \
526 > :: type \
527 > \
528 vc_simd_type < C_PROMOTE ( value_type , RHST ) , vsize > \
529 OPFUNC ( RHST rhs ) const \
530 { \
531 CONSTRAINT \
532 vc_simd_type < C_PROMOTE ( value_type , RHST ) , vsize > help ( *this ) ; \
533 return help.to_base() OP rhs ; \
534 } \
535 template < typename LHST , \
536 typename = typename std::enable_if \
537 < std::is_fundamental < LHST > :: value \
538 > :: type \
539 > \
540 friend vc_simd_type < C_PROMOTE ( LHST , value_type ) , vsize > \
541 OPFUNC ( LHST lhs , vc_simd_type rhs ) \
542 { \
543 CONSTRAINT \
544 vc_simd_type < C_PROMOTE ( LHST , value_type ) , vsize > help ( lhs ) ; \
545 return help.to_base() OP rhs.to_base() ; \
546 }
547
548 // binary operators and left and right scalar operations with
549 // value_type, unary operators -, ! and ~
550
551 // #define OP_FUNC(OPFUNC,OP,CONSTRAINT) \
552 // vc_simd_type OPFUNC ( const vc_simd_type & rhs ) const \
553 // { \
554 // CONSTRAINT \
555 // return to_base() OP rhs.to_base() ; \
556 // } \
557 // vc_simd_type OPFUNC ( const value_type & rhs ) const \
558 // { \
559 // CONSTRAINT \
560 // return to_base() OP rhs ; \
561 // } \
562 // friend vc_simd_type OPFUNC ( const value_type & lhs , \
563 // const vc_simd_type & rhs ) \
564 // { \
565 // CONSTRAINT \
566 // return lhs OP rhs.to_base() ; \
567 // }
568
569 OP_FUNC(operator+,+,)
570 OP_FUNC(operator-,-,)
571 OP_FUNC(operator*,*,)
572 OP_FUNC(operator/,/,)
573
574 OP_FUNC(operator%,%,INTEGRAL_ONLY)
575 OP_FUNC(operator&,&,INTEGRAL_ONLY)
576 OP_FUNC(operator|,|,INTEGRAL_ONLY)
577 OP_FUNC(operator^,^,INTEGRAL_ONLY)
578 OP_FUNC(operator<<,<<,INTEGRAL_ONLY)
579 OP_FUNC(operator>>,>>,INTEGRAL_ONLY)
580
581 OP_FUNC(operator&&,&&,BOOL_ONLY)
582 OP_FUNC(operator||,||,BOOL_ONLY)
583
584 #undef OP_FUNC
585
586 #define OP_FUNC(OPFUNC,OP,CONSTRAINT) \
587 vc_simd_type OPFUNC() const \
588 { \
589 return OP to_base() ; \
590 }
591
592 OP_FUNC(operator-,-,)
593 OP_FUNC(operator!,!,BOOL_ONLY)
594 OP_FUNC(operator~,~,INTEGRAL_ONLY)
595
596 #undef OP_FUNC
597
598 // provide methods to produce a mask on comparing a vector
599 // with another vector or a value_type.
600
601 #define COMPARE_FUNC(OP,OPFUNC) \
602 friend mask_type OPFUNC ( const vc_simd_type & lhs , \
603 const vc_simd_type & rhs ) \
604 { \
605 return lhs.to_base() OP rhs.to_base() ; \
606 } \
607 friend mask_type OPFUNC ( const vc_simd_type & lhs , \
608 const value_type & rhs ) \
609 { \
610 return lhs.to_base() OP rhs ; \
611 } \
612 friend mask_type OPFUNC ( const value_type & lhs , \
613 const vc_simd_type & rhs ) \
614 { \
615 return lhs OP rhs.to_base() ; \
616 }
617
618 COMPARE_FUNC(<,operator<) ;
619 COMPARE_FUNC(<=,operator<=) ;
620 COMPARE_FUNC(>,operator>) ;
621 COMPARE_FUNC(>=,operator>=) ;
622 COMPARE_FUNC(==,operator==) ;
623 COMPARE_FUNC(!=,operator!=) ;
624
625 #undef COMPARE_FUNC
626
627 // note: Vc's mask_type has associated functions any_of, all_of
628 // and none_of, so we needn't define them for this backend
629
630 // next we define a masked vector as an object holding two members:
631 // one mask, determining which of the vector's elements will be
632 // 'open' to an effect, and one reference to a vector, which will
633 // be affected by the operation.
634 // The resulting object will only be viable as long as the referred-to
635 // vector stays 'alive' - it's meant as a construct to be processed
636 // in the same scope, as the lhs of an assignment, typically using
637 // notation introduced by Vc: a vector's operator() is overloaded to
638 // to produce a masked_type when called with a mask_type object, and
639 // the resulting masked_type object is then assigned to.
640 // Note that this does not have any effect on those values in 'whither'
641 // for which the mask is false. They remain unchanged.
642
644 {
645 const mask_type whether ; // if the mask is true at whether[i]
646 vc_simd_type & whither ; // whither[i] will be assigned to
647
648 masked_type ( const mask_type & _whether ,
649 vc_simd_type & _whither )
650 : whether ( _whether ) ,
651 whither ( _whither )
652 { }
653
654 // for the masked vector, we define the complete set of assignments:
655
656 #define OPEQ_FUNC(OPFUNC,OPEQ,CONSTRAINT) \
657 vc_simd_type & OPFUNC ( const value_type & rhs ) \
658 { \
659 CONSTRAINT \
660 whither.to_base() ( whether ) OPEQ rhs ; \
661 return whither ; \
662 } \
663 vc_simd_type & OPFUNC ( const vc_simd_type & rhs ) \
664 { \
665 CONSTRAINT \
666 whither.to_base() ( whether ) OPEQ rhs.to_base() ; \
667 return whither ; \
668 }
669
670 OPEQ_FUNC(operator=,=,)
671 OPEQ_FUNC(operator+=,+=,)
672 OPEQ_FUNC(operator-=,-=,)
673 OPEQ_FUNC(operator*=,*=,)
674 OPEQ_FUNC(operator/=,/=,)
675 OPEQ_FUNC(operator%=,%=,INTEGRAL_ONLY)
676 OPEQ_FUNC(operator&=,&=,INTEGRAL_ONLY)
677 OPEQ_FUNC(operator|=,|=,INTEGRAL_ONLY)
678 OPEQ_FUNC(operator^=,^=,INTEGRAL_ONLY)
679 OPEQ_FUNC(operator<<=,<<=,INTEGRAL_ONLY)
680 OPEQ_FUNC(operator>>=,>>=,INTEGRAL_ONLY)
681
682 #undef OPEQ_FUNC
683
684 #undef INTEGRAL_ONLY
685 #undef BOOL_ONLY
686
687 } ;
688
689 // mimicking Vc, we define operator() with a mask_type argument
690 // to produce a masked_type object, which can be used later on to
691 // masked-assign to the referred-to vector. With this definition
692 // we can use the same syntax Vc uses, e.g. v1 ( v1 > v2 ) = v3
693 // This helps write code which compiles with Vc and without,
694 // because this idiom is 'very Vc'.
695
696 masked_type operator() ( const mask_type & mask )
697 {
698 return masked_type ( mask , *this ) ;
699 }
700
701 // member functions at_least and at_most. These functions provide the
702 // same functionality as max, or min, respectively. Given vc_simd_type X
703 // and some threshold Y, X.at_least ( Y ) == max ( X , Y )
704 // Having the functionality as a member function makes it easy to
705 // implement, e.g., min as: min ( X , Y ) { return X.at_most ( Y ) ; }
706
707 #define CLAMP(FNAME,REL) \
708 vc_simd_type FNAME ( const vc_simd_type & threshold ) const \
709 { \
710 return REL ( to_base() , threshold.to_base() ) ; \
711 } \
712 vc_simd_type FNAME ( const value_type & threshold ) const \
713 { \
714 return REL ( to_base() , threshold ) ; \
715 } \
716
717 CLAMP(at_least,Vc::max)
718 CLAMP(at_most,Vc::min)
719
720 #undef CLAMP
721
722 // sum of vector elements. Note that there is no type promotion; the
723 // summation is done to value_type. Caller must make sure that overflow
724 // is not a problem.
725
727 {
728 return to_base().sum() ;
729 }
730} ;
731
732template < typename T , std::size_t N >
733struct allocator_traits < vc_simd_type < T , N > >
734{
735 typedef Vc::Allocator < vc_simd_type < T , N > >
737} ;
738
739} ;
740
741#endif // #define VSPLINE_VC_SIMD_TYPE_H
class template simd_type provides a fixed-size container type for small sets of fundamentals which ar...
Definition: simd_type.h:147
Definition: basis.h:79
Vc::Allocator< vc_simd_type< T, N > > type
Definition: vc_simd_type.h:736
vspline creates vigra::MultiArrays of vectorized types. As long as the vectorized types are Vc::SimdA...
Definition: common.h:267
masked_type(const mask_type &_whether, vc_simd_type &_whither)
Definition: vc_simd_type.h:648
class template vc_simd_type provides a fixed-size SIMD type. This implementation of vspline::vc_simd_...
Definition: vc_simd_type.h:77
vc_simd_type(const vc_simd_type< U, vsize > &&ini)
Definition: vc_simd_type.h:192
value_type sum() const
Definition: vc_simd_type.h:726
static const vc_simd_type iota()
Definition: vc_simd_type.h:286
vc_simd_type(const value_type &ini)
Definition: vc_simd_type.h:163
friend std::istream & operator>>(std::istream &isr, vc_simd_type it)
Definition: vc_simd_type.h:316
void gather(const value_type *const p_src, const index_type &indexes)
Definition: vc_simd_type.h:335
void scatter(value_type *const p_trg, const _index_type &_indexes) const
Definition: vc_simd_type.h:355
static const int ivsize
Definition: vc_simd_type.h:107
vc_simd_type & operator=(const value_type &rhs)
Definition: vc_simd_type.h:135
vc_simd_type(const simd_type< unsigned char, vsize > &&ini)
Definition: vc_simd_type.h:235
friend std::ostream & operator<<(std::ostream &osr, vc_simd_type it)
Definition: vc_simd_type.h:309
masked_type operator()(const mask_type &mask)
Definition: vc_simd_type.h:696
COMPARE_FUNC(<=, operator<=)
COMPARE_FUNC(<, operator<)
COMPARE_FUNC(!=, operator!=)
void rscatter(value_type *p_trg, const index_ele_type &step) const
Definition: vc_simd_type.h:381
friend vc_simd_type tan(const vc_simd_type &arg)
Definition: vc_simd_type.h:418
Vc::SimdArray< _value_type, _vsize > base_t
Definition: vc_simd_type.h:78
vc_simd_type(const base_t &&ini)
Definition: vc_simd_type.h:174
index_type::value_type index_ele_type
Definition: vc_simd_type.h:127
void gather(const value_type *const p_src, const _index_type &_indexes)
Definition: vc_simd_type.h:348
COMPARE_FUNC(==, operator==)
_value_type value_type
Definition: vc_simd_type.h:105
void scatter(value_type *const p_trg, const index_type &indexes) const
Definition: vc_simd_type.h:341
vc_simd_type(const base_t &ini)
Definition: vc_simd_type.h:170
vc_simd_type(const vc_simd_type< U, vsize > &ini)
Definition: vc_simd_type.h:188
COMPARE_FUNC(>=, operator>=)
static const index_type IndexesFrom(const index_ele_type &step)
Definition: vc_simd_type.h:302
vc_simd_type(const vc_simd_type &)=default
void rgather(const value_type *const p_src, const index_ele_type &step)
Definition: vc_simd_type.h:375
static constexpr size_type size()
Definition: vc_simd_type.h:111
const base_t & to_base() const
Definition: vc_simd_type.h:87
vc_simd_type(const simd_type< unsigned char, vsize > &ini)
Definition: vc_simd_type.h:230
vc_simd_type(const value_type *const p_src, const index_type &indexes)
Definition: vc_simd_type.h:364
vc_simd_type(vc_simd_type &&)=default
static const size_type vsize
Definition: vc_simd_type.h:106
static const index_type IndexesFrom(const index_ele_type &start, const index_ele_type &step)
Definition: vc_simd_type.h:294
vc_simd_type(const value_type &&ini)
Definition: vc_simd_type.h:166
friend vc_simd_type pow(const vc_simd_type &base, const vc_simd_type &exponent)
Definition: vc_simd_type.h:440
COMPARE_FUNC(>, operator>)
#define BUILD_FROM_CONTAINER(SIZE_TYPE, VSZ)
Definition: vc_simd_type.h:258
#define OPEQ_FUNC(OPFUNC, OPEQ, CONSTRAINT)
Definition: vc_simd_type.h:656
#define BROADCAST_STD_FUNC(FUNC)
Definition: vc_simd_type.h:392
#define OP_FUNC(OPFUNC, OP, CONSTRAINT)
Definition: vc_simd_type.h:586
#define CLAMP(FNAME, REL)
Definition: vc_simd_type.h:707
#define INTEGRAL_ONLY
Definition: vc_simd_type.h:452
#define BOOL_ONLY
Definition: vc_simd_type.h:456
#define BROADCAST_STD_FUNC2(FUNC)
Definition: vc_simd_type.h:427