use std::collections::BTreeSet;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConditionExpr {
Ref(u32),
And(Vec<ConditionExpr>),
Or(Vec<ConditionExpr>),
Xor(Box<ConditionExpr>, Box<ConditionExpr>),
Not(Box<ConditionExpr>),
Package { id: u32, min: u32, max: u32 },
}
impl ConditionExpr {
pub fn condition_ids(&self) -> BTreeSet<u32> {
let mut ids = BTreeSet::new();
self.collect_ids(&mut ids);
ids
}
fn collect_ids(&self, ids: &mut BTreeSet<u32>) {
match self {
ConditionExpr::Ref(id) => {
ids.insert(*id);
}
ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
for expr in exprs {
expr.collect_ids(ids);
}
}
ConditionExpr::Xor(left, right) => {
left.collect_ids(ids);
right.collect_ids(ids);
}
ConditionExpr::Not(inner) => {
inner.collect_ids(ids);
}
ConditionExpr::Package { .. } => {
}
}
}
}
impl std::fmt::Display for ConditionExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConditionExpr::Ref(id) => write!(f, "[{id}]"),
ConditionExpr::And(exprs) => {
let parts: Vec<String> = exprs.iter().map(|e| format!("{e}")).collect();
write!(f, "({})", parts.join(" ∧ "))
}
ConditionExpr::Or(exprs) => {
let parts: Vec<String> = exprs.iter().map(|e| format!("{e}")).collect();
write!(f, "({})", parts.join(" ∨ "))
}
ConditionExpr::Xor(left, right) => write!(f, "({left} ⊻ {right})"),
ConditionExpr::Not(inner) => write!(f, "NOT {inner}"),
ConditionExpr::Package { id, min, max } => write!(f, "[{id}P{min}..{max}]"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ref_condition_ids() {
let expr = ConditionExpr::Ref(931);
assert_eq!(expr.condition_ids(), [931].into());
}
#[test]
fn test_and_condition_ids() {
let expr = ConditionExpr::And(vec![
ConditionExpr::Ref(1),
ConditionExpr::Ref(2),
ConditionExpr::Ref(3),
]);
assert_eq!(expr.condition_ids(), [1, 2, 3].into());
}
#[test]
fn test_nested_condition_ids() {
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::Or(vec![
ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
ConditionExpr::And(vec![ConditionExpr::Ref(3), ConditionExpr::Ref(4)]),
])),
Box::new(ConditionExpr::Ref(5)),
);
assert_eq!(expr.condition_ids(), [1, 2, 3, 4, 5].into());
}
#[test]
fn test_not_condition_ids() {
let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(42)));
assert_eq!(expr.condition_ids(), [42].into());
}
#[test]
fn test_display_ref() {
let expr = ConditionExpr::Ref(931);
assert_eq!(format!("{expr}"), "[931]");
}
#[test]
fn test_display_and() {
let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(format!("{expr}"), "([1] ∧ [2])");
}
#[test]
fn test_display_complex() {
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::And(vec![
ConditionExpr::Ref(102),
ConditionExpr::Ref(2006),
])),
Box::new(ConditionExpr::And(vec![
ConditionExpr::Ref(103),
ConditionExpr::Ref(2005),
])),
);
assert_eq!(format!("{expr}"), "(([102] ∧ [2006]) ⊻ ([103] ∧ [2005]))");
}
#[test]
fn test_display_not() {
let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
assert_eq!(format!("{expr}"), "NOT [1]");
}
#[test]
fn test_equality() {
let a = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
let b = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(a, b);
}
#[test]
fn test_inequality() {
let a = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
let b = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_ne!(a, b);
}
#[test]
fn test_package_condition_ids() {
let expr = ConditionExpr::Package {
id: 4,
min: 0,
max: 1,
};
assert!(
expr.condition_ids().is_empty(),
"Package nodes have no condition IDs"
);
}
#[test]
fn test_package_display() {
let expr = ConditionExpr::Package {
id: 4,
min: 0,
max: 1,
};
assert_eq!(format!("{expr}"), "[4P0..1]");
}
#[test]
fn test_clone() {
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::Ref(1)),
Box::new(ConditionExpr::Ref(2)),
);
let cloned = expr.clone();
assert_eq!(expr, cloned);
}
}