Skip to main content

ferray_ma/
mask_ops.rs

1// ferray-ma: Mask manipulation utilities (REQ-15, REQ-16, REQ-17)
2//
3// harden_mask, soften_mask, getmask, getdata, is_masked, count_masked
4
5use ferray_core::Array;
6use ferray_core::dimension::Dimension;
7use ferray_core::dtype::Element;
8use ferray_core::error::FerrayResult;
9
10use crate::MaskedArray;
11
12impl<T: Element, D: Dimension> MaskedArray<T, D> {
13    /// Harden the mask: prevent subsequent assignments from clearing mask bits.
14    ///
15    /// After this call, any attempt to set a mask bit to `false` via
16    /// `set_mask_flat` or `set_mask` will be silently ignored.
17    ///
18    /// # Errors
19    /// This function does not currently error but returns `Result` for API
20    /// consistency.
21    pub const fn harden_mask(&mut self) -> FerrayResult<()> {
22        self.hard_mask = true;
23        Ok(())
24    }
25
26    /// Soften the mask: allow subsequent assignments to clear mask bits.
27    ///
28    /// # Errors
29    /// This function does not currently error but returns `Result` for API
30    /// consistency.
31    pub const fn soften_mask(&mut self) -> FerrayResult<()> {
32        self.hard_mask = false;
33        Ok(())
34    }
35}
36
37/// Return the mask array of a masked array.
38///
39/// This is equivalent to `ma.mask()` but provided as a free function
40/// for API parity with `NumPy`'s `np.ma.getmask`.
41///
42/// # Errors
43/// This function does not currently error but returns `Result` for API
44/// consistency.
45pub fn getmask<T: Element, D: Dimension>(ma: &MaskedArray<T, D>) -> FerrayResult<Array<bool, D>> {
46    Ok(ma.mask().clone())
47}
48
49/// Return the underlying data array of a masked array.
50///
51/// This is equivalent to `ma.data()` but provided as a free function
52/// for API parity with `NumPy`'s `np.ma.getdata`.
53///
54/// # Errors
55/// This function does not currently error but returns `Result` for API
56/// consistency.
57pub fn getdata<T: Element + Copy, D: Dimension>(
58    ma: &MaskedArray<T, D>,
59) -> FerrayResult<Array<T, D>> {
60    Ok(ma.data().clone())
61}
62
63/// Return `true` if any element in the masked array is masked.
64///
65/// # Errors
66/// This function does not currently error but returns `Result` for API
67/// consistency.
68pub fn is_masked<T: Element, D: Dimension>(ma: &MaskedArray<T, D>) -> FerrayResult<bool> {
69    Ok(ma.mask().iter().any(|m| *m))
70}
71
72/// Count the number of masked elements, optionally along an axis.
73///
74/// If `axis` is `None`, returns the total count of masked elements as a
75/// single-element vector. If an axis is specified, this function currently
76/// returns the total count (axis-specific counting for multi-dimensional
77/// masked arrays is a future enhancement).
78///
79/// # Errors
80/// This function does not currently error but returns `Result` for API
81/// consistency.
82pub fn count_masked<T: Element, D: Dimension>(
83    ma: &MaskedArray<T, D>,
84    _axis: Option<usize>,
85) -> FerrayResult<usize> {
86    let count = ma.mask().iter().filter(|m| **m).count();
87    Ok(count)
88}