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 {
48 throw Exceptions::ZeroDivisionError("Division by zero.");
49 }
50
51 return op(left, right);
52 };
53
54 using result_value_type =
55 stratax::core::promote_t<
56 typename L::value_type,
57 typename R::value_type>;
58
59 return stratax::core::broadcasted_op<result_value_type>(
60 lhs,
61 rhs,
62 checked_op);
63}
64
83template<Array A, Numeric Scalar, typename Op>
84auto binary_scalar_op(const A& lhs, const Scalar& rhs, Op op, bool check_zero_divisor = false)
85{
86 if (check_zero_divisor && rhs == Scalar{})
87 {
88 throw Exceptions::ZeroDivisionError("Division by zero.");
89 }
90
91 return broadcasted_op(lhs, rhs, op);
92}
93
112template<Numeric Scalar, Array A, typename Op>
113auto binary_scalar_op(const Scalar& lhs, const A& rhs, Op op, bool check_zero_divisor = false)
114{
115 auto checked_op = [&](const auto& left, const auto& right)
116 {
117 if (check_zero_divisor && right == typename A::value_type{})
118 {
119 throw Exceptions::ZeroDivisionError("Division by zero.");
120 }
121
122 return op(left, right);
123 };
124
125 return broadcasted_op(lhs, rhs, checked_op);
126}
127
150template<Array L, Array R, typename Op>
151requires (
154)
155L& compound_op(
156 L& lhs,
157 const R& rhs,
158 Op op,
159 bool check_zero_divisor = false)
160{
161 const auto result_shape =
162 broadcasted_shape(lhs.shape(), rhs.shape());
163
164 // Compound assignment cannot change the lhs shape.
165 if (result_shape != lhs.shape())
166 {
168 "In-place broadcasting cannot change the left operand's shape.");
169 }
170
171 for (std::size_t i = 0; i < lhs.size(); ++i)
172 {
173 const std::size_t rhs_index =
174 stratax::core::broadcast_detail::flat_operand_index(
175 i,
176 lhs.shape(),
177 rhs.shape());
178
179 if (check_zero_divisor &&
180 rhs[rhs_index] == typename R::value_type{})
181 {
182 throw Exceptions::ZeroDivisionError("Division by zero.");
183 }
184
185 lhs[i] = static_cast<typename L::value_type>(
186 op(lhs[i], rhs[rhs_index]));
187 }
188
189 return lhs;
190}
191
210template<Array A, Numeric S, typename Op>
211A& compound_scalar_op(
212 A& lhs,
213 const S& rhs,
214 Op op,
215 bool check_zero_divisor = false)
216{
217 if (check_zero_divisor && rhs == S{})
218 {
219 throw Exceptions::ZeroDivisionError("Division by zero.");
220 }
221
222 for (std::size_t i = 0; i < lhs.size(); ++i)
223 {
224 lhs[i] = static_cast<typename A::value_type>(
225 op(lhs[i], rhs));
226 }
227
228 return lhs;
229}
230
232template<Array L, Array R>
233requires (
236)
237auto operator+(const L& lhs, const R& rhs)
238{
239 return binary_op(lhs, rhs, std::plus<>{});
240}
241
243template<Array L, Array R>
244requires (
247)
248auto operator-(const L& lhs, const R& rhs)
249{
250 return binary_op(lhs, rhs, std::minus<>{});
251}
252
254template<Array L, Array R>
255requires (
258)
259auto operator*(const L& lhs, const R& rhs)
260{
261 return binary_op(lhs, rhs, std::multiplies<>{});
262}
263
265template<Array L, Array R>
266requires (
269)
270auto operator/(const L& lhs, const R& rhs)
271{
272 return binary_op(lhs, rhs, std::divides<>{}, true);
273}
274
276template<Array A, Numeric Scalar>
278auto operator+(const A& lhs, const Scalar& rhs)
279{
280 return binary_scalar_op(lhs, rhs, std::plus<>{});
281}
282
284template<Array A, Numeric Scalar>
286auto operator-(const A& lhs, const Scalar& rhs)
287{
288 return binary_scalar_op(lhs, rhs, std::minus<>{});
289}
290
292template<Array A, Numeric Scalar>
294auto operator*(const A& lhs, const Scalar& rhs)
295{
296 return binary_scalar_op(lhs, rhs, std::multiplies<>{});
297}
298
300template<Array A, Numeric Scalar>
302auto operator/(const A& lhs, const Scalar& rhs)
303{
304 return binary_scalar_op(lhs, rhs, std::divides<>{}, true);
305}
306
308template<Numeric Scalar, Array A>
310auto operator+(const Scalar& lhs, const A& rhs)
311{
312 return rhs + lhs;
313}
314
316template<Numeric Scalar, Array A>
317auto operator-(const Scalar& lhs, const A& rhs)
318{
319 return binary_scalar_op(lhs, rhs, std::minus<>{});
320}
321
323template<Numeric Scalar, Array A>
325auto operator*(const Scalar& lhs, const A& rhs)
326{
327 return rhs * lhs;
328}
329
331template<Numeric Scalar, Array A>
333auto operator/(const Scalar& lhs, const A& rhs)
334{
335 return binary_scalar_op(lhs, rhs, std::divides<>{}, true);
336}
337
339template<Array L, Array R>
340requires (
343)
344L& operator+=(L& lhs, const R& rhs)
345{
346 return compound_op(lhs, rhs, std::plus<>{});
347}
348
350template<Array L, Array R>
351requires (
354)
355L& operator-=(L& lhs, const R& rhs)
356{
357 return compound_op(lhs, rhs, std::minus<>{});
358}
359
361template<Array L, Array R>
362requires (
365)
366L& operator*=(L& lhs, const R& rhs)
367{
368 return compound_op(lhs, rhs, std::multiplies<>{});
369}
370
372template<Array L, Array R>
373requires (
376)
377L& operator/=(L& lhs, const R& rhs)
378{
379 return compound_op(lhs, rhs, std::divides<>{}, true);
380}
381
383template<Array A, Numeric S>
385A& operator+=(A& lhs, const S& rhs)
386{
387 return compound_scalar_op(lhs, rhs, std::plus<>{});
388}
389
391template<Array A, Numeric S>
393A& operator-=(A& lhs, const S& rhs)
394{
395 return compound_scalar_op(lhs, rhs, std::minus<>{});
396}
397
399template<Array A, Numeric S>
401A& operator*=(A& lhs, const S& rhs)
402{
403 return compound_scalar_op(lhs, rhs, std::multiplies<>{});
404}
405
407template<Array A, Numeric S>
409A& operator/=(A& lhs, const S& rhs)
410{
411 return compound_scalar_op(lhs, rhs, std::divides<>{}, true);
412}
413
414template<Array A>
416A operator-(const A& arr)
417{
418 return arr * typename A::value_type{-1};
419}
420
421template<Array A>
423A operator+(const A& arr)
424{
425 return arr;
426}