use std::collections::{BTreeMap, BTreeSet};
pub const LEVEL_REFERENCE_FIELD: &str = "m_levelId";
pub const LEVEL_REFERENCE_SOURCE: &str = "partition_element_record_reference_list";
pub const LEVEL_ELEMENT_ID_PROPERTY: &str = "LevelElementId";
pub const CONSTRAINT_LEVELS_FIELD: &str = "m_constraintLevelIds";
pub const LEVEL_BIND_SOURCE_FIELD: &str = "m_levelBindSource";
pub const BASE_CONSTRAINT_SOURCE: &str = "partition_element_record_base_constraint";
pub const REFERENCES_FIELD: &str = "m_referenceIds";
pub const HOST_SOURCE: &str = "partition_element_record_host";
pub const LEVEL_OBJECT_SOURCE: &str = "partition_element_record_level_object";
pub const PLUMBING_LEVEL_OBJECT_SOURCE: &str = "partition_element_record_plumbing_level_object";
pub const BASE_AT_LEVEL_SOURCE: &str = "partition_element_record_base_at_level";
pub const SAME_CLASS_HOST_SOURCE: &str = "partition_element_record_same_class_host";
pub const BASE_ELEVATION_SOURCE: &str = "partition_element_record_base_elevation";
pub const BELOW_LEVEL_TOLERANCE_FEET: f64 = 0.25;
pub const CLEAR_OF_LEVEL_FEET: f64 = 0.5;
pub fn elevation_band_level(elevations: &BTreeMap<u32, f64>, base_feet: f64) -> Option<u32> {
if !base_feet.is_finite() {
return None;
}
let above = elevations
.values()
.copied()
.filter(|at| *at > base_feet + 1e-3)
.fold(None, |low: Option<f64>, at| {
Some(low.map_or(at, |l| l.min(at)))
});
match above {
Some(up) if up - base_feet <= BELOW_LEVEL_TOLERANCE_FEET => {
let mut at_up = elevations.iter().filter(|(_, at)| **at == up);
let (id, _) = at_up.next()?;
at_up.next().is_none().then_some(*id)
}
Some(up) if up - base_feet < CLEAR_OF_LEVEL_FEET => None,
_ => level_at_or_below(elevations, base_feet),
}
}
pub fn level_at_or_below(elevations: &BTreeMap<u32, f64>, base_feet: f64) -> Option<u32> {
if !base_feet.is_finite() {
return None;
}
let mut best: Option<(u32, f64)> = None;
let mut tied = false;
for (&id, &at) in elevations {
if at > base_feet + 1e-3 {
continue;
}
match best {
Some((_, seen)) if at < seen => {}
Some((_, seen)) if at == seen => tied = true,
_ => {
best = Some((id, at));
tied = false;
}
}
}
if tied { None } else { best.map(|(id, _)| id) }
}
pub fn named_levels(references: &[u64], level_ids: &BTreeSet<u32>) -> Vec<u32> {
let mut named = Vec::new();
for slot in references {
if let Ok(id) = u32::try_from(*slot) {
if level_ids.contains(&id) && !named.contains(&id) {
named.push(id);
}
}
}
named
}
pub fn base_constraint_level(
named: &[u32],
elevations: &BTreeMap<u32, f64>,
base_feet: f64,
) -> Option<u32> {
let [a, b] = named else {
return None;
};
let (ea, eb) = (*elevations.get(a)?, *elevations.get(b)?);
if ea == eb || !base_feet.is_finite() {
return None;
}
let (low, high, high_at) = if ea < eb { (*a, *b, eb) } else { (*b, *a, ea) };
Some(if high_at <= base_feet + 1e-3 {
high
} else {
low
})
}
pub fn unique_level_reference(references: &[u64], level_ids: &BTreeSet<u32>) -> Option<u32> {
let mut found: Option<u32> = None;
for slot in references {
let Ok(id) = u32::try_from(*slot) else {
continue;
};
if !level_ids.contains(&id) {
continue;
}
match found {
None => found = Some(id),
Some(seen) if seen == id => {}
Some(_) => return None,
}
}
found
}
#[cfg(test)]
mod tests {
use super::*;
fn levels(ids: &[u32]) -> BTreeSet<u32> {
ids.iter().copied().collect()
}
#[test]
fn one_named_level_resolves() {
assert_eq!(
unique_level_reference(&[9001, 20307, 71_299], &levels(&[20307, 20308])),
Some(20307)
);
}
#[test]
fn the_same_level_named_twice_is_still_one_level() {
assert_eq!(
unique_level_reference(&[20307, 55, 20307], &levels(&[20307])),
Some(20307)
);
}
#[test]
fn two_distinct_levels_fail_closed() {
assert_eq!(
unique_level_reference(&[20307, 20308], &levels(&[20307, 20308])),
None
);
}
#[test]
fn no_named_level_resolves_to_none() {
assert_eq!(unique_level_reference(&[1, 2, 3], &levels(&[20307])), None);
assert_eq!(unique_level_reference(&[], &levels(&[20307])), None);
}
#[test]
fn slots_outside_the_elementid_range_are_skipped() {
let out_of_range = u64::from(u32::MAX) + 1;
assert_eq!(
unique_level_reference(&[out_of_range, 20307], &levels(&[20307])),
Some(20307)
);
}
}