Stratax 0.4.0rc1
Loading...
Searching...
No Matches
Arithmetic.hpp
Go to the documentation of this file.
1
13#pragma once
14
20
21#include <functional>
22
48template<Array L, Array R, typename Op>
49requires (
52)
54 const L& lhs,
55 const R& rhs,
56 Op op,
57 bool check_zero_divisor = false)
58{
59 auto checked_op = [&](const auto& left, const auto& right)
60 {
61 if (check_zero_divisor)
62 {
63 stratax::core::validation::require_valid_division(left, right);
64 }
65
66 return op(left, right);
67 };
68
69 using result_value_type =
71 typename L::value_type,
72 typename R::value_type>;
73
74 return stratax::core::broadcasted_op<result_value_type>(
75 lhs,
76 rhs,
77 checked_op);
78}
79
99template<Array A, Numeric Scalar, typename Op>
100auto binary_scalar_op(const A& lhs, const Scalar& rhs, Op op, bool check_zero_divisor = false)
101{
102 if (check_zero_divisor && rhs == Scalar{})
103 {
104 throw Exceptions::ZeroDivisionError("Division by zero.");
105 }
106
107 auto checked_op = [&](const auto& left, const auto& right)
108 {
109 if (check_zero_divisor)
110 {
111 stratax::core::validation::require_valid_division(left, right);
112 }
113 return op(left, right);
114 };
115 return broadcasted_op(lhs, rhs, checked_op);
116}
117
137template<Numeric Scalar, Array A, typename Op>
138auto binary_scalar_op(const Scalar& lhs, const A& rhs, Op op, bool check_zero_divisor = false)
139{
140 auto checked_op = [&](const auto& left, const auto& right)
141 {
142 if (check_zero_divisor)
143 {
144 stratax::core::validation::require_valid_division(left, right);
145 }
146
147 return op(left, right);
148 };
149
150 return broadcasted_op(lhs, rhs, checked_op);
151}
152
180template<Array L, Array R, typename Op>
181requires (
184)
186 L& lhs,
187 const R& rhs,
188 Op op,
189 bool check_zero_divisor = false)
190{
191 const auto result_shape =
192 stratax::core::broadcast_detail::array_result_shape(lhs, rhs);
193
194 // Compound assignment cannot change the lhs shape.
195 if (result_shape != lhs.shape())
196 {
198 "In-place broadcasting cannot change the left operand's shape.");
199 }
200
201 // Validate all used divisors before changing any elements.
202 if (check_zero_divisor)
203 {
204 for (std::size_t i = 0; i < lhs.size(); ++i)
205 {
206 const auto rhs_index = stratax::core::broadcast_detail::flat_operand_index(
207 i, lhs.shape(), rhs.shape());
208 stratax::core::validation::require_valid_division(lhs[i], rhs[rhs_index]);
209 }
210 }
211
212 constexpr bool stage = !stratax::core::compound_detail::owns_storage<L> ||
213 !stratax::core::compound_detail::owns_storage<R>;
214 return stratax::core::compound_detail::write_results<stage>(lhs, [&](std::size_t i)
215 {
216 const auto rhs_index = stratax::core::broadcast_detail::flat_operand_index(
217 i, lhs.shape(), rhs.shape());
218 return op(lhs[i], rhs[rhs_index]);
219 });
220}
221
245template<Array A, Numeric S, typename Op>
247 A& lhs,
248 const S& rhs,
249 Op op,
250 bool check_zero_divisor = false)
251{
252 const S scalar = rhs;
253 if (check_zero_divisor)
254 {
255 if (scalar == S{})
256 {
257 throw Exceptions::ZeroDivisionError("Division by zero.");
258 }
259 for (std::size_t i = 0; i < lhs.size(); ++i)
260 {
261 stratax::core::validation::require_valid_division(lhs[i], scalar);
262 }
263 }
264
265 return stratax::core::compound_detail::write_results<
266 !stratax::core::compound_detail::owns_storage<A>>(lhs, [&](std::size_t i)
267 {
268 return op(lhs[i], scalar);
269 });
270}
271
273template<Array L, Array R>
274requires (
277)
278auto operator+(const L& lhs, const R& rhs)
279{
280 return binary_op(lhs, rhs, std::plus<>{});
281}
282
284template<Array L, Array R>
285requires (
288)
289auto operator-(const L& lhs, const R& rhs)
290{
291 return binary_op(lhs, rhs, std::minus<>{});
292}
293
295template<Array L, Array R>
296requires (
299)
300auto operator*(const L& lhs, const R& rhs)
301{
302 return binary_op(lhs, rhs, std::multiplies<>{});
303}
304
306template<Array L, Array R>
307requires (
310)
311auto operator/(const L& lhs, const R& rhs)
312{
313 return binary_op(lhs, rhs, std::divides<>{}, true);
314}
315
317template<Array A, Numeric Scalar>
319auto operator+(const A& lhs, const Scalar& rhs)
320{
321 return binary_scalar_op(lhs, rhs, std::plus<>{});
322}
323
325template<Array A, Numeric Scalar>
327auto operator-(const A& lhs, const Scalar& rhs)
328{
329 return binary_scalar_op(lhs, rhs, std::minus<>{});
330}
331
333template<Array A, Numeric Scalar>
335auto operator*(const A& lhs, const Scalar& rhs)
336{
337 return binary_scalar_op(lhs, rhs, std::multiplies<>{});
338}
339
341template<Array A, Numeric Scalar>
343auto operator/(const A& lhs, const Scalar& rhs)
344{
345 return binary_scalar_op(lhs, rhs, std::divides<>{}, true);
346}
347
349template<Numeric Scalar, Array A>
351auto operator+(const Scalar& lhs, const A& rhs)
352{
353 return rhs + lhs;
354}
355
357template<Numeric Scalar, Array A>
358auto operator-(const Scalar& lhs, const A& rhs)
359{
360 return binary_scalar_op(lhs, rhs, std::minus<>{});
361}
362
364template<Numeric Scalar, Array A>
366auto operator*(const Scalar& lhs, const A& rhs)
367{
368 return rhs * lhs;
369}
370
372template<Numeric Scalar, Array A>
374auto operator/(const Scalar& lhs, const A& rhs)
375{
376 return binary_scalar_op(lhs, rhs, std::divides<>{}, true);
377}
378
380template<Array L, Array R>
381requires (
384)
385L& operator+=(L& lhs, const R& rhs)
386{
387 return compound_op(lhs, rhs, std::plus<>{});
388}
389
391template<Array L, Array R>
392requires (
395)
396L& operator-=(L& lhs, const R& rhs)
397{
398 return compound_op(lhs, rhs, std::minus<>{});
399}
400
402template<Array L, Array R>
403requires (
406)
407L& operator*=(L& lhs, const R& rhs)
408{
409 return compound_op(lhs, rhs, std::multiplies<>{});
410}
411
413template<Array L, Array R>
414requires (
417)
418L& operator/=(L& lhs, const R& rhs)
419{
420 return compound_op(lhs, rhs, std::divides<>{}, true);
421}
422
424template<Array A, Numeric S>
426A& operator+=(A& lhs, const S& rhs)
427{
428 return compound_scalar_op(lhs, rhs, std::plus<>{});
429}
430
432template<Array A, Numeric S>
434A& operator-=(A& lhs, const S& rhs)
435{
436 return compound_scalar_op(lhs, rhs, std::minus<>{});
437}
438
440template<Array A, Numeric S>
442A& operator*=(A& lhs, const S& rhs)
443{
444 return compound_scalar_op(lhs, rhs, std::multiplies<>{});
445}
446
448template<Array A, Numeric S>
450A& operator/=(A& lhs, const S& rhs)
451{
452 return compound_scalar_op(lhs, rhs, std::divides<>{}, true);
453}
454
460template<Array A>
462A operator-(const A& arr)
463{
464 return arr * typename A::value_type{-1};
465}
466
471template<Array A>
473A operator+(const A& arr)
474{
475 return arr;
476}
L & compound_op(L &lhs, const R &rhs, Op op, bool check_zero_divisor=false)
Applies an arithmetic callable in place using array broadcasting.
auto operator*(const L &lhs, const R &rhs)
Multiplies two broadcast-compatible arrays element-wise.
L & operator/=(L &lhs, const R &rhs)
Divides by a broadcast-compatible array in place.
L & operator-=(L &lhs, const R &rhs)
Subtracts a broadcast-compatible array in place.
L & operator*=(L &lhs, const R &rhs)
Multiplies by a broadcast-compatible array in place.
auto operator+(const L &lhs, const R &rhs)
Adds two broadcast-compatible arrays.
L & operator+=(L &lhs, const R &rhs)
Adds a broadcast-compatible array in place.
auto binary_op(const L &lhs, const R &rhs, Op op, bool check_zero_divisor=false)
Applies an arithmetic callable to two broadcast-compatible arrays.
auto operator-(const L &lhs, const R &rhs)
Subtracts two broadcast-compatible arrays.
auto operator/(const L &lhs, const R &rhs)
Divides two broadcast-compatible arrays element-wise.
auto binary_scalar_op(const A &lhs, const Scalar &rhs, Op op, bool check_zero_divisor=false)
Applies an arithmetic callable to an array and a right scalar. The returned array preserves the conta...
A & compound_scalar_op(A &lhs, const S &rhs, Op op, bool check_zero_divisor=false)
Applies an arithmetic callable in place with a right scalar.
Trailing-axis shape compatibility and element projection.
auto broadcasted_op(const L &lhs, const S &rhs, Op op)
Applies a binary callable to every array element and a right scalar.
Internal result staging for compound operations on aliased arrays.
Dtype and array interface concepts.
Stratax exception types derived from std::runtime_error.
Shared validation for division operands and integral shift counts.
typename Promote< std::remove_cvref_t< L >, std::remove_cvref_t< R > >::type promote_t
Convenience alias for the promoted dtype of two types.
Operands cannot broadcast to the requested result.
An explicitly checked divisor is zero.
Supported integer, floating-point, or complex dtype, excluding bool.
Definition Concepts.hpp:67