1use ndarray::{Array3, Axis, s};
9
10pub fn rebin_counts(data: &Array3<f64>, factor: usize) -> Array3<f64> {
18 if factor < 2 {
19 return data.clone();
20 }
21 let n_old = data.shape()[0];
22 let height = data.shape()[1];
23 let width = data.shape()[2];
24 let n_new = n_old.div_ceil(factor);
25 let mut out = Array3::<f64>::zeros((n_new, height, width));
26 for i in 0..n_new {
27 let start = i * factor;
28 let end = (start + factor).min(n_old);
29 out.slice_mut(s![i, .., ..])
30 .assign(&data.slice(s![start..end, .., ..]).sum_axis(Axis(0)));
31 }
32 out
33}
34
35pub fn rebin_transmission(data: &Array3<f64>, factor: usize) -> Array3<f64> {
55 if factor < 2 {
56 return data.clone();
57 }
58 let n_old = data.shape()[0];
59 let height = data.shape()[1];
60 let width = data.shape()[2];
61 let n_new = n_old.div_ceil(factor);
62 let mut out = Array3::<f64>::zeros((n_new, height, width));
63 for i in 0..n_new {
64 let start = i * factor;
65 let end = (start + factor).min(n_old);
66 let group_len = (end - start) as f64;
67 let sum = data.slice(s![start..end, .., ..]).sum_axis(Axis(0));
68 out.slice_mut(s![i, .., ..]).assign(&(sum / group_len));
69 }
70 out
71}
72
73pub fn rebin_edges(edges: &[f64], factor: usize) -> Vec<f64> {
81 if factor < 2 || edges.len() <= 1 {
82 return edges.to_vec();
83 }
84 let mut out: Vec<f64> = edges.iter().step_by(factor).copied().collect();
85 if out.last() != edges.last() {
87 out.push(*edges.last().unwrap());
88 }
89 out
90}
91
92pub fn rebin_centers(centers: &[f64], factor: usize) -> Vec<f64> {
96 if factor < 2 || centers.is_empty() {
97 return centers.to_vec();
98 }
99 centers
100 .chunks(factor)
101 .map(|chunk| chunk.iter().sum::<f64>() / chunk.len() as f64)
102 .collect()
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use ndarray::Array3;
109
110 #[test]
111 fn counts_conservation() {
112 let data = Array3::from_shape_fn((6, 2, 3), |(t, y, x)| (t + y + x) as f64);
113 let original_sum: f64 = data.sum();
114 let rebinned = rebin_counts(&data, 2);
115 assert_eq!(rebinned.shape(), &[3, 2, 3]);
116 assert!((rebinned.sum() - original_sum).abs() < 1e-10);
117 }
118
119 #[test]
120 fn counts_conservation_remainder() {
121 let data = Array3::from_shape_fn((7, 2, 2), |(t, _y, _x)| t as f64 + 1.0);
123 let original_sum: f64 = data.sum();
124 let rebinned = rebin_counts(&data, 3);
125 assert_eq!(rebinned.shape(), &[3, 2, 2]);
126 assert!((rebinned.sum() - original_sum).abs() < 1e-10);
127 }
128
129 #[test]
130 fn counts_factor_one_is_identity() {
131 let data = Array3::from_shape_fn((5, 2, 2), |(t, y, x)| (t * 10 + y + x) as f64);
132 let rebinned = rebin_counts(&data, 1);
133 assert_eq!(rebinned, data);
134 }
135
136 #[test]
137 fn counts_factor_ge_nbins_single_bin() {
138 let data = Array3::from_shape_fn((4, 2, 2), |(t, _y, _x)| t as f64);
139 let rebinned = rebin_counts(&data, 100);
140 assert_eq!(rebinned.shape(), &[1, 2, 2]);
141 assert!((rebinned.sum() - data.sum()).abs() < 1e-10);
142 }
143
144 #[test]
145 fn transmission_averaging() {
146 let data = Array3::from_elem((6, 2, 2), 0.5);
148 let rebinned = rebin_transmission(&data, 3);
149 assert_eq!(rebinned.shape(), &[2, 2, 2]);
150 for &v in rebinned.iter() {
151 assert!((v - 0.5).abs() < 1e-10);
152 }
153 }
154
155 #[test]
156 fn transmission_averaging_remainder() {
157 let data = Array3::from_shape_fn((7, 1, 1), |(t, _y, _x)| t as f64);
160 let rebinned = rebin_transmission(&data, 3);
161 assert_eq!(rebinned.shape(), &[3, 1, 1]);
162 assert!((rebinned[[0, 0, 0]] - 1.0).abs() < 1e-10); assert!((rebinned[[1, 0, 0]] - 4.0).abs() < 1e-10); assert!((rebinned[[2, 0, 0]] - 6.0).abs() < 1e-10); }
166
167 #[test]
168 fn edges_even_division() {
169 let edges = vec![0.0, 1.0, 2.0, 3.0, 4.0]; let rebinned = rebin_edges(&edges, 2);
171 assert_eq!(rebinned, vec![0.0, 2.0, 4.0]); }
173
174 #[test]
175 fn edges_remainder() {
176 let edges = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0]; let rebinned = rebin_edges(&edges, 2);
178 assert_eq!(rebinned, vec![0.0, 2.0, 4.0, 5.0]);
180 }
181
182 #[test]
183 fn edges_factor_one() {
184 let edges = vec![1.0, 2.0, 3.0];
185 assert_eq!(rebin_edges(&edges, 1), edges);
186 }
187
188 #[test]
189 fn centers_averaging() {
190 let centers = vec![1.0, 3.0, 5.0, 7.0]; let rebinned = rebin_centers(¢ers, 2);
192 assert_eq!(rebinned, vec![2.0, 6.0]);
193 }
194
195 #[test]
196 fn centers_remainder() {
197 let centers = vec![1.0, 3.0, 5.0]; let rebinned = rebin_centers(¢ers, 2);
199 assert_eq!(rebinned, vec![2.0, 5.0]); }
201
202 #[test]
203 fn counts_empty_array() {
204 let data = Array3::<f64>::zeros((0, 2, 2));
205 let rebinned = rebin_counts(&data, 3);
206 assert_eq!(rebinned.shape(), &[0, 2, 2]);
207 }
208
209 #[test]
210 fn counts_factor_zero_returns_clone() {
211 let data = Array3::from_shape_fn((4, 1, 1), |(t, _, _)| t as f64);
212 let rebinned = rebin_counts(&data, 0);
213 assert_eq!(rebinned, data);
214 }
215
216 #[test]
217 fn transmission_factor_zero_returns_clone() {
218 let data = Array3::from_elem((4, 1, 1), 0.5);
219 let rebinned = rebin_transmission(&data, 0);
220 assert_eq!(rebinned, data);
221 }
222
223 #[test]
224 fn counts_nan_propagation() {
225 let mut data = Array3::from_shape_fn((4, 1, 1), |(t, _, _)| t as f64);
226 data[[1, 0, 0]] = f64::NAN; let rebinned = rebin_counts(&data, 2);
228 assert!(rebinned[[0, 0, 0]].is_nan());
230 assert!((rebinned[[1, 0, 0]] - 5.0).abs() < 1e-10);
232 }
233
234 #[test]
235 fn transmission_nan_propagation() {
236 let mut data = Array3::from_shape_fn((4, 1, 1), |(t, _, _)| t as f64);
237 data[[1, 0, 0]] = f64::NAN; let rebinned = rebin_transmission(&data, 2);
239 assert!(rebinned[[0, 0, 0]].is_nan());
241 assert!((rebinned[[1, 0, 0]] - 2.5).abs() < 1e-10);
243 }
244
245 #[test]
246 fn edges_factor_zero_returns_clone() {
247 let edges = vec![0.0, 1.0, 2.0, 3.0];
248 assert_eq!(rebin_edges(&edges, 0), edges);
249 }
250
251 #[test]
252 fn centers_factor_zero_returns_clone() {
253 let centers = vec![0.5, 1.5, 2.5];
254 assert_eq!(rebin_centers(¢ers, 0), centers);
255 }
256}