Stratax 0.3.1
Loading...
Searching...
No Matches
Bitwise.hpp
1#pragma once
2
3#include <stratax/core/dtypes/Concepts.hpp>
4#include <stratax/ops/Broadcasting.hpp>
5
6#include <functional>
7#include <climits>
8#include <cstdint>
9#include <type_traits>
10
11namespace stratax::core::bitwise_detail {
12
24template<typename Value, Integral Count>
25constexpr bool valid_shift_count(const Count& count) noexcept
26{
27 if constexpr (std::is_signed_v<std::remove_cvref_t<Count>>)
28 {
29 if (count < 0)
30 {
31 return false;
32 }
33 }
34
35 using value_type = std::remove_cvref_t<Value>;
36
37 return static_cast<std::uintmax_t>(count) <
38 sizeof(value_type) * CHAR_BIT;
39}
40
50template<typename Value, Integral Count>
51void require_valid_shift_count(const Count& count)
52{
53 if (!valid_shift_count<Value>(count))
54 {
55 throw Exceptions::ValueError("Invalid shift count.");
56 }
57}
58
67template<Array A, Integral Count, typename Op>
69auto shift_scalar_op(
70 const A& lhs,
71 const Count& rhs,
72 Op op)
73{
74 using value_type = typename A::value_type;
75
76 require_valid_shift_count<value_type>(rhs);
77
78 using result_type =
79 stratax::core::rebind_array_t<A, value_type>;
80
81 result_type result(lhs.shape());
82
83 auto out = result.begin();
84
85 for (auto it = lhs.begin(); it != lhs.end(); ++it, ++out)
86 {
87 *out = static_cast<value_type>(
88 op(*it, rhs));
89 }
90
91 return result;
92}
93
104template<Array L, Array R, typename Op>
105requires (
108)
109auto shift_array_op(
110 const L& lhs,
111 const R& rhs,
112 Op op)
113{
114 using value_type = typename L::value_type;
115
116 using result_type =
117 stratax::core::promote_array_t<
118 L,
119 R,
120 value_type>;
121
122 const auto result_shape =
123 broadcasted_shape(lhs.shape(), rhs.shape());
124
125 result_type result(result_shape);
126
127 for (std::size_t i = 0; i < result.size(); ++i)
128 {
129 const std::size_t lhs_index =
130 stratax::core::broadcast_detail::flat_operand_index(
131 i,
132 result_shape,
133 lhs.shape());
134
135 const std::size_t rhs_index =
136 stratax::core::broadcast_detail::flat_operand_index(
137 i,
138 result_shape,
139 rhs.shape());
140
141 const auto count = rhs[rhs_index];
142
143 require_valid_shift_count<value_type>(count);
144
145 result[i] = static_cast<value_type>(
146 op(lhs[lhs_index], count));
147 }
148
149 return result;
150}
151
160template<Integral Scalar, Array A, typename Op>
162auto scalar_shift_array_op(
163 const Scalar& lhs,
164 const A& rhs,
165 Op op)
166{
167 using value_type = std::remove_cvref_t<Scalar>;
168
169 using result_type =
170 stratax::core::rebind_array_t<
171 A,
172 value_type>;
173
174 result_type result(rhs.shape());
175
176 auto out = result.begin();
177
178 for (auto it = rhs.begin(); it != rhs.end(); ++it, ++out)
179 {
180 require_valid_shift_count<value_type>(*it);
181
182 *out = static_cast<value_type>(
183 op(lhs, *it));
184 }
185
186 return result;
187}
188
198template<Array L, Array R, typename Op>
199requires (
202)
203L& compound_bitwise_op(
204 L& lhs,
205 const R& rhs,
206 Op op)
207{
208 const auto result_shape =
209 broadcasted_shape(lhs.shape(), rhs.shape());
210
211 if (result_shape != lhs.shape())
212 {
214 "In-place broadcasting cannot change the left operand's shape.");
215 }
216
217 for (std::size_t i = 0; i < lhs.size(); ++i)
218 {
219 const std::size_t rhs_index =
220 stratax::core::broadcast_detail::flat_operand_index(
221 i,
222 lhs.shape(),
223 rhs.shape());
224
225 lhs[i] = static_cast<typename L::value_type>(
226 op(lhs[i], rhs[rhs_index]));
227 }
228
229 return lhs;
230}
231
239template<Array A, Integral Scalar, typename Op>
241A& compound_scalar_bitwise_op(
242 A& lhs,
243 const Scalar& rhs,
244 Op op)
245{
246 for (std::size_t i = 0; i < lhs.size(); ++i)
247 {
248 lhs[i] = static_cast<typename A::value_type>(
249 op(lhs[i], rhs));
250 }
251
252 return lhs;
253}
254
269template<Array L, Array R, typename Op>
270requires (
273)
274L& compound_shift_op(
275 L& lhs,
276 const R& rhs,
277 Op op)
278{
279 const auto result_shape =
280 broadcasted_shape(lhs.shape(), rhs.shape());
281
282 if (result_shape != lhs.shape())
283 {
285 "In-place broadcasting cannot change the left operand's shape.");
286 }
287
288 using value_type = typename L::value_type;
289
290 for (std::size_t i = 0; i < lhs.size(); ++i)
291 {
292 const std::size_t rhs_index =
293 stratax::core::broadcast_detail::flat_operand_index(
294 i,
295 lhs.shape(),
296 rhs.shape());
297
298 require_valid_shift_count<value_type>(rhs[rhs_index]);
299 }
300
301 for (std::size_t i = 0; i < lhs.size(); ++i)
302 {
303 const std::size_t rhs_index =
304 stratax::core::broadcast_detail::flat_operand_index(
305 i,
306 lhs.shape(),
307 rhs.shape());
308
309 const auto count = rhs[rhs_index];
310
311 lhs[i] = static_cast<value_type>(
312 op(lhs[i], count));
313 }
314
315 return lhs;
316}
317
336template<Array L, Array R, typename Op>
337requires (
340)
341auto binary_bitwise_op(
342 const L& lhs,
343 const R& rhs,
344 Op op)
345{
346 using result_value_type =
347 stratax::core::promote_t<
348 typename L::value_type,
349 typename R::value_type>;
350
351 return stratax::core::broadcasted_op<result_value_type>(
352 lhs,
353 rhs,
354 op);
355}
356
370template<Array A, Integral Scalar, typename Op>
371auto binary_scalar_bitwise_op(
372 const A& arr,
373 const Scalar& scalar,
374 Op op)
375{
376 using result_value_type =
377 promote_t<typename A::value_type, Scalar>;
378
379 using result_type =
380 rebind_array_t<A, result_value_type>;
381
382 result_type result(arr.shape());
383
384 for (std::size_t i = 0; i < arr.size(); ++i)
385 {
386 result[i] = static_cast<result_value_type>(
387 op(arr[i], scalar));
388 }
389
390 return result;
391}
392
406template<Integral Scalar, Array A, typename Op>
407auto binary_scalar_bitwise_op(
408 const Scalar& scalar,
409 const A& arr,
410 Op op)
411{
412 using result_value_type =
413 promote_t<Scalar, typename A::value_type>;
414
415 using result_type =
416 rebind_array_t<A, result_value_type>;
417
418 result_type result(arr.shape());
419
420 for (std::size_t i = 0; i < arr.size(); ++i)
421 {
422 result[i] = static_cast<result_value_type>(
423 op(scalar, arr[i]));
424 }
425
426 return result;
427}
428
435template<Array A, Integral Count, typename Op>
437A& compound_scalar_shift_op(
438 A& lhs,
439 const Count& rhs,
440 Op op)
441{
442 using value_type = typename A::value_type;
443
444 require_valid_shift_count<value_type>(rhs);
445
446 for (std::size_t i = 0; i < lhs.size(); ++i)
447 {
448 lhs[i] = static_cast<value_type>(
449 op(lhs[i], rhs));
450 }
451
452 return lhs;
453}
454
455} // namespace stratax::core::bitwise_detail
456
457// Unary
458
465template<Array A>
467A operator~(const A& value)
468{
469 A result(value.shape());
470
471 auto out = result.begin();
472 for (auto it = value.begin(); it != value.end(); ++it, ++out)
473 {
474 *out = static_cast<typename A::value_type>(std::bit_not<>{}(*it));
475 }
476
477 return result;
478}
479
480// Array-array
481
483template<Array L, Array R>
484requires (
487)
488auto operator&(const L& lhs, const R& rhs)
489{
490 return stratax::core::bitwise_detail::binary_bitwise_op(
491 lhs, rhs, std::bit_and<>{});
492}
493
495template<Array L, Array R>
496requires (
499)
500auto operator|(const L& lhs, const R& rhs)
501{
502 return stratax::core::bitwise_detail::binary_bitwise_op(
503 lhs, rhs, std::bit_or<>{});
504}
505
507template<Array L, Array R>
508requires (
511)
512auto operator^(const L& lhs, const R& rhs)
513{
514 return stratax::core::bitwise_detail::binary_bitwise_op(
515 lhs, rhs, std::bit_xor<>{});
516}
517
519template<Array L, Array R>
520requires (
523)
524auto operator<<(const L& lhs, const R& rhs)
525{
526 return stratax::core::bitwise_detail::shift_array_op(
527 lhs,
528 rhs,
529 [](auto value, auto count)
530 {
531 return value << count;
532 });
533}
534
536template<Array L, Array R>
537requires (
540)
541auto operator>>(const L& lhs, const R& rhs)
542{
543 return stratax::core::bitwise_detail::shift_array_op(
544 lhs,
545 rhs,
546 [](auto value, auto count)
547 {
548 return value >> count;
549 });
550}
551
552// Array-scalar
553
555template<Array A, Integral Scalar>
557auto operator&(const A& lhs, const Scalar& rhs)
558{
559 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
560 lhs, rhs, std::bit_and<>{});
561}
562
564template<Array A, Integral Scalar>
566auto operator|(const A& lhs, const Scalar& rhs)
567{
568 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
569 lhs, rhs, std::bit_or<>{});
570}
571
573template<Array A, Integral Scalar>
575auto operator^(const A& lhs, const Scalar& rhs)
576{
577 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
578 lhs, rhs, std::bit_xor<>{});
579}
580
582template<Array A, Integral Scalar>
584auto operator<<(const A& lhs, const Scalar& rhs)
585{
586 return stratax::core::bitwise_detail::shift_scalar_op(
587 lhs,
588 rhs,
589 [](auto value, auto count)
590 {
591 return value << count;
592 });
593}
594
596template<Array A, Integral Scalar>
598auto operator>>(const A& lhs, const Scalar& rhs)
599{
600 return stratax::core::bitwise_detail::shift_scalar_op(
601 lhs,
602 rhs,
603 [](auto value, auto count)
604 {
605 return value >> count;
606 });
607}
608
609// Scalar-array (reverse)
610
613template<Integral Scalar, Array A>
615auto operator&(const Scalar& lhs, const A& rhs)
616{
617 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
618 lhs, rhs, std::bit_and<>{});
619}
620
623template<Integral Scalar, Array A>
625auto operator|(const Scalar& lhs, const A& rhs)
626{
627 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
628 lhs, rhs, std::bit_or<>{});
629}
630
632template<Integral Scalar, Array A>
634auto operator^(const Scalar& lhs, const A& rhs)
635{
636 return stratax::core::bitwise_detail::binary_scalar_bitwise_op(
637 lhs, rhs, std::bit_xor<>{});
638}
639
640template<Integral Scalar, Array A>
642auto operator<<(const Scalar& lhs, const A& rhs)
643{
644 return stratax::core::bitwise_detail::scalar_shift_array_op(
645 lhs,
646 rhs,
647 [](auto value, auto count)
648 {
649 return value << count;
650 });
651}
652
653template<Integral Scalar, Array A>
655auto operator>>(const Scalar& lhs, const A& rhs)
656{
657 return stratax::core::bitwise_detail::scalar_shift_array_op(
658 lhs,
659 rhs,
660 [](auto value, auto count)
661 {
662 return value >> count;
663 });
664}
665
666// In-place array-array
667
669template<Array L, Array R>
670requires (
673)
674L& operator&=(L& lhs, const R& rhs)
675{
676 return stratax::core::bitwise_detail::compound_bitwise_op(
677 lhs, rhs, std::bit_and<>{});
678}
679
681template<Array L, Array R>
682requires (
685)
686L& operator|=(L& lhs, const R& rhs)
687{
688 return stratax::core::bitwise_detail::compound_bitwise_op(
689 lhs, rhs, std::bit_or<>{});
690}
691
693template<Array L, Array R>
694requires (
697)
698L& operator^=(L& lhs, const R& rhs)
699{
700 return stratax::core::bitwise_detail::compound_bitwise_op(
701 lhs, rhs, std::bit_xor<>{});
702}
703
705template<Array L, Array R>
706requires (
709)
710L& operator<<=(L& lhs, const R& rhs)
711{
712 return stratax::core::bitwise_detail::compound_shift_op(
713 lhs,
714 rhs,
715 [](auto value, auto count) {
716 return value << count;
717 });
718}
719
721template<Array L, Array R>
722requires (
725)
726L& operator>>=(L& lhs, const R& rhs)
727{
728 return stratax::core::bitwise_detail::compound_shift_op(
729 lhs,
730 rhs,
731 [](auto value, auto count) {
732 return value >> count;
733 });
734}
735
736// In-place array-scalar
737
738template<Array A, Integral Scalar>
740A& operator&=(A& lhs, const Scalar& rhs)
741{
742 return stratax::core::bitwise_detail::compound_scalar_bitwise_op(
743 lhs, rhs, std::bit_and<>{});
744}
745
746template<Array A, Integral Scalar>
748A& operator|=(A& lhs, const Scalar& rhs)
749{
750 return stratax::core::bitwise_detail::compound_scalar_bitwise_op(
751 lhs, rhs, std::bit_or<>{});
752}
753
754template<Array A, Integral Scalar>
756A& operator^=(A& lhs, const Scalar& rhs)
757{
758 return stratax::core::bitwise_detail::compound_scalar_bitwise_op(
759 lhs, rhs, std::bit_xor<>{});
760}
761
762template<Array A, Integral Scalar>
764A& operator<<=(A& lhs, const Scalar& rhs)
765{
766 return stratax::core::bitwise_detail::compound_scalar_shift_op(
767 lhs,
768 rhs,
769 [](auto value, auto count)
770 {
771 return value << count;
772 });
773}
774
775template<Array A, Integral Scalar>
777A& operator>>=(A& lhs, const Scalar& rhs)
778{
779 return stratax::core::bitwise_detail::compound_scalar_shift_op(
780 lhs,
781 rhs,
782 [](auto value, auto count)
783 {
784 return value >> count;
785 });
786}