Stratax 0.3.1
Loading...
Searching...
No Matches
Bitwise.hpp
1#pragma once
2
3#include <stratax/core/dtypes/Concepts.hpp>
4#include <stratax/core/validation/Validation.hpp>
5#include <stratax/ops/Broadcasting.hpp>
6
7#include <functional>
8#include <climits>
9#include <cstdint>
10#include <type_traits>
11
12namespace stratax::core::bitwise_detail {
13
25template<typename Value, Integral Count>
26constexpr bool valid_shift_count(const Count& count) noexcept
27{
28 if constexpr (std::is_signed_v<std::remove_cvref_t<Count>>)
29 {
30 if (count < 0)
31 {
32 return false;
33 }
34 }
35
36 using value_type = std::remove_cvref_t<Value>;
37
38 return static_cast<std::uintmax_t>(count) <
39 sizeof(value_type) * CHAR_BIT;
40}
41
51template<typename Value, Integral Count>
52void require_valid_shift_count(const Count& count)
53{
54 if (!valid_shift_count<Value>(count))
55 {
56 throw Exceptions::StrataxError(
57 "Shift count must be non-negative and less than the bit width of the shifted value.");
58 }
59}
60
69template<Array A, Integral Count, typename Op>
70requires Integral<typename A::value_type>
71auto shift_scalar_op(
72 const A& lhs,
73 const Count& rhs,
74 Op op)
75{
76 using value_type = typename A::value_type;
77
78 require_valid_shift_count<value_type>(rhs);
79
80 using result_type =
81 stratax::core::rebind_array_t<A, value_type>;
82
83 result_type result(lhs.shape());
84
85 auto out = result.begin();
86
87 for (auto it = lhs.begin(); it != lhs.end(); ++it, ++out)
88 {
89 *out = static_cast<value_type>(
90 op(*it, rhs));
91 }
92
93 return result;
94}
95
106template<Array L, Array R, typename Op>
107requires (
108 Integral<typename L::value_type> &&
109 Integral<typename R::value_type>
110)
111auto shift_array_op(
112 const L& lhs,
113 const R& rhs,
114 Op op)
115{
116 using value_type = typename L::value_type;
117
118 using result_type =
119 stratax::core::promote_array_t<
120 L,
121 R,
122 value_type>;
123
124 const auto result_shape =
125 broadcasted_shape(lhs.shape(), rhs.shape());
126
127 result_type result(result_shape);
128
129 for (std::size_t i = 0; i < result.size(); ++i)
130 {
131 const std::size_t lhs_index =
132 stratax::core::broadcast_detail::flat_operand_index(
133 i,
134 result_shape,
135 lhs.shape());
136
137 const std::size_t rhs_index =
138 stratax::core::broadcast_detail::flat_operand_index(
139 i,
140 result_shape,
141 rhs.shape());
142
143 const auto count = rhs[rhs_index];
144
145 require_valid_shift_count<value_type>(count);
146
147 result[i] = static_cast<value_type>(
148 op(lhs[lhs_index], count));
149 }
150
151 return result;
152}
153
162template<Integral Scalar, Array A, typename Op>
163requires Integral<typename A::value_type>
164auto scalar_shift_array_op(
165 const Scalar& lhs,
166 const A& rhs,
167 Op op)
168{
169 using value_type = std::remove_cvref_t<Scalar>;
170
171 using result_type =
172 stratax::core::rebind_array_t<
173 A,
174 value_type>;
175
176 result_type result(rhs.shape());
177
178 auto out = result.begin();
179
180 for (auto it = rhs.begin(); it != rhs.end(); ++it, ++out)
181 {
182 require_valid_shift_count<value_type>(*it);
183
184 *out = static_cast<value_type>(
185 op(lhs, *it));
186 }
187
188 return result;
189}
190
200template<Array L, Array R, typename Op>
201requires (
202 Integral<typename L::value_type> &&
203 Integral<typename R::value_type>
204)
205L& compound_bitwise_op(
206 L& lhs,
207 const R& rhs,
208 Op op)
209{
210 const auto result_shape =
211 broadcasted_shape(lhs.shape(), rhs.shape());
212
213 if (result_shape != lhs.shape())
214 {
215 throw Exceptions::BroadcastError(
216 "Compound bitwise assignment cannot change the left-hand shape.");
217 }
218
219 for (std::size_t i = 0; i < lhs.size(); ++i)
220 {
221 const std::size_t rhs_index =
222 stratax::core::broadcast_detail::flat_operand_index(
223 i,
224 lhs.shape(),
225 rhs.shape());
226
227 lhs[i] = static_cast<typename L::value_type>(
228 op(lhs[i], rhs[rhs_index]));
229 }
230
231 return lhs;
232}
233
241template<Array A, Integral Scalar, typename Op>
242requires Integral<typename A::value_type>
243A& compound_scalar_bitwise_op(
244 A& lhs,
245 const Scalar& rhs,
246 Op op)
247{
248 for (std::size_t i = 0; i < lhs.size(); ++i)
249 {
250 lhs[i] = static_cast<typename A::value_type>(
251 op(lhs[i], rhs));
252 }
253
254 return lhs;
255}
256
271template<Array L, Array R, typename Op>
272requires (
273 Integral<typename L::value_type> &&
274 Integral<typename R::value_type>
275)
276L& compound_shift_op(
277 L& lhs,
278 const R& rhs,
279 Op op)
280{
281 const auto result_shape =
282 broadcasted_shape(lhs.shape(), rhs.shape());
283
284 if (result_shape != lhs.shape())
285 {
286 throw Exceptions::BroadcastError(
287 "Compound shift assignment cannot change the left-hand shape.");
288 }
289
290 using value_type = typename L::value_type;
291
292 for (std::size_t i = 0; i < lhs.size(); ++i)
293 {
294 const std::size_t rhs_index =
295 stratax::core::broadcast_detail::flat_operand_index(
296 i,
297 lhs.shape(),
298 rhs.shape());
299
300 require_valid_shift_count<value_type>(rhs[rhs_index]);
301 }
302
303 for (std::size_t i = 0; i < lhs.size(); ++i)
304 {
305 const std::size_t rhs_index =
306 stratax::core::broadcast_detail::flat_operand_index(
307 i,
308 lhs.shape(),
309 rhs.shape());
310
311 const auto count = rhs[rhs_index];
312
313 lhs[i] = static_cast<value_type>(
314 op(lhs[i], count));
315 }
316
317 return lhs;
318}
319
338template<Array L, Array R, typename Op>
339requires (
340 Integral<typename L::value_type> &&
341 Integral<typename R::value_type>
342)
343auto binary_bitwise_op(
344 const L& lhs,
345 const R& rhs,
346 Op op)
347{
348 using result_value_type =
349 stratax::core::promote_t<
350 typename L::value_type,
351 typename R::value_type>;
352
353 return stratax::core::broadcasted_op<result_value_type>(
354 lhs,
355 rhs,
356 op);
357}
358
372template<Array A, Integral Scalar, typename Op>
373auto binary_scalar_bitwise_op(
374 const A& arr,
375 const Scalar& scalar,
376 Op op)
377{
378 using result_value_type =
379 promote_t<typename A::value_type, Scalar>;
380
381 using result_type =
382 rebind_array_t<A, result_value_type>;
383
384 result_type result(arr.shape());
385
386 for (std::size_t i = 0; i < arr.size(); ++i)
387 {
388 result[i] = static_cast<result_value_type>(
389 op(arr[i], scalar));
390 }
391
392 return result;
393}
394
408template<Integral Scalar, Array A, typename Op>
409auto binary_scalar_bitwise_op(
410 const Scalar& scalar,
411 const A& arr,
412 Op op)
413{
414 using result_value_type =
415 promote_t<Scalar, typename A::value_type>;
416
417 using result_type =
418 rebind_array_t<A, result_value_type>;
419
420 result_type result(arr.shape());
421
422 for (std::size_t i = 0; i < arr.size(); ++i)
423 {
424 result[i] = static_cast<result_value_type>(
425 op(scalar, arr[i]));
426 }
427
428 return result;
429}
430
437template<Array A, Integral Count, typename Op>
438requires Integral<typename A::value_type>
439A& compound_scalar_shift_op(
440 A& lhs,
441 const Count& rhs,
442 Op op)
443{
444 using value_type = typename A::value_type;
445
446 require_valid_shift_count<value_type>(rhs);
447
448 for (std::size_t i = 0; i < lhs.size(); ++i)
449 {
450 lhs[i] = static_cast<value_type>(
451 op(lhs[i], rhs));
452 }
453
454 return lhs;
455}
456
457} // namespace stratax::core::bitwise_detail
458
459// Unary
460
467template<Array A>
469A operator~(const A& value)
470{
471 A result(value.shape());
472
473 auto out = result.begin();
474 for (auto it = value.begin(); it != value.end(); ++it, ++out)
475 {
476 *out = static_cast<typename A::value_type>(std::bit_not<>{}(*it));
477 }
478
479 return result;
480}
481
482// Array-array
483
485template<Array L, Array R>
486requires (
489)
490auto operator&(const L& lhs, const R& rhs)
491{
492 return stratax::core::bitwise_detail::binary_bitwise_op(
493 lhs, rhs, std::bit_and<>{});
494}
495
497template<Array L, Array R>
498requires (
501)
502auto operator|(const L& lhs, const R& rhs)
503{
504 return stratax::core::bitwise_detail::binary_bitwise_op(
505 lhs, rhs, std::bit_or<>{});
506}
507
509template<Array L, Array R>
510requires (
513)
514auto operator^(const L& lhs, const R& rhs)
515{
516 return stratax::core::bitwise_detail::binary_bitwise_op(
517 lhs, rhs, std::bit_xor<>{});
518}
519
521template<Array L, Array R>
522requires (
525)
526auto operator<<(const L& lhs, const R& rhs)
527{
528 return stratax::core::bitwise_detail::shift_array_op(
529 lhs,
530 rhs,
531 [](auto value, auto count)
532 {
533 return value << count;
534 });
535}
536
538template<Array L, Array R>
539requires (
542)
543auto operator>>(const L& lhs, const R& rhs)
544{
545 return stratax::core::bitwise_detail::shift_array_op(
546 lhs,
547 rhs,
548 [](auto value, auto count)
549 {
550 return value >> count;
551 });
552}
553
554// Array-scalar
555
557template<Array A, Integral Scalar>
559auto operator&(const A& lhs, const Scalar& rhs)
560{
561 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
562 lhs, rhs, std::bit_and<>{});
563}
564
566template<Array A, Integral Scalar>
568auto operator|(const A& lhs, const Scalar& rhs)
569{
570 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
571 lhs, rhs, std::bit_or<>{});
572}
573
575template<Array A, Integral Scalar>
577auto operator^(const A& lhs, const Scalar& rhs)
578{
579 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
580 lhs, rhs, std::bit_xor<>{});
581}
582
584template<Array A, Integral Scalar>
586auto operator<<(const A& lhs, const Scalar& rhs)
587{
588 return stratax::core::bitwise_detail::shift_scalar_op(
589 lhs,
590 rhs,
591 [](auto value, auto count)
592 {
593 return value << count;
594 });
595}
596
598template<Array A, Integral Scalar>
600auto operator>>(const A& lhs, const Scalar& rhs)
601{
602 return stratax::core::bitwise_detail::shift_scalar_op(
603 lhs,
604 rhs,
605 [](auto value, auto count)
606 {
607 return value >> count;
608 });
609}
610
611// Scalar-array (reverse)
612
615template<Integral Scalar, Array A>
617auto operator&(const Scalar& lhs, const A& rhs)
618{
619 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
620 lhs, rhs, std::bit_and<>{});
621}
622
625template<Integral Scalar, Array A>
627auto operator|(const Scalar& lhs, const A& rhs)
628{
629 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
630 lhs, rhs, std::bit_or<>{});
631}
632
634template<Integral Scalar, Array A>
636auto operator^(const Scalar& lhs, const A& rhs)
637{
638 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
639 lhs, rhs, std::bit_xor<>{});
640}
641
642template<Integral Scalar, Array A>
644auto operator<<(const Scalar& lhs, const A& rhs)
645{
646 return stratax::core::bitwise_detail::scalar_shift_array_op(
647 lhs,
648 rhs,
649 [](auto value, auto count)
650 {
651 return value << count;
652 });
653}
654
655template<Integral Scalar, Array A>
657auto operator>>(const Scalar& lhs, const A& rhs)
658{
659 return stratax::core::bitwise_detail::scalar_shift_array_op(
660 lhs,
661 rhs,
662 [](auto value, auto count)
663 {
664 return value >> count;
665 });
666}
667
668// In-place array-array
669
671template<Array L, Array R>
672requires (
675)
676L& operator&=(L& lhs, const R& rhs)
677{
678 return stratax::core::bitwise_detail::compound_bitwise_op(
679 lhs, rhs, std::bit_and<>{});
680}
681
683template<Array L, Array R>
684requires (
687)
688L& operator|=(L& lhs, const R& rhs)
689{
690 return stratax::core::bitwise_detail::compound_bitwise_op(
691 lhs, rhs, std::bit_or<>{});
692}
693
695template<Array L, Array R>
696requires (
699)
700L& operator^=(L& lhs, const R& rhs)
701{
702 return stratax::core::bitwise_detail::compound_bitwise_op(
703 lhs, rhs, std::bit_xor<>{});
704}
705
707template<Array L, Array R>
708requires (
711)
712L& operator<<=(L& lhs, const R& rhs)
713{
714 return stratax::core::bitwise_detail::compound_shift_op(
715 lhs,
716 rhs,
717 [](auto value, auto count) {
718 return value << count;
719 });
720}
721
723template<Array L, Array R>
724requires (
727)
728L& operator>>=(L& lhs, const R& rhs)
729{
730 return stratax::core::bitwise_detail::compound_shift_op(
731 lhs,
732 rhs,
733 [](auto value, auto count) {
734 return value >> count;
735 });
736}
737
738// In-place array-scalar
739
740template<Array A, Integral Scalar>
742A& operator&=(A& lhs, const Scalar& rhs)
743{
744 return stratax::core::bitwise_detail::compound_scalar_bitwise_op(
745 lhs, rhs, std::bit_and<>{});
746}
747
748template<Array A, Integral Scalar>
750A& operator|=(A& lhs, const Scalar& rhs)
751{
752 return stratax::core::bitwise_detail::compound_scalar_bitwise_op(
753 lhs, rhs, std::bit_or<>{});
754}
755
756template<Array A, Integral Scalar>
758A& operator^=(A& lhs, const Scalar& rhs)
759{
760 return stratax::core::bitwise_detail::compound_scalar_bitwise_op(
761 lhs, rhs, std::bit_xor<>{});
762}
763
764template<Array A, Integral Scalar>
766A& operator<<=(A& lhs, const Scalar& rhs)
767{
768 return stratax::core::bitwise_detail::compound_scalar_shift_op(
769 lhs,
770 rhs,
771 [](auto value, auto count)
772 {
773 return value << count;
774 });
775}
776
777template<Array A, Integral Scalar>
779A& operator>>=(A& lhs, const Scalar& rhs)
780{
781 return stratax::core::bitwise_detail::compound_scalar_shift_op(
782 lhs,
783 rhs,
784 [](auto value, auto count)
785 {
786 return value >> count;
787 });
788}