1#![allow(
42 clippy::cast_possible_truncation,
43 clippy::cast_possible_wrap,
44 clippy::cast_precision_loss,
45 clippy::cast_sign_loss,
46 clippy::cast_lossless,
47 clippy::float_cmp,
48 clippy::missing_errors_doc,
49 clippy::missing_panics_doc,
50 clippy::many_single_char_names,
51 clippy::similar_names,
52 clippy::items_after_statements,
53 clippy::option_if_let_else,
54 clippy::too_long_first_doc_paragraph,
55 clippy::needless_pass_by_value,
56 clippy::match_same_arms
57)]
58
59pub mod algorithms;
60pub mod arithmetic;
61pub mod constructors;
62pub mod extras;
63pub mod filled;
64pub mod interop;
65#[cfg(feature = "io")]
71pub mod io;
72pub mod manipulation;
73pub mod mask_ops;
74pub mod masked_array;
75pub mod put;
78pub mod reductions;
79pub mod sorting;
80pub mod ufunc_support;
81
82pub use masked_array::MaskedArray;
84
85pub use constructors::{
87 fix_invalid, masked_equal, masked_greater, masked_greater_equal, masked_inside, masked_invalid,
88 masked_less, masked_less_equal, masked_not_equal, masked_outside, masked_where,
89};
90
91pub use arithmetic::{
93 masked_add, masked_add_array, masked_div, masked_div_array, masked_mul, masked_mul_array,
94 masked_sub, masked_sub_array,
95};
96
97pub use mask_ops::{count_masked, count_masked_axis, getdata, getmask, is_masked};
99
100pub use put::PutMode;
104
105pub use interop::{MaskAware, ma_apply_unary};
108
109pub use ufunc_support::{masked_binary, masked_unary};
114
115pub use ufunc_support::{
118 arccos_domain, arccosh_domain, arcsin_domain, arctanh_domain, divide_domain, log_domain,
119 log2_domain, log10_domain, masked_binary_domain, masked_unary_domain, sqrt_domain,
120};
121
122pub use algorithms::{ma_choose, ma_diff, ma_ediff1d, ma_nonzero, ma_where};
126
127pub use extras::{
131 NOMASK, clump_masked, clump_unmasked, common_fill_value, default_fill_value_bool,
132 default_fill_value_f32, default_fill_value_f64, default_fill_value_i64,
133 flatnotmasked_contiguous, flatnotmasked_edges, getmaskarray, ids, is_ma, is_masked_array,
134 ma_apply_along_axis, ma_apply_over_axes, ma_compress_cols, ma_compress_rowcols,
135 ma_compress_rows, ma_concatenate, ma_corrcoef, ma_cov, ma_equal, ma_greater, ma_greater_equal,
136 ma_in1d, ma_intersect1d, ma_isin, ma_less, ma_less_equal, ma_logical_and, ma_logical_not,
137 ma_logical_or, ma_logical_xor, ma_mask_rowcols, ma_median_axis, ma_not_equal, ma_setdiff1d,
138 ma_setxor1d, ma_union1d, ma_unique, ma_unique_masked, ma_vander, make_mask, make_mask_none,
139 mask_or, masked_all, masked_all_like, masked_values, maximum_fill_value, minimum_fill_value,
140 notmasked_contiguous_axis, notmasked_edges, notmasked_edges_axis2,
141};
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use ferray_core::Array;
147 use ferray_core::dimension::Ix1;
148
149 #[test]
153 fn ac1_masked_mean_skips_masked() {
154 let data =
155 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![1.0, 2.0, 3.0, 4.0, 5.0]).unwrap();
156 let mask =
157 Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false, false, true, false, false])
158 .unwrap();
159 let ma = MaskedArray::new(data, mask).unwrap();
160 let mean = ma.mean().unwrap();
161 assert!((mean - 3.0).abs() < 1e-10);
163 }
164
165 #[test]
169 fn ac2_filled_replaces_masked() {
170 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
171 let mask =
172 Array::<bool, Ix1>::from_vec(Ix1::new([4]), vec![false, true, false, true]).unwrap();
173 let ma = MaskedArray::new(data, mask).unwrap();
174 let filled = ma.filled(0.0).unwrap();
175 assert_eq!(filled.as_slice().unwrap(), &[1.0, 0.0, 3.0, 0.0]);
176 }
177
178 #[test]
182 fn ac3_compressed_returns_unmasked() {
183 let data =
184 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![10.0, 20.0, 30.0, 40.0, 50.0]).unwrap();
185 let mask =
186 Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false, true, false, true, false])
187 .unwrap();
188 let ma = MaskedArray::new(data, mask).unwrap();
189 let compressed = ma.compressed().unwrap();
190 assert_eq!(compressed.as_slice().unwrap(), &[10.0, 30.0, 50.0]);
191 }
192
193 #[test]
197 fn ac4_masked_invalid_nan_inf() {
198 let data =
199 Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, f64::NAN, 3.0, f64::INFINITY])
200 .unwrap();
201 let ma = masked_invalid(&data).unwrap();
202 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
203 assert_eq!(mask_vals, vec![false, true, false, true]);
204 }
205
206 #[test]
210 fn ac5_add_mask_union() {
211 let d1 = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
212 let m1 =
213 Array::<bool, Ix1>::from_vec(Ix1::new([4]), vec![false, true, false, false]).unwrap();
214 let ma1 = MaskedArray::new(d1, m1).unwrap();
215
216 let d2 = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![10.0, 20.0, 30.0, 40.0]).unwrap();
217 let m2 =
218 Array::<bool, Ix1>::from_vec(Ix1::new([4]), vec![false, false, true, false]).unwrap();
219 let ma2 = MaskedArray::new(d2, m2).unwrap();
220
221 let result = masked_add(&ma1, &ma2).unwrap();
222 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
223 assert_eq!(mask_vals, vec![false, true, true, false]);
225 let data_vals: Vec<f64> = result.data().iter().copied().collect();
227 assert!((data_vals[0] - 11.0).abs() < 1e-10);
228 assert!((data_vals[3] - 44.0).abs() < 1e-10);
229 }
230
231 #[test]
232 fn operator_add_matches_masked_add() {
233 let d1 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
234 let m1 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
235 let ma1 = MaskedArray::new(d1, m1).unwrap();
236
237 let d2 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![10.0, 20.0, 30.0]).unwrap();
238 let m2 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, true]).unwrap();
239 let ma2 = MaskedArray::new(d2, m2).unwrap();
240
241 let result = (&ma1 + &ma2).unwrap();
243 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
244 assert_eq!(mask_vals, vec![false, true, true]);
245 let data_vals: Vec<f64> = result.data().iter().copied().collect();
246 assert!((data_vals[0] - 11.0).abs() < 1e-10);
247 }
248
249 #[test]
253 fn ac7_ufunc_sin_masked() {
254 use std::f64::consts::FRAC_PI_2;
255 let data =
256 Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![0.0, FRAC_PI_2, FRAC_PI_2]).unwrap();
257 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
258 let ma = MaskedArray::new(data, mask).unwrap();
259 let result = ufunc_support::sin(&ma).unwrap();
260 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
261 assert_eq!(mask_vals, vec![false, true, false]);
262 let data_vals: Vec<f64> = result.data().iter().copied().collect();
263 assert!((data_vals[0] - 0.0).abs() < 1e-10);
265 assert!((data_vals[2] - 1.0).abs() < 1e-10);
266 }
267
268 #[test]
272 fn ac8_sort_masked_at_end() {
273 let data =
274 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![5.0, 1.0, 3.0, 2.0, 4.0]).unwrap();
275 let mask =
276 Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false, false, true, false, false])
277 .unwrap();
278 let ma = MaskedArray::new(data, mask).unwrap();
279 let sorted = ma.sort().unwrap();
280 let data_vals: Vec<f64> = sorted.data().iter().copied().collect();
281 let mask_vals: Vec<bool> = sorted.mask().iter().copied().collect();
282 assert_eq!(data_vals, vec![1.0, 2.0, 4.0, 5.0, 3.0]);
284 assert_eq!(mask_vals, vec![false, false, false, false, true]);
285 }
286
287 #[test]
288 fn ac8_harden_mask_prevents_clearing() {
289 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
290 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
291 let mut ma = MaskedArray::new(data, mask).unwrap();
292
293 ma.harden_mask().unwrap();
294 assert!(ma.is_hard_mask());
295
296 ma.set_mask_flat(1, false).unwrap();
298 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
299 assert_eq!(mask_vals, vec![false, true, false]);
300
301 ma.set_mask_flat(0, true).unwrap();
303 let mask_vals2: Vec<bool> = ma.mask().iter().copied().collect();
304 assert_eq!(mask_vals2, vec![true, true, false]);
305
306 ma.soften_mask().unwrap();
308 assert!(!ma.is_hard_mask());
309 ma.set_mask_flat(1, false).unwrap();
310 let mask_vals3: Vec<bool> = ma.mask().iter().copied().collect();
311 assert_eq!(mask_vals3, vec![true, false, false]);
312 }
313
314 #[test]
318 fn ac9_is_masked() {
319 let data1 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
320 let mask1 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
321 let ma1 = MaskedArray::new(data1, mask1).unwrap();
322 assert!(is_masked(&ma1).unwrap());
323
324 let data2 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
325 let mask2 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, false]).unwrap();
326 let ma2 = MaskedArray::new(data2, mask2).unwrap();
327 assert!(!is_masked(&ma2).unwrap());
328 }
329
330 #[test]
335 fn shape_mismatch_error() {
336 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
337 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([2]), vec![false, true]).unwrap();
338 assert!(MaskedArray::new(data, mask).is_err());
339 }
340
341 #[test]
342 fn from_data_no_mask() {
343 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
344 let ma = MaskedArray::from_data(data).unwrap();
345 assert!(!is_masked(&ma).unwrap());
346 assert_eq!(ma.count().unwrap(), 3);
347 }
348
349 #[test]
350 fn sum_skips_masked() {
351 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
352 let mask =
353 Array::<bool, Ix1>::from_vec(Ix1::new([4]), vec![false, true, false, true]).unwrap();
354 let ma = MaskedArray::new(data, mask).unwrap();
355 assert!((ma.sum().unwrap() - 4.0).abs() < 1e-10);
356 }
357
358 #[test]
359 fn min_max_skip_masked() {
360 let data =
361 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![5.0, 1.0, 3.0, 2.0, 4.0]).unwrap();
362 let mask =
363 Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false, true, false, false, false])
364 .unwrap();
365 let ma = MaskedArray::new(data, mask).unwrap();
366 assert!((ma.min().unwrap() - 2.0).abs() < 1e-10);
367 assert!((ma.max().unwrap() - 5.0).abs() < 1e-10);
368 }
369
370 #[test]
371 fn var_std_skip_masked() {
372 let data =
374 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![2.0, 99.0, 4.0, 6.0, 99.0]).unwrap();
375 let mask =
376 Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false, true, false, false, true])
377 .unwrap();
378 let ma = MaskedArray::new(data, mask).unwrap();
379 let mean = ma.mean().unwrap();
380 assert!((mean - 4.0).abs() < 1e-10);
381 let v = ma.var().unwrap();
383 assert!((v - 8.0 / 3.0).abs() < 1e-10);
384 let s = ma.std().unwrap();
385 assert!((s - (8.0_f64 / 3.0).sqrt()).abs() < 1e-10);
386 }
387
388 #[test]
389 fn count_elements() {
390 let data = Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![1.0; 5]).unwrap();
391 let mask =
392 Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false, true, true, false, false])
393 .unwrap();
394 let ma = MaskedArray::new(data, mask).unwrap();
395 assert_eq!(ma.count().unwrap(), 3);
396 }
397
398 #[test]
399 fn masked_equal_test() {
400 let data =
401 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![1.0, 2.0, 3.0, 2.0, 1.0]).unwrap();
402 let ma = masked_equal(&data, 2.0).unwrap();
403 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
404 assert_eq!(mask_vals, vec![false, true, false, true, false]);
405 }
406
407 #[test]
408 fn masked_greater_test() {
409 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
410 let ma = masked_greater(&data, 2.0).unwrap();
411 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
412 assert_eq!(mask_vals, vec![false, false, true, true]);
413 }
414
415 #[test]
416 fn masked_less_test() {
417 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
418 let ma = masked_less(&data, 3.0).unwrap();
419 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
420 assert_eq!(mask_vals, vec![true, true, false, false]);
421 }
422
423 #[test]
424 fn masked_not_equal_test() {
425 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
426 let ma = masked_not_equal(&data, 2.0).unwrap();
427 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
428 assert_eq!(mask_vals, vec![true, false, true]);
429 }
430
431 #[test]
432 fn masked_greater_equal_test() {
433 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
434 let ma = masked_greater_equal(&data, 3.0).unwrap();
435 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
436 assert_eq!(mask_vals, vec![false, false, true, true]);
437 }
438
439 #[test]
440 fn masked_less_equal_test() {
441 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
442 let ma = masked_less_equal(&data, 2.0).unwrap();
443 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
444 assert_eq!(mask_vals, vec![true, true, false, false]);
445 }
446
447 #[test]
448 fn masked_inside_test() {
449 let data =
450 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![1.0, 2.0, 3.0, 4.0, 5.0]).unwrap();
451 let ma = masked_inside(&data, 2.0, 4.0).unwrap();
452 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
453 assert_eq!(mask_vals, vec![false, true, true, true, false]);
454 }
455
456 #[test]
457 fn masked_outside_test() {
458 let data =
459 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![1.0, 2.0, 3.0, 4.0, 5.0]).unwrap();
460 let ma = masked_outside(&data, 2.0, 4.0).unwrap();
461 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
462 assert_eq!(mask_vals, vec![true, false, false, false, true]);
463 }
464
465 #[test]
466 fn masked_where_test() {
467 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
468 let cond =
469 Array::<bool, Ix1>::from_vec(Ix1::new([4]), vec![true, false, true, false]).unwrap();
470 let ma = masked_where(&cond, &data).unwrap();
471 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
472 assert_eq!(mask_vals, vec![true, false, true, false]);
473 }
474
475 #[test]
476 fn argsort_test() {
477 let data =
478 Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![5.0, 1.0, 3.0, 2.0, 4.0]).unwrap();
479 let mask =
480 Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false, false, true, false, false])
481 .unwrap();
482 let ma = MaskedArray::new(data, mask).unwrap();
483 let indices = ma.argsort().unwrap();
484 assert_eq!(indices.shape(), &[5]);
486 assert_eq!(indices.as_slice().unwrap(), &[1u64, 3, 4, 0, 2]);
487 }
488
489 #[test]
490 fn getmask_getdata_test() {
491 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
492 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
493 let ma = MaskedArray::new(data.clone(), mask.clone()).unwrap();
494
495 let got_mask = getmask(&ma).unwrap();
496 let got_data = getdata(&ma).unwrap();
497
498 assert_eq!(got_mask.as_slice().unwrap(), mask.as_slice().unwrap());
499 assert_eq!(got_data.as_slice().unwrap(), data.as_slice().unwrap());
500 }
501
502 #[test]
503 fn count_masked_test() {
504 let data = Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![1.0; 5]).unwrap();
505 let mask =
506 Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![true, false, true, true, false])
507 .unwrap();
508 let ma = MaskedArray::new(data, mask).unwrap();
509 assert_eq!(count_masked(&ma).unwrap(), 3);
510 }
511
512 #[test]
513 fn count_masked_axis_2d_along_rows() {
514 use ferray_core::dimension::Ix2;
519 let data = Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0; 6]).unwrap();
520 let mask = Array::<bool, Ix2>::from_vec(
521 Ix2::new([2, 3]),
522 vec![true, false, true, false, false, true],
523 )
524 .unwrap();
525 let ma = MaskedArray::new(data, mask).unwrap();
526 let counts = count_masked_axis(&ma, 1).unwrap();
528 assert_eq!(counts.shape(), &[2]);
529 assert_eq!(counts.as_slice().unwrap(), &[2u64, 1]);
530 }
531
532 #[test]
533 fn count_masked_axis_2d_along_cols() {
534 use ferray_core::dimension::Ix2;
536 let data = Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0; 6]).unwrap();
537 let mask = Array::<bool, Ix2>::from_vec(
538 Ix2::new([2, 3]),
539 vec![true, false, true, false, false, true],
540 )
541 .unwrap();
542 let ma = MaskedArray::new(data, mask).unwrap();
543 let counts = count_masked_axis(&ma, 0).unwrap();
544 assert_eq!(counts.shape(), &[3]);
545 assert_eq!(counts.as_slice().unwrap(), &[1u64, 0, 2]);
546 }
547
548 #[test]
549 fn count_masked_axis_rejects_out_of_bounds() {
550 use ferray_core::dimension::Ix2;
551 let data = Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0; 6]).unwrap();
552 let mask = Array::<bool, Ix2>::from_vec(Ix2::new([2, 3]), vec![false; 6]).unwrap();
553 let ma = MaskedArray::new(data, mask).unwrap();
554 assert!(count_masked_axis(&ma, 2).is_err());
555 }
556
557 #[test]
558 fn sort_axis_2d_per_row() {
559 use ferray_core::dimension::Ix2;
564 let data =
565 Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![3.0, 1.0, 99.0, 99.0, 4.0, 2.0])
566 .unwrap();
567 let mask = Array::<bool, Ix2>::from_vec(
568 Ix2::new([2, 3]),
569 vec![false, false, true, true, false, false],
570 )
571 .unwrap();
572 let ma = MaskedArray::new(data, mask).unwrap();
573 let sorted = ma.sort_axis(1).unwrap();
574 assert_eq!(sorted.shape(), &[2, 3]);
575 let d: Vec<f64> = sorted.data().iter().copied().collect();
576 let m: Vec<bool> = sorted.mask().iter().copied().collect();
577 assert!((d[0] - 1.0).abs() < 1e-12);
579 assert!((d[1] - 3.0).abs() < 1e-12);
580 assert!(m[2], "row 0 col 2 should be masked");
581 assert!((d[3] - 2.0).abs() < 1e-12);
583 assert!((d[4] - 4.0).abs() < 1e-12);
584 assert!(m[5], "row 1 col 2 should be masked");
585 }
586
587 #[test]
588 fn sort_axis_2d_per_column() {
589 use ferray_core::dimension::Ix2;
592 let data =
593 Array::<f64, Ix2>::from_vec(Ix2::new([2, 2]), vec![3.0, 2.0, 1.0, 99.0]).unwrap();
594 let mask = Array::<bool, Ix2>::from_vec(Ix2::new([2, 2]), vec![false, false, false, true])
595 .unwrap();
596 let ma = MaskedArray::new(data, mask).unwrap();
597 let sorted = ma.sort_axis(0).unwrap();
598 let d: Vec<f64> = sorted.data().iter().copied().collect();
599 let m: Vec<bool> = sorted.mask().iter().copied().collect();
600 assert!((d[0] - 1.0).abs() < 1e-12);
602 assert!((d[2] - 3.0).abs() < 1e-12);
603 assert!((d[1] - 2.0).abs() < 1e-12);
605 assert!(m[3]);
606 }
607
608 #[test]
609 fn sort_axis_rejects_out_of_bounds() {
610 use ferray_core::dimension::Ix2;
611 let data = Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0; 6]).unwrap();
612 let mask = Array::<bool, Ix2>::from_vec(Ix2::new([2, 3]), vec![false; 6]).unwrap();
613 let ma = MaskedArray::new(data, mask).unwrap();
614 assert!(ma.sort_axis(2).is_err());
615 }
616
617 #[test]
618 fn data_mut_only_exposes_element_slice() {
619 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
622 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([4]), vec![false; 4]).unwrap();
623 let mut ma = MaskedArray::new(data, mask).unwrap();
624 if let Some(s) = ma.data_mut() {
626 s[2] = 99.0;
627 }
628 assert_eq!(ma.shape(), &[4]);
629 let vals: Vec<f64> = ma.data().iter().copied().collect();
630 assert_eq!(vals, vec![1.0, 2.0, 99.0, 4.0]);
631 assert_eq!(ma.mask().shape(), &[4]);
633 }
634
635 #[test]
636 fn masked_add_array_test() {
637 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
638 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
639 let ma = MaskedArray::new(data, mask).unwrap();
640 let arr = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![10.0, 20.0, 30.0]).unwrap();
641 let result = masked_add_array(&ma, &arr).unwrap();
642 let data_vals: Vec<f64> = result.data().iter().copied().collect();
643 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
644 assert_eq!(mask_vals, vec![false, true, false]);
645 assert!((data_vals[0] - 11.0).abs() < 1e-10);
646 assert!((data_vals[2] - 33.0).abs() < 1e-10);
647 }
648
649 #[test]
650 fn all_masked_mean_is_nan() {
651 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
652 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![true, true, true]).unwrap();
653 let ma = MaskedArray::new(data, mask).unwrap();
654 assert!(ma.mean().unwrap().is_nan());
655 }
656
657 #[test]
658 fn all_masked_min_errors() {
659 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
660 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![true, true, true]).unwrap();
661 let ma = MaskedArray::new(data, mask).unwrap();
662 assert!(ma.min().is_err());
663 }
664
665 #[test]
666 fn ufunc_exp_masked() {
667 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![0.0, 1.0, 2.0]).unwrap();
668 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
669 let ma = MaskedArray::new(data, mask).unwrap();
670 let result = ufunc_support::exp(&ma).unwrap();
671 let data_vals: Vec<f64> = result.data().iter().copied().collect();
672 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
673 assert_eq!(mask_vals, vec![false, true, false]);
674 assert!((data_vals[0] - 1.0).abs() < 1e-10); assert!((data_vals[2] - 2.0_f64.exp()).abs() < 1e-10);
676 }
677
678 #[test]
679 fn ufunc_sqrt_masked() {
680 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![4.0, 9.0, 16.0, 25.0]).unwrap();
681 let mask =
682 Array::<bool, Ix1>::from_vec(Ix1::new([4]), vec![false, true, false, true]).unwrap();
683 let ma = MaskedArray::new(data, mask).unwrap();
684 let result = ufunc_support::sqrt(&ma).unwrap();
685 let data_vals: Vec<f64> = result.data().iter().copied().collect();
686 assert!((data_vals[0] - 2.0).abs() < 1e-10);
687 assert!((data_vals[2] - 4.0).abs() < 1e-10);
688 }
689
690 #[test]
691 fn set_mask_hardened() {
692 let data = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
693 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
694 let mut ma = MaskedArray::new(data, mask).unwrap();
695 ma.harden_mask().unwrap();
696
697 let new_mask =
699 Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, false]).unwrap();
700 ma.set_mask(new_mask).unwrap();
701 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
702 assert_eq!(mask_vals, vec![false, true, false]);
704 }
705
706 #[test]
707 fn masked_sub_test() {
708 let d1 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![10.0, 20.0, 30.0]).unwrap();
709 let m1 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, true]).unwrap();
710 let ma1 = MaskedArray::new(d1, m1).unwrap();
711
712 let d2 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
713 let m2 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
714 let ma2 = MaskedArray::new(d2, m2).unwrap();
715
716 let result = masked_sub(&ma1, &ma2).unwrap();
717 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
718 assert_eq!(mask_vals, vec![false, true, true]);
719 let data_vals: Vec<f64> = result.data().iter().copied().collect();
720 assert!((data_vals[0] - 9.0).abs() < 1e-10);
721 }
722
723 #[test]
724 fn masked_mul_test() {
725 let d1 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![2.0, 3.0, 4.0]).unwrap();
726 let m1 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
727 let ma1 = MaskedArray::new(d1, m1).unwrap();
728
729 let d2 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![5.0, 6.0, 7.0]).unwrap();
730 let m2 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, false]).unwrap();
731 let ma2 = MaskedArray::new(d2, m2).unwrap();
732
733 let result = masked_mul(&ma1, &ma2).unwrap();
734 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
735 assert_eq!(mask_vals, vec![false, true, false]);
736 let data_vals: Vec<f64> = result.data().iter().copied().collect();
737 assert!((data_vals[0] - 10.0).abs() < 1e-10);
738 assert!((data_vals[2] - 28.0).abs() < 1e-10);
739 }
740
741 #[test]
742 fn masked_div_test() {
743 let d1 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![10.0, 20.0, 30.0]).unwrap();
744 let m1 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, true]).unwrap();
745 let ma1 = MaskedArray::new(d1, m1).unwrap();
746
747 let d2 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![2.0, 5.0, 6.0]).unwrap();
748 let m2 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, false]).unwrap();
749 let ma2 = MaskedArray::new(d2, m2).unwrap();
750
751 let result = masked_div(&ma1, &ma2).unwrap();
752 let data_vals: Vec<f64> = result.data().iter().copied().collect();
753 assert!((data_vals[0] - 5.0).abs() < 1e-10);
754 assert!((data_vals[1] - 4.0).abs() < 1e-10);
755 }
756
757 #[test]
758 fn masked_invalid_negative_inf() {
759 let data =
760 Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, f64::NEG_INFINITY, 3.0]).unwrap();
761 let ma = masked_invalid(&data).unwrap();
762 let mask_vals: Vec<bool> = ma.mask().iter().copied().collect();
763 assert_eq!(mask_vals, vec![false, true, false]);
764 }
765
766 #[test]
767 fn empty_array_operations() {
768 let data = Array::<f64, Ix1>::from_vec(Ix1::new([0]), vec![]).unwrap();
769 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([0]), vec![]).unwrap();
770 let ma = MaskedArray::new(data, mask).unwrap();
771 assert_eq!(ma.count().unwrap(), 0);
772 assert!(ma.mean().unwrap().is_nan());
773 let compressed = ma.compressed().unwrap();
774 assert_eq!(compressed.size(), 0);
775 }
776
777 #[test]
778 fn ndim_shape_size() {
779 let data = Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![1.0; 5]).unwrap();
780 let mask = Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false; 5]).unwrap();
781 let ma = MaskedArray::new(data, mask).unwrap();
782 assert_eq!(ma.ndim(), 1);
783 assert_eq!(ma.shape(), &[5]);
784 assert_eq!(ma.size(), 5);
785 }
786
787 #[test]
788 fn ufunc_binary_power() {
789 let d1 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![2.0, 3.0, 4.0]).unwrap();
790 let m1 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, true, false]).unwrap();
791 let ma1 = MaskedArray::new(d1, m1).unwrap();
792
793 let d2 = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![3.0, 2.0, 2.0]).unwrap();
794 let m2 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, false]).unwrap();
795 let ma2 = MaskedArray::new(d2, m2).unwrap();
796
797 let result = ufunc_support::power(&ma1, &ma2).unwrap();
798 let data_vals: Vec<f64> = result.data().iter().copied().collect();
799 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
800 assert_eq!(mask_vals, vec![false, true, false]);
801 assert!((data_vals[0] - 8.0).abs() < 1e-10); assert!((data_vals[2] - 16.0).abs() < 1e-10); }
804
805 #[test]
806 fn filled_with_custom_value() {
807 let data = Array::<f64, Ix1>::from_vec(Ix1::new([4]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
808 let mask =
809 Array::<bool, Ix1>::from_vec(Ix1::new([4]), vec![true, false, true, false]).unwrap();
810 let ma = MaskedArray::new(data, mask).unwrap();
811 let filled = ma.filled(-999.0).unwrap();
812 assert_eq!(filled.as_slice().unwrap(), &[-999.0, 2.0, -999.0, 4.0]);
813 }
814
815 #[test]
818 fn masked_2d_construction() {
819 use ferray_core::dimension::Ix2;
820 let data =
821 Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
822 .unwrap();
823 let mask = Array::<bool, Ix2>::from_vec(
824 Ix2::new([2, 3]),
825 vec![false, true, false, false, false, true],
826 )
827 .unwrap();
828 let ma = MaskedArray::new(data, mask).unwrap();
829 assert_eq!(ma.ndim(), 2);
830 assert_eq!(ma.shape(), &[2, 3]);
831 assert_eq!(ma.size(), 6);
832 assert_eq!(ma.count().unwrap(), 4);
833 }
834
835 #[test]
836 fn masked_2d_mean() {
837 use ferray_core::dimension::Ix2;
838 let data =
839 Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
840 .unwrap();
841 let mask = Array::<bool, Ix2>::from_vec(
843 Ix2::new([2, 3]),
844 vec![false, true, false, false, false, true],
845 )
846 .unwrap();
847 let ma = MaskedArray::new(data, mask).unwrap();
848 let m = ma.mean().unwrap();
850 assert!((m - 3.25).abs() < 1e-10);
851 }
852
853 #[test]
854 fn masked_2d_sum() {
855 use ferray_core::dimension::Ix2;
856 let data =
857 Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
858 .unwrap();
859 let mask = Array::<bool, Ix2>::from_vec(
860 Ix2::new([2, 3]),
861 vec![false, true, false, false, false, true],
862 )
863 .unwrap();
864 let ma = MaskedArray::new(data, mask).unwrap();
865 assert!((ma.sum().unwrap() - 13.0).abs() < 1e-10);
867 }
868
869 #[test]
870 fn masked_2d_add_operator() {
871 use ferray_core::dimension::Ix2;
872 let d1 = Array::<f64, Ix2>::from_vec(Ix2::new([2, 2]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
873 let m1 = Array::<bool, Ix2>::from_vec(Ix2::new([2, 2]), vec![false, true, false, false])
874 .unwrap();
875 let ma1 = MaskedArray::new(d1, m1).unwrap();
876
877 let d2 =
878 Array::<f64, Ix2>::from_vec(Ix2::new([2, 2]), vec![10.0, 20.0, 30.0, 40.0]).unwrap();
879 let m2 = Array::<bool, Ix2>::from_vec(Ix2::new([2, 2]), vec![false, false, true, false])
880 .unwrap();
881 let ma2 = MaskedArray::new(d2, m2).unwrap();
882
883 let result = (&ma1 + &ma2).unwrap();
884 let mask_vals: Vec<bool> = result.mask().iter().copied().collect();
885 assert_eq!(mask_vals, vec![false, true, true, false]);
886 let data_vals: Vec<f64> = result.data().iter().copied().collect();
887 assert!((data_vals[0] - 11.0).abs() < 1e-10);
888 assert!((data_vals[3] - 44.0).abs() < 1e-10);
889 }
890
891 #[test]
892 fn masked_2d_compressed() {
893 use ferray_core::dimension::Ix2;
894 let data = Array::<f64, Ix2>::from_vec(Ix2::new([2, 2]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
895 let mask =
896 Array::<bool, Ix2>::from_vec(Ix2::new([2, 2]), vec![false, true, false, true]).unwrap();
897 let ma = MaskedArray::new(data, mask).unwrap();
898 let compressed = ma.compressed().unwrap();
899 assert_eq!(compressed.as_slice().unwrap(), &[1.0, 3.0]);
900 }
901}