vspline 1.1.0
Generic C++11 Code for Uniform B-Splines
common.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 common.h
41
42 \brief definitions common to all files in this project, utility code
43
44 This file contains
45
46 - some common enums and strings
47
48 - definition of a few utility types used throughout vspline
49
50 - exceptions used throughout vspline
51
52 It includes vspline/vector.h which defines vspline's use of
53 vectorization (meaning SIMD operation) and associated types ans code.
54*/
55
56#ifndef VSPLINE_COMMON
57#define VSPLINE_COMMON
58
59#include <vigra/multi_array.hxx>
60#include <vigra/tinyvector.hxx>
61
62namespace vspline
63{
64/// This enumeration is used for codes connected to boundary conditions.
65/// There are two aspects to boundary conditions: During prefiltering,
66/// the initial causal and anticausal coefficients have to be calculated
67/// in a way specific to the chosen boundary conditions. Bracing also needs
68/// these codes to pick the appropriate extrapolation code to extrapolate
69/// the coefficients beyond the core array.
70
71typedef enum {
72 MIRROR , // mirror on the bounds, so that f(-x) == f(x)
73 PERIODIC, // periodic boundary conditions
74 REFLECT , // reflect, so that f(-1) == f(0) (mirror between bounds)
75 NATURAL, // natural boundary conditions, f(-x) + f(x) == 2 * f(0)
76 CONSTANT , // clamp. used for framing, with explicit prefilter scheme
77 ZEROPAD , // used for boundary condition, bracing
78 GUESS , // used instead of ZEROPAD to keep margin errors lower
81
82/// bc_name is for diagnostic output of bc codes
83
84const std::string bc_name[] =
85{
86 "MIRROR " ,
87 "PERIODIC ",
88 "REFLECT " ,
89 "NATURAL ",
90 "CONSTANT " ,
91 "ZEROPAD " ,
92 "GUESS "
93} ;
94
95// why 'xlf'? it's for eXtra Large Float'. I use this for the 'most precise
96// floating point type which can be used with standard maths' - currently
97// this is long double, but in the future this might be quads or even better.
98// I use this type where I have very precise constants (like in poles.h)
99// which I try and preserve in as much precision as feasible before they
100// end up being cast down to some lesser type.
101
102typedef long double xlf_type ;
103
104/// using definition for the 'elementary type' of a type via vigra's
105/// ExpandElementResult mechanism. Since this is a frequently used idiom
106/// in vspline but quite a mouthful, here's an abbreviation:
107
108template < typename T >
109using ET =
110typename vigra::ExpandElementResult < T > :: type ;
111
112/// produce a std::integral_constant from the size obtained from
113/// vigra's ExpandElementResult mechanism
114
115template < typename T >
116using EN =
117typename
118std::integral_constant
119 < int ,
120 vigra::ExpandElementResult < T > :: size
121 > :: type ;
122
123/// is_element_expandable tests if a type T is known to vigra's
124/// ExpandElementResult mechanism. If this is so, the type is
125/// considered 'element-expandable'.
126
127template < class T >
128using is_element_expandable = typename
129 std::integral_constant
130 < bool ,
131 ! std::is_same
132 < typename vigra::ExpandElementResult < T > :: type ,
133 vigra::UnsuitableTypeForExpandElements
134 > :: value
135 > :: type ;
136
137// for mathematics, we use a few using declarations. First we have
138// promote_type, which yields a type an arithmetic operation between
139// a T1 and a T2 should yield. We use vigra's PromoteTraits to obtain
140// this type. Note how, by starting out with the formation of the
141// Promote type, we silently enforce that T1 and T2 have equal channel
142// count or are both fundamental types.
143
144template < class T1 , class T2 >
146 = typename vigra::PromoteTraits < T1 , T2 > :: Promote ;
147
148// using vigra's RealPromote on the promote_type gives us a real type
149// suitable for real arithmetics involving T1 and T2. We'll use this
150// type in template argument lists where T1 and T2 are given already,
151// as default value for 'math_type', which is often user-selectable in
152// vspline. With this 'sensible' default, which is what one would
153// 'usually' pick 'anyway', oftentimes the whole template argument
154// list of a specific vspline function can be fixed by ATD, making it
155// easy for the users: they merely have to pass, say, an input and output
156// array (fixing T1 and T2), and the arithmetics will be done in a suitable
157// type derived from these types. This allows users to stray from the
158// 'normal' path (by, for example, stating they want the code to perform
159// arithemtics in double precision even if T1 and T2 are merely float),
160// but makes 'normal' use of the code concise and legible, requiring
161// no explicit template arguments.
162
163template < class T1 , class T2 >
165 = typename vigra::NumericTraits
166 < promote_type < T1 , T2 > > :: RealPromote ;
167
168// while most of the T1, T2 we'll be dealing with will be small aggregates
169// of fundametal types - like TinyVectors of float - we also want a
170// convenient way to get the fundamental type involved - or the 'ele_type'
171// in vspline parlance. Again we use vigra to obtain this value. The
172// elementary type can be obtained by using vigra's ExpandElementResult
173// mechanism, which, as a traits class, can easily be extended to any
174// type holding an aggregate of equally typed fundamentals. Note that we might
175// start out with the elementary types of T1 and T2 and take their Promote,
176// yielding the same resultant type. If promote_type<T1,T2> is fundamental,
177// the ExpandElementResult mechanism passes it through unchanged.
178
179template < class T1 , class T2 >
181 = typename vigra::ExpandElementResult
182 < promote_type < T1 , T2 > > :: type ;
183
184// when doing arithmetic in vspline, typically we use real fundametal types
185// which 'suit' the data types we want to put into the calculations. Again we
186// use vigra to determine an apprpriate fundamental type by obtaining the
187// RealPromote of the promote_ele_type.
188
189template < class T1 , class T2 >
191 = typename vigra::NumericTraits
192 < promote_ele_type < T1 , T2 > > :: RealPromote ;
193
194/// produce the 'canonical type' for a given type T. If T is
195/// single-channel, this will be the elementary type itself,
196/// otherwise it's a TinyVector of the elementary type.
197/// optionally takes the number of elements the resulting
198/// type should have, to allow construction from a fundamental
199/// and a number of channels.
200
201// vspline 'respects the singular'. this is to mean that throughout
202// vspline I avoid using fixed-size containers holding a single value
203// and use the single value itself. While this requires some type
204// artistry in places, it makes using the code more natural. Only
205// when it comes to providing generic code to handle data which may
206// or may not be aggregates, vspline moves to using 'synthetic' types,
207// which are always TinyVectors, possibly with only one element.
208// user code can pass data as canonical or synthetic types. All
209// higher-level operations will produce the same type as output
210// as they receive as input. The move to 'synthetic' types is only
211// done internally to ease the writing of generic code.
212// both the 'canonical' and the 'synthetic' type have lost all
213// 'special' meaning of their components (like their meaning as
214// the real and imaginary part of a complex number). vspline will
215// process all types in it's scope as such 'neutral' aggregates of
216// several identically-typed elementary values.
217
218template < typename T , int N = EN < T > :: value >
220typename
221 std::conditional
222 < N == 1 ,
223 ET < T > ,
224 vigra::TinyVector < ET < T > , N >
225 > :: type ;
226
227template < typename T , int N = EN < T > :: value >
229vigra::TinyVector < ET < T > , N > ;
230
231template < class T , size_t sz = 1 >
233{
234 static_assert ( sz == 1 , "scalar values can only take size 1" ) ;
235} ;
236
237/// definition of a scalar with the same template argument list as
238/// a simdized type, to use 'scalar' in the same syntactic slot
239
240template < class T , size_t sz = 1 >
241using scalar =
242 typename std::conditional
243 < sz == 1 ,
244 T ,
246 > :: type ;
247
248// KFJ 2019-02-21 I want to switch to using size_t for all size-like
249// arguments. vigra::TinyVector takes int as it's size argument, so I
250// introduce 'tiny_vector' which takes size_t instead and static-casts
251// the size_t template argument to int. As long as there are no sizes
252// beyond an int's maximal size this is safe.
253
254template < typename T , size_t SZ >
255using tiny_vector = typename
256vigra::TinyVector < T , static_cast < int > ( SZ ) > ;
257
258/// vspline creates vigra::MultiArrays of vectorized types. As long as
259/// the vectorized types are Vc::SimdArray or vspline::simd_type, using
260/// std::allocator is fine, but when using other types, using a specific
261/// allocator may be necessary. Currently this is never the case, but I
262/// have the lookup of allocator type from this traits class in place if
263/// it should become necessary.
264
265template < typename T >
267{
268 typedef std::allocator < T > type ;
269} ;
270
271// TODO My use of exceptions is a bit sketchy...
272
273/// for interfaces which need specific implementations we use:
274
276: std::invalid_argument
277{
278 not_implemented ( const char * msg )
279 : std::invalid_argument ( msg ) { } ;
280} ;
281
282/// dimension-mismatch is thrown if two arrays have different dimensions
283/// which should have the same dimensions.
284
286: std::invalid_argument
287{
288 dimension_mismatch ( const char * msg )
289 : std::invalid_argument ( msg ) { } ;
290} ;
291
292/// shape mismatch is the exception which is thrown if the shapes of
293/// an input array and an output array do not match.
294
296: std::invalid_argument
297{
298 shape_mismatch ( const char * msg )
299 : std::invalid_argument ( msg ) { } ;
300} ;
301
302/// exception which is thrown if an opertion is requested which vspline
303/// does not support
304
306: std::invalid_argument
307{
308 not_supported ( const char * msg )
309 : std::invalid_argument ( msg ) { } ;
310} ;
311
312/// out_of_bounds is thrown by mapping mode REJECT for out-of-bounds coordinates
313/// this exception is left without a message, it only has a very specific application,
314/// and there it may be thrown often, so we don't want anything slowing it down.
315
317{
318} ;
319
320/// exception which is thrown when assigning an rvalue which is larger than
321/// what the lvalue can hold
322
324: std::invalid_argument
325{
326 numeric_overflow ( const char * msg )
327 : std::invalid_argument ( msg ) { } ;
328} ;
329
330} ; // end of namespace vspline
331
332// with these common definitions done, we now include 'vector.h', which
333// defines vectorization methods used throughout vspline.
334
335#include "vector.h"
336
337#ifdef USE_FMA
338
339// 2019-02-13 tentative use of fma in vspline
340// we need definitions of fma for fundamentals, vigra::TinyVectors and
341// vspline::simd_type. The least spcific template delegates to std::fma,
342// then we have two more specific templates for the container types.
343
344template < typename arg_t >
345arg_t fma ( const arg_t & arg1 , const arg_t & arg2 , const arg_t & arg3 )
346{
347 return std::fma ( arg1 , arg2 , arg3 ) ;
348}
349
350template < typename T , int SZ >
351vigra::TinyVector < T , SZ > fma ( const vigra::TinyVector < T , SZ > & arg1 ,
352 const vigra::TinyVector < T , SZ > & arg2 ,
353 const vigra::TinyVector < T , SZ > & arg3 )
354{
355 vigra::TinyVector < T , SZ > result ;
356 for ( size_t i = 0 ; i < SZ ; i++ )
357 result[i] = fma ( arg1[i] , arg2[i] , arg3[i] ) ;
358 return result ;
359}
360
361template < typename T , size_t SZ >
363 const vspline::simd_type < T , SZ > & arg2 ,
364 const vspline::simd_type < T , SZ > & arg3 )
365{
367 for ( size_t i = 0 ; i < SZ ; i++ )
368 result[i] = fma ( arg1[i] , arg2[i] , arg3[i] ) ;
369 return result ;
370}
371
372#endif // USE_FMA
373
374#endif // VSPLINE_COMMON
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
typename std::conditional< sz==1, T, invalid_scalar< T, sz > > ::type scalar
definition of a scalar with the same template argument list as a simdized type, to use 'scalar' in th...
Definition: common.h:246
typename vigra::PromoteTraits< T1, T2 > ::Promote promote_type
Definition: common.h:146
typename vigra::ExpandElementResult< T > ::type ET
using definition for the 'elementary type' of a type via vigra's ExpandElementResult mechanism....
Definition: common.h:110
vigra::TinyVector< ET< T >, N > synthetic_type
Definition: common.h:229
typename vigra::NumericTraits< promote_ele_type< T1, T2 > > ::RealPromote common_math_ele_type
Definition: common.h:192
typename vigra::ExpandElementResult< promote_type< T1, T2 > > ::type promote_ele_type
Definition: common.h:182
typename std::conditional< N==1, ET< T >, vigra::TinyVector< ET< T >, N > > ::type canonical_type
produce the 'canonical type' for a given type T. If T is single-channel, this will be the elementary ...
Definition: common.h:225
long double xlf_type
Definition: common.h:102
const std::string bc_name[]
bc_name is for diagnostic output of bc codes
Definition: common.h:84
typename std::integral_constant< int, vigra::ExpandElementResult< T > ::size > ::type EN
produce a std::integral_constant from the size obtained from vigra's ExpandElementResult mechanism
Definition: common.h:121
typename vigra::NumericTraits< promote_type< T1, T2 > > ::RealPromote common_math_type
Definition: common.h:166
typename vigra::TinyVector< T, static_cast< int >(SZ) > tiny_vector
Definition: common.h:256
typename std::integral_constant< bool, ! std::is_same< typename vigra::ExpandElementResult< T > ::type, vigra::UnsuitableTypeForExpandElements > ::value > ::type is_element_expandable
is_element_expandable tests if a type T is known to vigra's ExpandElementResult mechanism....
Definition: common.h:135
bc_code
This enumeration is used for codes connected to boundary conditions. There are two aspects to boundar...
Definition: common.h:71
@ CONSTANT
Definition: common.h:76
@ INVALID
Definition: common.h:79
@ GUESS
Definition: common.h:78
@ NATURAL
Definition: common.h:75
@ REFLECT
Definition: common.h:74
@ PERIODIC
Definition: common.h:73
@ MIRROR
Definition: common.h:72
@ ZEROPAD
Definition: common.h:77
vspline creates vigra::MultiArrays of vectorized types. As long as the vectorized types are Vc::SimdA...
Definition: common.h:267
std::allocator< T > type
Definition: common.h:268
dimension-mismatch is thrown if two arrays have different dimensions which should have the same dimen...
Definition: common.h:287
dimension_mismatch(const char *msg)
Definition: common.h:288
for interfaces which need specific implementations we use:
Definition: common.h:277
not_implemented(const char *msg)
Definition: common.h:278
exception which is thrown if an opertion is requested which vspline does not support
Definition: common.h:307
not_supported(const char *msg)
Definition: common.h:308
exception which is thrown when assigning an rvalue which is larger than what the lvalue can hold
Definition: common.h:325
numeric_overflow(const char *msg)
Definition: common.h:326
out_of_bounds is thrown by mapping mode REJECT for out-of-bounds coordinates this exception is left w...
Definition: common.h:317
shape mismatch is the exception which is thrown if the shapes of an input array and an output array d...
Definition: common.h:297
shape_mismatch(const char *msg)
Definition: common.h:298
code for horizontal vectorization in vspline