hoars 0.2.0

A library for dealing with the HOA (Hanoi Omega Automata) file format.
Documentation
use std::collections::{HashMap, HashSet};

use crate::{Id, LabelExpression};

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
enum Val {
    True,
    False,
    Undecided,
}

impl std::ops::BitOr for Val {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self::Output {
        match (self, rhs) {
            (Val::True, _) | (_, Val::True) => Val::True,
            (Val::False, Val::False) => Val::False,
            _ => Val::Undecided,
        }
    }
}

impl std::ops::BitAnd for Val {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self::Output {
        match (self, rhs) {
            (Val::False, _) | (_, Val::False) => Val::False,
            (Val::True, Val::True) => Val::True,
            _ => Val::Undecided,
        }
    }
}

impl std::ops::Not for Val {
    type Output = Self;

    fn not(self) -> Self::Output {
        match self {
            Val::True => Val::False,
            Val::False => Val::True,
            Val::Undecided => Val::Undecided,
        }
    }
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Interpretation {
    default: Val,
    map: HashMap<Id, Val>,
}

impl Interpretation {
    pub(crate) fn top() -> Self {
        Interpretation {
            default: Val::True,
            pos: HashSet::new(),
            neg: HashSet::new(),
        }
    }

    pub(crate) fn bot() -> Self {
        Interpretation {
            default: Val::False,
            pos: HashSet::new(),
            neg: HashSet::new(),
        }
    }

    pub(crate) fn just(id: Id) -> Self {
        Interpretation {
            default: Val::Undecided,
            pos: vec![id].into_iter().collect(),
            neg: HashSet::new(),
        }
    }
}

impl std::ops::BitOr for Interpretation {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self::Output {
        Interpretation {
            pos: self.pos.intersection(&rhs.pos).cloned().collect(),
            neg: self.neg.union(&rhs.neg).cloned().collect(),
            default: self.default | rhs.default,
        }
    }
}

impl std::ops::BitAnd for Interpretation {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self::Output {
        Interpretation {
            pos: self.pos.union(&rhs.pos).cloned().collect(),
            neg: self.neg.intersection(&rhs.neg).cloned().collect(),
            default: self.default & rhs.default,
        }
    }
}

impl std::ops::Not for Interpretation {
    type Output = Self;

    fn not(self) -> Self::Output {
        Interpretation {
            pos: self.neg,
            neg: self.pos,
            default: !self.default,
        }
    }
}

impl From<&LabelExpression> for Interpretation {
    fn from(expression: &LabelExpression) -> Self {
        match expression {
            LabelExpression::Boolean(b) => {
                if *b {
                    Interpretation::top()
                } else {
                    Interpretation::bot()
                }
            }
            LabelExpression::Integer(n) => Interpretation::just(*n),
            LabelExpression::Alias(_) => unimplemented!(),
            LabelExpression::Not(subexpr) => !Interpretation::from(subexpr.as_ref()),
            LabelExpression::And(l, r) => {
                Interpretation::from(l.as_ref()) & Interpretation::from(r.as_ref())
            }
            LabelExpression::Or(l, r) => {
                Interpretation::from(l.as_ref()) | Interpretation::from(r.as_ref())
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::LabelExpression;

    use super::Interpretation;

    #[test]
    fn label_expr_to_interpretation() {
        let expr = LabelExpression::And(
            Box::new(LabelExpression::Or(
                Box::new(LabelExpression::Integer(1)),
                Box::new(LabelExpression::Boolean(false)),
            )),
            Box::new(LabelExpression::Integer(2)),
        );
        let interpretation = Interpretation::from(&expr);
        println!("{:?}", interpretation);
    }
}