1use crate::helpers::simpsons_weights;
7use crate::iter_maybe_parallel;
8use crate::matrix::FdMatrix;
9use crate::streaming_depth::{
10 FullReferenceState, SortedReferenceState, StreamingBd, StreamingDepth, StreamingFraimanMuniz,
11 StreamingMbd,
12};
13use rand::prelude::*;
14use rand_distr::StandardNormal;
15#[cfg(feature = "parallel")]
16use rayon::iter::ParallelIterator;
17
18pub fn fraiman_muniz_1d(data_obj: &FdMatrix, data_ori: &FdMatrix, scale: bool) -> Vec<f64> {
28 if data_obj.nrows() == 0 || data_ori.nrows() == 0 || data_obj.ncols() == 0 {
29 return Vec::new();
30 }
31 let state = SortedReferenceState::from_reference(data_ori);
32 let streaming = StreamingFraimanMuniz::new(state, scale);
33 streaming.depth_batch(data_obj)
34}
35
36pub fn fraiman_muniz_2d(data_obj: &FdMatrix, data_ori: &FdMatrix, scale: bool) -> Vec<f64> {
38 fraiman_muniz_1d(data_obj, data_ori, scale)
40}
41
42pub fn modal_1d(data_obj: &FdMatrix, data_ori: &FdMatrix, h: f64) -> Vec<f64> {
51 let nobj = data_obj.nrows();
52 let nori = data_ori.nrows();
53 let n_points = data_obj.ncols();
54
55 if nobj == 0 || nori == 0 || n_points == 0 {
56 return Vec::new();
57 }
58
59 iter_maybe_parallel!(0..nobj)
60 .map(|i| {
61 let mut depth = 0.0;
62
63 for j in 0..nori {
64 let mut dist_sq = 0.0;
65 for t in 0..n_points {
66 let diff = data_obj[(i, t)] - data_ori[(j, t)];
67 dist_sq += diff * diff;
68 }
69 let dist = (dist_sq / n_points as f64).sqrt();
70 let kernel_val = (-0.5 * (dist / h).powi(2)).exp();
71 depth += kernel_val;
72 }
73
74 depth / nori as f64
75 })
76 .collect()
77}
78
79pub fn modal_2d(data_obj: &FdMatrix, data_ori: &FdMatrix, h: f64) -> Vec<f64> {
81 modal_1d(data_obj, data_ori, h)
82}
83
84fn random_depth_core(
91 data_obj: &FdMatrix,
92 data_ori: &FdMatrix,
93 nproj: usize,
94 init: f64,
95 aggregate: impl Fn(f64, f64) -> f64 + Sync,
96 finalize: impl Fn(f64, usize) -> f64 + Sync,
97) -> Vec<f64> {
98 let nobj = data_obj.nrows();
99 let nori = data_ori.nrows();
100 let n_points = data_obj.ncols();
101
102 if nobj == 0 || nori == 0 || n_points == 0 || nproj == 0 {
103 return Vec::new();
104 }
105
106 let mut rng = rand::thread_rng();
107 let m = n_points;
108 let mut projections = vec![0.0; nproj * m];
109 for p_idx in 0..nproj {
110 let base = p_idx * m;
111 let mut norm_sq = 0.0;
112 for t in 0..m {
113 let v: f64 = rng.sample(StandardNormal);
114 projections[base + t] = v;
115 norm_sq += v * v;
116 }
117 let inv_norm = 1.0 / norm_sq.sqrt();
118 for t in 0..m {
119 projections[base + t] *= inv_norm;
120 }
121 }
122
123 let mut sorted_proj_ori = vec![0.0; nproj * nori];
125 for p_idx in 0..nproj {
126 let proj = &projections[p_idx * m..(p_idx + 1) * m];
127 let spo = &mut sorted_proj_ori[p_idx * nori..(p_idx + 1) * nori];
128 for j in 0..nori {
129 let mut dot = 0.0;
130 for t in 0..m {
131 dot += data_ori[(j, t)] * proj[t];
132 }
133 spo[j] = dot;
134 }
135 spo.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
136 }
137
138 let denom = nori as f64 + 1.0;
139
140 iter_maybe_parallel!(0..nobj)
141 .map(|i| {
142 let mut acc = init;
143
144 for p_idx in 0..nproj {
145 let proj = &projections[p_idx * m..(p_idx + 1) * m];
146 let sorted_ori = &sorted_proj_ori[p_idx * nori..(p_idx + 1) * nori];
147
148 let mut proj_i = 0.0;
149 for t in 0..m {
150 proj_i += data_obj[(i, t)] * proj[t];
151 }
152
153 let below = sorted_ori.partition_point(|&v| v < proj_i);
155 let above = nori - sorted_ori.partition_point(|&v| v <= proj_i);
156 let depth = (below.min(above) as f64 + 1.0) / denom;
157
158 acc = aggregate(acc, depth);
159 }
160
161 finalize(acc, nproj)
162 })
163 .collect()
164}
165
166pub fn random_projection_1d(data_obj: &FdMatrix, data_ori: &FdMatrix, nproj: usize) -> Vec<f64> {
176 random_depth_core(
177 data_obj,
178 data_ori,
179 nproj,
180 0.0,
181 |acc, d| acc + d,
182 |acc, n| acc / n as f64,
183 )
184}
185
186pub fn random_projection_2d(data_obj: &FdMatrix, data_ori: &FdMatrix, nproj: usize) -> Vec<f64> {
188 random_projection_1d(data_obj, data_ori, nproj)
189}
190
191pub fn random_tukey_1d(data_obj: &FdMatrix, data_ori: &FdMatrix, nproj: usize) -> Vec<f64> {
195 random_depth_core(
196 data_obj,
197 data_ori,
198 nproj,
199 f64::INFINITY,
200 f64::min,
201 |acc, _| acc,
202 )
203}
204
205pub fn random_tukey_2d(data_obj: &FdMatrix, data_ori: &FdMatrix, nproj: usize) -> Vec<f64> {
207 random_tukey_1d(data_obj, data_ori, nproj)
208}
209
210pub fn functional_spatial_1d(data_obj: &FdMatrix, data_ori: &FdMatrix) -> Vec<f64> {
212 let nobj = data_obj.nrows();
213 let nori = data_ori.nrows();
214 let n_points = data_obj.ncols();
215
216 if nobj == 0 || nori == 0 || n_points == 0 {
217 return Vec::new();
218 }
219
220 iter_maybe_parallel!(0..nobj)
221 .map(|i| {
222 let mut sum_unit = vec![0.0; n_points];
223
224 for j in 0..nori {
225 let mut norm_sq = 0.0;
227 for t in 0..n_points {
228 let d = data_ori[(j, t)] - data_obj[(i, t)];
229 norm_sq += d * d;
230 }
231
232 let norm = norm_sq.sqrt();
233 if norm > 1e-10 {
234 let inv_norm = 1.0 / norm;
236 for t in 0..n_points {
237 sum_unit[t] += (data_ori[(j, t)] - data_obj[(i, t)]) * inv_norm;
238 }
239 }
240 }
241
242 let mut avg_norm_sq = 0.0;
243 for t in 0..n_points {
244 let avg = sum_unit[t] / nori as f64;
245 avg_norm_sq += avg * avg;
246 }
247
248 1.0 - avg_norm_sq.sqrt()
249 })
250 .collect()
251}
252
253pub fn functional_spatial_2d(data_obj: &FdMatrix, data_ori: &FdMatrix) -> Vec<f64> {
255 functional_spatial_1d(data_obj, data_ori)
256}
257
258fn kernel_pair_contribution(j: usize, k: usize, m1: &FdMatrix, m2: &[f64]) -> Option<f64> {
260 let denom_j_sq = 2.0 - 2.0 * m2[j];
261 if denom_j_sq < 1e-20 {
262 return None;
263 }
264 let denom_k_sq = 2.0 - 2.0 * m2[k];
265 if denom_k_sq < 1e-20 {
266 return None;
267 }
268 let denom = denom_j_sq.sqrt() * denom_k_sq.sqrt();
269 if denom <= 1e-20 {
270 return None;
271 }
272 let m_ijk = (1.0 + m1[(j, k)] - m2[j] - m2[k]) / denom;
273 if m_ijk.is_finite() {
274 Some(m_ijk)
275 } else {
276 None
277 }
278}
279
280fn kfsd_accumulate(m2: &[f64], m1: &FdMatrix, nori: usize) -> (f64, usize) {
287 let mut total_sum = 0.0;
288 let mut valid_count = 0;
289
290 for j in 0..nori {
292 if let Some(val) = kernel_pair_contribution(j, j, m1, m2) {
293 total_sum += val;
294 valid_count += 1;
295 }
296 }
297
298 for j in 0..nori {
300 for k in (j + 1)..nori {
301 if let Some(val) = kernel_pair_contribution(j, k, m1, m2) {
302 total_sum += 2.0 * val;
303 valid_count += 2;
304 }
305 }
306 }
307
308 (total_sum, valid_count)
309}
310
311fn kfsd_weighted(data_obj: &FdMatrix, data_ori: &FdMatrix, h: f64, weights: &[f64]) -> Vec<f64> {
314 let nobj = data_obj.nrows();
315 let nori = data_ori.nrows();
316 let n_points = data_obj.ncols();
317 let h_sq = h * h;
318
319 let m1_upper: Vec<(usize, usize, f64)> = iter_maybe_parallel!(0..nori)
321 .flat_map(|j| {
322 ((j + 1)..nori)
323 .map(|k| {
324 let mut sum = 0.0;
325 for t in 0..n_points {
326 let diff = data_ori[(j, t)] - data_ori[(k, t)];
327 sum += weights[t] * diff * diff;
328 }
329 (j, k, (-sum / h_sq).exp())
330 })
331 .collect::<Vec<_>>()
332 })
333 .collect();
334
335 let mut m1 = FdMatrix::zeros(nori, nori);
336 for j in 0..nori {
337 m1[(j, j)] = 1.0;
338 }
339 for (j, k, kval) in m1_upper {
340 m1[(j, k)] = kval;
341 m1[(k, j)] = kval;
342 }
343
344 let nori_f64 = nori as f64;
345
346 iter_maybe_parallel!(0..nobj)
347 .map(|i| {
348 let m2: Vec<f64> = (0..nori)
349 .map(|j| {
350 let mut sum = 0.0;
351 for t in 0..n_points {
352 let diff = data_obj[(i, t)] - data_ori[(j, t)];
353 sum += weights[t] * diff * diff;
354 }
355 (-sum / h_sq).exp()
356 })
357 .collect();
358
359 let (total_sum, valid_count) = kfsd_accumulate(&m2, &m1, nori);
360
361 if valid_count > 0 && total_sum >= 0.0 {
362 1.0 - total_sum.sqrt() / nori_f64
363 } else if total_sum < 0.0 {
364 1.0
365 } else {
366 0.0
367 }
368 })
369 .collect()
370}
371
372
373pub fn kernel_functional_spatial_1d(
377 data_obj: &FdMatrix,
378 data_ori: &FdMatrix,
379 argvals: &[f64],
380 h: f64,
381) -> Vec<f64> {
382 let nobj = data_obj.nrows();
383 let nori = data_ori.nrows();
384 let n_points = data_obj.ncols();
385
386 if nobj == 0 || nori == 0 || n_points == 0 {
387 return Vec::new();
388 }
389
390 let weights = simpsons_weights(argvals);
391 kfsd_weighted(data_obj, data_ori, h, &weights)
392}
393
394pub fn kernel_functional_spatial_2d(data_obj: &FdMatrix, data_ori: &FdMatrix, h: f64) -> Vec<f64> {
396 let nobj = data_obj.nrows();
397 let nori = data_ori.nrows();
398 let n_points = data_obj.ncols();
399
400 if nobj == 0 || nori == 0 || n_points == 0 {
401 return Vec::new();
402 }
403
404 let weights = vec![1.0; n_points];
405 kfsd_weighted(data_obj, data_ori, h, &weights)
406}
407
408pub fn band_1d(data_obj: &FdMatrix, data_ori: &FdMatrix) -> Vec<f64> {
412 if data_obj.nrows() == 0 || data_ori.nrows() < 2 || data_obj.ncols() == 0 {
413 return Vec::new();
414 }
415 let state = FullReferenceState::from_reference(data_ori);
416 let streaming = StreamingBd::new(state);
417 streaming.depth_batch(data_obj)
418}
419
420pub fn modified_band_1d(data_obj: &FdMatrix, data_ori: &FdMatrix) -> Vec<f64> {
424 if data_obj.nrows() == 0 || data_ori.nrows() < 2 || data_obj.ncols() == 0 {
425 return Vec::new();
426 }
427 let state = SortedReferenceState::from_reference(data_ori);
428 let streaming = StreamingMbd::new(state);
429 streaming.depth_batch(data_obj)
430}
431
432pub fn modified_epigraph_index_1d(data_obj: &FdMatrix, data_ori: &FdMatrix) -> Vec<f64> {
436 let nobj = data_obj.nrows();
437 let nori = data_ori.nrows();
438 let n_points = data_obj.ncols();
439
440 if nobj == 0 || nori == 0 || n_points == 0 {
441 return Vec::new();
442 }
443
444 iter_maybe_parallel!(0..nobj)
445 .map(|i| {
446 let mut total = 0.0;
447
448 for j in 0..nori {
449 let mut count = 0.0;
450
451 for t in 0..n_points {
452 let xi = data_obj[(i, t)];
453 let xj = data_ori[(j, t)];
454
455 if xi < xj {
456 count += 1.0;
457 } else if (xi - xj).abs() < 1e-12 {
458 count += 0.5;
459 }
460 }
461
462 total += count / n_points as f64;
463 }
464
465 total / nori as f64
466 })
467 .collect()
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473 use std::f64::consts::PI;
474
475 fn uniform_grid(n: usize) -> Vec<f64> {
476 (0..n).map(|i| i as f64 / (n - 1) as f64).collect()
477 }
478
479 fn generate_centered_data(n: usize, m: usize) -> FdMatrix {
480 let argvals = uniform_grid(m);
481 let mut data = vec![0.0; n * m];
482 for i in 0..n {
483 let offset = (i as f64 - n as f64 / 2.0) / (n as f64);
484 for j in 0..m {
485 data[i + j * n] = (2.0 * PI * argvals[j]).sin() + offset;
486 }
487 }
488 FdMatrix::from_column_major(data, n, m).unwrap()
489 }
490
491 #[test]
494 fn test_fraiman_muniz() {
495 let data = FdMatrix::from_column_major(vec![1.0, 1.0, 2.0, 2.0], 2, 2).unwrap(); let depths = fraiman_muniz_1d(&data, &data, true);
498 assert_eq!(depths.len(), 2);
499 }
500
501 #[test]
502 fn test_fraiman_muniz_central_deeper() {
503 let n = 20;
504 let m = 30;
505 let data = generate_centered_data(n, m);
506 let depths = fraiman_muniz_1d(&data, &data, true);
507
508 let central_depth = depths[n / 2];
510 let edge_depth = depths[0];
511 assert!(
512 central_depth > edge_depth,
513 "Central curve should be deeper: {} > {}",
514 central_depth,
515 edge_depth
516 );
517 }
518
519 #[test]
520 fn test_fraiman_muniz_range() {
521 let n = 15;
522 let m = 20;
523 let data = generate_centered_data(n, m);
524 let depths = fraiman_muniz_1d(&data, &data, true);
525
526 for d in &depths {
527 assert!(*d >= 0.0 && *d <= 1.0, "Depth should be in [0, 1]");
528 }
529 }
530
531 #[test]
532 fn test_fraiman_muniz_invalid() {
533 let empty = FdMatrix::zeros(0, 0);
534 assert!(fraiman_muniz_1d(&empty, &empty, true).is_empty());
535 }
536
537 #[test]
540 fn test_modal_central_deeper() {
541 let n = 20;
542 let m = 30;
543 let data = generate_centered_data(n, m);
544 let depths = modal_1d(&data, &data, 0.5);
545
546 let central_depth = depths[n / 2];
547 let edge_depth = depths[0];
548 assert!(central_depth > edge_depth, "Central curve should be deeper");
549 }
550
551 #[test]
552 fn test_modal_positive() {
553 let n = 10;
554 let m = 20;
555 let data = generate_centered_data(n, m);
556 let depths = modal_1d(&data, &data, 0.5);
557
558 for d in &depths {
559 assert!(*d > 0.0, "Modal depth should be positive");
560 }
561 }
562
563 #[test]
564 fn test_modal_invalid() {
565 let empty = FdMatrix::zeros(0, 0);
566 assert!(modal_1d(&empty, &empty, 0.5).is_empty());
567 }
568
569 #[test]
572 fn test_rp_depth_range() {
573 let n = 15;
574 let m = 20;
575 let data = generate_centered_data(n, m);
576 let depths = random_projection_1d(&data, &data, 50);
577
578 for d in &depths {
579 assert!(*d >= 0.0 && *d <= 1.0, "RP depth should be in [0, 1]");
580 }
581 }
582
583 #[test]
584 fn test_rp_depth_invalid() {
585 let empty = FdMatrix::zeros(0, 0);
586 assert!(random_projection_1d(&empty, &empty, 10).is_empty());
587 }
588
589 #[test]
592 fn test_random_tukey_range() {
593 let n = 15;
594 let m = 20;
595 let data = generate_centered_data(n, m);
596 let depths = random_tukey_1d(&data, &data, 50);
597
598 for d in &depths {
599 assert!(*d >= 0.0 && *d <= 1.0, "Tukey depth should be in [0, 1]");
600 }
601 }
602
603 #[test]
604 fn test_random_tukey_invalid() {
605 let empty = FdMatrix::zeros(0, 0);
606 assert!(random_tukey_1d(&empty, &empty, 10).is_empty());
607 }
608
609 #[test]
612 fn test_functional_spatial_range() {
613 let n = 15;
614 let m = 20;
615 let data = generate_centered_data(n, m);
616 let depths = functional_spatial_1d(&data, &data);
617
618 for d in &depths {
619 assert!(*d >= 0.0 && *d <= 1.0, "Spatial depth should be in [0, 1]");
620 }
621 }
622
623 #[test]
624 fn test_functional_spatial_invalid() {
625 let empty = FdMatrix::zeros(0, 0);
626 assert!(functional_spatial_1d(&empty, &empty).is_empty());
627 }
628
629 #[test]
632 fn test_band_depth_central_deeper() {
633 let n = 10;
634 let m = 20;
635 let data = generate_centered_data(n, m);
636 let depths = band_1d(&data, &data);
637
638 let central_depth = depths[n / 2];
640 let edge_depth = depths[0];
641 assert!(
642 central_depth >= edge_depth,
643 "Central curve should have higher band depth"
644 );
645 }
646
647 #[test]
648 fn test_band_depth_range() {
649 let n = 10;
650 let m = 20;
651 let data = generate_centered_data(n, m);
652 let depths = band_1d(&data, &data);
653
654 for d in &depths {
655 assert!(*d >= 0.0 && *d <= 1.0, "Band depth should be in [0, 1]");
656 }
657 }
658
659 #[test]
660 fn test_band_depth_invalid() {
661 let empty = FdMatrix::zeros(0, 0);
662 assert!(band_1d(&empty, &empty).is_empty());
663 let single = FdMatrix::from_column_major(vec![1.0, 2.0], 1, 2).unwrap();
664 assert!(band_1d(&single, &single).is_empty()); }
666
667 #[test]
670 fn test_modified_band_depth_range() {
671 let n = 10;
672 let m = 20;
673 let data = generate_centered_data(n, m);
674 let depths = modified_band_1d(&data, &data);
675
676 for d in &depths {
677 assert!(*d >= 0.0 && *d <= 1.0, "MBD should be in [0, 1]");
678 }
679 }
680
681 #[test]
682 fn test_modified_band_depth_invalid() {
683 let empty = FdMatrix::zeros(0, 0);
684 assert!(modified_band_1d(&empty, &empty).is_empty());
685 }
686
687 #[test]
690 fn test_mei_range() {
691 let n = 15;
692 let m = 20;
693 let data = generate_centered_data(n, m);
694 let mei = modified_epigraph_index_1d(&data, &data);
695
696 for d in &mei {
697 assert!(*d >= 0.0 && *d <= 1.0, "MEI should be in [0, 1]");
698 }
699 }
700
701 #[test]
702 fn test_mei_invalid() {
703 let empty = FdMatrix::zeros(0, 0);
704 assert!(modified_epigraph_index_1d(&empty, &empty).is_empty());
705 }
706
707 #[test]
710 fn test_kfsd_1d_range() {
711 let n = 10;
712 let m = 20;
713 let argvals = uniform_grid(m);
714 let data = generate_centered_data(n, m);
715 let depths = kernel_functional_spatial_1d(&data, &data, &argvals, 0.5);
716
717 assert_eq!(depths.len(), n);
718 for d in &depths {
719 assert!(
720 *d >= -0.01 && *d <= 1.01,
721 "KFSD depth should be near [0, 1], got {}",
722 d
723 );
724 assert!(d.is_finite(), "KFSD depth should be finite");
725 }
726
727 let central_depth = depths[n / 2];
729 let edge_depth = depths[0];
730 assert!(
731 central_depth > edge_depth,
732 "Central KFSD depth {} should be > edge depth {}",
733 central_depth,
734 edge_depth
735 );
736 }
737
738 #[test]
739 fn test_kfsd_1d_identical() {
740 let n = 5;
742 let m = 10;
743 let argvals = uniform_grid(m);
744 let data_vec: Vec<f64> = (0..n * m)
745 .map(|i| (2.0 * PI * (i % m) as f64 / (m - 1) as f64).sin())
746 .collect();
747 let data = FdMatrix::from_column_major(data_vec, n, m).unwrap();
748
749 let depths = kernel_functional_spatial_1d(&data, &data, &argvals, 0.5);
752
753 assert_eq!(depths.len(), n);
754 for d in &depths {
755 assert!(
756 d.is_finite(),
757 "KFSD depth should be finite for identical curves"
758 );
759 }
760 }
761
762 #[test]
763 fn test_kfsd_1d_invalid() {
764 let argvals = uniform_grid(10);
765 let empty = FdMatrix::zeros(0, 0);
766 assert!(kernel_functional_spatial_1d(&empty, &empty, &argvals, 0.5).is_empty());
767 let empty_obj = FdMatrix::zeros(0, 0);
768 assert!(kernel_functional_spatial_1d(&empty_obj, &empty_obj, &argvals, 0.5).is_empty());
769 }
770
771 #[test]
774 fn test_kfsd_2d_range() {
775 let n = 8;
776 let m = 15;
777 let data = generate_centered_data(n, m);
778 let depths = kernel_functional_spatial_2d(&data, &data, 0.5);
779
780 assert_eq!(depths.len(), n);
781 for d in &depths {
782 assert!(
783 *d >= -0.01 && *d <= 1.01,
784 "KFSD 2D depth should be near [0, 1], got {}",
785 d
786 );
787 assert!(d.is_finite(), "KFSD 2D depth should be finite");
788 }
789 }
790
791 #[test]
794 fn test_fraiman_muniz_2d_delegates() {
795 let n = 10;
796 let m = 15;
797 let data = generate_centered_data(n, m);
798 let depths_1d = fraiman_muniz_1d(&data, &data, true);
799 let depths_2d = fraiman_muniz_2d(&data, &data, true);
800 assert_eq!(depths_1d, depths_2d);
801 }
802
803 #[test]
804 fn test_modal_2d_delegates() {
805 let n = 10;
806 let m = 15;
807 let data = generate_centered_data(n, m);
808 let depths_1d = modal_1d(&data, &data, 0.5);
809 let depths_2d = modal_2d(&data, &data, 0.5);
810 assert_eq!(depths_1d, depths_2d);
811 }
812
813 #[test]
814 fn test_functional_spatial_2d_delegates() {
815 let n = 10;
816 let m = 15;
817 let data = generate_centered_data(n, m);
818 let depths_1d = functional_spatial_1d(&data, &data);
819 let depths_2d = functional_spatial_2d(&data, &data);
820 assert_eq!(depths_1d, depths_2d);
821 }
822
823 #[test]
824 fn test_random_projection_2d_returns_valid() {
825 let n = 10;
826 let m = 15;
827 let data = generate_centered_data(n, m);
828 let depths = random_projection_2d(&data, &data, 20);
829 assert_eq!(depths.len(), n);
830 for d in &depths {
831 assert!(*d >= 0.0 && *d <= 1.0, "RP 2D depth should be in [0, 1]");
832 }
833 }
834
835 fn golden_data() -> FdMatrix {
839 let n = 5;
840 let m = 10;
841 let argvals: Vec<f64> = (0..m).map(|i| i as f64 / (m - 1) as f64).collect();
842 let mut data = vec![0.0; n * m];
843 for i in 0..n {
844 let offset = (i as f64 - n as f64 / 2.0) / (n as f64);
845 for j in 0..m {
846 data[i + j * n] = (2.0 * PI * argvals[j]).sin() + offset;
847 }
848 }
849 FdMatrix::from_column_major(data, n, m).unwrap()
850 }
851
852 #[test]
853 fn test_fm_golden_values_scaled() {
854 let data = golden_data();
855 let depths = fraiman_muniz_1d(&data, &data, true);
856 let expected = [0.4, 0.8, 0.8, 0.4, 0.0];
857 assert_eq!(depths.len(), expected.len());
858 for (d, e) in depths.iter().zip(expected.iter()) {
859 assert!(
860 (d - e).abs() < 1e-10,
861 "FM scaled golden mismatch: got {}, expected {}",
862 d,
863 e
864 );
865 }
866 }
867
868 #[test]
869 fn test_fm_golden_values_unscaled() {
870 let data = golden_data();
871 let depths = fraiman_muniz_1d(&data, &data, false);
872 let expected = [0.2, 0.4, 0.4, 0.2, 0.0];
873 assert_eq!(depths.len(), expected.len());
874 for (d, e) in depths.iter().zip(expected.iter()) {
875 assert!(
876 (d - e).abs() < 1e-10,
877 "FM unscaled golden mismatch: got {}, expected {}",
878 d,
879 e
880 );
881 }
882 }
883
884 #[test]
885 fn test_mbd_golden_values() {
886 let data = golden_data();
887 let depths = modified_band_1d(&data, &data);
888 let expected = [0.4, 0.7, 0.8, 0.7, 0.4];
889 assert_eq!(depths.len(), expected.len());
890 for (d, e) in depths.iter().zip(expected.iter()) {
891 assert!(
892 (d - e).abs() < 1e-10,
893 "MBD golden mismatch: got {}, expected {}",
894 d,
895 e
896 );
897 }
898 }
899}