use super::intset::IntSet;
use super::value::CovError;
pub const MAX_REPEAT: u128 = 1 << 16;
pub const MAX_STATES: usize = 1 << 18;
pub const MAX_ALTS: u128 = 1 << 22;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Rep {
Once,
Consec(u32, u32),
Goto(u32, u32),
NonConsec(u32, u32),
}
#[derive(Clone, Debug, PartialEq)]
pub struct TransElem {
pub set: IntSet,
pub rep: Rep,
}
pub type TransSeq = Vec<TransElem>;
pub type Alt = Vec<IntSet>;
impl TransElem {
pub fn once(set: IntSet) -> TransElem {
TransElem {
set,
rep: Rep::Once,
}
}
pub fn is_bounded(&self) -> bool {
matches!(self.rep, Rep::Once | Rep::Consec(..))
}
fn min_samples(&self) -> u64 {
match self.rep {
Rep::Once => 1,
Rep::Consec(lo, _) | Rep::Goto(lo, _) | Rep::NonConsec(lo, _) => lo as u64,
}
}
}
pub fn is_bounded(seq: &[TransElem]) -> bool {
seq.iter().all(TransElem::is_bounded)
}
pub fn min_samples(seq: &[TransElem]) -> u64 {
seq.iter().map(TransElem::min_samples).sum()
}
pub fn max_samples(seq: &[TransElem]) -> usize {
let mut n = 0usize;
for e in seq {
match e.rep {
Rep::Once => n += 1,
Rep::Consec(_, hi) => n += hi as usize,
Rep::Goto(..) | Rep::NonConsec(..) => return 0,
}
}
n
}
pub fn expand_alts(bin: &str, seq: &[TransElem]) -> Result<Vec<Alt>, CovError> {
let mut alts: Vec<Alt> = vec![Vec::new()];
for e in seq {
let counts: Vec<u32> = match e.rep {
Rep::Once => vec![1],
Rep::Consec(lo, hi) => (lo..=hi).collect(),
Rep::Goto(..) | Rep::NonConsec(..) => {
return Err(CovError::TransUnbounded {
bin: bin.to_string(),
what: "an alternative list",
});
}
};
if alts.len() as u128 * counts.len() as u128 > MAX_ALTS {
return Err(CovError::TooManyValues {
ctx: format!("transition bin {bin}"),
limit: MAX_ALTS,
});
}
let mut next = Vec::with_capacity(alts.len() * counts.len());
for a in &alts {
for &c in &counts {
let mut a = a.clone();
for _ in 0..c {
a.push(e.set.clone());
}
next.push(a);
}
}
alts = next;
}
Ok(alts)
}
pub fn value_seqs(bin: &str, alt: &Alt) -> Result<Vec<Vec<i128>>, CovError> {
let mut total: u128 = 1;
for s in alt {
total = total.saturating_mul(s.count());
}
if total > MAX_ALTS {
return Err(CovError::TooManyValues {
ctx: format!("transition bin {bin}"),
limit: MAX_ALTS,
});
}
let mut out: Vec<Vec<i128>> = vec![Vec::new()];
for s in alt {
let vals = s.values(MAX_ALTS).ok_or_else(|| CovError::TooManyValues {
ctx: format!("transition bin {bin}"),
limit: MAX_ALTS,
})?;
let mut next = Vec::with_capacity(out.len() * vals.len());
for prefix in &out {
for &v in &vals {
let mut p = prefix.clone();
p.push(v);
next.push(p);
}
}
out = next;
}
Ok(out)
}
fn intersect(a: &IntSet, b: &IntSet) -> IntSet {
a.subtract(&a.subtract(b))
}
pub fn remove_window(alt: &Alt, ign: &Alt) -> Vec<Alt> {
let (n, m) = (alt.len(), ign.len());
if m == 0 || m > n {
return vec![alt.clone()];
}
let mut work = vec![alt.clone()];
for k in 0..=(n - m) {
let mut next = Vec::new();
for w in work {
if !(0..m).all(|j| w[k + j].intersects(&ign[j])) {
next.push(w);
continue;
}
for j in 0..m {
let mut p = w.clone();
for i in 0..j {
p[k + i] = intersect(&w[k + i], &ign[i]);
}
p[k + j] = w[k + j].subtract(&ign[j]);
if p.iter().all(|s| !s.is_empty()) {
next.push(p);
}
}
}
work = next;
}
work
}
pub fn alt_to_seq(alt: Alt) -> TransSeq {
alt.into_iter().map(TransElem::once).collect()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Label {
In(usize),
NotIn(usize),
}
#[derive(Default)]
struct Builder {
edges: Vec<Vec<(Label, usize)>>,
eps: Vec<Vec<usize>>,
finals: Vec<bool>,
sets: Vec<IntSet>,
}
impl Builder {
fn state(&mut self) -> Result<usize, CovError> {
if self.edges.len() >= MAX_STATES {
return Err(CovError::TooManyValues {
ctx: "transition bin states".to_string(),
limit: MAX_STATES as u128,
});
}
self.edges.push(Vec::new());
self.eps.push(Vec::new());
self.finals.push(false);
Ok(self.edges.len() - 1)
}
fn set(&mut self, s: &IntSet) -> usize {
if let Some(i) = self.sets.iter().position(|x| x == s) {
return i;
}
self.sets.push(s.clone());
self.sets.len() - 1
}
fn edge(&mut self, from: usize, l: Label, to: usize) {
self.edges[from].push((l, to));
}
fn epsilon(&mut self, from: usize, to: usize) {
self.eps[from].push(to);
}
fn add_seq(&mut self, seq: &[TransElem]) -> Result<(), CovError> {
let mut cur = 0usize;
for (idx, e) in seq.iter().enumerate() {
let at_start = idx == 0;
let set = self.set(&e.set);
let end = self.state()?;
match e.rep {
Rep::Once => {
self.edge(cur, Label::In(set), end);
}
Rep::Consec(lo, hi) => {
let mut s = cur;
if lo == 0 {
self.epsilon(cur, end);
}
for k in 1..=hi {
let n = self.state()?;
self.edge(s, Label::In(set), n);
if k >= lo {
self.epsilon(n, end);
}
s = n;
}
}
Rep::Goto(lo, hi) | Rep::NonConsec(lo, hi) => {
let non_consec = matches!(e.rep, Rep::NonConsec(..));
let wait = match (non_consec, seq.get(idx + 1)) {
(true, Some(next)) => Some(self.set(&e.set.union(&next.set))),
_ => None,
};
let mut n = cur;
for k in 0..=hi {
if k >= lo {
match wait {
Some(w) => {
let t = self.state()?;
self.epsilon(n, t);
self.edge(t, Label::NotIn(w), t);
self.epsilon(t, end);
}
None => self.epsilon(n, end),
}
}
if k < hi {
let mut w = n;
if !(k == 0 && at_start) {
w = self.state()?;
self.epsilon(n, w);
self.edge(w, Label::NotIn(set), w);
}
let next_n = self.state()?;
self.edge(w, Label::In(set), next_n);
n = next_n;
}
}
}
}
cur = end;
}
self.finals[cur] = true;
Ok(())
}
fn finish(self) -> Nfa {
let n = self.edges.len();
let mut closure = Vec::with_capacity(n);
for s in 0..n {
let mut seen = vec![false; n];
let mut stack = vec![s];
seen[s] = true;
while let Some(x) = stack.pop() {
for &y in &self.eps[x] {
if !seen[y] {
seen[y] = true;
stack.push(y);
}
}
}
closure.push((0..n).filter(|&i| seen[i]).collect::<Vec<usize>>());
}
Nfa {
edges: self.edges,
closure,
finals: self.finals,
sets: self.sets,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Nfa {
edges: Vec<Vec<(Label, usize)>>,
closure: Vec<Vec<usize>>,
finals: Vec<bool>,
sets: Vec<IntSet>,
}
impl Nfa {
pub fn compile(seqs: &[TransSeq]) -> Result<Nfa, CovError> {
let mut b = Builder::default();
b.state()?; for s in seqs {
b.add_seq(s)?;
}
Ok(b.finish())
}
pub fn states(&self) -> usize {
self.edges.len()
}
fn label_matches(&self, l: Label, v: i128) -> bool {
match l {
Label::In(i) => self.sets[i].contains(v),
Label::NotIn(i) => !self.sets[i].contains(v),
}
}
pub fn can_accept(&self) -> bool {
let n = self.edges.len();
let mut seen = vec![false; n];
let mut stack: Vec<usize> = self.closure[0].clone();
for &s in &stack {
seen[s] = true;
}
while let Some(s) = stack.pop() {
if self.finals[s] {
return true;
}
for &(l, t) in &self.edges[s] {
let live = match l {
Label::In(i) => !self.sets[i].is_empty(),
Label::NotIn(_) => true,
};
if live {
for &c in &self.closure[t] {
if !seen[c] {
seen[c] = true;
stack.push(c);
}
}
}
}
}
false
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Step {
pub matched: bool,
pub pending: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TransMatcher {
nfa: Nfa,
active: Vec<bool>,
pub max_len: usize,
dead: bool,
pub last: IntSet,
}
impl TransMatcher {
pub fn new(nfa: Nfa, max_len: usize) -> TransMatcher {
let dead = !nfa.can_accept();
let active = vec![false; nfa.states()];
TransMatcher {
nfa,
active,
max_len,
dead,
last: IntSet::new(),
}
}
pub fn with_last(mut self, last: IntSet) -> TransMatcher {
self.last = last;
self
}
pub fn is_dead(&self) -> bool {
self.dead
}
pub fn clear(&mut self) {
self.active.iter_mut().for_each(|a| *a = false);
}
pub fn step(&mut self, v: i128) -> Step {
let n = self.nfa.states();
let mut cont = vec![false; n];
for s in 0..n {
if self.active[s] {
self.advance(s, v, &mut cont);
}
}
let pending = (0..n).any(|s| cont[s] && !self.nfa.edges[s].is_empty());
let mut next = cont;
for &s in &self.nfa.closure[0] {
self.advance(s, v, &mut next);
}
let matched = (0..n).any(|s| next[s] && self.nfa.finals[s]);
self.active = next;
Step { matched, pending }
}
fn advance(&self, s: usize, v: i128, into: &mut [bool]) {
for &(l, t) in &self.nfa.edges[s] {
if self.nfa.label_matches(l, v) {
for &c in &self.nfa.closure[t] {
into[c] = true;
}
}
}
}
}