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