HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_FixedArrayMath.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_FixedArrayMath.h (UT Library, C++)
7  *
8  * COMMENTS: Define common math operations on fixed array-like types,
9  * which can be used to implement classes such as
10  * UT_Vector2.h, UT_Vector3.h, UT_Vector4.h, UT_FixedVector.h and more.
11  *
12  */
13 
14 #pragma once
15 
16 #ifndef __UT_FixedArrayMath__
17 #define __UT_FixedArrayMath__
18 
19 #include "UT_FixedArray.h"
20 #include <SYS/SYS_StaticAssert.h>
21 #include <SYS/SYS_TypeTraits.h>
22 #include <SYS/SYS_Math.h>
23 #include <SYS/SYS_Inline.h>
24 #include <utility>
25 
26 // namespace UT::FA
27 namespace UT { namespace FA {
28 
29 //
30 // Each function object below implements a math operation that involves
31 // one or more fixed array-like types.
32 // Having each operation as a function object instead of
33 // a free-standing function facilitates (partial) specialization and avoids
34 // the overloading issues that free-standing functions have.
35 // Each function object is parametrized by the element type and tuple size
36 // of the fixed array-like type it works with.
37 // The element type is not are not restricted to a scalar type,
38 // but may itself be a vector-like type.
39 //
40 // None of the below function objects use any type traits to
41 // decide which numeric types and numeric thresholds to use.
42 // Instead, it's up to client code to provide such types and thresholds.
43 //
44 
45 // Assign each element of as to the corresponding element of ts:
46 // For each i in [ 0, SIZE ) do ts[ i ] = as[ i ];
47 template< typename T, exint SIZE >
48 struct Set
49 {
50  template< typename TS >
51  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const TS& as ) const noexcept
52  {
53  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
54 
55  for ( exint i = 0; i != SIZE; ++i )
56  {
57  ts[ i ] = as[ i ];
58  }
59  }
60 };
61 
62 // Convert each element of as to the corresponding element of ts:
63 // For each i in [ 0, SIZE ) do ts[ i ] = as[ i ];
64 template< typename T, exint SIZE, typename A >
65 struct Convert
66 {
67  template< typename TS, typename AS >
68  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const AS& as ) const noexcept
69  {
70  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
71  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< AS, A, SIZE > );
72 
73  for ( exint i = 0; i != SIZE; ++i )
74  {
75  ts[ i ] = as[ i ];
76  }
77  }
78 };
79 
80 // Add each element of as to the corresponding element of ts:
81 // For each i in [ 0, SIZE ) do ts[ i ] += as[ i ];
82 template< typename T, exint SIZE >
83 struct Add
84 {
85  template< typename TS >
86  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const TS& as ) const noexcept
87  {
88  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
89 
90  for ( exint i = 0; i != SIZE; ++i )
91  {
92  ts[ i ] += as[ i ];
93  }
94  }
95 };
96 
97 // Subtract each element of as from the corresponding element of ts:
98 // For each i in [ 0, SIZE ) do ts[ i ] -= as[ i ];
99 template< typename T, exint SIZE >
100 struct Subtract
101 {
102  template< typename TS >
103  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const TS& as ) const noexcept
104  {
105  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
106 
107  for ( exint i = 0; i != SIZE; ++i )
108  {
109  ts[ i ] -= as[ i ];
110  }
111  }
112 };
113 
114 // Multiply each element of 'ts' by 'a':
115 // For each i in [ 0, SIZE ) do ts[ i ] *= a;
116 template< typename T, exint SIZE, typename S = T >
117 struct Scale
118 {
119  template< typename TS >
120  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const S& a ) const noexcept
121  {
122  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
123 
124  for ( exint i = 0; i != SIZE; ++i )
125  {
126  ts[ i ] *= a;
127  }
128  }
129 };
130 
131 // Divide each element of 'ts' by 'a':
132 // For each i in [ 0, SIZE ) do ts[ i ] /= a;
133 template< typename T, exint SIZE, typename S = T >
134 struct Divide
135 {
136  template< typename TS >
137  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const S& a ) const noexcept
138  {
139  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
140 
141  for ( exint i = 0; i != SIZE; ++i )
142  {
143  ts[ i ] /= a;
144  }
145  }
146 };
147 
148 template< typename T, typename S, typename = void >
150 
151 template< typename T, typename S >
152 struct HasAddScaledFunction< T, S, SYS_Void_t< decltype( addScaled( std::declval< T& >(), std::declval< S >(), std::declval< T >() ) ) > > : SYS_TrueType {};
153 
154 template< typename T, typename S >
156 
157 //
158 // To 'ts' add 'a' times 'bs'.
159 // This is done by performing an addScaled operation on each of the
160 // elements, if a suitable "addScaled" function exists.
161 // Otherwise, the addScaled for the elements is implemented
162 // in terms of the "+=" and "*" operators.
163 //
164 template< typename T, exint SIZE, typename S = T >
165 struct AddScaled
166 {
167  template< typename TS >
168  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const S& a, const TS& bs ) const noexcept
169  {
170  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
171 
172  if constexpr( HasAddScaledFunction_v< T, S > )
173  {
174  for ( exint i = 0; i != SIZE; ++i )
175  {
176  addScaled( ts[ i ], a, bs[ i ] );
177  }
178  }
179  else // constexpr
180  {
181  for ( exint i = 0; i != SIZE; ++i )
182  {
183  ts[ i ] += a * bs[ i ];
184  }
185  }
186  }
187 };
188 
189 // From each element of 'ts', subtract 'a' times the corresponding element of 'bs'.
190 // For each i in [ 0, SIZE ) do ts[ i ] -= a * bs[ i ];
191 template< typename T, exint SIZE, typename S = T >
193 {
194  template< typename TS >
195  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const S& a, const TS& bs ) const noexcept
196  {
197  AddScaled< T, SIZE, S >{}( ts, -a, bs );
198  }
199 };
200 
201 // Negate each element of ts:
202 // For each i in [ 0, SIZE ) do ts[ i ] = -ts[ i ];
203 template< typename T, exint SIZE >
204 struct Negate
205 {
206  template< typename TS >
207  constexpr SYS_FORCE_INLINE void operator()( TS& ts ) const noexcept
208  {
209  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
210 
211  for ( exint i = 0; i != SIZE; ++i )
212  {
213  ts[ i ] = -ts[ i ];
214  }
215  }
216 };
217 
218 // Assign 'a' to each element of ts:
219 // For each i in [ 0, SIZE ) do ts[ i ] = a
220 template< typename T, exint SIZE >
222 {
223  template< typename TS >
224  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const T& a ) const noexcept
225  {
226  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
227 
228  for ( exint i = 0; i != SIZE; ++i )
229  {
230  ts[ i ] = a;
231  }
232  }
233 };
234 
235 // Add 'a' to each element of ts:
236 // For each i in [ 0, SIZE ) do ts[ i ] += a
237 template< typename T, exint SIZE >
239 {
240  template< typename TS >
241  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const T& a ) const noexcept
242  {
243  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
244 
245  for ( exint i = 0; i != SIZE; ++i )
246  {
247  ts[ i ] += a;
248  }
249  }
250 };
251 
252 // Subtract 'a' from each element of ts:
253 // For each i in [ 0, SIZE ) do ts[ i ] -= a
254 template< typename T, exint SIZE >
256 {
257  template< typename TS >
258  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const T& a ) const noexcept
259  {
260  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
261 
262  for ( exint i = 0; i != SIZE; ++i )
263  {
264  ts[ i ] -= a;
265  }
266  }
267 };
268 
269 // Multiply each element of ts with the corresponding element of as:
270 // For each i in [ 0, SIZE ) do ts[ i ] *= as[ i ];
271 template< typename T, exint SIZE >
273 {
274  template< typename TS >
275  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const TS& as ) const noexcept
276  {
277  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
278 
279  for( exint i = 0; i != SIZE; ++i )
280  {
281  ts[ i ] *= as[ i ];
282  }
283  }
284 };
285 
286 // Divide each element of ts by the corresponding element of as:
287 // For each i in [ 0, SIZE ) do ts[ i ] /= as[ i ];
288 template< typename T, exint SIZE >
290 {
291  template< typename TS >
292  constexpr SYS_FORCE_INLINE void operator()( TS& ts, const TS& as ) const noexcept
293  {
294  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
295 
296  for( exint i = 0; i != SIZE; ++i )
297  {
298  ts[ i ] /= as[ i ];
299  }
300  }
301 };
302 
303 template< typename T, typename = void >
305 
306 template< typename T >
307 struct HasDotFunction< T, SYS_Void_t< decltype( dot( std::declval< T >(), std::declval< T >() ) ) > > : SYS_TrueType {};
308 
309 template< typename T >
311 
312 //
313 // Dot returns the dot product of as and bs.
314 // If there exists a "dot" function for the element type T of as and bs,
315 // then the sum of the per-element dot products is returned.
316 // Otherwise the sum of the per-element products obtained from
317 // using the binary "*" operation is returned.
318 //
319 // To make Dot work correctly over a complex number type T,
320 // provide a function "dot" that returns the product of as[ i ]
321 // and the complex conjugate of bs[ i ].
322 //
323 template< typename T, exint SIZE >
324 struct Dot
325 {
326  template< typename TS >
327  constexpr SYS_FORCE_INLINE auto operator()( const TS& as, const TS& bs ) const noexcept
328  {
329  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
330 
331  if constexpr( HasDotFunction_v< T > )
332  {
333  auto r{ dot( as[ 0 ], bs[ 0 ] ) };
334 
335  for ( exint i = 1; i != SIZE; ++i )
336  {
337  r += dot( as[ i ], bs[ i ] );
338  }
339 
340  return r;
341  }
342  else // constexpr
343  {
344  const auto a_0{ as[ 0 ] };
345  const auto b_0{ bs[ 0 ] };
346  auto r{ a_0 * b_0 };
347 
348  for ( exint i = 1; i != SIZE; ++i )
349  {
350  const auto a_i{ as[ i ] };
351  const auto b_i{ bs[ i ] };
352  r += a_i * b_i;
353  }
354 
355  return r;
356  }
357  }
358 };
359 
360 template< typename T, exint SIZE >
361 struct Length2
362 {
363  template< typename TS >
364  constexpr SYS_FORCE_INLINE auto operator()( const TS& as ) const noexcept
365  {
366  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
367 
368  return Dot< T, SIZE >{}( as, as );
369  }
370 };
371 
372 template< typename T, exint SIZE >
373 struct Distance2
374 {
375  template< typename TS >
376  constexpr SYS_FORCE_INLINE auto operator()( const TS& as, const TS& bs ) const noexcept
377  {
378  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
379 
380  TS ds{};
381  for ( exint i = 0; i != SIZE; ++i )
382  {
383  ds[ i ] = bs[ i ];
384  ds[ i ] -= as[ i ];
385  }
386 
387  return Length2< T, SIZE >{}( ds );
388  }
389 };
390 
391 //TODO: This uses three floating-point types: T, U, and MF. Can this be simplified?
392 template< typename T, exint SIZE, typename MF = T, typename U = T >
393 struct Normalize
394 {
395  template< typename TS >
396  SYS_FORCE_INLINE MF operator()( TS& ts, const MF l2_min ) const noexcept
397  {
398  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
399 
400  const U l2{ Length2< T, SIZE >{}( ts ) };
401 
402  if ( l2 <= l2_min )
403  {
404  return MF{ 0 };
405  }
406 
407  if ( l2 == U{ 1 } )
408  {
409  return MF{ 1 };
410  }
411 
412  const MF l{ SYSsqrt( MF( l2 ) ) };
413 
414  // Check if the square root is equal 1. sqrt(1+dx) ~ 1+dx/2,
415  // so it may get rounded to 1 when it wasn't 1 before.
416  if ( l != MF{ 1 } )
417  {
418  //TODO: Is this reciprocal pre-compute still worth it?
419  const MF il{ MF{ 1 } / l };
420 
421  for ( exint i = 0; i != SIZE; ++i )
422  {
423  ts[ i ] *= il;
424  }
425  }
426 
427  return l;
428  }
429 };
430 
431 // Return whether p is true for all the elements (each one)
432 template< typename T, exint SIZE >
433 struct AllOf
434 {
435  template< typename TS, typename UNARY_PREDICATE >
436  constexpr SYS_FORCE_INLINE bool operator()( const TS& as, const UNARY_PREDICATE p ) const noexcept
437  {
438  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
439 
440  for ( exint i = 0; i != SIZE; ++i )
441  {
442  if( ! p( as[ i ] ) )
443  {
444  return false;
445  }
446  }
447 
448  return true;
449  }
450 };
451 
452 // Return whether p is true for any elements (at least one)
453 template< typename T, exint SIZE >
454 struct AnyOf
455 {
456  template< typename TS, typename UNARY_PREDICATE >
457  constexpr SYS_FORCE_INLINE bool operator()( const TS& as, const UNARY_PREDICATE p ) const noexcept
458  {
459  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
460 
461  for ( exint i = 0; i != SIZE; ++i )
462  {
463  if( p( as[ i ] ) )
464  {
465  return true;
466  }
467  }
468 
469  return false;
470  }
471 };
472 
473 // Return whether as[ i ] == 0, for each component i in [ 0, SIZE )
474 // This relies only on operator== for the underlying type T.
475 template< typename T, exint SIZE >
477 {
478  template< typename TS >
479  constexpr SYS_FORCE_INLINE bool operator()( const TS& as ) const noexcept
480  {
481  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
482 
483  for ( exint i = 0; i != SIZE; ++i )
484  {
485  if( ! ( as[ i ] == T{ 0 } ) )
486  {
487  return false;
488  }
489  }
490 
491  return true;
492  }
493 };
494 
495 // Return whether as[ i ] == bs[ i ], for each component i in [ 0, SIZE )
496 // This relies only on operator== for the underlying type T.
497 template< typename T, exint SIZE >
498 struct AreEqual
499 {
500  template< typename TS >
501  constexpr SYS_FORCE_INLINE bool operator()( const TS& as, const TS& bs ) const noexcept
502  {
503  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
504 
505  for ( exint i = 0; i != SIZE; ++i )
506  {
507  if( ! ( as[ i ] == bs[ i ] ) )
508  {
509  return false;
510  }
511  }
512 
513  return true;
514  }
515 };
516 
517 // Return whether the maximum norm of 'as' is less than or equal to 'e'.
518 // This returns true if and only if each element of 'as' has absolute value
519 // less than or equal to 'e'
520 // This relies only on operator< for the underlying type T.
521 template< typename T, exint SIZE >
523 {
524  template< typename TS >
525  constexpr SYS_FORCE_INLINE bool operator()( const TS& as, const T e ) const noexcept
526  {
527  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
528 
529  for ( exint i = 0; i != SIZE; ++i )
530  {
531  if ( ( as[ i ] < -e ) || ( e < as[ i ] ) )
532  {
533  return false;
534  }
535  }
536 
537  return true;
538  }
539 };
540 
541 // Return whether the maximum metric of 'as' and 'bs' is less than or equal to 'e'.
542 // This returns true if and only if each componentwise difference has absolute value
543 // less than or equal to 'e'
544 // This relies only on operator< for the underlying type T.
545 template< typename T, exint SIZE >
547 {
548  template< typename TS >
549  constexpr SYS_FORCE_INLINE bool operator()( const TS& as, const TS& bs, const T e ) const noexcept
550  {
551  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
552 
553  for ( exint i = 0; i != SIZE; ++i )
554  {
555  if ( ( as[ i ] < bs[ i ] - e ) || ( bs[ i ] + e < as[ i ] ) )
556  {
557  return false;
558  }
559  }
560 
561  return true;
562  }
563 };
564 
565 // Return the maximum component
566 // This relies only on operator< for the underlying type T.
567 template< typename T, exint SIZE >
568 struct Max
569 {
570  template< typename TS >
571  constexpr SYS_FORCE_INLINE T operator()( const TS& as ) const noexcept
572  {
573  T t{ as[ 0 ] };
574 
575  for ( exint i = 1; i != SIZE; ++i )
576  {
577  t = ( t < as[ i ] ) ? as[ i ] : t;
578  }
579 
580  return t;
581  }
582 };
583 
584 // Return the minimum component
585 // This relies only on operator< for the underlying type T.
586 template< typename T, exint SIZE >
587 struct Min
588 {
589  template< typename TS >
590  constexpr SYS_FORCE_INLINE T operator()( const TS& as ) const noexcept
591  {
592  T t{ as[ 0 ] };
593 
594  for ( exint i = 1; i != SIZE; ++i )
595  {
596  t = ( as[ i ] < t ) ? as[ i ] : t;
597  }
598 
599  return t;
600  }
601 };
602 
603 // Return the sum of the components
604 // This relies only on operator+= for the underlying type T.
605 template< typename T, exint SIZE >
606 struct Sum
607 {
608  template< typename TS >
609  constexpr SYS_FORCE_INLINE T operator()( const TS& as ) const noexcept
610  {
611  T t{ as[ 0 ] };
612 
613  for ( exint i = 1; i != SIZE; ++i )
614  {
615  t += as[ i ];
616  }
617 
618  return t;
619  }
620 };
621 
622 // Based on lexicographical order, return
623 // -1, if as < bs
624 // 1, if bs < as
625 // 0, otherwise.
626 // This relies only on operator< for the underlying type T.
627 template< typename T, exint SIZE >
629 {
630  template< typename TS >
631  constexpr SYS_FORCE_INLINE int operator()( const TS& as, const TS& bs ) const noexcept
632  {
633  SYS_STATIC_ASSERT( SYS_IsFixedArrayOf_v< TS, T, SIZE > );
634 
635  for ( exint i = 0; i != SIZE; ++i )
636  {
637  if( as[ i ] < bs[ i ] )
638  {
639  return -1;
640  }
641 
642  if( bs[ i ] < as[ i ] )
643  {
644  return 1;
645  }
646  }
647 
648  return 0;
649  }
650 };
651 
652 } }
653 // end of namespace UT::FA
654 
655 #endif
656 
SYS_FORCE_INLINE MF operator()(TS &ts, const MF l2_min) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const T &a) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const AS &as) const noexcept
#define SYS_STATIC_ASSERT(expr)
constexpr SYS_FORCE_INLINE auto operator()(const TS &as, const TS &bs) const noexcept
void SYS_Void_t
Alternative for C++17's std::void that can be used in C++14:
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const S &a) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const S &a) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const TS &as) const noexcept
int64 exint
Definition: SYS_Types.h:125
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const TS &as) const noexcept
constexpr SYS_FORCE_INLINE bool operator()(const TS &as, const T e) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const TS &as) const noexcept
constexpr SYS_FORCE_INLINE bool operator()(const TS &as, const UNARY_PREDICATE p) const noexcept
constexpr SYS_FORCE_INLINE T operator()(const TS &as) const noexcept
fpreal64 dot(const CE_VectorT< T > &a, const CE_VectorT< T > &b)
Definition: CE_Vector.h:130
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
constexpr SYS_FORCE_INLINE bool operator()(const TS &as) const noexcept
constexpr SYS_FORCE_INLINE bool operator()(const TS &as, const TS &bs) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const S &a, const TS &bs) const noexcept
constexpr SYS_FORCE_INLINE T operator()(const TS &as) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts) const noexcept
constexpr SYS_FORCE_INLINE T operator()(const TS &as) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const TS &as) const noexcept
GLdouble t
Definition: glad.h:2397
constexpr SYS_FORCE_INLINE auto operator()(const TS &as, const TS &bs) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const T &a) const noexcept
constexpr bool HasAddScaledFunction_v
constexpr bool HasDotFunction_v
#define SIZE
Definition: simple.C:41
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const T &a) const noexcept
constexpr SYS_FORCE_INLINE bool operator()(const TS &as, const UNARY_PREDICATE p) const noexcept
GLboolean r
Definition: glcorearb.h:1222
constexpr SYS_FORCE_INLINE bool operator()(const TS &as, const TS &bs, const T e) const noexcept
constexpr SYS_FORCE_INLINE int operator()(const TS &as, const TS &bs) const noexcept
constexpr SYS_FORCE_INLINE auto operator()(const TS &as) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const S &a, const TS &bs) const noexcept
constexpr SYS_FORCE_INLINE void operator()(TS &ts, const TS &as) const noexcept