use super::MaskedArray;
use crate::error::Result;
use num_traits::Zero;
use std::ops::{Add, Div, Mul, Sub};
impl<T: Clone + Add<Output = T>> Add for &MaskedArray<T> {
type Output = MaskedArray<T>;
fn add(self, other: &MaskedArray<T>) -> MaskedArray<T> {
let result_data = match self.data.add_broadcast(&other.data) {
Ok(res) => res,
Err(_) => panic!(
"Failed to add MaskedArrays with incompatible shapes: {:?} vs {:?}",
self.data.shape(),
other.data.shape()
),
};
let mask_combined = match self.mask.zip_with(&other.mask, |a, b| a || b) {
Ok(res) => res,
Err(_) => panic!(
"Failed to combine masks with incompatible shapes: {:?} vs {:?}",
self.mask.shape(),
other.mask.shape()
),
};
MaskedArray {
data: result_data,
mask: mask_combined,
fill_value: self.fill_value.clone(),
}
}
}
impl<T: Clone + Sub<Output = T>> Sub for &MaskedArray<T> {
type Output = MaskedArray<T>;
fn sub(self, other: &MaskedArray<T>) -> MaskedArray<T> {
let result_data = match self.data.subtract_broadcast(&other.data) {
Ok(res) => res,
Err(_) => panic!(
"Failed to subtract MaskedArrays with incompatible shapes: {:?} vs {:?}",
self.data.shape(),
other.data.shape()
),
};
let mask_combined = match self.mask.zip_with(&other.mask, |a, b| a || b) {
Ok(res) => res,
Err(_) => panic!(
"Failed to combine masks with incompatible shapes: {:?} vs {:?}",
self.mask.shape(),
other.mask.shape()
),
};
MaskedArray {
data: result_data,
mask: mask_combined,
fill_value: self.fill_value.clone(),
}
}
}
impl<T: Clone + Mul<Output = T>> Mul for &MaskedArray<T> {
type Output = MaskedArray<T>;
fn mul(self, other: &MaskedArray<T>) -> MaskedArray<T> {
let result_data = match self.data.multiply_broadcast(&other.data) {
Ok(res) => res,
Err(_) => panic!(
"Failed to multiply MaskedArrays with incompatible shapes: {:?} vs {:?}",
self.data.shape(),
other.data.shape()
),
};
let mask_combined = match self.mask.zip_with(&other.mask, |a, b| a || b) {
Ok(res) => res,
Err(_) => panic!(
"Failed to combine masks with incompatible shapes: {:?} vs {:?}",
self.mask.shape(),
other.mask.shape()
),
};
MaskedArray {
data: result_data,
mask: mask_combined,
fill_value: self.fill_value.clone(),
}
}
}
impl<T: Clone + Div<Output = T> + PartialEq + Zero> Div for &MaskedArray<T> {
type Output = MaskedArray<T>;
fn div(self, other: &MaskedArray<T>) -> MaskedArray<T> {
let zero = T::zero();
let other_data_op = crate::kernels::borrow::operand(&other.data);
let other_mask_op = crate::kernels::borrow::operand(&other.mask);
let mut division_mask_vec = Vec::with_capacity(other.size());
for (value, is_masked) in other_data_op.iter().zip(other_mask_op.iter()) {
division_mask_vec.push(*is_masked || *value == zero);
}
let division_mask = crate::array::Array::from_vec_shape(division_mask_vec, &other.shape())
.unwrap_or_else(|e| panic!("{e}"));
let result_data = match self.data.divide_broadcast(&other.data) {
Ok(res) => res,
Err(_) => panic!(
"Failed to divide MaskedArrays with incompatible shapes: {:?} vs {:?}",
self.data.shape(),
other.data.shape()
),
};
let mask_combined = match self.mask.zip_with(&division_mask, |a, b| a || b) {
Ok(res) => res,
Err(_) => panic!(
"Failed to combine masks with incompatible shapes: {:?} vs {:?}",
self.mask.shape(),
division_mask.shape()
),
};
MaskedArray {
data: result_data,
mask: mask_combined,
fill_value: self.fill_value.clone(),
}
}
}
impl<T: Clone + PartialOrd> MaskedArray<T> {
pub fn equal(&self, other: &Self) -> Result<MaskedArray<bool>> {
let data = self.data.equal(&other.data)?;
let mask = self.mask.zip_with(&other.mask, |a, b| a || b)?;
Ok(MaskedArray {
data,
mask,
fill_value: false,
})
}
pub fn not_equal(&self, other: &Self) -> Result<MaskedArray<bool>> {
let data = self.data.not_equal(&other.data)?;
let mask = self.mask.zip_with(&other.mask, |a, b| a || b)?;
Ok(MaskedArray {
data,
mask,
fill_value: false,
})
}
pub fn less_than(&self, other: &Self) -> Result<MaskedArray<bool>> {
let data = self.data.less_than(&other.data)?;
let mask = self.mask.zip_with(&other.mask, |a, b| a || b)?;
Ok(MaskedArray {
data,
mask,
fill_value: false,
})
}
pub fn less_equal(&self, other: &Self) -> Result<MaskedArray<bool>> {
let data = self.data.less_equal(&other.data)?;
let mask = self.mask.zip_with(&other.mask, |a, b| a || b)?;
Ok(MaskedArray {
data,
mask,
fill_value: false,
})
}
pub fn greater_than(&self, other: &Self) -> Result<MaskedArray<bool>> {
let data = self.data.greater_than(&other.data)?;
let mask = self.mask.zip_with(&other.mask, |a, b| a || b)?;
Ok(MaskedArray {
data,
mask,
fill_value: false,
})
}
pub fn greater_equal(&self, other: &Self) -> Result<MaskedArray<bool>> {
let data = self.data.greater_equal(&other.data)?;
let mask = self.mask.zip_with(&other.mask, |a, b| a || b)?;
Ok(MaskedArray {
data,
mask,
fill_value: false,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::array::Array;
fn ma(data: Vec<f64>, mask: Vec<bool>) -> MaskedArray<f64> {
let shape = vec![data.len()];
MaskedArray {
data: Array::from_vec_shape(data, &shape).expect("valid shape"),
mask: Array::from_vec_shape(mask, &shape).expect("valid shape"),
fill_value: 0.0,
}
}
#[test]
fn div_masks_division_by_zero_even_when_unmasked() {
let a = ma(vec![1.0, 2.0, 3.0], vec![false, false, false]);
let b = ma(vec![0.0, 2.0, 0.0], vec![false, false, false]);
let r = &a / &b;
assert_eq!(r.get_mask().to_vec(), vec![true, false, true]);
assert_eq!(r.filled(Some(-1.0)).to_vec()[1], 1.0);
}
#[test]
fn sub_propagates_mask_as_or() {
let a = ma(vec![1.0, 2.0, 3.0], vec![false, true, false]);
let b = ma(vec![5.0, 4.0, 3.0], vec![false, false, true]);
let r = &a - &b;
assert_eq!(r.get_mask().to_vec(), vec![false, true, true]);
assert_eq!(r.filled(None).to_vec()[0], -4.0);
}
#[test]
fn equal_propagates_mask_and_compares_raw_data() {
let a = ma(vec![1.0, 2.0, 3.0, 4.0], vec![false, true, false, false]);
let b = ma(vec![1.0, 5.0, 3.0, 2.0], vec![false, false, true, false]);
let eq = a.equal(&b).expect("shapes match");
assert_eq!(eq.get_mask().to_vec(), vec![false, true, true, false]);
assert_eq!(eq.get_data().to_vec(), vec![true, false, true, false]);
}
#[test]
fn less_than_propagates_mask() {
let a = ma(vec![1.0, 2.0, 3.0, 4.0], vec![false, true, false, false]);
let b = ma(vec![1.0, 5.0, 3.0, 2.0], vec![false, false, true, false]);
let lt = a.less_than(&b).expect("shapes match");
assert_eq!(lt.get_mask().to_vec(), vec![false, true, true, false]);
assert_eq!(lt.get_data().to_vec(), vec![false, true, false, false]);
}
#[test]
fn not_equal_le_gt_ge_all_propagate_mask_or() {
let a = ma(vec![1.0, 2.0], vec![true, false]);
let b = ma(vec![1.0, 3.0], vec![false, false]);
let expected = vec![true, false];
assert_eq!(
a.not_equal(&b).expect("same shape").get_mask().to_vec(),
expected
);
assert_eq!(
a.less_equal(&b).expect("same shape").get_mask().to_vec(),
expected
);
assert_eq!(
a.greater_than(&b).expect("same shape").get_mask().to_vec(),
expected
);
assert_eq!(
a.greater_equal(&b).expect("same shape").get_mask().to_vec(),
expected
);
}
#[test]
fn comparison_shape_mismatch_is_an_error_not_a_panic() {
let a = ma(vec![1.0, 2.0], vec![false, false]);
let b = ma(vec![1.0, 2.0, 3.0], vec![false, false, false]);
assert!(a.equal(&b).is_err());
}
}