crate::ix!();
pub trait CompareLevelSpecific {
fn compare_level_specific(&self, other: &GrowerTreeConfiguration) -> CompareOutcome;
}
impl CompareLevelSpecific for GrowerTreeConfiguration {
fn compare_level_specific(&self, other: &GrowerTreeConfiguration) -> CompareOutcome {
match (self.level_specific(), other.level_specific()) {
(None, None) => CompareOutcome::Exact,
(None, Some(_)) | (Some(_), None) => CompareOutcome::Partial(0.5),
(Some(a), Some(b)) => {
let ab = a.breadth_per_level();
let bb = b.breadth_per_level();
let ad = a.density_per_level();
let bd = b.density_per_level();
if ab.len() != bb.len() || ad.len() != bd.len() {
return CompareOutcome::Incompatible;
}
let mut total_score = 1.0;
let len_min = ab.len(); for i in 0..len_min {
let diff = (ab[i] as i32 - bb[i] as i32).abs();
if diff > 10 {
total_score *= 0.5;
} else if diff > 2 {
total_score *= 0.8;
}
}
let len_min_d = ad.len(); for i in 0..len_min_d {
let diff = (ad[i] as i32 - bd[i] as i32).abs();
if diff > 10 {
total_score *= 0.5;
} else if diff > 2 {
total_score *= 0.8;
}
}
if total_score < 0.2 {
CompareOutcome::Incompatible
} else if total_score < 1.0 {
CompareOutcome::Partial(total_score)
} else {
CompareOutcome::Exact
}
}
}
}
}
#[cfg(test)]
mod compare_level_specific_tests {
use super::*;
use traced_test::traced_test;
#[traced_test]
fn both_none() {
let a = GrowerTreeConfiguration::default();
let b = a.clone();
assert_eq!(a.compare_level_specific(&b), CompareOutcome::Exact);
}
#[traced_test]
fn same_arrays_exact() {
let lsc = TreeLevelSpecificConfigurationBuilder::default()
.breadth_per_level(vec![2,3])
.density_per_level(vec![1,1])
.build().unwrap();
let a = GrowerTreeConfiguration::default()
.to_builder().level_specific(Some(lsc.clone())).build().unwrap();
let b = a.clone();
assert_eq!(a.compare_level_specific(&b), CompareOutcome::Exact);
}
#[traced_test]
fn partial_mismatch() {
let lsc1 = TreeLevelSpecificConfigurationBuilder::default()
.breadth_per_level(vec![2,3,4])
.density_per_level(vec![1,1,2])
.build().unwrap();
let lsc2 = TreeLevelSpecificConfigurationBuilder::default()
.breadth_per_level(vec![2,3,9])
.density_per_level(vec![1,1,2])
.build().unwrap();
let a = GrowerTreeConfiguration::default()
.to_builder().level_specific(Some(lsc1)).build().unwrap();
let b = a.to_builder().level_specific(Some(lsc2)).build().unwrap();
match a.compare_level_specific(&b) {
CompareOutcome::Partial(_) => {},
other => panic!("expected partial, got {:?}", other),
}
}
#[traced_test]
fn different_length_incompatible() {
let lsc1 = TreeLevelSpecificConfigurationBuilder::default()
.breadth_per_level(vec![2,3])
.density_per_level(vec![1,1])
.build().unwrap();
let lsc2 = TreeLevelSpecificConfigurationBuilder::default()
.breadth_per_level(vec![2,3,4])
.density_per_level(vec![1,1,1])
.build().unwrap();
let a = GrowerTreeConfiguration::default()
.to_builder().level_specific(Some(lsc1)).build().unwrap();
let b = a.to_builder().level_specific(Some(lsc2)).build().unwrap();
let got = a.compare_level_specific(&b);
assert_eq!(got, CompareOutcome::Incompatible, "length mismatch => Incompatible");
}
}