HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
numeric_array.hpp
1// Copyright Take Vos 2020-2021.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "os_detect.hpp"
8#include "aligned_array.hpp"
9#include "concepts.hpp"
10#include "cast.hpp"
11#include "type_traits.hpp"
12#if TT_PROCESSOR == TT_CPU_X64
13#include "detail/f32x4_sse.hpp"
14#endif
15
16#include <cstdint>
17#include <ostream>
18#include <string>
19#include <array>
20#include <type_traits>
21#include <concepts>
22#include <bit>
23
24namespace tt {
25
26template<arithmetic T, ssize_t N>
28public:
29 static_assert(N >= 0);
30
32 using value_type = typename container_type::value_type;
33 using size_type = typename container_type::size_type;
34 using difference_type = typename container_type::difference_type;
35 using reference = typename container_type::reference;
36 using const_reference = typename container_type::const_reference;
37 using pointer = typename container_type::pointer;
38 using const_pointer = typename container_type::const_pointer;
39 using iterator = typename container_type::iterator;
40 using const_iterator = typename container_type::const_iterator;
41
42 constexpr numeric_array() noexcept = default;
43 constexpr numeric_array(numeric_array const &rhs) noexcept = default;
44 constexpr numeric_array(numeric_array &&rhs) noexcept = default;
45 constexpr numeric_array &operator=(numeric_array const &rhs) noexcept = default;
46 constexpr numeric_array &operator=(numeric_array &&rhs) noexcept = default;
47
48 [[nodiscard]] constexpr numeric_array(std::initializer_list<T> rhs) noexcept : v()
49 {
50 auto src = std::begin(rhs);
51 auto dst = std::begin(v);
52
53 // Copy all values from the initializer list.
54 while (src != std::end(rhs) && dst != std::end(v)) {
55 *(dst++) = *(src++);
56 }
57
58 tt_axiom(
59 dst != std::end(v) || src == std::end(rhs),
60 "Expecting the std:initializer_list size to be <= to the size of the numeric array");
61
62 // Set all other elements to zero
63 while (dst != std::end(v)) {
64 *(dst++) = {};
65 }
66 }
67
68 [[nodiscard]] constexpr numeric_array(T const &first) noexcept requires(N == 1) : numeric_array({first}) {}
69
70 template<arithmetic... Rest>
71 requires(sizeof...(Rest) + 2 <= N)
72 [[nodiscard]] constexpr numeric_array(T const &first, T const &second, Rest const &...rest) noexcept :
73 numeric_array({first, second, narrow_cast<T>(rest)...})
74 {
75 }
76
77 [[nodiscard]] explicit constexpr numeric_array(aligned_array<T, N> const &rhs) noexcept : v(rhs) {}
78
79 [[nodiscard]] static constexpr numeric_array broadcast(T rhs) noexcept
80 {
81 auto r = numeric_array{};
82 for (ssize_t i = 0; i != N; ++i) {
83 r[i] = rhs;
84 }
85 return r;
86 }
87
90 [[nodiscard]] static constexpr numeric_array point() noexcept
91 {
92 auto r = numeric_array{};
93 r.back() = T{1};
94 return r;
95 }
96
99 [[nodiscard]] static constexpr numeric_array point(numeric_array rhs) noexcept
100 {
101 rhs.back() = T{1};
102 return rhs;
103 }
104
105 [[nodiscard]] static constexpr numeric_array point(std::initializer_list<T> rhs) noexcept
106 {
107 auto r = numeric_array(rhs);
108
109 if (std::size(rhs) < N) {
110 r.v.back() = T{1};
111 }
112
113 tt_axiom(r.v.back() != T{}, "Last element of a point should be non-zero");
114 return r;
115 }
116
117 template<arithmetic... Rest>
118 [[nodiscard]] static constexpr numeric_array point(T const &first, Rest const &...rest) noexcept requires(sizeof...(Rest) < N)
119 {
120 return point({first, narrow_cast<T>(rest)...});
121 }
122
123 [[nodiscard]] static constexpr numeric_array color(std::initializer_list<T> rhs) noexcept
124 {
125 return point(rhs);
126 }
127
128 template<arithmetic... Rest>
129 [[nodiscard]] static constexpr numeric_array color(T const &first, Rest const &...rest) noexcept requires(sizeof...(Rest) < N)
130 {
131 return color({first, narrow_cast<T>(rest)...});
132 }
133
134 template<arithmetic U, ssize_t M>
135 [[nodiscard]] explicit constexpr numeric_array(std::array<U, M> const &rhs) noexcept : v()
136 {
137 auto src = std::begin(rhs);
138 auto dst = std::begin(v);
139 auto src_last = std::end(rhs);
140 auto dst_last = std::end(v);
141
142 while (src != src_last && dst != dst_last) {
143 *(dst++) = narrow_cast<T>(*(src++));
144 }
145 while (dst != dst_last) {
146 *(dst++) = T{};
147 }
148 while (src != src_last) {
149 tt_axiom(*(src++) == U{});
150 }
151 }
152
153 template<arithmetic U, ssize_t M>
154 [[nodiscard]] explicit constexpr numeric_array(numeric_array<U, M> const &rhs) noexcept : v()
155 {
156 auto src = std::begin(rhs);
157 auto dst = std::begin(v);
158 auto src_last = std::end(rhs);
159 auto dst_last = std::end(v);
160
161 while (src != src_last && dst != dst_last) {
162 *(dst++) = narrow_cast<T>(*(src++));
163 }
164 while (dst != dst_last) {
165 *(dst++) = T{};
166 }
167 while (src != src_last) {
168 tt_axiom(*(src++) == U{});
169 }
170 }
171
172 [[nodiscard]] explicit numeric_array(__m128 const &rhs) noexcept requires(N == 4 && std::is_same_v<T, float> && has_sse) :
173 v(rhs)
174 {
175 }
176
177 [[nodiscard]] explicit numeric_array(__m128d const &rhs) noexcept requires(N == 2 && std::is_same_v<T, double> && has_sse) :
178 v(rhs)
179 {
180 }
181
182 [[nodiscard]] explicit numeric_array(__m128i const &rhs) noexcept
183 requires(std::is_integral_v<T> &&std::is_signed_v<T> && sizeof(T) * N == 16 && has_sse) :
184 v(rhs)
185 {
186 }
187
188 [[nodiscard]] explicit numeric_array(__m256 const &rhs) noexcept requires(N == 8 && std::is_same_v<T, float> && has_sse) :
189 v(rhs)
190 {
191 }
192
193 [[nodiscard]] explicit numeric_array(__m256d const &rhs) noexcept requires(N == 4 && std::is_same_v<T, double> && has_sse) :
194 v(rhs)
195 {
196 }
197
198 [[nodiscard]] explicit numeric_array(__m256i const &rhs) noexcept
199 requires(std::is_integral_v<T> &&std::is_signed_v<T> && sizeof(T) * N == 32 && has_sse) :
200 v(rhs)
201 {
202 }
203
204 template<arithmetic U, ssize_t M>
205 [[nodiscard]] explicit constexpr operator std::array<U, M>() const noexcept
206 {
207 auto r = std::array<U, M>{};
208
209 auto src = std::begin(v);
210 auto dst = std::begin(r);
211 auto src_last = std::end(v);
212 auto dst_last = std::end(r);
213
214 while (src != src_last && dst != dst_last) {
215 *(dst++) = narrow_cast<U>(*(src++));
216 }
217 while (dst != dst_last) {
218 *(dst++) = U{};
219 }
220
221 // Check if the rest of the data are zeros, or one in homogeneous coordinates.
222 while (src != src_last) {
223 tt_axiom(*src == T{} || *src == T{1});
224 ++src;
225 }
226
227 return r;
228 }
229
230 template<arithmetic U, ssize_t M>
231 [[nodiscard]] explicit constexpr operator numeric_array<U, M>() const noexcept
232 {
233 auto r = std::array<U, M>{};
234
235 auto src = std::begin(v);
236 auto dst = std::begin(r);
237 auto src_last = std::end(v);
238 auto dst_last = std::end(r);
239
240 while (src != src_last && dst != dst_last) {
241 *(dst++) = narrow_cast<U>(*(src++));
242 }
243 while (dst != dst_last) {
244 *(dst++) = U{};
245 }
246 while (src != src_last) {
247 tt_axiom(*(src++) == T{});
248 }
249
250 return r;
251 }
252
253 [[nodiscard]] explicit operator __m128() const noexcept requires(N == 4 && std::is_same_v<T, float> && has_sse)
254 {
255 return static_cast<__m128>(v);
256 }
257
258 [[nodiscard]] explicit operator __m128d() const noexcept requires(N == 2 && std::is_same_v<T, double> && has_sse)
259 {
260 return static_cast<__m128d>(v);
261 }
262
263 [[nodiscard]] explicit operator __m128i() const noexcept
264 requires(std::is_integral_v<T> &&std::is_signed_v<T> && sizeof(T) * N == 16 && has_sse)
265 {
266 return static_cast<__m128i>(v);
267 }
268
269 [[nodiscard]] explicit operator __m256() const noexcept requires(N == 8 && std::is_same_v<T, float> && has_sse)
270 {
271 return static_cast<__m256>(v);
272 }
273
274 [[nodiscard]] explicit operator __m256d() const noexcept requires(N == 4 && std::is_same_v<T, double> && has_sse)
275 {
276 return static_cast<__m256d>(v);
277 }
278
279 [[nodiscard]] explicit operator __m256i() const noexcept
280 requires(std::is_integral_v<T> &&std::is_signed_v<T> && sizeof(T) * N == 32 && has_sse)
281 {
282 return static_cast<__m256i>(v);
283 }
284
285 [[nodiscard]] constexpr T const &operator[](ssize_t i) const noexcept
286 {
287 tt_axiom(i < N);
288 return v[i];
289 }
290
291 [[nodiscard]] constexpr T &operator[](ssize_t i) noexcept
292 {
293 tt_axiom(i < N);
294 return v[i];
295 }
296
297 [[nodiscard]] constexpr reference front() noexcept
298 {
299 return v.front();
300 }
301
302 [[nodiscard]] constexpr const_reference front() const noexcept
303 {
304 return v.front();
305 }
306
307 [[nodiscard]] constexpr reference back() noexcept
308 {
309 return v.back();
310 }
311
312 [[nodiscard]] constexpr const_reference back() const noexcept
313 {
314 return v.back();
315 }
316
317 [[nodiscard]] constexpr pointer data() noexcept
318 {
319 return v.data();
320 }
321
322 [[nodiscard]] constexpr const_pointer data() const noexcept
323 {
324 return v.data();
325 }
326
327 [[nodiscard]] constexpr iterator begin() noexcept
328 {
329 return v.begin();
330 }
331
332 [[nodiscard]] constexpr const_iterator begin() const noexcept
333 {
334 return v.begin();
335 }
336
337 [[nodiscard]] constexpr const_iterator cbegin() const noexcept
338 {
339 return v.cbegin();
340 }
341
342 [[nodiscard]] constexpr iterator end() noexcept
343 {
344 return v.end();
345 }
346
347 [[nodiscard]] constexpr const_iterator end() const noexcept
348 {
349 return v.end();
350 }
351
352 [[nodiscard]] constexpr const_iterator cend() const noexcept
353 {
354 return v.cend();
355 }
356
357 [[nodiscard]] constexpr bool empty() const noexcept
358 {
359 return v.empty();
360 }
361
362 [[nodiscard]] constexpr size_type size() const noexcept
363 {
364 return v.size();
365 }
366
367 [[nodiscard]] constexpr size_type max_size() const noexcept
368 {
369 return v.max_size();
370 }
371
372 constexpr bool is_point() const noexcept
373 {
374 return v.back() != T{};
375 }
376
377 constexpr bool is_vector() const noexcept
378 {
379 return v.back() == T{};
380 }
381
382 constexpr bool is_opaque() const noexcept
383 {
384 return a() == T{1};
385 }
386
387 constexpr bool is_transparent() const noexcept
388 {
389 return a() == T{0};
390 }
391
392 [[nodiscard]] constexpr T const &x() const noexcept requires(N >= 1)
393 {
394 return get<0>(v);
395 }
396
397 [[nodiscard]] constexpr T const &y() const noexcept requires(N >= 2)
398 {
399 return get<1>(v);
400 }
401
402 [[nodiscard]] constexpr T const &z() const noexcept requires(N >= 3)
403 {
404 return get<2>(v);
405 }
406
407 [[nodiscard]] constexpr T const &w() const noexcept requires(N >= 4)
408 {
409 return get<3>(v);
410 }
411
412 [[nodiscard]] constexpr T &x() noexcept requires(N >= 1)
413 {
414 return get<0>(v);
415 }
416
417 [[nodiscard]] constexpr T &y() noexcept requires(N >= 2)
418 {
419 return get<1>(v);
420 }
421
422 [[nodiscard]] constexpr T &z() noexcept requires(N >= 3)
423 {
424 return get<2>(v);
425 }
426
427 [[nodiscard]] constexpr T &w() noexcept requires(N >= 4)
428 {
429 return get<3>(v);
430 }
431
432 [[nodiscard]] constexpr T const &r() const noexcept requires(N >= 1)
433 {
434 return get<0>(v);
435 }
436
437 [[nodiscard]] constexpr T const &g() const noexcept requires(N >= 2)
438 {
439 return get<1>(v);
440 }
441
442 [[nodiscard]] constexpr T const &b() const noexcept requires(N >= 3)
443 {
444 return get<2>(v);
445 }
446
447 [[nodiscard]] constexpr T const &a() const noexcept requires(N >= 4)
448 {
449 return get<3>(v);
450 }
451
452 [[nodiscard]] constexpr T &r() noexcept requires(N >= 1)
453 {
454 return get<0>(v);
455 }
456
457 [[nodiscard]] constexpr T &g() noexcept requires(N >= 2)
458 {
459 return get<1>(v);
460 }
461
462 [[nodiscard]] constexpr T &b() noexcept requires(N >= 3)
463 {
464 return get<2>(v);
465 }
466
467 [[nodiscard]] constexpr T &a() noexcept requires(N >= 4)
468 {
469 return get<3>(v);
470 }
471
472 [[nodiscard]] constexpr T const &width() const noexcept requires(N >= 1)
473 {
474 return get<0>(v);
475 }
476
477 [[nodiscard]] constexpr T const &height() const noexcept requires(N >= 2)
478 {
479 return get<1>(v);
480 }
481
482 [[nodiscard]] constexpr T const &depth() const noexcept requires(N >= 3)
483 {
484 return get<2>(v);
485 }
486
487 [[nodiscard]] constexpr T &width() noexcept requires(N >= 1)
488 {
489 return get<0>(v);
490 }
491
492 [[nodiscard]] constexpr T &height() noexcept requires(N >= 2)
493 {
494 return get<1>(v);
495 }
496
497 [[nodiscard]] constexpr T &depth() noexcept requires(N >= 3)
498 {
499 return get<2>(v);
500 }
501
502 constexpr numeric_array &operator+=(numeric_array const &rhs) noexcept
503 {
504 for (ssize_t i = 0; i != N; ++i) {
505 v[i] += rhs.v[i];
506 }
507 return *this;
508 }
509
510 constexpr numeric_array &operator+=(T const &rhs) noexcept
511 {
512 for (ssize_t i = 0; i != N; ++i) {
513 v[i] += rhs;
514 }
515 return *this;
516 }
517
518 constexpr numeric_array &operator-=(numeric_array const &rhs) noexcept
519 {
520 for (ssize_t i = 0; i != N; ++i) {
521 v[i] -= rhs.v[i];
522 }
523 return *this;
524 }
525
526 constexpr numeric_array &operator-=(T const &rhs) noexcept
527 {
528 for (ssize_t i = 0; i != N; ++i) {
529 v[i] -= rhs;
530 }
531 return *this;
532 }
533
534 constexpr numeric_array &operator*=(numeric_array const &rhs) noexcept
535 {
536 for (ssize_t i = 0; i != N; ++i) {
537 v[i] *= rhs.v[i];
538 }
539 return *this;
540 }
541
542 constexpr numeric_array &operator*=(T const &rhs) noexcept
543 {
544 for (ssize_t i = 0; i != N; ++i) {
545 v[i] *= rhs;
546 }
547 return *this;
548 }
549
550 constexpr numeric_array &operator/=(numeric_array const &rhs) noexcept
551 {
552 for (ssize_t i = 0; i != N; ++i) {
553 v[i] /= rhs.v[i];
554 }
555 return *this;
556 }
557
558 constexpr numeric_array &operator/=(T const &rhs) noexcept
559 {
560 for (ssize_t i = 0; i != N; ++i) {
561 v[i] /= rhs;
562 }
563 return *this;
564 }
565
566 constexpr numeric_array &operator%=(numeric_array const &rhs) noexcept
567 {
568 for (ssize_t i = 0; i != N; ++i) {
569 v[i] %= rhs.v[i];
570 }
571 return *this;
572 }
573
574 constexpr numeric_array &operator%=(T const &rhs) noexcept
575 {
576 for (ssize_t i = 0; i != N; ++i) {
577 v[i] %= rhs;
578 }
579 return *this;
580 }
581
582 constexpr static ssize_t get_zero = -1;
583 constexpr static ssize_t get_one = -2;
584
589 template<ssize_t I>
590 [[nodiscard]] friend constexpr T &get(numeric_array &rhs) noexcept
591 {
592 static_assert(I >= 0 && I < N, "Index out of bounds");
593 return get<I>(rhs.v);
594 }
595
601 template<ssize_t I>
602 [[nodiscard]] friend constexpr T get(numeric_array &&rhs) noexcept
603 {
604 static_assert(I >= -2 && I < N, "Index out of bounds");
605 if constexpr (I == get_zero) {
606 return T{0};
607 } else if constexpr (I == get_one) {
608 return T{1};
609 } else {
610 return get<I>(rhs.v);
611 }
612 }
613
619 template<ssize_t I>
620 [[nodiscard]] friend constexpr T get(numeric_array const &rhs) noexcept
621 {
622 static_assert(I >= -2 && I < N, "Index out of bounds");
623 if constexpr (I == get_zero) {
624 return T{0};
625 } else if constexpr (I == get_one) {
626 return T{1};
627 } else {
628 return get<I>(rhs.v);
629 }
630 }
631
636 template<size_t Mask = ~size_t{0}>
637 [[nodiscard]] friend constexpr numeric_array zero(numeric_array rhs) noexcept
638 {
639 if (!std::is_constant_evaluated()) {
640 if constexpr (is_f32x4 && has_sse) {
641 return numeric_array{f32x4_sse_zero<Mask & 0xf>(rhs.v)};
642 }
643 }
644
645 auto r = numeric_array{};
646 for (ssize_t i = 0; i != N; ++i) {
647 if ((Mask >> i) & 1 == 1) {
648 r.v[i] = T{0};
649 } else {
650 r.v[i] = rhs.v[i];
651 }
652 }
653 return r;
654 }
655
660 template<size_t Mask = ~size_t{0}>
661 [[nodiscard]] friend constexpr numeric_array neg(numeric_array rhs) noexcept
662 {
663 if (!std::is_constant_evaluated()) {
664 if constexpr (is_f32x4 && has_sse) {
665 return numeric_array{f32x4_sse_neg<Mask & 0xf>(rhs.v)};
666 }
667 }
668
669 auto r = numeric_array{};
670 for (ssize_t i = 0; i != N; ++i) {
671 if (((Mask >> i) & 1) == 1) {
672 r.v[i] = -rhs.v[i];
673 } else {
674 r.v[i] = rhs.v[i];
675 }
676 }
677 return r;
678 }
679
680 [[nodiscard]] friend constexpr numeric_array operator-(numeric_array const &rhs) noexcept
681 {
682 auto r = numeric_array{};
683 for (ssize_t i = 0; i != N; ++i) {
684 // -rhs.v[i] will cause a memory load with msvc.
685 r.v[i] = T{} - rhs.v[i];
686 }
687 return r;
688 }
689
690 [[nodiscard]] friend constexpr numeric_array abs(numeric_array const &rhs) noexcept
691 {
692 auto neg_rhs = -rhs;
693
694 auto r = numeric_array{};
695 for (ssize_t i = 0; i != N; ++i) {
696 r.v[i] = rhs.v[i] < T{} ? neg_rhs.v[i] : rhs.v[i];
697 }
698 return r;
699 }
700
701 [[nodiscard]] friend constexpr numeric_array rcp(numeric_array const &rhs) noexcept
702 {
703 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
704 return numeric_array{f32x4_sse_rcp(rhs.v)};
705 }
706
707 auto r = numeric_array{};
708 for (ssize_t i = 0; i != N; ++i) {
709 r[i] = 1.0f / rhs.v[i];
710 }
711 return r;
712 }
713
714 [[nodiscard]] friend constexpr numeric_array sqrt(numeric_array const &rhs) noexcept
715 {
716 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
717 return numeric_array{f32x4_sse_sqrt(rhs.v)};
718 }
719
720 auto r = numeric_array{};
721 for (ssize_t i = 0; i != N; ++i) {
722 r[i] = std::sqrt(rhs.v[i]);
723 }
724 return r;
725 }
726
727 [[nodiscard]] friend constexpr numeric_array rcp_sqrt(numeric_array const &rhs) noexcept
728 {
729 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
730 return numeric_array{f32x4_sse_rcp_sqrt(rhs.v)};
731 }
732
733 auto r = numeric_array{};
734 for (ssize_t i = 0; i != N; ++i) {
735 r[i] = 1.0f / std::sqrt(rhs.v[i]);
736 }
737 return r;
738 }
739
740 [[nodiscard]] friend constexpr numeric_array floor(numeric_array const &rhs) noexcept
741 {
742 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
743 return numeric_array{f32x4_sse_floor(rhs.v)};
744 }
745
746 auto r = numeric_array{};
747 for (ssize_t i = 0; i != N; ++i) {
748 r[i] = std::floor(rhs.v[i]);
749 }
750 return r;
751 }
752
753 [[nodiscard]] friend constexpr numeric_array ceil(numeric_array const &rhs) noexcept
754 {
755 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
756 return numeric_array{f32x4_sse_ceil(rhs.v)};
757 }
758
759 auto r = numeric_array{};
760 for (ssize_t i = 0; i != N; ++i) {
761 r[i] = std::ceil(rhs.v[i]);
762 }
763 return r;
764 }
765
766 [[nodiscard]] friend constexpr numeric_array round(numeric_array const &rhs) noexcept
767 {
768 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
769 return numeric_array{f32x4_sse_round(rhs.v)};
770 }
771
772 auto r = numeric_array{};
773 for (ssize_t i = 0; i != N; ++i) {
774 r[i] = std::round(rhs.v[i]);
775 }
776 return r;
777 }
778
786 template<ssize_t Mask>
787 [[nodiscard]] friend constexpr T dot(numeric_array const &lhs, numeric_array const &rhs) noexcept
788 {
789 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
790 return f32x4_sse_dot<Mask>(lhs.v, rhs.v);
791 }
792
793 auto r = T{};
794 for (ssize_t i = 0; i != N; ++i) {
795 if (static_cast<bool>(Mask & (1_uz << i))) {
796 r += lhs.v[i] * rhs.v[i];
797 }
798 }
799 return r;
800 }
801
809 template<ssize_t Mask>
810 [[nodiscard]] friend constexpr T hypot(numeric_array const &rhs) noexcept
811 {
812 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
813 return f32x4_sse_hypot<Mask>(rhs.v);
814 }
815 return std::sqrt(dot<Mask>(rhs, rhs));
816 }
817
825 template<ssize_t Mask>
826 [[nodiscard]] friend constexpr T squared_hypot(numeric_array const &rhs) noexcept
827 {
828 return dot<Mask>(rhs, rhs);
829 }
830
837 template<ssize_t Mask>
838 [[nodiscard]] friend constexpr T rcp_hypot(numeric_array const &rhs) noexcept
839 {
840 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
841 return f32x4_sse_rcp_hypot<Mask>(rhs.v);
842 }
843
844 return 1.0f / hypot<Mask>(rhs);
845 }
846
855 template<ssize_t Mask>
856 [[nodiscard]] friend constexpr numeric_array normalize(numeric_array const &rhs) noexcept
857 {
858 tt_axiom(rhs.is_vector());
859
860 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
861 return numeric_array{f32x4_sse_normalize<Mask>(rhs.v)};
862 }
863
864 ttlet rcp_hypot_ = rcp_hypot<Mask>(rhs);
865
866 auto r = numeric_array{};
867 for (size_t i = 0; i != N; ++i) {
868 if (static_cast<bool>(Mask & (1_uz << i))) {
869 r.v[i] = rhs.v[i] * rcp_hypot_;
870 }
871 }
872 return r;
873 }
874
875 [[nodiscard]] friend constexpr unsigned int eq(numeric_array const &lhs, numeric_array const &rhs) noexcept
876 requires(N <= sizeof(unsigned int) * CHAR_BIT)
877 {
878 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
879 return f32x4_sse_eq_mask(lhs.v, rhs.v);
880 } else {
881 unsigned int r = 0;
882 for (ssize_t i = 0; i != N; ++i) {
883 r |= static_cast<unsigned int>(lhs.v[i] == rhs.v[i]) << i;
884 }
885 return r;
886 }
887 }
888
889 [[nodiscard]] friend constexpr unsigned int ne(numeric_array const &lhs, numeric_array const &rhs) noexcept
890 requires(N <= sizeof(unsigned int) * CHAR_BIT)
891 {
892 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
893 return f32x4_sse_ne_mask(lhs.v, rhs.v);
894 } else {
895 unsigned int r = 0;
896 for (ssize_t i = 0; i != N; ++i) {
897 r |= static_cast<unsigned int>(lhs.v[i] != rhs.v[i]) << i;
898 }
899 return r;
900 }
901 }
902
903 [[nodiscard]] friend constexpr unsigned int lt(numeric_array const &lhs, numeric_array const &rhs) noexcept
904 requires(N <= sizeof(unsigned int) * CHAR_BIT)
905 {
906 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
907 return f32x4_sse_lt_mask(lhs.v, rhs.v);
908 } else {
909 unsigned int r = 0;
910 for (ssize_t i = 0; i != N; ++i) {
911 r |= static_cast<unsigned int>(lhs.v[i] < rhs.v[i]) << i;
912 }
913 return r;
914 }
915 }
916
917 [[nodiscard]] friend constexpr unsigned int gt(numeric_array const &lhs, numeric_array const &rhs) noexcept
918 requires(N <= sizeof(unsigned int) * CHAR_BIT)
919 {
920 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
921 return f32x4_sse_gt_mask(lhs.v, rhs.v);
922 } else {
923 unsigned int r = 0;
924 for (ssize_t i = 0; i != N; ++i) {
925 r |= static_cast<unsigned int>(lhs.v[i] > rhs.v[i]) << i;
926 }
927 return r;
928 }
929 }
930
931 [[nodiscard]] friend constexpr unsigned int le(numeric_array const &lhs, numeric_array const &rhs) noexcept
932 requires(N <= sizeof(unsigned int) * CHAR_BIT)
933 {
934 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
935 return f32x4_sse_le_mask(lhs.v, rhs.v);
936 } else {
937 unsigned int r = 0;
938 for (ssize_t i = 0; i != N; ++i) {
939 r |= static_cast<unsigned int>(lhs.v[i] <= rhs.v[i]) << i;
940 }
941 return r;
942 }
943 }
944
945 [[nodiscard]] friend constexpr unsigned int ge(numeric_array const &lhs, numeric_array const &rhs) noexcept
946 requires(N <= sizeof(unsigned int) * CHAR_BIT)
947 {
948 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
949 return f32x4_sse_ge_mask(lhs.v, rhs.v);
950 } else {
951 unsigned int r = 0;
952 for (ssize_t i = 0; i != N; ++i) {
953 r |= static_cast<unsigned int>(lhs.v[i] >= rhs.v[i]) << i;
954 }
955 return r;
956 }
957 }
958
959 [[nodiscard]] friend constexpr bool operator==(numeric_array const &lhs, numeric_array const &rhs) noexcept
960 {
961 if (!std::is_constant_evaluated()) {
962 if constexpr (is_f32x4 && has_sse) {
963 // MSVC cannot vectorize comparison.
964 return f32x4_sse_eq(lhs.v, rhs.v);
965 }
966 }
967
968 auto r = true;
969 for (ssize_t i = 0; i != N; ++i) {
970 r &= (lhs.v[i] == rhs.v[i]);
971 }
972 return r;
973 }
974
975 [[nodiscard]] friend constexpr bool operator!=(numeric_array const &lhs, numeric_array const &rhs) noexcept
976 {
977 return !(lhs == rhs);
978 }
979
980 [[nodiscard]] friend constexpr numeric_array operator+(numeric_array const &lhs, numeric_array const &rhs) noexcept
981 {
982 auto r = numeric_array{};
983 for (ssize_t i = 0; i != N; ++i) {
984 r.v[i] = lhs.v[i] + rhs.v[i];
985 }
986 return r;
987 }
988
989 [[nodiscard]] friend constexpr numeric_array operator+(numeric_array const &lhs, T const &rhs) noexcept
990 {
991 auto r = numeric_array{};
992 for (ssize_t i = 0; i != N; ++i) {
993 r.v[i] = lhs.v[i] + rhs;
994 }
995 return r;
996 }
997
998 [[nodiscard]] friend constexpr numeric_array operator+(T const &lhs, numeric_array const &rhs) noexcept
999 {
1000 auto r = numeric_array{};
1001 for (ssize_t i = 0; i != N; ++i) {
1002 r.v[i] = lhs + rhs.v[i];
1003 }
1004 return r;
1005 }
1006
1007 [[nodiscard]] friend constexpr numeric_array hadd(numeric_array const &lhs, numeric_array const &rhs) noexcept
1008 {
1009 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
1010 return numeric_array{f32x4_sse_hadd(lhs.v, rhs.v)};
1011
1012 } else {
1013 tt_axiom(N % 2 == 0);
1014
1015 auto r = numeric_array{};
1016
1017 ssize_t src_i = 0;
1018 ssize_t dst_i = 0;
1019 while (src_i != N) {
1020 auto tmp = lhs[src_i++];
1021 tmp += lhs[src_i++];
1022 r.v[dst_i++] = tmp;
1023 }
1024
1025 src_i = 0;
1026 while (src_i != N) {
1027 auto tmp = rhs[src_i++];
1028 tmp += rhs[src_i++];
1029 r.v[dst_i++] = tmp;
1030 }
1031 return r;
1032 }
1033 }
1034
1035 [[nodiscard]] friend constexpr numeric_array hsub(numeric_array const &lhs, numeric_array const &rhs) noexcept
1036 {
1037 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
1038 return numeric_array{f32x4_sse_hsub(lhs.v, rhs.v)};
1039
1040 } else {
1041 tt_axiom(N % 2 == 0);
1042
1043 auto r = numeric_array{};
1044
1045 ssize_t src_i = 0;
1046 ssize_t dst_i = 0;
1047 while (src_i != N) {
1048 auto tmp = lhs[src_i++];
1049 tmp -= lhs[src_i++];
1050 r.v[dst_i++] = tmp;
1051 }
1052
1053 src_i = 0;
1054 while (src_i != N) {
1055 auto tmp = rhs[src_i++];
1056 tmp -= rhs[src_i++];
1057 r.v[dst_i++] = tmp;
1058 }
1059 return r;
1060 }
1061 }
1062
1063 [[nodiscard]] friend constexpr numeric_array operator-(numeric_array const &lhs, numeric_array const &rhs) noexcept
1064 {
1065 auto r = numeric_array{};
1066 for (ssize_t i = 0; i != N; ++i) {
1067 r.v[i] = lhs.v[i] - rhs.v[i];
1068 }
1069 return r;
1070 }
1071
1072 [[nodiscard]] friend constexpr numeric_array operator-(numeric_array const &lhs, T const &rhs) noexcept
1073 {
1074 auto r = numeric_array{};
1075 for (ssize_t i = 0; i != N; ++i) {
1076 r.v[i] = lhs.v[i] - rhs;
1077 }
1078 return r;
1079 }
1080
1085 template<size_t Mask = ~size_t{0}>
1086 [[nodiscard]] friend constexpr numeric_array addsub(numeric_array const &lhs, numeric_array const &rhs) noexcept
1087 {
1088 if (!std::is_constant_evaluated()) {
1089 if constexpr (is_f32x4 && has_sse) {
1090 return numeric_array{f32x4_sse_addsub<Mask & 0xf>(lhs.v, rhs.v)};
1091 }
1092 }
1093
1094 auto r = numeric_array{};
1095 for (ssize_t i = 0; i != N; ++i) {
1096 if ((Mask >> i) & 1 == 1) {
1097 r.v[i] = lhs.v[i] + rhs.v[i];
1098 } else {
1099 r.v[i] = lhs.v[i] - rhs.v[i];
1100 }
1101 }
1102 return r;
1103 }
1104
1105 [[nodiscard]] friend constexpr numeric_array operator-(T const &lhs, numeric_array const &rhs) noexcept
1106 {
1107 auto r = numeric_array{};
1108 for (ssize_t i = 0; i != N; ++i) {
1109 r.v[i] = lhs - rhs.v[i];
1110 }
1111 return r;
1112 }
1113
1114 [[nodiscard]] friend constexpr numeric_array operator*(numeric_array const &lhs, numeric_array const &rhs) noexcept
1115 {
1116 auto r = numeric_array{};
1117 for (ssize_t i = 0; i != N; ++i) {
1118 r.v[i] = lhs.v[i] * rhs.v[i];
1119 }
1120 return r;
1121 }
1122
1123 [[nodiscard]] friend constexpr numeric_array operator*(numeric_array const &lhs, T const &rhs) noexcept
1124 {
1125 auto r = numeric_array{};
1126 for (ssize_t i = 0; i != N; ++i) {
1127 r.v[i] = lhs.v[i] * rhs;
1128 }
1129 return r;
1130 }
1131
1132 [[nodiscard]] friend constexpr numeric_array operator*(T const &lhs, numeric_array const &rhs) noexcept
1133 {
1134 auto r = numeric_array{};
1135 for (ssize_t i = 0; i != N; ++i) {
1136 r.v[i] = lhs * rhs.v[i];
1137 }
1138 return r;
1139 }
1140
1141 [[nodiscard]] friend constexpr numeric_array operator/(numeric_array const &lhs, numeric_array const &rhs) noexcept
1142 {
1143 auto r = numeric_array{};
1144 for (ssize_t i = 0; i != N; ++i) {
1145 r.v[i] = lhs.v[i] / rhs.v[i];
1146 }
1147 return r;
1148 }
1149
1150 [[nodiscard]] friend constexpr numeric_array operator/(numeric_array const &lhs, T const &rhs) noexcept
1151 {
1152 auto r = numeric_array{};
1153 for (ssize_t i = 0; i != N; ++i) {
1154 r.v[i] = lhs.v[i] / rhs;
1155 }
1156 return r;
1157 }
1158
1159 [[nodiscard]] friend constexpr numeric_array operator/(T const &lhs, numeric_array const &rhs) noexcept
1160 {
1161 auto r = numeric_array{};
1162 for (ssize_t i = 0; i != N; ++i) {
1163 r.v[i] = lhs / rhs.v[i];
1164 }
1165 return r;
1166 }
1167
1168 [[nodiscard]] friend constexpr numeric_array operator%(numeric_array const &lhs, numeric_array const &rhs) noexcept
1169 {
1170 auto r = numeric_array{};
1171 for (ssize_t i = 0; i != N; ++i) {
1172 r.v[i] = lhs.v[i] % rhs.v[i];
1173 }
1174 return r;
1175 }
1176
1177 [[nodiscard]] friend constexpr numeric_array operator%(numeric_array const &lhs, T const &rhs) noexcept
1178 {
1179 auto r = numeric_array{};
1180 for (ssize_t i = 0; i != N; ++i) {
1181 r.v[i] = lhs.v[i] % rhs;
1182 }
1183 return r;
1184 }
1185
1186 [[nodiscard]] friend constexpr numeric_array operator%(T const &lhs, numeric_array const &rhs) noexcept
1187 {
1188 auto r = numeric_array{};
1189 for (ssize_t i = 0; i != N; ++i) {
1190 r.v[i] = lhs % rhs.v[i];
1191 }
1192 return r;
1193 }
1194
1195 [[nodiscard]] friend constexpr numeric_array min(numeric_array const &lhs, numeric_array const &rhs) noexcept
1196 {
1197 auto r = numeric_array{};
1198 for (ssize_t i = 0; i != N; ++i) {
1199 // std::min() causes vectorization failure with msvc
1200 r.v[i] = lhs.v[i] < rhs.v[i] ? lhs.v[i] : rhs.v[i];
1201 }
1202 return r;
1203 }
1204
1205 [[nodiscard]] friend constexpr numeric_array max(numeric_array const &lhs, numeric_array const &rhs) noexcept
1206 {
1207 auto r = numeric_array{};
1208 for (ssize_t i = 0; i != N; ++i) {
1209 // std::max() causes vectorization failure with msvc
1210 r.v[i] = lhs.v[i] > rhs.v[i] ? lhs.v[i] : rhs.v[i];
1211 }
1212 return r;
1213 }
1214
1215 [[nodiscard]] friend constexpr numeric_array
1216 clamp(numeric_array const &lhs, numeric_array const &low, numeric_array const &high) noexcept
1217 {
1218 auto r = numeric_array{};
1219 for (ssize_t i = 0; i != N; ++i) {
1220 // std::clamp() causes vectorization failure with msvc
1221 r.v[i] = lhs.v[i] < low.v[i] ? low.v[i] : lhs.v[i] > high.v[i] ? high.v[i] : lhs.v[i];
1222 }
1223 return r;
1224 }
1225
1228 [[nodiscard]] friend constexpr numeric_array cross_2D(numeric_array const &rhs) noexcept requires(N >= 2)
1229 {
1230 tt_axiom(rhs.z() == 0.0f && rhs.is_vector());
1231 return numeric_array{-rhs.y(), rhs.x()};
1232 }
1233
1236 [[nodiscard]] friend constexpr numeric_array normal_2D(numeric_array const &rhs) noexcept requires(N >= 2)
1237 {
1238 return normalize<0b0011>(cross_2D(rhs));
1239 }
1240
1243 [[nodiscard]] friend constexpr float cross_2D(numeric_array const &lhs, numeric_array const &rhs) noexcept
1244 requires(N >= 2)
1245 {
1246 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
1247 return f32x4_sse_viktor_cross(lhs.v, rhs.v);
1248
1249 } else {
1250 return lhs.x() * rhs.y() - lhs.y() * rhs.x();
1251 }
1252 }
1253
1254 // x=a.y*b.z - a.z*b.y
1255 // y=a.z*b.x - a.x*b.z
1256 // z=a.x*b.y - a.y*b.x
1257 // w=a.w*b.w - a.w*b.w
1258 [[nodiscard]] constexpr friend numeric_array cross_3D(numeric_array const &lhs, numeric_array const &rhs) noexcept
1259 {
1260 if (!std::is_constant_evaluated()) {
1261 if constexpr (is_f32x4 && has_sse) {
1262 return numeric_array{f32x4_sse_cross(lhs.v, rhs.v)};
1263 }
1264 }
1265
1266 return numeric_array{
1267 lhs.y() * rhs.z() - lhs.z() * rhs.y(),
1268 lhs.z() * rhs.x() - lhs.x() * rhs.z(),
1269 lhs.x() * rhs.y() - lhs.y() * rhs.x(),
1270 0.0f};
1271 }
1272
1273 // w + x*i + y*j + z*k
1274 //
1275 // (w1*x2 + x1*w2 + y1*z2 - z1*y2)i
1276 // + (w1*y2 - x1*z2 + y1*w2 + z1*x2)j
1277 // + (w1*z2 + x1*y2 - y1*x2 + z1*w2)k
1278 // + (w1*w2 - x1*x2 - y1*y2 - z1*z2)
1279 template<int D>
1280 requires(D == 4) [[nodiscard]] friend numeric_array
1281 hamilton_cross(numeric_array const &lhs, numeric_array const &rhs) noexcept
1282 {
1283 ttlet col0 = lhs.wwww() * rhs;
1284 ttlet col1 = lhs.xxxx() * rhs.wzyx();
1285 ttlet col2 = lhs.yyyy() * rhs.zwxy();
1286 ttlet col3 = lhs.zzzz() * rhs.yxwz();
1287
1288 ttlet col01 = addsub(col0, col1);
1289 ttlet col012 = addsub(col01.xzyw(), col2.xzyw()).xzyw();
1290
1291 return numeric_array{
1292
1293 };
1294 }
1295
1298 [[nodiscard]] friend constexpr numeric_array midpoint(numeric_array const &p1, numeric_array const &p2) noexcept
1299 {
1300 tt_axiom(p1.is_point());
1301 tt_axiom(p2.is_point());
1302 return (p1 + p2) * 0.5f;
1303 }
1304
1307 [[nodiscard]] friend constexpr numeric_array reflect_point(numeric_array const &p, numeric_array const anchor) noexcept
1308 {
1309 tt_axiom(p.is_point());
1310 tt_axiom(anchor.is_point());
1311 return anchor - (p - anchor);
1312 }
1313
1314 template<typename... Columns>
1315 [[nodiscard]] friend constexpr std::array<numeric_array, N> transpose(Columns const &...columns) noexcept
1316 {
1317 static_assert(sizeof...(Columns) == N, "Can only transpose square matrices");
1318
1320
1321 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
1322 auto tmp = f32x4_sse_transpose(columns.v...);
1323 for (int i = 0; i != N; ++i) {
1324 r[i] = numeric_array{tmp[i]};
1325 }
1326
1327 } else {
1328 transpose_detail<0, Columns...>(columns..., r);
1329 }
1330
1331 return r;
1332 }
1333
1334 [[nodiscard]] friend numeric_array desaturate(numeric_array const &color, T brightness) noexcept
1335 requires(N == 4 && std::is_floating_point_v<T>)
1336 {
1337 // Use luminance ratios and change the brightness.
1338 // luminance ratios according to BT.709.
1339 ttlet RGB0 = color * numeric_array::color({T{0.2126}, T{0.7152}, T{0.0722}, T{0.0}}) * brightness;
1340 ttlet tmp = hadd(RGB0, RGB0);
1341 ttlet luminance = hadd(tmp, tmp);
1342 return luminance.xxx0() + color._000w();
1343 }
1344
1345 [[nodiscard]] friend numeric_array composit(numeric_array const &under, numeric_array const &over) noexcept
1346 requires(N == 4 && std::is_floating_point_v<T>)
1347 {
1348 if (over.is_transparent()) {
1349 return under;
1350 }
1351 if (over.is_opaque()) {
1352 return over;
1353 }
1354
1355 ttlet over_alpha = over.wwww();
1356 ttlet under_alpha = under.wwww();
1357
1358 ttlet over_color = over.xyz1();
1359 ttlet under_color = under.xyz1();
1360
1361 ttlet output_color = over_color * over_alpha + under_color * under_alpha * (1.0 - over_alpha);
1362
1363 return output_color / output_color.www1();
1364 }
1365
1366 [[nodiscard]] friend std::string to_string(numeric_array const &rhs) noexcept
1367 {
1368 auto r = std::string{};
1369
1370 r += '(';
1371 for (ssize_t i = 0; i != N; ++i) {
1372 if (i != 0) {
1373 r += "; ";
1374 }
1375 r += fmt::format("{}", rhs[i]);
1376 }
1377 r += ')';
1378 return r;
1379 }
1380
1381 friend std::ostream &operator<<(std::ostream &lhs, numeric_array const &rhs)
1382 {
1383 return lhs << to_string(rhs);
1384 }
1385
1393 template<ssize_t... Elements>
1394 [[nodiscard]] constexpr numeric_array swizzle() const
1395 {
1396 static_assert(sizeof...(Elements) <= N);
1397
1398 if (!std::is_constant_evaluated()) {
1399 if constexpr (is_f32x4 && has_sse) {
1400 return numeric_array{f32x4_sse_swizzle<Elements...>(v)};
1401 }
1402 }
1403
1404 auto r = numeric_array{};
1405 swizzle_detail<0, Elements...>(r);
1406 return r;
1407 }
1408
1409#define SWIZZLE(swizzle_name, D, ...) \
1410 [[nodiscard]] constexpr numeric_array swizzle_name() const noexcept requires(D == N) \
1411 { \
1412 return swizzle<__VA_ARGS__>(); \
1413 }
1414
1415#define SWIZZLE_4D_GEN1(name, ...) \
1416 SWIZZLE(name##0, 4, __VA_ARGS__, get_zero) \
1417 SWIZZLE(name##1, 4, __VA_ARGS__, get_one) \
1418 SWIZZLE(name##x, 4, __VA_ARGS__, 0) \
1419 SWIZZLE(name##y, 4, __VA_ARGS__, 1) \
1420 SWIZZLE(name##z, 4, __VA_ARGS__, 2) \
1421 SWIZZLE(name##w, 4, __VA_ARGS__, 3)
1422
1423#define SWIZZLE_4D_GEN2(name, ...) \
1424 SWIZZLE_4D_GEN1(name##0, __VA_ARGS__, get_zero) \
1425 SWIZZLE_4D_GEN1(name##1, __VA_ARGS__, get_one) \
1426 SWIZZLE_4D_GEN1(name##x, __VA_ARGS__, 0) \
1427 SWIZZLE_4D_GEN1(name##y, __VA_ARGS__, 1) \
1428 SWIZZLE_4D_GEN1(name##z, __VA_ARGS__, 2) \
1429 SWIZZLE_4D_GEN1(name##w, __VA_ARGS__, 3)
1430
1431#define SWIZZLE_4D_GEN3(name, ...) \
1432 SWIZZLE_4D_GEN2(name##0, __VA_ARGS__, get_zero) \
1433 SWIZZLE_4D_GEN2(name##1, __VA_ARGS__, get_one) \
1434 SWIZZLE_4D_GEN2(name##x, __VA_ARGS__, 0) \
1435 SWIZZLE_4D_GEN2(name##y, __VA_ARGS__, 1) \
1436 SWIZZLE_4D_GEN2(name##z, __VA_ARGS__, 2) \
1437 SWIZZLE_4D_GEN2(name##w, __VA_ARGS__, 3)
1438
1439 SWIZZLE_4D_GEN3(_0, get_zero)
1440 SWIZZLE_4D_GEN3(_1, get_one)
1441 SWIZZLE_4D_GEN3(x, 0)
1442 SWIZZLE_4D_GEN3(y, 1)
1443 SWIZZLE_4D_GEN3(z, 2)
1444 SWIZZLE_4D_GEN3(w, 3)
1445
1446#define SWIZZLE_3D_GEN1(name, ...) \
1447 SWIZZLE(name##0, 3, __VA_ARGS__, get_zero) \
1448 SWIZZLE(name##1, 3, __VA_ARGS__, get_one) \
1449 SWIZZLE(name##x, 3, __VA_ARGS__, 0) \
1450 SWIZZLE(name##y, 3, __VA_ARGS__, 1) \
1451 SWIZZLE(name##z, 3, __VA_ARGS__, 2)
1452
1453#define SWIZZLE_3D_GEN2(name, ...) \
1454 SWIZZLE_3D_GEN1(name##0, __VA_ARGS__, get_zero) \
1455 SWIZZLE_3D_GEN1(name##1, __VA_ARGS__, get_one) \
1456 SWIZZLE_3D_GEN1(name##x, __VA_ARGS__, 0) \
1457 SWIZZLE_3D_GEN1(name##y, __VA_ARGS__, 1) \
1458 SWIZZLE_3D_GEN1(name##z, __VA_ARGS__, 2)
1459
1460 SWIZZLE_3D_GEN2(_0, get_zero)
1461 SWIZZLE_3D_GEN2(_1, get_one)
1462 SWIZZLE_3D_GEN2(x, 0)
1463 SWIZZLE_3D_GEN2(y, 1)
1464 SWIZZLE_3D_GEN2(z, 2)
1465
1466#define SWIZZLE_2D_GEN1(name, ...) \
1467 SWIZZLE(name##0, 2, __VA_ARGS__, get_zero) \
1468 SWIZZLE(name##1, 2, __VA_ARGS__, get_one) \
1469 SWIZZLE(name##x, 2, __VA_ARGS__, 0) \
1470 SWIZZLE(name##y, 2, __VA_ARGS__, 1)
1471
1472 SWIZZLE_2D_GEN1(_0, get_zero)
1473 SWIZZLE_2D_GEN1(_1, get_one)
1474 SWIZZLE_2D_GEN1(x, 0)
1475 SWIZZLE_2D_GEN1(y, 1)
1476
1477#undef SWIZZLE
1478#undef SWIZZLE_4D_GEN1
1479#undef SWIZZLE_4D_GEN2
1480#undef SWIZZLE_4D_GEN3
1481#undef SWIZZLE_3D_GEN1
1482#undef SWIZZLE_3D_GEN2
1483#undef SWIZZLE_2D_GEN1
1484
1485private:
1486 container_type v;
1487
1488 template<int I, typename First, typename... Rest>
1489 [[nodiscard]] friend constexpr void
1490 transpose_detail(First const &first, Rest const &...rest, std::array<numeric_array, N> &r) noexcept
1491 {
1492 for (ssize_t j = 0; j != N; ++j) {
1493 r[j][I] = first[j];
1494 }
1495
1496 if constexpr (sizeof...(Rest) != 0) {
1497 transpose_detail<I + 1, Rest...>(rest..., r);
1498 }
1499 }
1500
1501 template<ssize_t I, ssize_t FirstElement, ssize_t... RestElements>
1502 [[nodiscard]] constexpr void swizzle_detail(numeric_array &r) const noexcept
1503 {
1504 static_assert(I < N);
1505 static_assert(FirstElement >= -2 && FirstElement < N, "Index out of bounds");
1506
1507 get<I>(r) = get<FirstElement>(*this);
1508 if constexpr (sizeof...(RestElements) != 0) {
1509 swizzle_detail<I + 1, RestElements...>(r);
1510 }
1511 }
1512
1513 constexpr static bool is_i8x1 = std::is_same_v<T, int8_t> && N == 1;
1514 constexpr static bool is_i8x2 = std::is_same_v<T, int8_t> && N == 2;
1515 constexpr static bool is_i8x4 = std::is_same_v<T, int8_t> && N == 4;
1516 constexpr static bool is_i8x8 = std::is_same_v<T, int8_t> && N == 8;
1517 constexpr static bool is_i8x16 = std::is_same_v<T, int8_t> && N == 16;
1518 constexpr static bool is_i8x32 = std::is_same_v<T, int8_t> && N == 32;
1519 constexpr static bool is_i8x64 = std::is_same_v<T, int8_t> && N == 64;
1520 constexpr static bool is_u8x1 = std::is_same_v<T, uint8_t> && N == 1;
1521 constexpr static bool is_u8x2 = std::is_same_v<T, uint8_t> && N == 2;
1522 constexpr static bool is_u8x4 = std::is_same_v<T, uint8_t> && N == 4;
1523 constexpr static bool is_u8x8 = std::is_same_v<T, uint8_t> && N == 8;
1524 constexpr static bool is_u8x16 = std::is_same_v<T, uint8_t> && N == 16;
1525 constexpr static bool is_u8x32 = std::is_same_v<T, uint8_t> && N == 32;
1526 constexpr static bool is_u8x64 = std::is_same_v<T, uint8_t> && N == 64;
1527
1528 constexpr static bool is_i16x1 = std::is_same_v<T, int16_t> && N == 1;
1529 constexpr static bool is_i16x2 = std::is_same_v<T, int16_t> && N == 2;
1530 constexpr static bool is_i16x4 = std::is_same_v<T, int16_t> && N == 4;
1531 constexpr static bool is_i16x8 = std::is_same_v<T, int16_t> && N == 8;
1532 constexpr static bool is_i16x16 = std::is_same_v<T, int16_t> && N == 16;
1533 constexpr static bool is_i16x32 = std::is_same_v<T, int16_t> && N == 32;
1534 constexpr static bool is_u16x1 = std::is_same_v<T, uint16_t> && N == 1;
1535 constexpr static bool is_u16x2 = std::is_same_v<T, uint16_t> && N == 2;
1536 constexpr static bool is_u16x4 = std::is_same_v<T, uint16_t> && N == 4;
1537 constexpr static bool is_u16x8 = std::is_same_v<T, uint16_t> && N == 8;
1538 constexpr static bool is_u16x16 = std::is_same_v<T, uint16_t> && N == 16;
1539 constexpr static bool is_u16x32 = std::is_same_v<T, uint16_t> && N == 32;
1540
1541 constexpr static bool is_i32x1 = std::is_same_v<T, int32_t> && N == 1;
1542 constexpr static bool is_i32x2 = std::is_same_v<T, int32_t> && N == 2;
1543 constexpr static bool is_i32x4 = std::is_same_v<T, int32_t> && N == 4;
1544 constexpr static bool is_i32x8 = std::is_same_v<T, int32_t> && N == 8;
1545 constexpr static bool is_i32x16 = std::is_same_v<T, int32_t> && N == 16;
1546 constexpr static bool is_u32x1 = std::is_same_v<T, uint32_t> && N == 1;
1547 constexpr static bool is_u32x2 = std::is_same_v<T, uint32_t> && N == 2;
1548 constexpr static bool is_u32x4 = std::is_same_v<T, uint32_t> && N == 4;
1549 constexpr static bool is_u32x8 = std::is_same_v<T, uint32_t> && N == 8;
1550 constexpr static bool is_u32x16 = std::is_same_v<T, uint32_t> && N == 16;
1551 constexpr static bool is_f32x1 = std::is_same_v<T, float> && N == 1;
1552 constexpr static bool is_f32x2 = std::is_same_v<T, float> && N == 2;
1553 constexpr static bool is_f32x4 = std::is_same_v<T, float> && N == 4;
1554 constexpr static bool is_f32x8 = std::is_same_v<T, float> && N == 8;
1555 constexpr static bool is_f32x16 = std::is_same_v<T, float> && N == 16;
1556
1557 constexpr static bool is_i64x1 = std::is_same_v<T, int64_t> && N == 1;
1558 constexpr static bool is_i64x2 = std::is_same_v<T, int64_t> && N == 2;
1559 constexpr static bool is_i64x4 = std::is_same_v<T, int64_t> && N == 4;
1560 constexpr static bool is_i64x8 = std::is_same_v<T, int64_t> && N == 8;
1561 constexpr static bool is_u64x1 = std::is_same_v<T, uint64_t> && N == 1;
1562 constexpr static bool is_u64x2 = std::is_same_v<T, uint64_t> && N == 2;
1563 constexpr static bool is_u64x4 = std::is_same_v<T, uint64_t> && N == 4;
1564 constexpr static bool is_u64x8 = std::is_same_v<T, uint64_t> && N == 8;
1565 constexpr static bool is_f64x1 = std::is_same_v<T, double> && N == 1;
1566 constexpr static bool is_f64x2 = std::is_same_v<T, double> && N == 2;
1567 constexpr static bool is_f64x4 = std::is_same_v<T, double> && N == 4;
1568 constexpr static bool is_f64x8 = std::is_same_v<T, double> && N == 8;
1569};
1570
1571using i8x1 = numeric_array<int8_t, 1>;
1572using i8x2 = numeric_array<int8_t, 2>;
1573using i8x4 = numeric_array<int8_t, 4>;
1574using i8x8 = numeric_array<int8_t, 8>;
1575using i8x16 = numeric_array<int8_t, 16>;
1576using i8x32 = numeric_array<int8_t, 32>;
1577using i8x64 = numeric_array<int8_t, 64>;
1578
1579using u8x1 = numeric_array<uint8_t, 1>;
1580using u8x2 = numeric_array<uint8_t, 2>;
1581using u8x4 = numeric_array<uint8_t, 4>;
1582using u8x8 = numeric_array<uint8_t, 8>;
1583using u8x16 = numeric_array<uint8_t, 16>;
1584using u8x32 = numeric_array<uint8_t, 32>;
1585using u8x64 = numeric_array<uint8_t, 64>;
1586
1587using i16x1 = numeric_array<int16_t, 1>;
1588using i16x2 = numeric_array<int16_t, 2>;
1589using i16x4 = numeric_array<int16_t, 4>;
1590using i16x8 = numeric_array<int16_t, 8>;
1591using i16x16 = numeric_array<int16_t, 16>;
1592using i16x32 = numeric_array<int16_t, 32>;
1593
1594using u16x1 = numeric_array<uint16_t, 1>;
1595using u16x2 = numeric_array<uint16_t, 2>;
1596using u16x4 = numeric_array<uint16_t, 4>;
1597using u16x8 = numeric_array<uint16_t, 8>;
1598using u16x16 = numeric_array<uint16_t, 16>;
1599using u16x32 = numeric_array<uint16_t, 32>;
1600
1601using i32x1 = numeric_array<int32_t, 1>;
1602using i32x2 = numeric_array<int32_t, 2>;
1603using i32x4 = numeric_array<int32_t, 4>;
1604using i32x8 = numeric_array<int32_t, 8>;
1605using i32x16 = numeric_array<int32_t, 16>;
1606
1607using u32x1 = numeric_array<uint32_t, 1>;
1608using u32x2 = numeric_array<uint32_t, 2>;
1609using u32x4 = numeric_array<uint32_t, 4>;
1610using u32x8 = numeric_array<uint32_t, 8>;
1611using u32x16 = numeric_array<uint32_t, 16>;
1612
1613using f32x1 = numeric_array<float, 1>;
1614using f32x2 = numeric_array<float, 2>;
1615using f32x4 = numeric_array<float, 4>;
1616using f32x8 = numeric_array<float, 8>;
1617using f32x16 = numeric_array<float, 16>;
1618
1619using i64x1 = numeric_array<int64_t, 1>;
1620using i64x2 = numeric_array<int64_t, 2>;
1621using i64x4 = numeric_array<int64_t, 4>;
1622using i64x8 = numeric_array<int64_t, 8>;
1623
1624using u64x1 = numeric_array<uint64_t, 1>;
1625using u64x2 = numeric_array<uint64_t, 2>;
1626using u64x4 = numeric_array<uint64_t, 4>;
1627using u64x8 = numeric_array<uint64_t, 8>;
1628
1629using f64x1 = numeric_array<double, 1>;
1630using f64x2 = numeric_array<double, 2>;
1631using f64x4 = numeric_array<double, 4>;
1632using f64x8 = numeric_array<double, 8>;
1633
1634} // namespace tt
1635
1636namespace std {
1637
1638template<class T, std::size_t N>
1639struct tuple_size<tt::numeric_array<T, N>> : std::integral_constant<std::size_t, N> {
1640};
1641
1642template<std::size_t I, class T, std::size_t N>
1643struct tuple_element<I, tt::numeric_array<T, N>> {
1644 using type = T;
1645};
1646
1647} // namespace std
STL namespace.
Definition aligned_array.hpp:23
Definition numeric_array.hpp:27
friend constexpr T get(numeric_array &&rhs) noexcept
Get a element from the numeric array.
Definition numeric_array.hpp:602
static constexpr numeric_array point(numeric_array rhs) noexcept
Convert vector to point.
Definition numeric_array.hpp:99
friend constexpr numeric_array neg(numeric_array rhs) noexcept
Negate individual elements.
Definition numeric_array.hpp:661
friend constexpr T get(numeric_array const &rhs) noexcept
Get a element from the numeric array.
Definition numeric_array.hpp:620
friend constexpr T squared_hypot(numeric_array const &rhs) noexcept
Take the squared length of the vector.
Definition numeric_array.hpp:826
friend constexpr numeric_array cross_2D(numeric_array const &rhs) noexcept
Calculate the 2D normal on a 2D vector.
Definition numeric_array.hpp:1228
friend constexpr numeric_array reflect_point(numeric_array const &p, numeric_array const anchor) noexcept
Find the point on the other side and at the same distance of an anchor-point.
Definition numeric_array.hpp:1307
friend constexpr numeric_array midpoint(numeric_array const &p1, numeric_array const &p2) noexcept
Find a point at the midpoint between two points.
Definition numeric_array.hpp:1298
friend constexpr T & get(numeric_array &rhs) noexcept
Get a element from the numeric array.
Definition numeric_array.hpp:590
friend constexpr T dot(numeric_array const &lhs, numeric_array const &rhs) noexcept
Take a dot product.
Definition numeric_array.hpp:787
friend constexpr T rcp_hypot(numeric_array const &rhs) noexcept
Take a reciprocal of the length.
Definition numeric_array.hpp:838
friend constexpr T hypot(numeric_array const &rhs) noexcept
Take the length of the vector.
Definition numeric_array.hpp:810
friend constexpr float cross_2D(numeric_array const &lhs, numeric_array const &rhs) noexcept
Calculate the cross-product between two 2D vectors.
Definition numeric_array.hpp:1243
friend constexpr numeric_array normal_2D(numeric_array const &rhs) noexcept
Calculate the 2D unit-normal on a 2D vector.
Definition numeric_array.hpp:1236
constexpr numeric_array swizzle() const
swizzle around the elements of the numeric array.
Definition numeric_array.hpp:1394
friend constexpr numeric_array addsub(numeric_array const &lhs, numeric_array const &rhs) noexcept
Add or subtract individual elements.
Definition numeric_array.hpp:1086
friend constexpr numeric_array normalize(numeric_array const &rhs) noexcept
Normalize a vector.
Definition numeric_array.hpp:856
static constexpr numeric_array point() noexcept
Get a point at the origin.
Definition numeric_array.hpp:90
friend constexpr numeric_array zero(numeric_array rhs) noexcept
Set individual elements to zero.
Definition numeric_array.hpp:637
Definition concepts.hpp:15
Definition concepts.hpp:18
Definition concepts.hpp:21
T begin(T... args)
T ceil(T... args)
T end(T... args)
T floor(T... args)
T round(T... args)
T sqrt(T... args)