29 static_assert(N >= 0);
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;
60 "Expecting the std:initializer_list size to be <= to the size of the numeric array");
71 requires(
sizeof...(Rest) + 2 <= N)
72 [[nodiscard]]
constexpr numeric_array(T
const &first, T
const &second, Rest
const &...rest) noexcept :
79 [[nodiscard]]
static constexpr numeric_array broadcast(T rhs)
noexcept
82 for (
ssize_t i = 0; i != N; ++i) {
109 if (std::size(rhs) < N) {
113 tt_axiom(r.v.back() != T{},
"Last element of a point should be non-zero");
117 template<arithmetic... Rest>
118 [[nodiscard]]
static constexpr numeric_array
point(T
const &first, Rest
const &...rest)
noexcept requires(
sizeof...(Rest) < N)
120 return point({first, narrow_cast<T>(rest)...});
128 template<arithmetic... Rest>
129 [[nodiscard]]
static constexpr numeric_array color(T
const &first, Rest
const &...rest)
noexcept requires(
sizeof...(Rest) < N)
131 return color({first, narrow_cast<T>(rest)...});
134 template<arithmetic U, s
size_t M>
135 [[nodiscard]]
explicit constexpr numeric_array(
std::array<U, M> const &rhs) noexcept : v()
142 while (src != src_last && dst != dst_last) {
143 *(dst++) = narrow_cast<T>(*(src++));
145 while (dst != dst_last) {
148 while (src != src_last) {
149 tt_axiom(*(src++) == U{});
153 template<arithmetic U, s
size_t M>
154 [[nodiscard]]
explicit constexpr numeric_array(numeric_array<U, M>
const &rhs) noexcept : v()
161 while (src != src_last && dst != dst_last) {
162 *(dst++) = narrow_cast<T>(*(src++));
164 while (dst != dst_last) {
167 while (src != src_last) {
168 tt_axiom(*(src++) == U{});
172 [[nodiscard]]
explicit numeric_array(__m128
const &rhs)
noexcept requires(N == 4 && std::is_same_v<T, float> && has_sse) :
177 [[nodiscard]]
explicit numeric_array(__m128d
const &rhs)
noexcept requires(N == 2 && std::is_same_v<T, double> && has_sse) :
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) :
188 [[nodiscard]]
explicit numeric_array(__m256
const &rhs)
noexcept requires(N == 8 && std::is_same_v<T, float> && has_sse) :
193 [[nodiscard]]
explicit numeric_array(__m256d
const &rhs)
noexcept requires(N == 4 && std::is_same_v<T, double> && has_sse) :
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) :
204 template<arithmetic U, s
size_t M>
214 while (src != src_last && dst != dst_last) {
215 *(dst++) = narrow_cast<U>(*(src++));
217 while (dst != dst_last) {
222 while (src != src_last) {
223 tt_axiom(*src == T{} || *src == T{1});
230 template<arithmetic U, s
size_t M>
231 [[nodiscard]]
explicit constexpr operator numeric_array<U, M>() const noexcept
240 while (src != src_last && dst != dst_last) {
241 *(dst++) = narrow_cast<U>(*(src++));
243 while (dst != dst_last) {
246 while (src != src_last) {
247 tt_axiom(*(src++) == T{});
253 [[nodiscard]]
explicit operator __m128() const noexcept requires(N == 4 &&
std::is_same_v<T,
float> && has_sse)
255 return static_cast<__m128
>(v);
258 [[nodiscard]]
explicit operator __m128d() const noexcept requires(N == 2 &&
std::is_same_v<T,
double> && has_sse)
260 return static_cast<__m128d
>(v);
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)
266 return static_cast<__m128i
>(v);
269 [[nodiscard]]
explicit operator __m256() const noexcept requires(N == 8 &&
std::is_same_v<T,
float> && has_sse)
271 return static_cast<__m256
>(v);
274 [[nodiscard]]
explicit operator __m256d() const noexcept requires(N == 4 &&
std::is_same_v<T,
double> && has_sse)
276 return static_cast<__m256d
>(v);
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)
282 return static_cast<__m256i
>(v);
285 [[nodiscard]]
constexpr T
const &operator[](ssize_t i)
const noexcept
291 [[nodiscard]]
constexpr T &operator[](ssize_t i)
noexcept
297 [[nodiscard]]
constexpr reference front() noexcept
302 [[nodiscard]]
constexpr const_reference front() const noexcept
307 [[nodiscard]]
constexpr reference back() noexcept
312 [[nodiscard]]
constexpr const_reference back() const noexcept
317 [[nodiscard]]
constexpr pointer data() noexcept
322 [[nodiscard]]
constexpr const_pointer data() const noexcept
327 [[nodiscard]]
constexpr iterator begin() noexcept
332 [[nodiscard]]
constexpr const_iterator begin() const noexcept
337 [[nodiscard]]
constexpr const_iterator cbegin() const noexcept
342 [[nodiscard]]
constexpr iterator end() noexcept
347 [[nodiscard]]
constexpr const_iterator end() const noexcept
352 [[nodiscard]]
constexpr const_iterator cend() const noexcept
357 [[nodiscard]]
constexpr bool empty() const noexcept
362 [[nodiscard]]
constexpr size_type size() const noexcept
367 [[nodiscard]]
constexpr size_type max_size() const noexcept
372 constexpr bool is_point() const noexcept
374 return v.back() != T{};
377 constexpr bool is_vector() const noexcept
379 return v.back() == T{};
382 constexpr bool is_opaque() const noexcept
387 constexpr bool is_transparent() const noexcept
392 [[nodiscard]]
constexpr T
const &x() const noexcept requires(N >= 1)
397 [[nodiscard]]
constexpr T
const &y() const noexcept requires(N >= 2)
402 [[nodiscard]]
constexpr T
const &z() const noexcept requires(N >= 3)
407 [[nodiscard]]
constexpr T
const &w() const noexcept requires(N >= 4)
412 [[nodiscard]]
constexpr T &x() noexcept requires(N >= 1)
417 [[nodiscard]]
constexpr T &y() noexcept requires(N >= 2)
422 [[nodiscard]]
constexpr T &z() noexcept requires(N >= 3)
427 [[nodiscard]]
constexpr T &w() noexcept requires(N >= 4)
432 [[nodiscard]]
constexpr T
const &r() const noexcept requires(N >= 1)
437 [[nodiscard]]
constexpr T
const &g() const noexcept requires(N >= 2)
442 [[nodiscard]]
constexpr T
const &b() const noexcept requires(N >= 3)
447 [[nodiscard]]
constexpr T
const &a() const noexcept requires(N >= 4)
452 [[nodiscard]]
constexpr T &r() noexcept requires(N >= 1)
457 [[nodiscard]]
constexpr T &g() noexcept requires(N >= 2)
462 [[nodiscard]]
constexpr T &b() noexcept requires(N >= 3)
467 [[nodiscard]]
constexpr T &a() noexcept requires(N >= 4)
472 [[nodiscard]]
constexpr T
const &width() const noexcept requires(N >= 1)
477 [[nodiscard]]
constexpr T
const &height() const noexcept requires(N >= 2)
482 [[nodiscard]]
constexpr T
const &depth() const noexcept requires(N >= 3)
487 [[nodiscard]]
constexpr T &width() noexcept requires(N >= 1)
492 [[nodiscard]]
constexpr T &height() noexcept requires(N >= 2)
497 [[nodiscard]]
constexpr T &depth() noexcept requires(N >= 3)
502 constexpr numeric_array &operator+=(numeric_array
const &rhs)
noexcept
504 for (ssize_t i = 0; i != N; ++i) {
510 constexpr numeric_array &operator+=(T
const &rhs)
noexcept
512 for (ssize_t i = 0; i != N; ++i) {
518 constexpr numeric_array &operator-=(numeric_array
const &rhs)
noexcept
520 for (ssize_t i = 0; i != N; ++i) {
526 constexpr numeric_array &operator-=(T
const &rhs)
noexcept
528 for (ssize_t i = 0; i != N; ++i) {
534 constexpr numeric_array &operator*=(numeric_array
const &rhs)
noexcept
536 for (ssize_t i = 0; i != N; ++i) {
542 constexpr numeric_array &operator*=(T
const &rhs)
noexcept
544 for (ssize_t i = 0; i != N; ++i) {
550 constexpr numeric_array &operator/=(numeric_array
const &rhs)
noexcept
552 for (ssize_t i = 0; i != N; ++i) {
558 constexpr numeric_array &operator/=(T
const &rhs)
noexcept
560 for (ssize_t i = 0; i != N; ++i) {
566 constexpr numeric_array &operator%=(numeric_array
const &rhs)
noexcept
568 for (ssize_t i = 0; i != N; ++i) {
574 constexpr numeric_array &operator%=(T
const &rhs)
noexcept
576 for (ssize_t i = 0; i != N; ++i) {
582 constexpr static ssize_t get_zero = -1;
583 constexpr static ssize_t get_one = -2;
592 static_assert(I >= 0 && I < N,
"Index out of bounds");
593 return get<I>(rhs.v);
604 static_assert(I >= -2 && I < N,
"Index out of bounds");
605 if constexpr (I == get_zero) {
607 }
else if constexpr (I == get_one) {
610 return get<I>(rhs.v);
622 static_assert(I >= -2 && I < N,
"Index out of bounds");
623 if constexpr (I == get_zero) {
625 }
else if constexpr (I == get_one) {
628 return get<I>(rhs.v);
636 template<
size_t Mask = ~size_t{0}>
639 if (!std::is_constant_evaluated()) {
640 if constexpr (is_f32x4 && has_sse) {
646 for (
ssize_t i = 0; i != N; ++i) {
647 if (
static_cast<bool>((Mask >> i) & 1)) {
660 template<
size_t Mask = ~size_t{0}>
663 if (!std::is_constant_evaluated()) {
664 if constexpr (is_f32x4 && has_sse) {
670 for (
ssize_t i = 0; i != N; ++i) {
671 if (
static_cast<bool>((Mask >> i) & 1)) {
683 for (ssize_t i = 0; i != N; ++i) {
685 r.v[i] = T{} - rhs.v[i];
690 [[nodiscard]]
friend constexpr numeric_array abs(numeric_array
const &rhs)
noexcept
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];
701 [[nodiscard]]
friend constexpr numeric_array rcp(numeric_array
const &rhs)
noexcept
703 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
704 return numeric_array{f32x4_sse_rcp(rhs.v)};
707 auto r = numeric_array{};
708 for (ssize_t i = 0; i != N; ++i) {
709 r[i] = 1.0f / rhs.v[i];
714 [[nodiscard]]
friend constexpr numeric_array sqrt(numeric_array
const &rhs)
noexcept
716 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
717 return numeric_array{f32x4_sse_sqrt(rhs.v)};
720 auto r = numeric_array{};
721 for (ssize_t i = 0; i != N; ++i) {
727 [[nodiscard]]
friend constexpr numeric_array rcp_sqrt(numeric_array
const &rhs)
noexcept
729 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
730 return numeric_array{f32x4_sse_rcp_sqrt(rhs.v)};
733 auto r = numeric_array{};
734 for (ssize_t i = 0; i != N; ++i) {
740 [[nodiscard]]
friend constexpr numeric_array floor(numeric_array
const &rhs)
noexcept
742 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
743 return numeric_array{f32x4_sse_floor(rhs.v)};
746 auto r = numeric_array{};
747 for (ssize_t i = 0; i != N; ++i) {
753 [[nodiscard]]
friend constexpr numeric_array ceil(numeric_array
const &rhs)
noexcept
755 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
756 return numeric_array{f32x4_sse_ceil(rhs.v)};
759 auto r = numeric_array{};
760 for (ssize_t i = 0; i != N; ++i) {
766 [[nodiscard]]
friend constexpr numeric_array round(numeric_array
const &rhs)
noexcept
768 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
769 return numeric_array{f32x4_sse_round(rhs.v)};
772 auto r = numeric_array{};
773 for (ssize_t i = 0; i != N; ++i) {
786 template<s
size_t Mask>
789 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
790 return f32x4_sse_dot<Mask>(lhs.v, rhs.v);
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];
809 template<s
size_t Mask>
812 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
813 return f32x4_sse_hypot<Mask>(rhs.v);
825 template<s
size_t Mask>
828 return dot<Mask>(rhs, rhs);
837 template<s
size_t Mask>
840 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
841 return f32x4_sse_rcp_hypot<Mask>(rhs.v);
844 return 1.0f / hypot<Mask>(rhs);
855 template<s
size_t Mask>
858 tt_axiom(rhs.is_vector());
860 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
864 ttlet rcp_hypot_ = rcp_hypot<Mask>(rhs);
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_;
876 requires(N <=
sizeof(
unsigned int) * CHAR_BIT)
878 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
879 return f32x4_sse_eq_mask(lhs.v, rhs.v);
882 for (ssize_t i = 0; i != N; ++i) {
883 r |=
static_cast<unsigned int>(lhs.v[i] == rhs.v[i]) << i;
889 [[nodiscard]]
friend constexpr unsigned int ne(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
890 requires(N <=
sizeof(
unsigned int) * CHAR_BIT)
892 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
893 return f32x4_sse_ne_mask(lhs.v, rhs.v);
896 for (ssize_t i = 0; i != N; ++i) {
897 r |=
static_cast<unsigned int>(lhs.v[i] != rhs.v[i]) << i;
903 [[nodiscard]]
friend constexpr unsigned int lt(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
904 requires(N <=
sizeof(
unsigned int) * CHAR_BIT)
906 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
907 return f32x4_sse_lt_mask(lhs.v, rhs.v);
910 for (ssize_t i = 0; i != N; ++i) {
911 r |=
static_cast<unsigned int>(lhs.v[i] < rhs.v[i]) << i;
917 [[nodiscard]]
friend constexpr unsigned int gt(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
918 requires(N <=
sizeof(
unsigned int) * CHAR_BIT)
920 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
921 return f32x4_sse_gt_mask(lhs.v, rhs.v);
924 for (ssize_t i = 0; i != N; ++i) {
925 r |=
static_cast<unsigned int>(lhs.v[i] > rhs.v[i]) << i;
931 [[nodiscard]]
friend constexpr unsigned int le(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
932 requires(N <=
sizeof(
unsigned int) * CHAR_BIT)
934 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
935 return f32x4_sse_le_mask(lhs.v, rhs.v);
938 for (ssize_t i = 0; i != N; ++i) {
939 r |=
static_cast<unsigned int>(lhs.v[i] <= rhs.v[i]) << i;
945 [[nodiscard]]
friend constexpr unsigned int ge(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
946 requires(N <=
sizeof(
unsigned int) * CHAR_BIT)
948 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
949 return f32x4_sse_ge_mask(lhs.v, rhs.v);
952 for (ssize_t i = 0; i != N; ++i) {
953 r |=
static_cast<unsigned int>(lhs.v[i] >= rhs.v[i]) << i;
959 [[nodiscard]]
friend constexpr bool operator==(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
961 if (!std::is_constant_evaluated()) {
962 if constexpr (is_f32x4 && has_sse) {
964 return f32x4_sse_eq(lhs.v, rhs.v);
969 for (ssize_t i = 0; i != N; ++i) {
970 r &= (lhs.v[i] == rhs.v[i]);
975 [[nodiscard]]
friend constexpr bool operator!=(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
977 return !(lhs == rhs);
980 [[nodiscard]]
friend constexpr numeric_array operator+(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
982 auto r = numeric_array{};
983 for (ssize_t i = 0; i != N; ++i) {
984 r.v[i] = lhs.v[i] + rhs.v[i];
989 [[nodiscard]]
friend constexpr numeric_array operator+(numeric_array
const &lhs, T
const &rhs)
noexcept
991 auto r = numeric_array{};
992 for (ssize_t i = 0; i != N; ++i) {
993 r.v[i] = lhs.v[i] + rhs;
998 [[nodiscard]]
friend constexpr numeric_array operator+(T
const &lhs, numeric_array
const &rhs)
noexcept
1000 auto r = numeric_array{};
1001 for (ssize_t i = 0; i != N; ++i) {
1002 r.v[i] = lhs + rhs.v[i];
1007 [[nodiscard]]
friend constexpr numeric_array hadd(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
1009 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
1010 return numeric_array{f32x4_sse_hadd(lhs.v, rhs.v)};
1013 tt_axiom(N % 2 == 0);
1015 auto r = numeric_array{};
1019 while (src_i != N) {
1020 auto tmp = lhs[src_i++];
1021 tmp += lhs[src_i++];
1026 while (src_i != N) {
1027 auto tmp = rhs[src_i++];
1028 tmp += rhs[src_i++];
1035 [[nodiscard]]
friend constexpr numeric_array hsub(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
1037 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
1038 return numeric_array{f32x4_sse_hsub(lhs.v, rhs.v)};
1041 tt_axiom(N % 2 == 0);
1043 auto r = numeric_array{};
1047 while (src_i != N) {
1048 auto tmp = lhs[src_i++];
1049 tmp -= lhs[src_i++];
1054 while (src_i != N) {
1055 auto tmp = rhs[src_i++];
1056 tmp -= rhs[src_i++];
1063 [[nodiscard]]
friend constexpr numeric_array operator-(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
1065 auto r = numeric_array{};
1066 for (ssize_t i = 0; i != N; ++i) {
1067 r.v[i] = lhs.v[i] - rhs.v[i];
1072 [[nodiscard]]
friend constexpr numeric_array operator-(numeric_array
const &lhs, T
const &rhs)
noexcept
1074 auto r = numeric_array{};
1075 for (ssize_t i = 0; i != N; ++i) {
1076 r.v[i] = lhs.v[i] - rhs;
1085 template<
size_t Mask = ~size_t{0}>
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)};
1095 for (
ssize_t i = 0; i != N; ++i) {
1096 if (
static_cast<bool>((Mask >> i) & 1)) {
1097 r.v[i] = lhs.v[i] + rhs.v[i];
1099 r.v[i] = lhs.v[i] - rhs.v[i];
1108 for (ssize_t i = 0; i != N; ++i) {
1109 r.v[i] = lhs - rhs.v[i];
1114 [[nodiscard]]
friend constexpr numeric_array operator*(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
1116 auto r = numeric_array{};
1117 for (ssize_t i = 0; i != N; ++i) {
1118 r.v[i] = lhs.v[i] * rhs.v[i];
1123 [[nodiscard]]
friend constexpr numeric_array operator*(numeric_array
const &lhs, T
const &rhs)
noexcept
1125 auto r = numeric_array{};
1126 for (ssize_t i = 0; i != N; ++i) {
1127 r.v[i] = lhs.v[i] * rhs;
1132 [[nodiscard]]
friend constexpr numeric_array operator*(T
const &lhs, numeric_array
const &rhs)
noexcept
1134 auto r = numeric_array{};
1135 for (ssize_t i = 0; i != N; ++i) {
1136 r.v[i] = lhs * rhs.v[i];
1141 [[nodiscard]]
friend constexpr numeric_array operator/(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
1143 auto r = numeric_array{};
1144 for (ssize_t i = 0; i != N; ++i) {
1145 r.v[i] = lhs.v[i] / rhs.v[i];
1150 [[nodiscard]]
friend constexpr numeric_array operator/(numeric_array
const &lhs, T
const &rhs)
noexcept
1152 auto r = numeric_array{};
1153 for (ssize_t i = 0; i != N; ++i) {
1154 r.v[i] = lhs.v[i] / rhs;
1159 [[nodiscard]]
friend constexpr numeric_array operator/(T
const &lhs, numeric_array
const &rhs)
noexcept
1161 auto r = numeric_array{};
1162 for (ssize_t i = 0; i != N; ++i) {
1163 r.v[i] = lhs / rhs.v[i];
1168 [[nodiscard]]
friend constexpr numeric_array operator%(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
1170 auto r = numeric_array{};
1171 for (ssize_t i = 0; i != N; ++i) {
1172 r.v[i] = lhs.v[i] % rhs.v[i];
1177 [[nodiscard]]
friend constexpr numeric_array operator%(numeric_array
const &lhs, T
const &rhs)
noexcept
1179 auto r = numeric_array{};
1180 for (ssize_t i = 0; i != N; ++i) {
1181 r.v[i] = lhs.v[i] % rhs;
1186 [[nodiscard]]
friend constexpr numeric_array operator%(T
const &lhs, numeric_array
const &rhs)
noexcept
1188 auto r = numeric_array{};
1189 for (ssize_t i = 0; i != N; ++i) {
1190 r.v[i] = lhs % rhs.v[i];
1195 [[nodiscard]]
friend constexpr numeric_array min(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
1197 auto r = numeric_array{};
1198 for (ssize_t i = 0; i != N; ++i) {
1200 r.v[i] = lhs.v[i] < rhs.v[i] ? lhs.v[i] : rhs.v[i];
1205 [[nodiscard]]
friend constexpr numeric_array max(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
1207 auto r = numeric_array{};
1208 for (ssize_t i = 0; i != N; ++i) {
1210 r.v[i] = lhs.v[i] > rhs.v[i] ? lhs.v[i] : rhs.v[i];
1215 [[nodiscard]]
friend constexpr numeric_array
1216 clamp(numeric_array
const &lhs, numeric_array
const &low, numeric_array
const &high)
noexcept
1218 auto r = numeric_array{};
1219 for (ssize_t i = 0; i != N; ++i) {
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];
1230 tt_axiom(rhs.z() == 0.0f && rhs.is_vector());
1238 return normalize<0b0011>(
cross_2D(rhs));
1246 if (is_f32x4 && has_sse && !std::is_constant_evaluated()) {
1247 return f32x4_sse_viktor_cross(lhs.v, rhs.v);
1250 return lhs.x() * rhs.y() - lhs.y() * rhs.x();
1260 if (!std::is_constant_evaluated()) {
1261 if constexpr (is_f32x4 && has_sse) {
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(),
1280 requires(D == 4) [[nodiscard]]
friend numeric_array
1281 hamilton_cross(numeric_array
const &lhs, numeric_array
const &rhs)
noexcept
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();
1288 ttlet col01 =
addsub(col0, col1);
1289 ttlet col012 =
addsub(col01.xzyw(), col2.xzyw()).xzyw();
1291 return numeric_array{
1300 tt_axiom(p1.is_point());
1301 tt_axiom(p2.is_point());
1302 return (p1 + p2) * 0.5f;
1309 tt_axiom(p.is_point());
1310 tt_axiom(anchor.is_point());
1311 return anchor - (p - anchor);
1314 template<
typename... Columns>
1317 static_assert(
sizeof...(Columns) == N,
"Can only transpose square matrices");
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]};
1328 transpose_detail<0, Columns...>(columns..., r);
1334 [[nodiscard]]
friend numeric_array desaturate(numeric_array
const &color, T brightness)
noexcept
1335 requires(N == 4 && std::is_floating_point_v<T>)
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();
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>)
1348 if (over.is_transparent()) {
1351 if (over.is_opaque()) {
1355 ttlet over_alpha = over.wwww();
1356 ttlet under_alpha = under.wwww();
1358 ttlet over_color = over.xyz1();
1359 ttlet under_color = under.xyz1();
1361 ttlet output_color = over_color * over_alpha + under_color * under_alpha * (1.0 - over_alpha);
1363 return output_color / output_color.www1();
1366 [[nodiscard]]
friend std::string to_string(numeric_array
const &rhs)
noexcept
1371 for (ssize_t i = 0; i != N; ++i) {
1375 r += fmt::format(
"{}", rhs[i]);
1383 return lhs << to_string(rhs);
1393 template<ssize_t... Elements>
1396 static_assert(
sizeof...(Elements) <= N);
1398 if (!std::is_constant_evaluated()) {
1399 if constexpr (is_f32x4 && has_sse) {
1405 swizzle_detail<0, Elements...>(r);
1409#define SWIZZLE(swizzle_name, D, ...) \
1410 [[nodiscard]] constexpr numeric_array swizzle_name() const noexcept requires(D == N) \
1412 return swizzle<__VA_ARGS__>(); \
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)
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)
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)
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)
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)
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)
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)
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)
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)
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
1488 template<
int I,
typename First,
typename... Rest>
1489 [[nodiscard]]
friend constexpr void
1492 for (
ssize_t j = 0; j != N; ++j) {
1496 if constexpr (
sizeof...(Rest) != 0) {
1497 transpose_detail<I + 1, Rest...>(rest..., r);
1501 template<ssize_t I, ssize_t FirstElement, ssize_t... RestElements>
1502 [[nodiscard]]
constexpr void swizzle_detail(numeric_array &r)
const noexcept
1504 static_assert(I < N);
1505 static_assert(FirstElement >= -2 && FirstElement < N,
"Index out of bounds");
1507 get<I>(r) = get<FirstElement>(*
this);
1508 if constexpr (
sizeof...(RestElements) != 0) {
1509 swizzle_detail<I + 1, RestElements...>(r);
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;
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;
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;
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;