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