1use ndarray::{Array3, Axis, s};
8
9pub fn rebin_counts(data: &Array3<f64>, factor: usize) -> Array3<f64> {
17 if factor < 2 {
18 return data.clone();
19 }
20 let n_old = data.shape()[0];
21 let height = data.shape()[1];
22 let width = data.shape()[2];
23 let n_new = n_old.div_ceil(factor);
24 let mut out = Array3::<f64>::zeros((n_new, height, width));
25 for i in 0..n_new {
26 let start = i * factor;
27 let end = (start + factor).min(n_old);
28 out.slice_mut(s![i, .., ..])
29 .assign(&data.slice(s![start..end, .., ..]).sum_axis(Axis(0)));
30 }
31 out
32}
33
34pub fn rebin_edges(edges: &[f64], factor: usize) -> Vec<f64> {
42 if factor < 2 || edges.len() <= 1 {
43 return edges.to_vec();
44 }
45 let mut out: Vec<f64> = edges.iter().step_by(factor).copied().collect();
46 if out.last() != edges.last() {
48 out.push(*edges.last().unwrap());
49 }
50 out
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use ndarray::Array3;
57
58 #[test]
59 fn counts_conservation() {
60 let data = Array3::from_shape_fn((6, 2, 3), |(t, y, x)| (t + y + x) as f64);
61 let original_sum: f64 = data.sum();
62 let rebinned = rebin_counts(&data, 2);
63 assert_eq!(rebinned.shape(), &[3, 2, 3]);
64 assert!((rebinned.sum() - original_sum).abs() < 1e-10);
65 }
66
67 #[test]
68 fn counts_conservation_remainder() {
69 let data = Array3::from_shape_fn((7, 2, 2), |(t, _y, _x)| t as f64 + 1.0);
71 let original_sum: f64 = data.sum();
72 let rebinned = rebin_counts(&data, 3);
73 assert_eq!(rebinned.shape(), &[3, 2, 2]);
74 assert!((rebinned.sum() - original_sum).abs() < 1e-10);
75 }
76
77 #[test]
78 fn counts_factor_one_is_identity() {
79 let data = Array3::from_shape_fn((5, 2, 2), |(t, y, x)| (t * 10 + y + x) as f64);
80 let rebinned = rebin_counts(&data, 1);
81 assert_eq!(rebinned, data);
82 }
83
84 #[test]
85 fn counts_factor_ge_nbins_single_bin() {
86 let data = Array3::from_shape_fn((4, 2, 2), |(t, _y, _x)| t as f64);
87 let rebinned = rebin_counts(&data, 100);
88 assert_eq!(rebinned.shape(), &[1, 2, 2]);
89 assert!((rebinned.sum() - data.sum()).abs() < 1e-10);
90 }
91
92 #[test]
93 fn edges_even_division() {
94 let edges = vec![0.0, 1.0, 2.0, 3.0, 4.0]; let rebinned = rebin_edges(&edges, 2);
96 assert_eq!(rebinned, vec![0.0, 2.0, 4.0]); }
98
99 #[test]
100 fn edges_remainder() {
101 let edges = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0]; let rebinned = rebin_edges(&edges, 2);
103 assert_eq!(rebinned, vec![0.0, 2.0, 4.0, 5.0]);
105 }
106
107 #[test]
108 fn edges_factor_one() {
109 let edges = vec![1.0, 2.0, 3.0];
110 assert_eq!(rebin_edges(&edges, 1), edges);
111 }
112
113 #[test]
114 fn counts_empty_array() {
115 let data = Array3::<f64>::zeros((0, 2, 2));
116 let rebinned = rebin_counts(&data, 3);
117 assert_eq!(rebinned.shape(), &[0, 2, 2]);
118 }
119
120 #[test]
121 fn counts_factor_zero_returns_clone() {
122 let data = Array3::from_shape_fn((4, 1, 1), |(t, _, _)| t as f64);
123 let rebinned = rebin_counts(&data, 0);
124 assert_eq!(rebinned, data);
125 }
126
127 #[test]
128 fn counts_nan_propagation() {
129 let mut data = Array3::from_shape_fn((4, 1, 1), |(t, _, _)| t as f64);
130 data[[1, 0, 0]] = f64::NAN; let rebinned = rebin_counts(&data, 2);
132 assert!(rebinned[[0, 0, 0]].is_nan());
134 assert!((rebinned[[1, 0, 0]] - 5.0).abs() < 1e-10);
136 }
137
138 #[test]
139 fn edges_factor_zero_returns_clone() {
140 let edges = vec![0.0, 1.0, 2.0, 3.0];
141 assert_eq!(rebin_edges(&edges, 0), edges);
142 }
143}