1use crate::charset::CharSet;
18use crate::limits::Budget;
19use crate::sha256::sha256_hex;
20use crate::term::{TermId, TermStore, NO_CTX};
21use rexact_syntax::error::ErrorCode;
22use std::collections::HashMap;
23
24const UNSET: u32 = u32::MAX;
25const TERM_BYTES: u64 = 96;
27
28pub struct Dfa {
30 classes: Vec<CharSet>,
31 lookup: Vec<(u32, u32, u32)>,
33 states: Vec<TermId>,
34 ctxs: Vec<u32>,
36 index: HashMap<(TermId, u32), u32>,
37 trans: Vec<u32>,
38 accepting: Vec<bool>,
39 seen_terms: usize,
40 alphabet: &'static str,
41}
42
43impl Dfa {
44 pub fn new(
46 store: &mut TermStore,
47 start: TermId,
48 alphabet: &'static str,
49 budget: &mut Budget,
50 ) -> Result<Dfa, ErrorCode> {
51 let classes = (*store.partition(start)).clone();
52 let mut lookup: Vec<(u32, u32, u32)> = Vec::new();
53 for (i, c) in classes.iter().enumerate() {
54 for &(lo, hi) in c.ranges() {
55 lookup.push((lo, hi, i as u32));
56 }
57 }
58 lookup.sort_unstable();
59 let mut d = Dfa {
60 classes,
61 lookup,
62 states: Vec::new(),
63 ctxs: Vec::new(),
64 index: HashMap::new(),
65 trans: Vec::new(),
66 accepting: Vec::new(),
67 seen_terms: store.len(),
68 alphabet,
69 };
70 let ctx = if store.ctx_dep(start) {
71 store.ctx_start()
72 } else {
73 NO_CTX
74 };
75 d.intern_state(store, start, ctx, budget)?;
76 Ok(d)
77 }
78
79 pub fn num_states(&self) -> usize {
81 self.states.len()
82 }
83 pub fn num_classes(&self) -> usize {
85 self.classes.len()
86 }
87
88 fn intern_state(
89 &mut self,
90 store: &TermStore,
91 t: TermId,
92 ctx: u32,
93 budget: &mut Budget,
94 ) -> Result<u32, ErrorCode> {
95 let ctx = if store.ctx_dep(t) { ctx } else { NO_CTX };
97 if let Some(&s) = self.index.get(&(t, ctx)) {
98 return Ok(s);
99 }
100 budget.add_states(1)?;
101 budget.add_memory(4 * self.classes.len() as u64 + TERM_BYTES)?;
102 budget.check_time()?;
103 let id = self.states.len() as u32;
104 self.states.push(t);
105 self.ctxs.push(ctx);
106 self.index.insert((t, ctx), id);
107 self.trans
108 .extend(std::iter::repeat(UNSET).take(self.classes.len()));
109 self.accepting.push(store.accepting(t, ctx));
110 Ok(id)
111 }
112
113 pub fn class_of(&self, c: u32) -> Option<usize> {
115 let i = self.lookup.partition_point(|&(lo, _, _)| lo <= c);
116 let &(_, hi, k) = self.lookup.get(i.checked_sub(1)?)?;
117 (c <= hi).then_some(k as usize)
118 }
119
120 pub fn step(
122 &mut self,
123 store: &mut TermStore,
124 s: u32,
125 class: usize,
126 budget: &mut Budget,
127 ) -> Result<u32, ErrorCode> {
128 let slot = s as usize * self.classes.len() + class;
129 if self.trans[slot] != UNSET {
130 return Ok(self.trans[slot]);
131 }
132 let rep = self.classes[class].smallest().unwrap_or(0);
133 let (term, ctx) = (self.states[s as usize], self.ctxs[s as usize]);
134 let d = store.deriv_in(term, rep, ctx);
135 let next_ctx = if store.ctx_dep(d) {
136 store.advance_ctx(ctx, rep)
137 } else {
138 NO_CTX
139 };
140 let grown = store.len().saturating_sub(self.seen_terms) as u64;
141 self.seen_terms = store.len();
142 budget.add_memory(grown * TERM_BYTES)?;
143 let t = self.intern_state(store, d, next_ctx, budget)?;
144 self.trans[slot] = t;
145 Ok(t)
146 }
147
148 pub fn matches(
150 &mut self,
151 store: &mut TermStore,
152 input: &[u32],
153 budget: &mut Budget,
154 ) -> Result<bool, ErrorCode> {
155 let mut s = 0u32;
156 for &c in input {
157 let Some(k) = self.class_of(c) else {
158 return Ok(false);
159 };
160 s = self.step(store, s, k, budget)?;
161 if self.states[s as usize] == store.empty() {
162 return Ok(false);
163 }
164 }
165 Ok(self.accepting[s as usize])
166 }
167
168 pub fn explore(&mut self, store: &mut TermStore, budget: &mut Budget) -> Result<(), ErrorCode> {
170 let mut i = 0;
171 while i < self.states.len() {
172 for c in 0..self.classes.len() {
173 self.step(store, i as u32, c, budget)?;
174 }
175 i += 1;
176 }
177 Ok(())
178 }
179
180 pub fn minimize(
182 &mut self,
183 store: &mut TermStore,
184 budget: &mut Budget,
185 ) -> Result<MinDfa, ErrorCode> {
186 self.explore(store, budget)?;
187 let (n, k) = (self.states.len(), self.classes.len());
188
189 let (block, count) = hopcroft(n, k, &self.trans, &self.accepting, budget)?;
192
193 let mut rep = vec![usize::MAX; count];
195 for (i, &b) in block.iter().enumerate() {
196 if rep[b as usize] == usize::MAX {
197 rep[b as usize] = i;
198 }
199 }
200 let q = |b: usize, c: usize| block[self.trans[rep[b] * k + c] as usize];
201
202 let mut groups: Vec<Vec<usize>> = Vec::new();
204 let mut by_col: HashMap<Vec<u32>, usize> = HashMap::new();
205 for c in 0..k {
206 let col: Vec<u32> = (0..count).map(|b| q(b, c)).collect();
207 let g = *by_col.entry(col).or_insert_with(|| {
208 groups.push(Vec::new());
209 groups.len() - 1
210 });
211 groups[g].push(c);
212 }
213 let merged: Vec<CharSet> = groups
214 .iter()
215 .map(|g| {
216 g.iter()
217 .fold(CharSet::empty(), |acc, &c| acc.union(&self.classes[c]))
218 })
219 .collect();
220 let k2 = groups.len();
221
222 let start = block[0] as usize;
224 let mut canon = vec![u32::MAX; count];
225 let mut order = vec![start];
226 canon[start] = 0;
227 let mut i = 0;
228 while i < order.len() {
229 for g in &groups {
230 let t = q(order[i], g[0]) as usize;
231 if canon[t] == u32::MAX {
232 canon[t] = order.len() as u32;
233 order.push(t);
234 }
235 }
236 i += 1;
237 }
238 let mut trans = Vec::with_capacity(order.len() * k2);
239 let mut accepting = Vec::with_capacity(order.len());
240 for &b in &order {
241 for g in &groups {
242 trans.push(canon[q(b, g[0]) as usize]);
243 }
244 accepting.push(self.accepting[rep[b]]);
245 }
246 Ok(MinDfa {
247 alphabet: self.alphabet,
248 classes: merged,
249 trans,
250 accepting,
251 })
252 }
253}
254
255#[derive(Debug, Clone, PartialEq, Eq)]
257pub struct MinDfa {
258 alphabet: &'static str,
259 classes: Vec<CharSet>,
260 trans: Vec<u32>,
261 accepting: Vec<bool>,
262}
263
264impl MinDfa {
265 pub fn num_states(&self) -> usize {
267 self.accepting.len()
268 }
269 pub fn num_classes(&self) -> usize {
271 self.classes.len()
272 }
273 pub fn classes(&self) -> &[CharSet] {
275 &self.classes
276 }
277 pub fn is_accepting(&self, s: usize) -> bool {
279 self.accepting[s]
280 }
281 pub fn next(&self, s: usize, class: usize) -> usize {
283 self.trans[s * self.classes.len() + class] as usize
284 }
285
286 pub fn accepts(&self, input: &[u32]) -> bool {
288 let mut s = 0usize;
289 for &c in input {
290 match self.classes.iter().position(|k| k.contains(c)) {
291 Some(k) => s = self.next(s, k),
292 None => return false,
293 }
294 }
295 self.accepting[s]
296 }
297
298 pub fn canonical_form(&self) -> String {
300 let mut out = format!(
301 "rexact-dfa v1\nalphabet {}\nclasses {}\n",
302 self.alphabet,
303 self.classes.len()
304 );
305 for c in &self.classes {
306 let parts: Vec<String> = c
307 .ranges()
308 .iter()
309 .map(|&(l, h)| format!("{l:X}-{h:X}"))
310 .collect();
311 out.push_str(&parts.join(","));
312 out.push('\n');
313 }
314 out.push_str(&format!("states {}\n", self.accepting.len()));
315 out.push_str(
316 &self
317 .accepting
318 .iter()
319 .map(|&a| if a { '1' } else { '0' })
320 .collect::<String>(),
321 );
322 out.push('\n');
323 let k = self.classes.len();
324 for s in 0..self.accepting.len() {
325 let row: Vec<String> = (0..k).map(|c| self.trans[s * k + c].to_string()).collect();
326 out.push_str(&row.join(" "));
327 out.push('\n');
328 }
329 out
330 }
331
332 pub fn fingerprint(&self) -> String {
335 sha256_hex(self.canonical_form().as_bytes())
336 }
337}
338
339fn hopcroft(
342 n: usize,
343 k: usize,
344 trans: &[u32],
345 accepting: &[bool],
346 budget: &mut Budget,
347) -> Result<(Vec<u32>, usize), ErrorCode> {
348 let mut start = vec![0u32; k * (n + 1)];
351 let mut list = vec![0u32; k * n];
352 for c in 0..k {
353 let base = c * (n + 1);
354 for s in 0..n {
355 start[base + trans[s * k + c] as usize + 1] += 1;
356 }
357 for t in 0..n {
358 start[base + t + 1] += start[base + t];
359 }
360 let mut fill: Vec<u32> = start[base..base + n].to_vec();
361 for s in 0..n {
362 let t = trans[s * k + c] as usize;
363 list[c * n + fill[t] as usize] = s as u32;
364 fill[t] += 1;
365 }
366 }
367
368 let mut block_of = vec![0u32; n];
369 let mut members: Vec<Vec<u32>> = Vec::new();
370 let (mut fin, mut non) = (Vec::new(), Vec::new());
371 for (s, &is_final) in accepting.iter().enumerate() {
372 if is_final {
373 fin.push(s as u32);
374 } else {
375 non.push(s as u32);
376 }
377 }
378 for g in [non, fin] {
379 if !g.is_empty() {
380 let id = members.len() as u32;
381 for &s in &g {
382 block_of[s as usize] = id;
383 }
384 members.push(g);
385 }
386 }
387 let mut in_work = vec![false; members.len()];
388 let mut inx: Vec<Vec<u32>> = vec![Vec::new(); members.len()];
389 let mut work: Vec<u32> = Vec::new();
390 if members.len() == 2 {
391 let small = if members[0].len() <= members[1].len() {
392 0
393 } else {
394 1
395 };
396 work.push(small);
397 in_work[small as usize] = true;
398 }
399
400 let mut mark = vec![0u32; n];
401 let mut stamp = 0u32;
402 let mut touched: Vec<u32> = Vec::new();
403 while let Some(a) = work.pop() {
404 in_work[a as usize] = false;
405 budget.check_time()?;
406 let splitter = members[a as usize].clone();
407 for c in 0..k {
408 stamp += 1;
409 touched.clear();
410 let base = c * (n + 1);
411 for &t in &splitter {
412 let lo = start[base + t as usize] as usize;
413 let hi = start[base + t as usize + 1] as usize;
414 for &s in &list[c * n + lo..c * n + hi] {
415 if mark[s as usize] != stamp {
416 mark[s as usize] = stamp;
417 let b = block_of[s as usize];
418 if inx[b as usize].is_empty() {
419 touched.push(b);
420 }
421 inx[b as usize].push(s);
422 }
423 }
424 }
425 for &b in &touched {
426 let moved = std::mem::take(&mut inx[b as usize]);
427 if moved.len() == members[b as usize].len() {
428 continue; }
430 let nb = members.len() as u32;
431 members[b as usize].retain(|&s| mark[s as usize] != stamp);
432 for &s in &moved {
433 block_of[s as usize] = nb;
434 }
435 members.push(moved);
436 in_work.push(false);
437 inx.push(Vec::new());
438 if in_work[b as usize] {
439 in_work[nb as usize] = true;
440 work.push(nb);
441 } else {
442 let small = if members[b as usize].len() <= members[nb as usize].len() {
443 b
444 } else {
445 nb
446 };
447 in_work[small as usize] = true;
448 work.push(small);
449 }
450 }
451 }
452 }
453 Ok((block_of, members.len()))
454}