Skip to content

Commit 5e64627

Browse files
committed
Move out arithmetic ops
1 parent c7bdad8 commit 5e64627

File tree

2 files changed

+353
-347
lines changed

2 files changed

+353
-347
lines changed

src/impl_ops.rs

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
2+
use std::any::Any;
3+
use libnum::Complex;
4+
5+
/// Elements that can be used as direct operands in arithmetic with arrays.
6+
///
7+
/// For example, `f64` is a `ScalarOperand` which means that for an array `a`,
8+
/// arithmetic like `a + 1.0`, and, `a * 2.`, and `a += 3.` are allowed.
9+
///
10+
/// In the description below, let `A` be an array or array view,
11+
/// let `B` be an array with owned data,
12+
/// and let `C` be an array with mutable data.
13+
///
14+
/// `ScalarOperand` determines for which scalars `K` operations `&A @ K`, and `B @ K`,
15+
/// and `C @= K` are defined, as **right hand side** operands, for applicable
16+
/// arithmetic operators (denoted `@`).
17+
///
18+
/// **Left hand side** scalar operands are implemented differently
19+
/// (one `impl` per concrete scalar type); they are
20+
/// implemented for the default `ScalarOperand` types, allowing
21+
/// operations `K @ &A`, and `K @ B`.
22+
///
23+
/// This trait ***does not*** limit which elements can be stored in an array in general.
24+
/// Non-`ScalarOperand` types can still participate in arithmetic as array elements in
25+
/// in array-array operations.
26+
pub trait ScalarOperand : Any + Clone { }
27+
impl ScalarOperand for bool { }
28+
impl ScalarOperand for i8 { }
29+
impl ScalarOperand for u8 { }
30+
impl ScalarOperand for i16 { }
31+
impl ScalarOperand for u16 { }
32+
impl ScalarOperand for i32 { }
33+
impl ScalarOperand for u32 { }
34+
impl ScalarOperand for i64 { }
35+
impl ScalarOperand for u64 { }
36+
impl ScalarOperand for f32 { }
37+
impl ScalarOperand for f64 { }
38+
impl ScalarOperand for Complex<f32> { }
39+
impl ScalarOperand for Complex<f64> { }
40+
41+
macro_rules! impl_binary_op(
42+
($trt:ident, $mth:ident, $imth:ident, $imth_scalar:ident, $doc:expr) => (
43+
/// Perform elementwise
44+
#[doc=$doc]
45+
/// between `self` and `rhs`,
46+
/// and return the result (based on `self`).
47+
///
48+
/// `self` must be an `OwnedArray` or `RcArray`.
49+
///
50+
/// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
51+
///
52+
/// **Panics** if broadcasting isn’t possible.
53+
impl<A, S, S2, D, E> $trt<ArrayBase<S2, E>> for ArrayBase<S, D>
54+
where A: Clone + $trt<A, Output=A>,
55+
S: DataOwned<Elem=A> + DataMut,
56+
S2: Data<Elem=A>,
57+
D: Dimension,
58+
E: Dimension,
59+
{
60+
type Output = ArrayBase<S, D>;
61+
fn $mth(self, rhs: ArrayBase<S2, E>) -> ArrayBase<S, D>
62+
{
63+
self.$mth(&rhs)
64+
}
65+
}
66+
67+
/// Perform elementwise
68+
#[doc=$doc]
69+
/// between `self` and reference `rhs`,
70+
/// and return the result (based on `self`).
71+
///
72+
/// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
73+
///
74+
/// **Panics** if broadcasting isn’t possible.
75+
impl<'a, A, S, S2, D, E> $trt<&'a ArrayBase<S2, E>> for ArrayBase<S, D>
76+
where A: Clone + $trt<A, Output=A>,
77+
S: DataMut<Elem=A>,
78+
S2: Data<Elem=A>,
79+
D: Dimension,
80+
E: Dimension,
81+
{
82+
type Output = ArrayBase<S, D>;
83+
fn $mth (mut self, rhs: &ArrayBase<S2, E>) -> ArrayBase<S, D>
84+
{
85+
self.$imth(rhs);
86+
self
87+
}
88+
}
89+
90+
/// Perform elementwise
91+
#[doc=$doc]
92+
/// between references `self` and `rhs`,
93+
/// and return the result as a new `OwnedArray`.
94+
///
95+
/// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
96+
///
97+
/// **Panics** if broadcasting isn’t possible.
98+
impl<'a, A, S, S2, D, E> $trt<&'a ArrayBase<S2, E>> for &'a ArrayBase<S, D>
99+
where A: Clone + $trt<A, Output=A>,
100+
S: Data<Elem=A>,
101+
S2: Data<Elem=A>,
102+
D: Dimension,
103+
E: Dimension,
104+
{
105+
type Output = OwnedArray<A, D>;
106+
fn $mth (self, rhs: &'a ArrayBase<S2, E>) -> OwnedArray<A, D>
107+
{
108+
// FIXME: Can we co-broadcast arrays here? And how?
109+
self.to_owned().$mth(rhs)
110+
}
111+
}
112+
113+
/// Perform elementwise
114+
#[doc=$doc]
115+
/// between `self` and the scalar `x`,
116+
/// and return the result (based on `self`).
117+
///
118+
/// `self` must be an `OwnedArray` or `RcArray`.
119+
impl<A, S, D, B> $trt<B> for ArrayBase<S, D>
120+
where A: Clone + $trt<B, Output=A>,
121+
S: DataOwned<Elem=A> + DataMut,
122+
D: Dimension,
123+
B: ScalarOperand,
124+
{
125+
type Output = ArrayBase<S, D>;
126+
fn $mth (mut self, x: B) -> ArrayBase<S, D>
127+
{
128+
self.unordered_foreach_mut(move |elt| {
129+
*elt = elt.clone().$mth(x.clone());
130+
});
131+
self
132+
}
133+
}
134+
135+
/// Perform elementwise
136+
#[doc=$doc]
137+
/// between the reference `self` and the scalar `x`,
138+
/// and return the result as a new `OwnedArray`.
139+
impl<'a, A, S, D, B> $trt<B> for &'a ArrayBase<S, D>
140+
where A: Clone + $trt<B, Output=A>,
141+
S: Data<Elem=A>,
142+
D: Dimension,
143+
B: ScalarOperand,
144+
{
145+
type Output = OwnedArray<A, D>;
146+
fn $mth(self, x: B) -> OwnedArray<A, D>
147+
{
148+
self.to_owned().$mth(x)
149+
}
150+
}
151+
);
152+
);
153+
154+
macro_rules! impl_scalar_op {
155+
($scalar:ty, $trt:ident, $mth:ident, $doc:expr) => (
156+
// these have no doc -- they are not visible in rustdoc
157+
// Perform elementwise
158+
// between the scalar `self` and array `rhs`,
159+
// and return the result (based on `self`).
160+
impl<S, D> $trt<ArrayBase<S, D>> for $scalar
161+
where S: DataMut<Elem=$scalar>,
162+
D: Dimension,
163+
{
164+
type Output = ArrayBase<S, D>;
165+
fn $mth (self, mut rhs: ArrayBase<S, D>) -> ArrayBase<S, D>
166+
{
167+
rhs.unordered_foreach_mut(move |elt| {
168+
*elt = self.$mth(*elt);
169+
});
170+
rhs
171+
}
172+
}
173+
174+
// Perform elementwise
175+
// between the scalar `self` and array `rhs`,
176+
// and return the result as a new `OwnedArray`.
177+
impl<'a, S, D> $trt<&'a ArrayBase<S, D>> for $scalar
178+
where S: Data<Elem=$scalar>,
179+
D: Dimension,
180+
{
181+
type Output = OwnedArray<$scalar, D>;
182+
fn $mth (self, rhs: &ArrayBase<S, D>) -> OwnedArray<$scalar, D>
183+
{
184+
self.$mth(rhs.to_owned())
185+
}
186+
}
187+
);
188+
}
189+
190+
191+
mod arithmetic_ops {
192+
use super::*;
193+
use imp_prelude::*;
194+
195+
use std::ops::*;
196+
use libnum::Complex;
197+
198+
impl_binary_op!(Add, add, iadd, iadd_scalar, "addition");
199+
impl_binary_op!(Sub, sub, isub, isub_scalar, "subtraction");
200+
impl_binary_op!(Mul, mul, imul, imul_scalar, "multiplication");
201+
impl_binary_op!(Div, div, idiv, idiv_scalar, "division");
202+
impl_binary_op!(Rem, rem, irem, irem_scalar, "remainder");
203+
impl_binary_op!(BitAnd, bitand, ibitand, ibitand_scalar, "bit and");
204+
impl_binary_op!(BitOr, bitor, ibitor, ibitor_scalar, "bit or");
205+
impl_binary_op!(BitXor, bitxor, ibitxor, ibitxor_scalar, "bit xor");
206+
impl_binary_op!(Shl, shl, ishl, ishl_scalar, "left shift");
207+
impl_binary_op!(Shr, shr, ishr, ishr_scalar, "right shift");
208+
209+
macro_rules! all_scalar_ops {
210+
($int_scalar:ty) => (
211+
impl_scalar_op!($int_scalar, Add, add, "addition");
212+
impl_scalar_op!($int_scalar, Sub, sub, "subtraction");
213+
impl_scalar_op!($int_scalar, Mul, mul, "multiplication");
214+
impl_scalar_op!($int_scalar, Div, div, "division");
215+
impl_scalar_op!($int_scalar, Rem, rem, "remainder");
216+
impl_scalar_op!($int_scalar, BitAnd, bitand, "bit and");
217+
impl_scalar_op!($int_scalar, BitOr, bitor, "bit or");
218+
impl_scalar_op!($int_scalar, BitXor, bitxor, "bit xor");
219+
impl_scalar_op!($int_scalar, Shl, shl, "left shift");
220+
impl_scalar_op!($int_scalar, Shr, shr, "right shift");
221+
);
222+
}
223+
all_scalar_ops!(i8);
224+
all_scalar_ops!(u8);
225+
all_scalar_ops!(i16);
226+
all_scalar_ops!(u16);
227+
all_scalar_ops!(i32);
228+
all_scalar_ops!(u32);
229+
all_scalar_ops!(i64);
230+
all_scalar_ops!(u64);
231+
232+
impl_scalar_op!(bool, BitAnd, bitand, "bit and");
233+
impl_scalar_op!(bool, BitOr, bitor, "bit or");
234+
impl_scalar_op!(bool, BitXor, bitxor, "bit xor");
235+
236+
impl_scalar_op!(f32, Add, add, "addition");
237+
impl_scalar_op!(f32, Sub, sub, "subtraction");
238+
impl_scalar_op!(f32, Mul, mul, "multiplication");
239+
impl_scalar_op!(f32, Div, div, "division");
240+
impl_scalar_op!(f32, Rem, rem, "remainder");
241+
242+
impl_scalar_op!(f64, Add, add, "addition");
243+
impl_scalar_op!(f64, Sub, sub, "subtraction");
244+
impl_scalar_op!(f64, Mul, mul, "multiplication");
245+
impl_scalar_op!(f64, Div, div, "division");
246+
impl_scalar_op!(f64, Rem, rem, "remainder");
247+
248+
impl_scalar_op!(Complex<f32>, Add, add, "addition");
249+
impl_scalar_op!(Complex<f32>, Sub, sub, "subtraction");
250+
impl_scalar_op!(Complex<f32>, Mul, mul, "multiplication");
251+
impl_scalar_op!(Complex<f32>, Div, div, "division");
252+
253+
impl_scalar_op!(Complex<f64>, Add, add, "addition");
254+
impl_scalar_op!(Complex<f64>, Sub, sub, "subtraction");
255+
impl_scalar_op!(Complex<f64>, Mul, mul, "multiplication");
256+
impl_scalar_op!(Complex<f64>, Div, div, "division");
257+
258+
impl<A, S, D> Neg for ArrayBase<S, D>
259+
where A: Clone + Neg<Output=A>,
260+
S: DataMut<Elem=A>,
261+
D: Dimension
262+
{
263+
type Output = Self;
264+
/// Perform an elementwise negation of `self` and return the result.
265+
fn neg(mut self) -> Self {
266+
self.ineg();
267+
self
268+
}
269+
}
270+
271+
impl<A, S, D> Not for ArrayBase<S, D>
272+
where A: Clone + Not<Output=A>,
273+
S: DataMut<Elem=A>,
274+
D: Dimension
275+
{
276+
type Output = Self;
277+
/// Perform an elementwise unary not of `self` and return the result.
278+
fn not(mut self) -> Self {
279+
self.inot();
280+
self
281+
}
282+
}
283+
}
284+
285+
#[cfg(feature = "assign_ops")]
286+
mod assign_ops {
287+
use super::*;
288+
use imp_prelude::*;
289+
290+
macro_rules! impl_assign_op {
291+
($trt:ident, $method:ident, $doc:expr) => {
292+
use std::ops::$trt;
293+
294+
#[doc=$doc]
295+
/// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
296+
///
297+
/// **Panics** if broadcasting isn’t possible.
298+
///
299+
/// **Requires crate feature `"assign_ops"`**
300+
impl<'a, A, S, S2, D, E> $trt<&'a ArrayBase<S2, E>> for ArrayBase<S, D>
301+
where A: Clone + $trt<A>,
302+
S: DataMut<Elem=A>,
303+
S2: Data<Elem=A>,
304+
D: Dimension,
305+
E: Dimension,
306+
{
307+
fn $method(&mut self, rhs: &ArrayBase<S2, E>) {
308+
self.zip_mut_with(rhs, |x, y| {
309+
x.$method(y.clone());
310+
});
311+
}
312+
}
313+
314+
#[doc=$doc]
315+
/// **Requires crate feature `"assign_ops"`**
316+
impl<A, S, D> $trt<A> for ArrayBase<S, D>
317+
where A: ScalarOperand + $trt<A>,
318+
S: DataMut<Elem=A>,
319+
D: Dimension,
320+
{
321+
fn $method(&mut self, rhs: A) {
322+
self.unordered_foreach_mut(move |elt| {
323+
elt.$method(rhs.clone());
324+
});
325+
}
326+
}
327+
328+
};
329+
}
330+
331+
impl_assign_op!(AddAssign, add_assign,
332+
"Perform `self += rhs` as elementwise addition (in place).\n");
333+
impl_assign_op!(SubAssign, sub_assign,
334+
"Perform `self -= rhs` as elementwise subtraction (in place).\n");
335+
impl_assign_op!(MulAssign, mul_assign,
336+
"Perform `self *= rhs` as elementwise multiplication (in place).\n");
337+
impl_assign_op!(DivAssign, div_assign,
338+
"Perform `self /= rhs` as elementwise division (in place).\n");
339+
impl_assign_op!(RemAssign, rem_assign,
340+
"Perform `self %= rhs` as elementwise remainder (in place).\n");
341+
impl_assign_op!(BitAndAssign, bitand_assign,
342+
"Perform `self &= rhs` as elementwise bit and (in place).\n");
343+
impl_assign_op!(BitOrAssign, bitor_assign,
344+
"Perform `self |= rhs` as elementwise bit or (in place).\n");
345+
impl_assign_op!(BitXorAssign, bitxor_assign,
346+
"Perform `self ^= rhs` as elementwise bit xor (in place).\n");
347+
impl_assign_op!(ShlAssign, shl_assign,
348+
"Perform `self <<= rhs` as elementwise left shift (in place).\n");
349+
impl_assign_op!(ShrAssign, shr_assign,
350+
"Perform `self >>= rhs` as elementwise right shift (in place).\n");
351+
}

0 commit comments

Comments
 (0)