capability_grower_configuration_comparison/
compare_level_specific.rs1crate::ix!();
3
4pub trait CompareLevelSpecific {
5 fn compare_level_specific(&self, other: &GrowerTreeConfiguration) -> CompareOutcome;
6}
7
8impl CompareLevelSpecific for GrowerTreeConfiguration {
9 fn compare_level_specific(&self, other: &GrowerTreeConfiguration) -> CompareOutcome {
10 match (self.level_specific(), other.level_specific()) {
11 (None, None) => CompareOutcome::Exact,
12 (None, Some(_)) | (Some(_), None) => CompareOutcome::Partial(0.5),
13 (Some(a), Some(b)) => {
14 let ab = a.breadth_per_level();
16 let bb = b.breadth_per_level();
17 let ad = a.density_per_level();
18 let bd = b.density_per_level();
19
20 if ab.len() != bb.len() || ad.len() != bd.len() {
21 return CompareOutcome::Incompatible;
22 }
23
24 let mut total_score = 1.0;
25
26 let len_min = ab.len(); for i in 0..len_min {
29 let diff = (ab[i] as i32 - bb[i] as i32).abs();
30 if diff > 10 {
31 total_score *= 0.5;
32 } else if diff > 2 {
33 total_score *= 0.8;
34 }
35 }
36 let len_min_d = ad.len(); for i in 0..len_min_d {
39 let diff = (ad[i] as i32 - bd[i] as i32).abs();
40 if diff > 10 {
41 total_score *= 0.5;
42 } else if diff > 2 {
43 total_score *= 0.8;
44 }
45 }
46
47 if total_score < 0.2 {
48 CompareOutcome::Incompatible
49 } else if total_score < 1.0 {
50 CompareOutcome::Partial(total_score)
51 } else {
52 CompareOutcome::Exact
53 }
54 }
55 }
56 }
57}
58
59#[cfg(test)]
60mod compare_level_specific_tests {
61 use super::*;
62 use traced_test::traced_test;
63
64 #[traced_test]
65 fn both_none() {
66 let a = GrowerTreeConfiguration::default();
67 let b = a.clone();
68 assert_eq!(a.compare_level_specific(&b), CompareOutcome::Exact);
69 }
70
71 #[traced_test]
72 fn same_arrays_exact() {
73 let lsc = TreeLevelSpecificConfigurationBuilder::default()
74 .breadth_per_level(vec![2,3])
75 .density_per_level(vec![1,1])
76 .build().unwrap();
77 let a = GrowerTreeConfiguration::default()
78 .to_builder().level_specific(Some(lsc.clone())).build().unwrap();
79 let b = a.clone();
80 assert_eq!(a.compare_level_specific(&b), CompareOutcome::Exact);
81 }
82
83 #[traced_test]
84 fn partial_mismatch() {
85 let lsc1 = TreeLevelSpecificConfigurationBuilder::default()
86 .breadth_per_level(vec![2,3,4])
87 .density_per_level(vec![1,1,2])
88 .build().unwrap();
89 let lsc2 = TreeLevelSpecificConfigurationBuilder::default()
90 .breadth_per_level(vec![2,3,9])
91 .density_per_level(vec![1,1,2])
92 .build().unwrap();
93 let a = GrowerTreeConfiguration::default()
94 .to_builder().level_specific(Some(lsc1)).build().unwrap();
95 let b = a.to_builder().level_specific(Some(lsc2)).build().unwrap();
96 match a.compare_level_specific(&b) {
97 CompareOutcome::Partial(_) => {},
98 other => panic!("expected partial, got {:?}", other),
99 }
100 }
101
102 #[traced_test]
103 fn different_length_incompatible() {
104 let lsc1 = TreeLevelSpecificConfigurationBuilder::default()
105 .breadth_per_level(vec![2,3])
106 .density_per_level(vec![1,1])
107 .build().unwrap();
108 let lsc2 = TreeLevelSpecificConfigurationBuilder::default()
109 .breadth_per_level(vec![2,3,4])
110 .density_per_level(vec![1,1,1])
111 .build().unwrap();
112 let a = GrowerTreeConfiguration::default()
113 .to_builder().level_specific(Some(lsc1)).build().unwrap();
114 let b = a.to_builder().level_specific(Some(lsc2)).build().unwrap();
115
116 let got = a.compare_level_specific(&b);
117 assert_eq!(got, CompareOutcome::Incompatible, "length mismatch => Incompatible");
118 }
119}