use crate::rbe_struct::RbeStruct;
use crate::{Max, Rbe};
use core::hash::Hash;
use std::collections::HashMap;
use std::fmt::{Debug, Display};
impl<A> RbeStruct<A>
where
A: Hash + Eq + Display + Clone + Debug,
{
pub fn feasible(&self, lo: &HashMap<A, usize>, hi: &HashMap<A, usize>) -> bool {
self.rbe().feasible(lo, hi)
}
}
impl<A> Rbe<A>
where
A: Hash + Eq + Display + Clone + Debug,
{
pub fn feasible(&self, lo: &HashMap<A, usize>, hi: &HashMap<A, usize>) -> bool {
Feasibility { lo, hi }.fx(self)
}
}
struct Feasibility<'a, A>
where
A: Hash + Eq + Display + Clone + Debug,
{
lo: &'a HashMap<A, usize>,
hi: &'a HashMap<A, usize>,
}
impl<A> Feasibility<'_, A>
where
A: Hash + Eq + Display + Clone + Debug,
{
fn lo(&self, a: &A) -> usize {
*self.lo.get(a).unwrap_or(&0)
}
fn hi(&self, a: &A) -> usize {
*self.hi.get(a).unwrap_or(&0)
}
fn zero(&self, e: &Rbe<A>) -> bool {
match e {
Rbe::Fail { .. } | Rbe::Empty => true,
Rbe::Symbol { value, .. } => self.lo(value) == 0,
Rbe::And { values } | Rbe::Or { values } => values.iter().all(|v| self.zero(v)),
Rbe::Star { value } | Rbe::Plus { value } | Rbe::Repeat { value, .. } => self.zero(value),
}
}
fn fx(&self, e: &Rbe<A>) -> bool {
match e {
Rbe::Fail { .. } => false,
Rbe::Empty => true,
Rbe::Symbol { value, card } => {
let lo_ok = match card.max {
Max::Unbounded => true,
Max::IntMax(n) => self.lo(value) <= n,
};
lo_ok && self.hi(value) >= card.min.value
},
Rbe::And { values } => values.iter().all(|v| self.fx(v)),
Rbe::Or { values } => values
.iter()
.enumerate()
.any(|(i, v)| self.fx(v) && values.iter().enumerate().all(|(j, other)| j == i || self.zero(other))),
Rbe::Star { value } => self.zero(value) || (self.fi(value) && self.once(value)),
Rbe::Plus { value } => self.fi(value) && self.once(value),
Rbe::Repeat { value, card } => {
if card.max == Max::IntMax(0) {
self.zero(value)
} else if card.min.value == 0 && card.max == Max::IntMax(1) {
self.zero(value) || self.fx(value)
} else if card.min.value == 0 {
self.zero(value) || (self.fi(value) && self.once(value))
} else {
self.fi(value) && self.once(value)
}
},
}
}
fn fi(&self, e: &Rbe<A>) -> bool {
match e {
Rbe::Fail { .. } => false,
Rbe::Empty => true,
Rbe::Symbol { value, card } => {
(card.min.value == 0 || self.hi(value) >= card.min.value)
&& (card.max != Max::IntMax(0) || self.lo(value) == 0)
},
Rbe::And { values } => values.iter().all(|v| self.fi(v)),
Rbe::Or { values } => values.iter().all(|v| self.zero(v) || self.fi(v)),
Rbe::Star { value } => self.zero(value) || self.fi(value),
Rbe::Plus { value } => self.fi(value) && self.once(value),
Rbe::Repeat { value, card } => {
if card.max == Max::IntMax(0) {
self.zero(value)
} else if card.min.value == 0 {
self.zero(value) || self.fi(value)
} else {
self.fi(value) && self.once(value)
}
},
}
}
fn once(&self, e: &Rbe<A>) -> bool {
match e {
Rbe::Fail { .. } => false,
Rbe::Empty => true,
Rbe::Symbol { value, card } => card.min.value == 0 || self.hi(value) >= card.min.value,
Rbe::And { values } => values.iter().all(|v| self.once(v)),
Rbe::Or { values } => values.iter().any(|v| self.once(v)),
Rbe::Star { value: _ } => true,
Rbe::Plus { value } => self.once(value),
Rbe::Repeat { value, card } => card.min.value == 0 || self.once(value),
}
}
}
#[cfg(test)]
mod tests {
use crate::{Cardinality, Max, Rbe};
use std::collections::HashMap;
fn sym(c: char, min: usize, max: Max) -> Rbe<char> {
Rbe::Symbol {
value: c,
card: Cardinality::from(crate::Min::from(min), max),
}
}
fn counts(pairs: &[(char, usize)]) -> HashMap<char, usize> {
pairs.iter().cloned().collect()
}
fn blowup() -> Rbe<char> {
Rbe::Or {
values: vec![
sym('a', 1, Max::IntMax(2)),
Rbe::And {
values: vec![sym('b', 1, Max::Unbounded), sym('q', 1, Max::IntMax(1))],
},
],
}
}
#[test]
fn or_branch_refuted_without_cooccurring_symbol() {
assert!(!blowup().feasible(&counts(&[('b', 1)]), &counts(&[('b', 3), ('q', 0)])));
assert!(blowup().feasible(&counts(&[('b', 1)]), &counts(&[('b', 3), ('q', 1)])));
}
#[test]
fn exact_upper_bound_refutes() {
assert!(!blowup().feasible(&counts(&[('a', 3)]), &counts(&[('a', 3)])));
assert!(blowup().feasible(&counts(&[('a', 2)]), &counts(&[('a', 2)])));
}
#[test]
fn mandatory_symbol_needs_candidates() {
let e = Rbe::And {
values: vec![sym('a', 1, Max::IntMax(1)), sym('b', 1, Max::IntMax(1))],
};
assert!(!e.feasible(&HashMap::new(), &counts(&[('a', 1)])));
assert!(e.feasible(&HashMap::new(), &counts(&[('a', 1), ('b', 1)])));
}
#[test]
fn or_exclusivity_does_not_survive_repetition() {
let a_or_b = Rbe::Or {
values: vec![sym('a', 1, Max::IntMax(1)), sym('b', 1, Max::IntMax(1))],
};
let mixed_lo = counts(&[('a', 1), ('b', 1)]);
let mixed_hi = counts(&[('a', 1), ('b', 1)]);
assert!(!a_or_b.feasible(&mixed_lo, &mixed_hi));
let plus = Rbe::Plus {
value: Box::new(a_or_b),
};
assert!(plus.feasible(&mixed_lo, &mixed_hi));
}
#[test]
fn count_coupling_is_deliberately_ignored() {
let e = Rbe::Plus {
value: Box::new(Rbe::And {
values: vec![sym('a', 1, Max::IntMax(1)), sym('b', 1, Max::IntMax(1))],
}),
};
assert!(e.feasible(&counts(&[('a', 2), ('b', 1)]), &counts(&[('a', 2), ('b', 1)])));
}
}