HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
array_generic.hpp
1
2
3#pragma once
4
5#include "array_intrinsic.hpp"
6#include "../macros.hpp"
7#include <cstddef>
8#include <cmath>
9#include <array>
10#include <type_traits>
11#include <bit>
12#include <algorithm>
13
14hi_export_module(hikogui.SIMD.array_generic);
15
16hi_warning_push();
17// False positive: warning C4702: unreachable code.
18hi_warning_ignore_msvc(4702);
19
20hi_export namespace hi {
21inline namespace v1 {
22
26template<typename T, std::size_t N>
28 static_assert(N > 1);
29 static_assert(std::has_single_bit(N));
30
31 using value_type = T;
32 using array_type = std::array<T, N>;
33 using intrinsic_type = array_intrinsic<T, N>;
34
35 // clang-format off
36 using mask_type =
37 std::conditional_t<sizeof(T) * CHAR_BIT == 8, uint8_t,
38 std::conditional_t<sizeof(T) * CHAR_BIT == 16, uint16_t,
39 std::conditional_t<sizeof(T) * CHAR_BIT == 32, uint32_t,
40 std::conditional_t<sizeof(T) * CHAR_BIT == 64, uint64_t, void>>>>;
41 // clang-format on
42 using signed_mask_type = std::make_signed_t<mask_type>;
43
44 [[nodiscard]] hi_force_inline constexpr static mask_type to_mask(value_type a) noexcept
45 {
46 return std::bit_cast<mask_type>(a);
47 }
48
49 [[nodiscard]] hi_force_inline constexpr static signed_mask_type to_signed_mask(value_type a) noexcept
50 {
51 return std::bit_cast<signed_mask_type>(a);
52 }
53
54 template<std::unsigned_integral M>
55 [[nodiscard]] hi_force_inline constexpr static value_type to_value(M a) noexcept
56 {
57 return std::bit_cast<value_type>(static_cast<mask_type>(a));
58 }
59
60 template<std::signed_integral M>
61 [[nodiscard]] hi_force_inline constexpr static value_type to_value(M a) noexcept
62 {
63 return std::bit_cast<value_type>(static_cast<signed_mask_type>(a));
64 }
65
66 constexpr static value_type _zero_mask = to_value(mask_type{0});
67 constexpr static value_type _ones_mask = to_value(~mask_type{0});
68
69 [[nodiscard]] hi_force_inline static array_type undefined() noexcept
70 {
71 if (not std::is_constant_evaluated()) {
72 if constexpr (requires { intrinsic_type::undefined(); }) {
73 return intrinsic_type::undefined();
74 }
75 }
76 array_type r;
77 return r;
78 }
79
80 template<std::same_as<T>... Args>
81 [[nodiscard]] hi_force_inline constexpr static array_type set(Args... args) noexcept
82 requires(sizeof...(Args) == N)
83 {
84 if (not std::is_constant_evaluated()) {
85 if constexpr (requires { intrinsic_type::set(args...); }) {
86 return intrinsic_type::set(args...);
87 }
88 }
89 return array_type{args...};
90 }
91
92 [[nodiscard]] hi_force_inline constexpr static array_type set(value_type value) noexcept
93 {
94 if (not std::is_constant_evaluated()) {
95 if constexpr (requires { intrinsic_type::set(value); }) {
96 return intrinsic_type::set(value);
97 }
98 }
99 auto r = array_type{};
100 std::get<0>(r) = value;
101 return r;
102 }
103
104 [[nodiscard]] hi_force_inline constexpr static array_type set_zero() noexcept
105 {
106 if (not std::is_constant_evaluated()) {
107 if constexpr (requires { intrinsic_type::set_zero(); }) {
108 return intrinsic_type::set_zero();
109 }
110 }
111 auto r = array_type{};
112 for (std::size_t i = 0; i != N; ++i) {
113 r[i] = _zero_mask;
114 }
115 return r;
116 }
117
118 [[nodiscard]] hi_force_inline constexpr static array_type set_all_ones() noexcept
119 {
120 if (not std::is_constant_evaluated()) {
121 if constexpr (requires { intrinsic_type::set_all_ones(); }) {
122 return intrinsic_type::set_all_ones();
123 }
124 }
125 auto r = array_type{};
126 for (std::size_t i = 0; i != N; ++i) {
127 r[i] = _ones_mask;
128 }
129 return r;
130 }
131
132 [[nodiscard]] hi_force_inline constexpr static array_type set_one() noexcept
133 {
134 if (not std::is_constant_evaluated()) {
135 if constexpr (requires { intrinsic_type::set_one(); }) {
136 return intrinsic_type::set_one();
137 }
138 }
139 auto r = array_type{};
140 for (std::size_t i = 0; i != N; ++i) {
141 r[i] = value_type{1};
142 }
143 return r;
144 }
145
146 [[nodiscard]] hi_force_inline constexpr static array_type broadcast(value_type a) noexcept
147 {
148 if (not std::is_constant_evaluated()) {
149 if constexpr (requires { intrinsic_type::broadcast(a); }) {
150 return intrinsic_type::broadcast(a);
151 }
152 }
153 auto r = array_type{};
154 for (std::size_t i = 0; i != N; ++i) {
155 r[i] = a;
156 }
157 return r;
158 }
159
160 [[nodiscard]] hi_force_inline constexpr static array_type broadcast(array_type a) noexcept
161 {
162 if (not std::is_constant_evaluated()) {
163 if constexpr (requires { intrinsic_type::broadcast(a); }) {
164 return intrinsic_type::broadcast(a);
165 }
166 }
167 auto r = array_type{};
168 for (std::size_t i = 0; i != N; ++i) {
169 r[i] = std::get<0>(a);
170 }
171 return r;
172 }
173
174 template<size_t I>
175 [[nodiscard]] hi_force_inline constexpr static value_type get(array_type a) noexcept
176 {
177 if (not std::is_constant_evaluated()) {
178 if constexpr (requires { intrinsic_type::template get<I>(a); }) {
179 return intrinsic_type::template get<I>(a);
180 }
181 }
182 return std::get<I>(a);
183 }
184
189 [[nodiscard]] hi_force_inline constexpr static array_type set_mask(size_t mask) noexcept
190 {
191 if (not std::is_constant_evaluated()) {
192 if constexpr (requires { intrinsic_type::set_mask(mask); }) {
193 return intrinsic_type::set_mask(mask);
194 }
195 }
196 auto r = array_type{};
197 for (std::size_t i = 0; i != N; ++i) {
198 r[i] = mask & 1 ? _ones_mask : _zero_mask;
199 mask >>= 1;
200 }
201 return r;
202 }
203
208 [[nodiscard]] hi_force_inline constexpr static size_t get_mask(array_type a) noexcept
209 {
210 if (not std::is_constant_evaluated()) {
211 if constexpr (requires { intrinsic_type::get_mask(a); }) {
212 return intrinsic_type::get_mask(a);
213 }
214 }
215 size_t mask = 0;
216 for (std::size_t i = 0; i != N; ++i) {
217 auto const tmp = to_signed_mask(a[i]) < 0 ? size_t{1} : size_t{0};
218 mask |= tmp << i;
219 }
220 return mask;
221 }
222
223 template<typename O>
224 [[nodiscard]] hi_force_inline constexpr static array_type convert(std::array<O, N> a) noexcept
225 {
226 if (not std::is_constant_evaluated()) {
227 if constexpr (requires { intrinsic_type::convert(a); }) {
228 return intrinsic_type::convert(a);
229 }
230 }
231
232 auto r = array_type{};
233 for (std::size_t i = 0; i != N; ++i) {
234 r[i] = static_cast<value_type>(a[i]);
235 }
236 return r;
237 }
238
239 [[nodiscard]] hi_force_inline constexpr static array_type neg(array_type a) noexcept
240 {
241 if (not std::is_constant_evaluated()) {
242 if constexpr (requires { intrinsic_type::neg(a); }) {
243 return intrinsic_type::neg(a);
244 }
245 }
246
247 for (std::size_t i = 0; i != N; ++i) {
248 a[i] = -a[i];
249 }
250 return a;
251 }
252
253 template<std::size_t Mask>
254 [[nodiscard]] hi_force_inline constexpr static array_type neg_mask(array_type a) noexcept
255 {
256 if (not std::is_constant_evaluated()) {
257 if constexpr (requires { intrinsic_type::template neg_mask<Mask>(a); }) {
258 return intrinsic_type::template neg_mask<Mask>(a);
259 }
260 }
261
262 return blend<Mask>(a, neg(a));
263 }
264
265 [[nodiscard]] hi_force_inline constexpr static array_type inv(array_type a) noexcept
266 {
267 if (not std::is_constant_evaluated()) {
268 if constexpr (requires { intrinsic_type::inv(a); }) {
269 return intrinsic_type::inv(a);
270 }
271 }
272
273 for (std::size_t i = 0; i != N; ++i) {
274 a[i] = to_value(~to_mask(a[i]));
275 }
276 return a;
277 }
278
279 [[nodiscard]] hi_force_inline constexpr static array_type rcp(array_type a) noexcept
280 {
281 if (not std::is_constant_evaluated()) {
282 if constexpr (requires { intrinsic_type::rcp(a); }) {
283 return intrinsic_type::rcp(a);
284 }
285 }
286
287 for (std::size_t i = 0; i != N; ++i) {
288 a[i] = value_type{1} / a[i];
289 }
290 return a;
291 }
292
293 [[nodiscard]] hi_force_inline static array_type sqrt(array_type a) noexcept
294 {
295 if (not std::is_constant_evaluated()) {
296 if constexpr (requires { intrinsic_type::sqrt(a); }) {
297 return intrinsic_type::sqrt(a);
298 }
299 }
300
301 for (std::size_t i = 0; i != N; ++i) {
302 a[i] = std::sqrt(a[i]);
303 }
304 return a;
305 }
306
307 [[nodiscard]] hi_force_inline static array_type rsqrt(array_type a) noexcept
308 {
309 if (not std::is_constant_evaluated()) {
310 if constexpr (requires { intrinsic_type::rsqrt(a); }) {
311 return intrinsic_type::rsqrt(a);
312 }
313 }
314
315 for (std::size_t i = 0; i != N; ++i) {
316 a[i] = value_type{1} / std::sqrt(a[i]);
317 }
318 return a;
319 }
320
321 [[nodiscard]] hi_force_inline constexpr static array_type abs(array_type a) noexcept
322 {
323 if (not std::is_constant_evaluated()) {
324 if constexpr (requires { intrinsic_type::abs(a); }) {
325 return intrinsic_type::abs(a);
326 }
327 }
328
329 for (std::size_t i = 0; i != N; ++i) {
330 a[i] = std::abs(a[i]);
331 }
332 return a;
333 }
334
335 [[nodiscard]] hi_force_inline constexpr static array_type round(array_type a) noexcept
336 {
337 if (not std::is_constant_evaluated()) {
338 if constexpr (requires { intrinsic_type::round(a); }) {
339 return intrinsic_type::round(a);
340 }
341 }
342
343 for (std::size_t i = 0; i != N; ++i) {
344 a[i] = std::round(a[i]);
345 }
346 return a;
347 }
348
349 [[nodiscard]] hi_force_inline constexpr static array_type floor(array_type a) noexcept
350 {
351 if (not std::is_constant_evaluated()) {
352 if constexpr (requires { intrinsic_type::floor(a); }) {
353 return intrinsic_type::floor(a);
354 }
355 }
356
357 for (std::size_t i = 0; i != N; ++i) {
358 a[i] = std::floor(a[i]);
359 }
360 return a;
361 }
362
363 [[nodiscard]] hi_force_inline constexpr static array_type ceil(array_type a) noexcept
364 {
365 if (not std::is_constant_evaluated()) {
366 if constexpr (requires { intrinsic_type::ceil(a); }) {
367 return intrinsic_type::ceil(a);
368 }
369 }
370
371 for (std::size_t i = 0; i != N; ++i) {
372 a[i] = std::ceil(a[i]);
373 }
374 return a;
375 }
376
377 [[nodiscard]] hi_force_inline constexpr static array_type add(array_type a, array_type b) noexcept
378 {
379 if (not std::is_constant_evaluated()) {
380 if constexpr (requires { intrinsic_type::add(a, b); }) {
381 return intrinsic_type::add(a, b);
382 }
383 }
384
385 for (std::size_t i = 0; i != N; ++i) {
386 a[i] = a[i] + b[i];
387 }
388 return a;
389 }
390
391 [[nodiscard]] hi_force_inline constexpr static array_type sub(array_type a, array_type b) noexcept
392 {
393 if (not std::is_constant_evaluated()) {
394 if constexpr (requires { intrinsic_type::sub(a, b); }) {
395 return intrinsic_type::sub(a, b);
396 }
397 }
398
399 for (std::size_t i = 0; i != N; ++i) {
400 a[i] = a[i] - b[i];
401 }
402 return a;
403 }
404
409 template<std::size_t Mask>
410 [[nodiscard]] hi_force_inline constexpr static array_type addsub_mask(array_type a, array_type b) noexcept
411 {
412 if (not std::is_constant_evaluated()) {
413 if constexpr (requires { intrinsic_type::template addsub_mask<Mask>(a, b); }) {
414 return intrinsic_type::template addsub_mask<Mask>(a, b);
415 }
416 }
417
418 return blend<Mask>(sub(a, b), add(a, b));
419 }
420
421 [[nodiscard]] hi_force_inline constexpr static array_type mul(array_type a, array_type b) noexcept
422 {
423 if (not std::is_constant_evaluated()) {
424 if constexpr (requires { intrinsic_type::mul(a, b); }) {
425 return intrinsic_type::mul(a, b);
426 }
427 }
428
429 for (std::size_t i = 0; i != N; ++i) {
430 a[i] = a[i] * b[i];
431 }
432 return a;
433 }
434
435 [[nodiscard]] hi_force_inline constexpr static array_type div(array_type a, array_type b) noexcept
436 {
437 if (not std::is_constant_evaluated()) {
438 if constexpr (requires { intrinsic_type::div(a, b); }) {
439 return intrinsic_type::div(a, b);
440 }
441 }
442
443 for (std::size_t i = 0; i != N; ++i) {
444 a[i] = a[i] / b[i];
445 }
446 return a;
447 }
448
449 [[nodiscard]] hi_force_inline constexpr static array_type mod(array_type a, array_type b) noexcept
450 {
451 if (not std::is_constant_evaluated()) {
452 if constexpr (requires { intrinsic_type::mod(a, b); }) {
453 return intrinsic_type::mod(a, b);
454 }
455 }
456
457 for (std::size_t i = 0; i != N; ++i) {
458 if constexpr (std::floating_point<T>) {
459 a[i] = std::fmod(a[i], b[i]);
460 } else {
461 a[i] = a[i] % b[i];
462 }
463 }
464 return a;
465 }
466
467 [[nodiscard]] hi_force_inline constexpr static array_type eq(array_type a, array_type b) noexcept
468 {
469 if (not std::is_constant_evaluated()) {
470 if constexpr (requires { intrinsic_type::eq(a, b); }) {
471 return intrinsic_type::eq(a, b);
472 }
473 }
474
475 for (std::size_t i = 0; i != N; ++i) {
476 a[i] = a[i] == b[i] ? _ones_mask : _zero_mask;
477 }
478 return a;
479 }
480
481 [[nodiscard]] hi_force_inline constexpr static array_type ne(array_type a, array_type b) noexcept
482 {
483 if (not std::is_constant_evaluated()) {
484 if constexpr (requires { intrinsic_type::ne(a, b); }) {
485 return intrinsic_type::ne(a, b);
486 }
487 }
488
489 for (std::size_t i = 0; i != N; ++i) {
490 a[i] = a[i] != b[i] ? _ones_mask : _zero_mask;
491 }
492 return a;
493 }
494
495 [[nodiscard]] hi_force_inline constexpr static array_type lt(array_type a, array_type b) noexcept
496 {
497 if (not std::is_constant_evaluated()) {
498 if constexpr (requires { intrinsic_type::lt(a, b); }) {
499 return intrinsic_type::lt(a, b);
500 }
501 }
502
503 for (std::size_t i = 0; i != N; ++i) {
504 a[i] = a[i] < b[i] ? _ones_mask : _zero_mask;
505 }
506 return a;
507 }
508
509 [[nodiscard]] hi_force_inline constexpr static array_type gt(array_type a, array_type b) noexcept
510 {
511 if (not std::is_constant_evaluated()) {
512 if constexpr (requires { intrinsic_type::gt(a, b); }) {
513 return intrinsic_type::gt(a, b);
514 }
515 }
516
517 for (std::size_t i = 0; i != N; ++i) {
518 a[i] = a[i] > b[i] ? _ones_mask : _zero_mask;
519 }
520 return a;
521 }
522
523 [[nodiscard]] hi_force_inline constexpr static array_type le(array_type a, array_type b) noexcept
524 {
525 if (not std::is_constant_evaluated()) {
526 if constexpr (requires { intrinsic_type::le(a, b); }) {
527 return intrinsic_type::le(a, b);
528 }
529 }
530
531 for (std::size_t i = 0; i != N; ++i) {
532 a[i] = a[i] <= b[i] ? _ones_mask : _zero_mask;
533 }
534 return a;
535 }
536
537 [[nodiscard]] hi_force_inline constexpr static array_type ge(array_type a, array_type b) noexcept
538 {
539 if (not std::is_constant_evaluated()) {
540 if constexpr (requires { intrinsic_type::ge(a, b); }) {
541 return intrinsic_type::ge(a, b);
542 }
543 }
544
545 for (std::size_t i = 0; i != N; ++i) {
546 a[i] = a[i] >= b[i] ? _ones_mask : _zero_mask;
547 }
548 return a;
549 }
550
556 [[nodiscard]] hi_force_inline constexpr static bool test(array_type a, array_type b) noexcept
557 {
558 if (not std::is_constant_evaluated()) {
559 if constexpr (requires { intrinsic_type::test(a, b); }) {
560 return intrinsic_type::test(a, b);
561 }
562 }
563
564 auto r = mask_type{0};
565 for (std::size_t i = 0; i != N; ++i) {
566 r |= to_mask(a[i]) & to_mask(b[i]);
567 }
568 return r == 0;
569 }
570
575 [[nodiscard]] hi_force_inline constexpr static bool all_equal(array_type a, array_type b) noexcept
576 {
577 // All bits will be zero when a == b.
578 auto const tmp = ne(a, b);
579 // Return true is all bits in tmp are zero.
580 return test(tmp, tmp);
581 }
582
583 [[nodiscard]] hi_force_inline constexpr static array_type max(array_type a, array_type b) noexcept
584 {
585 if (not std::is_constant_evaluated()) {
586 if constexpr (requires { intrinsic_type::max(a, b); }) {
587 return intrinsic_type::max(a, b);
588 }
589 }
590
591 for (std::size_t i = 0; i != N; ++i) {
592 a[i] = std::max(a[i], b[i]);
593 }
594 return a;
595 }
596
597 [[nodiscard]] hi_force_inline constexpr static array_type min(array_type a, array_type b) noexcept
598 {
599 if (not std::is_constant_evaluated()) {
600 if constexpr (requires { intrinsic_type::min(a, b); }) {
601 return intrinsic_type::min(a, b);
602 }
603 }
604
605 for (std::size_t i = 0; i != N; ++i) {
606 a[i] = std::min(a[i], b[i]);
607 }
608 return a;
609 }
610
611 [[nodiscard]] hi_force_inline constexpr static array_type clamp(array_type v, array_type lo, array_type hi) noexcept
612 {
613 if (not std::is_constant_evaluated()) {
614 if constexpr (requires { intrinsic_type::clamp(v, lo, hi); }) {
615 return intrinsic_type::clamp(v, lo, hi);
616 }
617 }
618
619 for (std::size_t i = 0; i != N; ++i) {
620 v[i] = std::clamp(v[i], lo[i], hi[i]);
621 }
622 return v;
623 }
624
625 [[nodiscard]] hi_force_inline constexpr static array_type _or(array_type a, array_type b) noexcept
626 {
627 if (not std::is_constant_evaluated()) {
628 if constexpr (requires { intrinsic_type::_or(a, b); }) {
629 return intrinsic_type::_or(a, b);
630 }
631 }
632
633 for (std::size_t i = 0; i != N; ++i) {
634 a[i] = to_value(to_mask(a[i]) | to_mask(b[i]));
635 }
636 return a;
637 }
638
639 [[nodiscard]] hi_force_inline constexpr static array_type _and(array_type a, array_type b) noexcept
640 {
641 if (not std::is_constant_evaluated()) {
642 if constexpr (requires { intrinsic_type::_and(a, b); }) {
643 return intrinsic_type::_and(a, b);
644 }
645 }
646
647 for (std::size_t i = 0; i != N; ++i) {
648 a[i] = to_value(to_mask(a[i]) & to_mask(b[i]));
649 }
650 return a;
651 }
652
653 [[nodiscard]] hi_force_inline constexpr static array_type _xor(array_type a, array_type b) noexcept
654 {
655 if (not std::is_constant_evaluated()) {
656 if constexpr (requires { intrinsic_type::_xor(a, b); }) {
657 return intrinsic_type::_xor(a, b);
658 }
659 }
660
661 for (std::size_t i = 0; i != N; ++i) {
662 a[i] = to_value(to_mask(a[i]) ^ to_mask(b[i]));
663 }
664 return a;
665 }
666
673 [[nodiscard]] hi_force_inline constexpr static array_type andnot(array_type a, array_type b) noexcept
674 {
675 if (not std::is_constant_evaluated()) {
676 if constexpr (requires { intrinsic_type::andnot(a, b); }) {
677 return intrinsic_type::andnot(a, b);
678 }
679 }
680
681 for (std::size_t i = 0; i != N; ++i) {
682 a[i] = to_value(~to_mask(a[i]) & to_mask(b[i]));
683 }
684 return a;
685 }
686
687 [[nodiscard]] hi_force_inline constexpr static array_type sll(array_type a, unsigned int b) noexcept
688 {
689 if (not std::is_constant_evaluated()) {
690 if constexpr (requires { intrinsic_type::sll(a, b); }) {
691 return intrinsic_type::sll(a, b);
692 }
693 }
694
695 if (b >= sizeof(value_type) * CHAR_BIT) {
696 a = {};
697 } else {
698 for (std::size_t i = 0; i != N; ++i) {
699 a[i] = to_value(to_mask(a[0]) << b);
700 }
701 }
702 return a;
703 }
704
705 [[nodiscard]] hi_force_inline constexpr static array_type srl(array_type a, unsigned int b) noexcept
706 {
707 if (not std::is_constant_evaluated()) {
708 if constexpr (requires { intrinsic_type::srl(a, b); }) {
709 return intrinsic_type::srl(a, b);
710 }
711 }
712
713 if (b >= sizeof(value_type) * CHAR_BIT) {
714 a = {};
715 } else {
716 for (std::size_t i = 0; i != N; ++i) {
717 a[i] = to_value(to_mask(a[0]) >> b);
718 }
719 }
720 return a;
721 }
722
723 [[nodiscard]] hi_force_inline constexpr static array_type sra(array_type a, unsigned int b) noexcept
724 {
725 if (not std::is_constant_evaluated()) {
726 if constexpr (requires { intrinsic_type::sra(a, b); }) {
727 return intrinsic_type::sra(a, b);
728 }
729 }
730
731 if (b >= sizeof(value_type) * CHAR_BIT) {
732 b = sizeof(value_type) * CHAR_BIT - 1;
733 }
734
735 for (std::size_t i = 0; i != N; ++i) {
736 a[i] = to_value(to_signed_mask(a[0]) >> b);
737 }
738 return a;
739 }
740
744 [[nodiscard]] hi_force_inline constexpr static array_type hadd(array_type a, array_type b) noexcept
745 {
746 if (not std::is_constant_evaluated()) {
747 if constexpr (requires { intrinsic_type::hadd(a, b); }) {
748 return intrinsic_type::hadd(a, b);
749 }
750 }
751
752 auto r = array_type{};
753 auto dst_i = 0;
754
755 for (std::size_t src_i = 0; src_i != N; src_i += 2) {
756 r[dst_i++] = a[src_i] + a[src_i + 1];
757 }
758
759 for (std::size_t src_i = 0; src_i != N; src_i += 2) {
760 r[dst_i++] = b[src_i] + b[src_i + 1];
761 }
762 return r;
763 }
764
765 [[nodiscard]] hi_force_inline constexpr static array_type hsub(array_type a, array_type b) noexcept
766 {
767 if (not std::is_constant_evaluated()) {
768 if constexpr (requires { intrinsic_type::hsub(a, b); }) {
769 return intrinsic_type::hsub(a, b);
770 }
771 }
772
773 auto r = array_type{};
774 auto dst_i = 0;
775
776 for (std::size_t src_i = 0; src_i != N; src_i += 2) {
777 r[dst_i++] = a[src_i] - a[src_i + 1];
778 }
779
780 for (std::size_t src_i = 0; src_i != N; src_i += 2) {
781 r[dst_i++] = b[src_i] - b[src_i + 1];
782 }
783 return r;
784 }
785
786 template<size_t I, int First, int... Rest>
787 hi_force_inline constexpr static void _shuffle(array_type& r, array_type a) noexcept
788 {
789 static_assert(std::cmp_less(First, N));
790
791 if constexpr (First < 0) {
792 std::get<I>(r) = std::get<I>(a);
793 } else {
794 std::get<I>(r) = std::get<First>(a);
795 }
796
797 if constexpr (sizeof...(Rest)) {
798 _shuffle<I + 1, Rest...>(r, a);
799 }
800 }
801
802 template<size_t I, int First, int... Rest>
803 [[nodiscard]] constexpr static bool _have_to_shuffle() noexcept
804 {
805 static_assert(std::cmp_less(First, N));
806
807 if constexpr (First >= 0 and First != I) {
808 return true;
809 }
810
811 if constexpr (sizeof...(Rest)) {
812 return _have_to_shuffle<I + 1, Rest...>();
813 } else {
814 return false;
815 }
816 }
817
818 template<int... Indices>
819 [[nodiscard]] hi_force_inline constexpr static array_type shuffle(array_type a) noexcept
820 {
821 static_assert(sizeof...(Indices) == N);
822
823 if constexpr (not _have_to_shuffle<0, Indices...>()) {
824 // Short circuit for performance.
825 return a;
826 }
827
828 if (not std::is_constant_evaluated()) {
829 if constexpr (requires { intrinsic_type::template shuffle<Indices...>(a); }) {
830 return intrinsic_type::template shuffle<Indices...>(a);
831 }
832 }
833
834 auto r = array_type{};
835 _shuffle<0, Indices...>(r, a);
836 return r;
837 }
838
839 template<size_t Mask>
840 [[nodiscard]] hi_force_inline constexpr static array_type blend(array_type a, array_type b) noexcept
841 {
842 if constexpr (Mask == 0) {
843 // Short circuit for performance.
844 return a;
845 } else if constexpr (Mask == (1ULL << N) - 1) {
846 // Short circuit for performance.
847 return b;
848 }
849
850 if (not std::is_constant_evaluated()) {
851 if constexpr (requires { intrinsic_type::template blend<Mask>(a, b); }) {
852 return intrinsic_type::template blend<Mask>(a, b);
853 }
854 }
855
856 auto mask = Mask;
857 for (std::size_t i = 0; i != N; ++i) {
858 a[i] = mask & 1 ? b[i] : a[i];
859 mask >>= 1;
860 }
861 return a;
862 }
863
864 hi_warning_push();
865 // C26494 Variable '...' is uninitialized. Always initialize an object (type.5).
866 // Internal to _MM_TRANSPOSE4_PS
867 hi_warning_ignore_msvc(26494);
868 template<std::derived_from<array_type>... Columns>
869 [[nodiscard]] constexpr static std::array<array_type, N> transpose(Columns... columns) noexcept
870 requires(sizeof...(Columns) == N)
871 {
872 if (not std::is_constant_evaluated()) {
873 if constexpr (requires { intrinsic_type::transpose(columns...); }) {
874 return intrinsic_type::transpose(columns...);
875 }
876 }
877
878 auto r = std::array<array_type, N>{};
879 auto f = [&r, &columns... ]<std::size_t... Ints>(std::index_sequence<Ints...>)
880 {
881 auto tf = [&r](auto i, auto v) {
882 for (std::size_t j = 0; j != N; ++j) {
883 r[j][i] = v[j];
884 }
885 return 0;
886 };
887 static_cast<void>((tf(Ints, columns) + ...));
888 };
889 f(std::make_index_sequence<sizeof...(columns)>{});
890 return r;
891 }
892 hi_warning_pop();
893
894 template<size_t I, int Value, int First, int... Rest>
895 constexpr static void _make_swizzle_blend_mask(std::size_t& r) noexcept
896 {
897 if constexpr (First == Value) {
898 r |= 1ULL << I;
899 }
900
901 if constexpr (sizeof...(Rest)) {
902 _make_swizzle_blend_mask<I + 1, Value, Rest...>(r);
903 }
904 }
905
906 template<int Value, int... Indices>
907 [[nodiscard]] constexpr static std::size_t _make_swizzle_blend_mask() noexcept
908 {
909 auto r = std::size_t{0};
910 _make_swizzle_blend_mask<0, Value, Indices...>(r);
911 return r;
912 }
913
924 template<int... Indices>
925 [[nodiscard]] hi_force_inline constexpr static array_type swizzle(array_type a) noexcept
926 {
927 constexpr auto zero_mask = _make_swizzle_blend_mask<-1, Indices...>();
928 constexpr auto one_mask = _make_swizzle_blend_mask<-2, Indices...>();
929
930 auto tmp = shuffle<Indices...>(a);
931 if constexpr (zero_mask != 0) {
932 tmp = blend<zero_mask>(tmp, set_zero());
933 }
934 if constexpr (one_mask != 0) {
935 tmp = blend<one_mask>(tmp, set_one());
936 }
937 return tmp;
938 }
939
940 [[nodiscard]] hi_force_inline constexpr static array_type sum(array_type a) noexcept
941 {
942 if (not std::is_constant_evaluated()) {
943 if constexpr (requires { intrinsic_type::sum(a); }) {
944 return intrinsic_type::sum(a);
945 }
946 }
947
948 auto r = value_type{0};
949 for (std::size_t i = 0; i != N; ++i) {
950 r = r + a[i];
951 }
952 return broadcast(r);
953 }
954
955 template<size_t Mask>
956 [[nodiscard]] hi_force_inline constexpr static array_type dot(array_type a, array_type b) noexcept
957 {
958 if (not std::is_constant_evaluated()) {
959 if constexpr (requires { intrinsic_type::template dot<Mask>(a, b); }) {
960 return intrinsic_type::template dot<Mask>(a, b);
961 }
962 }
963
964 auto const tmp1 = mul(a, b);
965 auto const tmp2 = blend<Mask>(set_zero(), tmp1);
966 return sum(tmp2);
967 }
968
969 template<size_t Mask>
970 [[nodiscard]] hi_force_inline constexpr static array_type hypot(array_type a) noexcept
971 {
972 if (not std::is_constant_evaluated()) {
973 if constexpr (requires { intrinsic_type::template hypot<Mask>(a); }) {
974 return intrinsic_type::template hypot<Mask>(a);
975 }
976 }
977
978 return sqrt(dot<Mask>(a, a));
979 }
980
981 template<size_t Mask>
982 [[nodiscard]] hi_force_inline constexpr static array_type rhypot(array_type a) noexcept
983 {
984 if (not std::is_constant_evaluated()) {
985 if constexpr (requires { intrinsic_type::template rhypot<Mask>(a); }) {
986 return intrinsic_type::template rhypot<Mask>(a);
987 }
988 }
989
990 return rsqrt(dot<Mask>(a, a));
991 }
992
993 template<size_t Mask>
994 [[nodiscard]] hi_force_inline constexpr static array_type normalize(array_type a) noexcept
995 {
996 if (not std::is_constant_evaluated()) {
997 if constexpr (requires { intrinsic_type::template normalize<Mask>(a); }) {
998 return intrinsic_type::template normalize<Mask>(a);
999 }
1000 }
1001
1002 return mul(rhypot<Mask>(a), a);
1003 }
1004
1005};
1006
1007static_assert(array_generic<float, 4>::neg(std::array{1.0f, 2.0f, -2.0f, 0.0f}) == std::array{-1.0f, -2.0f, 2.0f, 0.0f});
1008
1009} // namespace v1
1010}
1011
1012hi_warning_pop();
@ round
The end cap of the line is round.
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
The HikoGUI namespace.
Definition recursive_iterator.hpp:15
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:378
Intrinsic operations on arrays.
Definition array_generic.hpp:27
hi_force_inline static constexpr array_type addsub_mask(array_type a, array_type b) noexcept
Add or subtract based on the mask.
Definition array_generic.hpp:410
hi_force_inline static constexpr size_t get_mask(array_type a) noexcept
Get an integer mask where each bit in the mask corresponds with the top-bit of each element.
Definition array_generic.hpp:208
hi_force_inline static constexpr array_type andnot(array_type a, array_type b) noexcept
andnot of two operands
Definition array_generic.hpp:673
hi_force_inline static constexpr array_type swizzle(array_type a) noexcept
Swizzle elements.
Definition array_generic.hpp:925
hi_force_inline static constexpr array_type set_mask(size_t mask) noexcept
Set each element to all ones or all zero based on the bits of the mask.
Definition array_generic.hpp:189
hi_force_inline static constexpr bool test(array_type a, array_type b) noexcept
Test the two operands.
Definition array_generic.hpp:556
hi_force_inline static constexpr bool all_equal(array_type a, array_type b) noexcept
Compare the two operands.
Definition array_generic.hpp:575
hi_force_inline static constexpr array_type hadd(array_type a, array_type b) noexcept
Add the elements of both operands pairwise and return the packed result.
Definition array_generic.hpp:744
Intrinsic operations on arrays.
Definition array_intrinsic.hpp:16
T ceil(T... args)
T floor(T... args)
T fmod(T... args)
T max(T... args)
T min(T... args)
T round(T... args)
T sqrt(T... args)