use std::collections::HashMap;
use regex_automata::Input;
use regex_automata::dfa::{Automaton, dense};
use regex_automata::util::primitives::StateID;
pub const MAX_CORE_STATES: usize = 96;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Decline {
Quits,
MatchesEmpty,
TooWide,
}
pub struct Projection {
pub states: usize,
pub classes: usize,
pub delta: Vec<u16>,
pub accept: Vec<bool>,
pub start: u16,
pub class_of: [u8; 256],
}
impl Projection {
#[inline]
#[must_use]
pub fn step(&self, s: u16, k: usize) -> u16 {
self.delta[usize::from(s) * self.classes + k]
}
pub fn of(dfa: &dense::DFA<Vec<u32>>) -> Result<Self, Decline> {
let start = dfa
.start_state_forward(&Input::new(b""))
.map_err(|_| Decline::Quits)?;
if dfa.is_match_state(start) {
return Err(Decline::MatchesEmpty);
}
let successors = Self::explore(dfa, start)?;
let states = successors.len();
let (class_of, classes) = Self::refine(&successors);
let mut delta = vec![0u16; states * classes];
for (s, row) in successors.iter().enumerate() {
for (byte, &k) in class_of.iter().enumerate() {
delta[s * classes + usize::from(k)] = row.next[byte];
}
}
Ok(Self {
states,
classes,
delta,
accept: successors.iter().map(|r| r.accepts).collect(),
start: 0,
class_of,
})
}
fn explore(dfa: &dense::DFA<Vec<u32>>, start: StateID) -> Result<Vec<Row>, Decline> {
let mut id: HashMap<StateID, u16> = HashMap::from([(start, 0u16)]);
let mut queue = vec![start];
let mut rows: Vec<Row> = Vec::new();
let mut head = 0;
while head < queue.len() {
let s = queue[head];
head += 1;
let mut next = [0u16; 256];
for (slot, byte) in next.iter_mut().zip(0..=u8::MAX) {
let t = dfa.next_state(s, byte);
if dfa.is_quit_state(t) {
return Err(Decline::Quits);
}
*slot = match id.get(&t) {
Some(&known) => known,
None => {
if queue.len() == MAX_CORE_STATES {
return Err(Decline::TooWide);
}
let fresh = queue.len() as u16;
id.insert(t, fresh);
queue.push(t);
fresh
},
};
}
rows.push(Row {
next,
accepts: dfa.is_match_state(s) || dfa.is_match_state(dfa.next_eoi_state(s)),
});
}
Ok(rows)
}
fn refine(rows: &[Row]) -> ([u8; 256], usize) {
let mut class_of = [0u8; 256];
let mut seen: HashMap<Vec<u16>, u8> = HashMap::new();
for (byte, slot) in class_of.iter_mut().enumerate() {
let column: Vec<u16> = rows.iter().map(|r| r.next[byte]).collect();
let next = seen.len() as u8;
*slot = *seen.entry(column).or_insert(next);
}
(class_of, seen.len())
}
}
struct Row {
next: [u16; 256],
accepts: bool,
}