1use ferray_core::Array;
7use ferray_core::dimension::Ix1;
8use ferray_core::error::{FerrayError, FerrayResult};
9
10use std::f64::consts::PI;
11
12fn bessel_i0_scalar(x: f64) -> f64 {
17 let ax = x.abs();
18
19 if ax <= 3.75 {
20 let t = (ax / 3.75).powi(2);
21 1.0 + t
22 * (3.5156229
23 + t * (3.0899424
24 + t * (1.2067492 + t * (0.2659732 + t * (0.0360768 + t * 0.0045813)))))
25 } else {
26 let t = 3.75 / ax;
27 let poly = 0.39894228
28 + t * (0.01328592
29 + t * (0.00225319
30 + t * (-0.00157565
31 + t * (0.00916281
32 + t * (-0.02057706
33 + t * (0.02635537 + t * (-0.01647633 + t * 0.00392377)))))));
34 poly * ax.exp() / ax.sqrt()
35 }
36}
37
38pub fn bartlett(m: usize) -> FerrayResult<Array<f64, Ix1>> {
52 if m == 0 {
53 return Array::from_vec(Ix1::new([0]), vec![]);
54 }
55 if m == 1 {
56 return Array::from_vec(Ix1::new([1]), vec![1.0]);
57 }
58
59 let half = (m - 1) as f64 / 2.0;
60 let mut data = Vec::with_capacity(m);
61 for n in 0..m {
62 let val = 1.0 - ((n as f64 - half) / half).abs();
63 data.push(val);
64 }
65 Array::from_vec(Ix1::new([m]), data)
66}
67
68pub fn blackman(m: usize) -> FerrayResult<Array<f64, Ix1>> {
82 if m == 0 {
83 return Array::from_vec(Ix1::new([0]), vec![]);
84 }
85 if m == 1 {
86 return Array::from_vec(Ix1::new([1]), vec![1.0]);
87 }
88
89 let denom = (m - 1) as f64;
90 let mut data = Vec::with_capacity(m);
91 for n in 0..m {
92 let x = n as f64;
93 let val = 0.42 - 0.5 * (2.0 * PI * x / denom).cos() + 0.08 * (4.0 * PI * x / denom).cos();
94 data.push(val);
95 }
96 Array::from_vec(Ix1::new([m]), data)
97}
98
99pub fn hamming(m: usize) -> FerrayResult<Array<f64, Ix1>> {
113 if m == 0 {
114 return Array::from_vec(Ix1::new([0]), vec![]);
115 }
116 if m == 1 {
117 return Array::from_vec(Ix1::new([1]), vec![1.0]);
118 }
119
120 let denom = (m - 1) as f64;
121 let mut data = Vec::with_capacity(m);
122 for n in 0..m {
123 let val = 0.54 - 0.46 * (2.0 * PI * n as f64 / denom).cos();
124 data.push(val);
125 }
126 Array::from_vec(Ix1::new([m]), data)
127}
128
129pub fn hanning(m: usize) -> FerrayResult<Array<f64, Ix1>> {
143 if m == 0 {
144 return Array::from_vec(Ix1::new([0]), vec![]);
145 }
146 if m == 1 {
147 return Array::from_vec(Ix1::new([1]), vec![1.0]);
148 }
149
150 let denom = (m - 1) as f64;
151 let mut data = Vec::with_capacity(m);
152 for n in 0..m {
153 let val = 0.5 * (1.0 - (2.0 * PI * n as f64 / denom).cos());
154 data.push(val);
155 }
156 Array::from_vec(Ix1::new([m]), data)
157}
158
159pub fn kaiser(m: usize, beta: f64) -> FerrayResult<Array<f64, Ix1>> {
175 if beta.is_nan() || beta < 0.0 {
176 return Err(FerrayError::invalid_value(format!(
177 "kaiser: beta must be non-negative, got {beta}"
178 )));
179 }
180
181 if m == 0 {
182 return Array::from_vec(Ix1::new([0]), vec![]);
183 }
184 if m == 1 {
185 return Array::from_vec(Ix1::new([1]), vec![1.0]);
186 }
187
188 let i0_beta = bessel_i0_scalar(beta);
189 let alpha = (m as f64 - 1.0) / 2.0;
190 let mut data = Vec::with_capacity(m);
191 for n in 0..m {
192 let t = (n as f64 - alpha) / alpha;
193 let arg = beta * (1.0 - t * t).max(0.0).sqrt();
194 data.push(bessel_i0_scalar(arg) / i0_beta);
195 }
196 Array::from_vec(Ix1::new([m]), data)
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 fn assert_close(actual: &[f64], expected: &[f64], tol: f64) {
205 assert_eq!(
206 actual.len(),
207 expected.len(),
208 "length mismatch: {} vs {}",
209 actual.len(),
210 expected.len()
211 );
212 for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
213 assert!(
214 (a - e).abs() <= tol,
215 "element {i}: {a} vs {e} (diff = {})",
216 (a - e).abs()
217 );
218 }
219 }
220
221 #[test]
226 fn bartlett_m0() {
227 let w = bartlett(0).unwrap();
228 assert_eq!(w.shape(), &[0]);
229 }
230
231 #[test]
232 fn bartlett_m1() {
233 let w = bartlett(1).unwrap();
234 assert_eq!(w.as_slice().unwrap(), &[1.0]);
235 }
236
237 #[test]
238 fn blackman_m0() {
239 let w = blackman(0).unwrap();
240 assert_eq!(w.shape(), &[0]);
241 }
242
243 #[test]
244 fn blackman_m1() {
245 let w = blackman(1).unwrap();
246 assert_eq!(w.as_slice().unwrap(), &[1.0]);
247 }
248
249 #[test]
250 fn hamming_m0() {
251 let w = hamming(0).unwrap();
252 assert_eq!(w.shape(), &[0]);
253 }
254
255 #[test]
256 fn hamming_m1() {
257 let w = hamming(1).unwrap();
258 assert_eq!(w.as_slice().unwrap(), &[1.0]);
259 }
260
261 #[test]
262 fn hanning_m0() {
263 let w = hanning(0).unwrap();
264 assert_eq!(w.shape(), &[0]);
265 }
266
267 #[test]
268 fn hanning_m1() {
269 let w = hanning(1).unwrap();
270 assert_eq!(w.as_slice().unwrap(), &[1.0]);
271 }
272
273 #[test]
274 fn kaiser_m0() {
275 let w = kaiser(0, 14.0).unwrap();
276 assert_eq!(w.shape(), &[0]);
277 }
278
279 #[test]
280 fn kaiser_m1() {
281 let w = kaiser(1, 14.0).unwrap();
282 assert_eq!(w.as_slice().unwrap(), &[1.0]);
283 }
284
285 #[test]
286 fn kaiser_negative_beta() {
287 assert!(kaiser(5, -1.0).is_err());
288 }
289
290 #[test]
291 fn kaiser_nan_beta() {
292 assert!(kaiser(5, f64::NAN).is_err());
293 }
294
295 #[test]
300 fn bartlett_5_ac1() {
301 let w = bartlett(5).unwrap();
302 let expected = [0.0, 0.5, 1.0, 0.5, 0.0];
303 assert_close(w.as_slice().unwrap(), &expected, 1e-14);
304 }
305
306 #[test]
311 fn kaiser_5_14_ac2() {
312 let w = kaiser(5, 14.0).unwrap();
313 let s = w.as_slice().unwrap();
314 assert_eq!(s.len(), 5);
315 let expected: [f64; 5] = [
317 7.72686684e-06,
318 1.64932188e-01,
319 1.0,
320 1.64932188e-01,
321 7.72686684e-06,
322 ];
323 for (i, (&a, &e)) in s.iter().zip(expected.iter()).enumerate() {
325 let tol = if e.abs() < 1e-4 { 1e-8 } else { 1e-6 };
326 assert!(
327 (a - e).abs() <= tol,
328 "kaiser element {i}: {a} vs {e} (diff = {})",
329 (a - e).abs()
330 );
331 }
332 }
333
334 #[test]
340 fn blackman_5() {
341 let w = blackman(5).unwrap();
342 assert_eq!(w.shape(), &[5]);
343 let s = w.as_slice().unwrap();
344 let expected = [
345 -1.38777878e-17,
346 3.40000000e-01,
347 1.00000000e+00,
348 3.40000000e-01,
349 -1.38777878e-17,
350 ];
351 assert_close(s, &expected, 1e-10);
352 }
353
354 #[test]
356 fn hamming_5() {
357 let w = hamming(5).unwrap();
358 assert_eq!(w.shape(), &[5]);
359 let s = w.as_slice().unwrap();
360 let expected = [0.08, 0.54, 1.0, 0.54, 0.08];
361 assert_close(s, &expected, 1e-14);
362 }
363
364 #[test]
366 fn hanning_5() {
367 let w = hanning(5).unwrap();
368 assert_eq!(w.shape(), &[5]);
369 let s = w.as_slice().unwrap();
370 let expected = [0.0, 0.5, 1.0, 0.5, 0.0];
371 assert_close(s, &expected, 1e-14);
372 }
373
374 #[test]
376 fn bartlett_12() {
377 let w = bartlett(12).unwrap();
378 assert_eq!(w.shape(), &[12]);
379 let s = w.as_slice().unwrap();
380 assert!((s[0] - 0.0).abs() < 1e-14);
382 assert!((s[11] - 0.0).abs() < 1e-14);
383 for i in 0..6 {
385 assert!((s[i] - s[11 - i]).abs() < 1e-14, "symmetry at {i}");
386 }
387 }
388
389 #[test]
391 fn all_windows_symmetric() {
392 let m = 7;
393 let windows: Vec<Array<f64, Ix1>> = vec![
394 bartlett(m).unwrap(),
395 blackman(m).unwrap(),
396 hamming(m).unwrap(),
397 hanning(m).unwrap(),
398 kaiser(m, 5.0).unwrap(),
399 ];
400 for (idx, w) in windows.iter().enumerate() {
401 let s = w.as_slice().unwrap();
402 for i in 0..m / 2 {
403 assert!(
404 (s[i] - s[m - 1 - i]).abs() < 1e-12,
405 "window {idx} not symmetric at {i}"
406 );
407 }
408 }
409 }
410
411 #[test]
413 fn all_windows_peak_at_center() {
414 let m = 9;
415 let windows: Vec<Array<f64, Ix1>> = vec![
416 bartlett(m).unwrap(),
417 blackman(m).unwrap(),
418 hamming(m).unwrap(),
419 hanning(m).unwrap(),
420 kaiser(m, 5.0).unwrap(),
421 ];
422 for (idx, w) in windows.iter().enumerate() {
423 let s = w.as_slice().unwrap();
424 let center = s[m / 2];
425 assert!(
426 (center - 1.0).abs() < 1e-10,
427 "window {idx} center = {center}, expected 1.0"
428 );
429 }
430 }
431
432 #[test]
434 fn kaiser_beta_zero() {
435 let w = kaiser(5, 0.0).unwrap();
436 let s = w.as_slice().unwrap();
437 for &v in s {
438 assert!((v - 1.0).abs() < 1e-10, "expected 1.0, got {v}");
439 }
440 }
441
442 #[test]
443 fn bessel_i0_scalar_zero() {
444 assert!((bessel_i0_scalar(0.0) - 1.0).abs() < 1e-6);
445 }
446
447 #[test]
448 fn bessel_i0_scalar_known() {
449 assert!((bessel_i0_scalar(1.0) - 1.2660658).abs() < 1e-4);
451 assert!((bessel_i0_scalar(5.0) - 27.2398718).abs() < 1e-2);
453 }
454}