Stratax 0.3.1
Loading...
Searching...
No Matches
Arithmetic.hpp
1#pragma once
2
3#include <stratax/core/dtypes/Concepts.hpp>
4#include <stratax/exceptions/Exceptions.hpp>
5#include <stratax/ops/Broadcasting.hpp>
6
7#include <functional>
8
32template<Array L, Array R, typename Op>
33requires (
36)
37auto binary_op(
38 const L& lhs,
39 const R& rhs,
40 Op op,
41 bool check_zero_divisor = false)
42{
43 auto checked_op = [&](const auto& left, const auto& right)
44 {
45 if (check_zero_divisor &&
46 right == typename R::value_type{})
47 {
49 "Array division divisor element cannot be zero.");
50 }
51
52 return op(left, right);
53 };
54
55 using result_value_type =
56 stratax::core::promote_t<
57 typename L::value_type,
58 typename R::value_type>;
59
60 return stratax::core::broadcasted_op<result_value_type>(
61 lhs,
62 rhs,
63 checked_op);
64}
65
84template<Array A, Numeric Scalar, typename Op>
85auto binary_scalar_op(const A& lhs, const Scalar& rhs, Op op, bool check_zero_divisor = false)
86{
87 if (check_zero_divisor && rhs == Scalar{})
88 {
89 throw Exceptions::ZeroDivisionError("Array division scalar divisor cannot be zero.");
90 }
91
92 return broadcasted_op(lhs, rhs, op);
93}
94
113template<Numeric Scalar, Array A, typename Op>
114auto binary_scalar_op(const Scalar& lhs, const A& rhs, Op op, bool check_zero_divisor = false)
115{
116 auto checked_op = [&](const auto& left, const auto& right)
117 {
118 if (check_zero_divisor && right == typename A::value_type{})
119 {
120 throw Exceptions::ZeroDivisionError("Scalar division divisor element cannot be zero.");
121 }
122
123 return op(left, right);
124 };
125
126 return broadcasted_op(lhs, rhs, checked_op);
127}
128
151template<Array L, Array R, typename Op>
152requires (
155)
156L& compound_op(
157 L& lhs,
158 const R& rhs,
159 Op op,
160 bool check_zero_divisor = false)
161{
162 const auto result_shape =
163 broadcasted_shape(lhs.shape(), rhs.shape());
164
165 // Compound assignment cannot change the lhs shape.
166 if (result_shape != lhs.shape())
167 {
169 "Compound assignment cannot change the left-hand shape.");
170 }
171
172 for (std::size_t i = 0; i < lhs.size(); ++i)
173 {
174 const std::size_t rhs_index =
175 stratax::core::broadcast_detail::flat_operand_index(
176 i,
177 lhs.shape(),
178 rhs.shape());
179
180 if (check_zero_divisor &&
181 rhs[rhs_index] == typename R::value_type{})
182 {
184 "Compound division divisor element cannot be zero.");
185 }
186
187 lhs[i] = static_cast<typename L::value_type>(
188 op(lhs[i], rhs[rhs_index]));
189 }
190
191 return lhs;
192}
193
212template<Array A, Numeric S, typename Op>
213A& compound_scalar_op(
214 A& lhs,
215 const S& rhs,
216 Op op,
217 bool check_zero_divisor = false)
218{
219 if (check_zero_divisor && rhs == S{})
220 {
222 "Compound division scalar divisor cannot be zero.");
223 }
224
225 for (std::size_t i = 0; i < lhs.size(); ++i)
226 {
227 lhs[i] = static_cast<typename A::value_type>(
228 op(lhs[i], rhs));
229 }
230
231 return lhs;
232}
233
235template<Array L, Array R>
236requires (
239)
240auto operator+(const L& lhs, const R& rhs)
241{
242 return binary_op(lhs, rhs, std::plus<>{});
243}
244
246template<Array L, Array R>
247requires (
250)
251auto operator-(const L& lhs, const R& rhs)
252{
253 return binary_op(lhs, rhs, std::minus<>{});
254}
255
257template<Array L, Array R>
258requires (
261)
262auto operator*(const L& lhs, const R& rhs)
263{
264 return binary_op(lhs, rhs, std::multiplies<>{});
265}
266
268template<Array L, Array R>
269requires (
272)
273auto operator/(const L& lhs, const R& rhs)
274{
275 return binary_op(lhs, rhs, std::divides<>{}, true);
276}
277
279template<Array A, Numeric Scalar>
281auto operator+(const A& lhs, const Scalar& rhs)
282{
283 return binary_scalar_op(lhs, rhs, std::plus<>{});
284}
285
287template<Array A, Numeric Scalar>
289auto operator-(const A& lhs, const Scalar& rhs)
290{
291 return binary_scalar_op(lhs, rhs, std::minus<>{});
292}
293
295template<Array A, Numeric Scalar>
297auto operator*(const A& lhs, const Scalar& rhs)
298{
299 return binary_scalar_op(lhs, rhs, std::multiplies<>{});
300}
301
303template<Array A, Numeric Scalar>
305auto operator/(const A& lhs, const Scalar& rhs)
306{
307 return binary_scalar_op(lhs, rhs, std::divides<>{}, true);
308}
309
311template<Numeric Scalar, Array A>
313auto operator+(const Scalar& lhs, const A& rhs)
314{
315 return rhs + lhs;
316}
317
319template<Numeric Scalar, Array A>
320auto operator-(const Scalar& lhs, const A& rhs)
321{
322 return binary_scalar_op(lhs, rhs, std::minus<>{});
323}
324
326template<Numeric Scalar, Array A>
328auto operator*(const Scalar& lhs, const A& rhs)
329{
330 return rhs * lhs;
331}
332
334template<Numeric Scalar, Array A>
336auto operator/(const Scalar& lhs, const A& rhs)
337{
338 return binary_scalar_op(lhs, rhs, std::divides<>{}, true);
339}
340
342template<Array L, Array R>
343requires (
346)
347L& operator+=(L& lhs, const R& rhs)
348{
349 return compound_op(lhs, rhs, std::plus<>{});
350}
351
353template<Array L, Array R>
354requires (
357)
358L& operator-=(L& lhs, const R& rhs)
359{
360 return compound_op(lhs, rhs, std::minus<>{});
361}
362
364template<Array L, Array R>
365requires (
368)
369L& operator*=(L& lhs, const R& rhs)
370{
371 return compound_op(lhs, rhs, std::multiplies<>{});
372}
373
375template<Array L, Array R>
376requires (
379)
380L& operator/=(L& lhs, const R& rhs)
381{
382 return compound_op(lhs, rhs, std::divides<>{}, true);
383}
384
386template<Array A, Numeric S>
388A& operator+=(A& lhs, const S& rhs)
389{
390 return compound_scalar_op(lhs, rhs, std::plus<>{});
391}
392
394template<Array A, Numeric S>
396A& operator-=(A& lhs, const S& rhs)
397{
398 return compound_scalar_op(lhs, rhs, std::minus<>{});
399}
400
402template<Array A, Numeric S>
404A& operator*=(A& lhs, const S& rhs)
405{
406 return compound_scalar_op(lhs, rhs, std::multiplies<>{});
407}
408
410template<Array A, Numeric S>
412A& operator/=(A& lhs, const S& rhs)
413{
414 return compound_scalar_op(lhs, rhs, std::divides<>{}, true);
415}
416
417template<Array A>
419A operator-(const A& arr)
420{
421 return arr * typename A::value_type{-1};
422}
423
424template<Array A>
426A operator+(const A& arr)
427{
428 return arr;
429}