1use std::collections::{HashMap, HashSet};
2use std::fmt;
3use std::hash::Hash;
4use std::cmp::Ord;
5use std::rc::Rc;
6use std::slice::Iter;
7use once_cell::unsync::OnceCell;
8use terms::{
9 Pattern,
10 PatternKind,
11 variable::Family
12};
13use ta::{
14 Symbol,
15 State,
16 NoLabel,
17 Ranked,
18 Rank,
19 bottom_up::{
20 Automaton,
21 Configuration,
22 Indexed,
23 width_search::{
24 SearchPattern,
25 TermFragment
26 }
27 },
28 Mux,
29 combinations
30};
31use crate::{
32 Convoluted,
33 MaybeBottom,
34};
35use super::{
36 ConvolutedPatternBuilder,
37 };
39
40type Renaming<X> = immutable_map::TreeMap<X, X>;
41
42#[derive(Clone, PartialEq, Eq, Hash)]
44pub struct Atom<F: Symbol, X: Family + Ord + Clone, Q: State>(Convoluted<Pattern<F, X>>, Q);
45
46impl<F: Symbol, X: Family + Ord + Clone, Q: State> Atom<F, X, Q> {
47 fn matches(&self, other: &Atom<F, X, Q>, renaming: &Renaming<X>) -> Option<Renaming<X>> {
48 if self.1 == other.1 {
49 convoluted_pattern_renaming(&self.0, &other.0, renaming)
50 } else {
51 None
52 }
53 }
54
55 fn variables(&self) -> HashSet<X> {
56 let mut set = HashSet::new();
57
58 for pattern in &self.0.0 {
59 match pattern {
60 MaybeBottom::Some(pattern) => set.extend(pattern.variables().cloned()),
61 _ => ()
62 }
63 }
64
65 set
66 }
67
68 fn instanciated(&self, map: &HashMap<X, Pattern<F, X>>) -> Atom<F, X, Q> {
69 Atom(Convoluted(self.0.0.iter().map(|pattern| {
70 match pattern {
71 MaybeBottom::Some(pattern) => MaybeBottom::Some(pattern.map_variables(&|x| {
72 match map.get(x) {
73 Some(px) => px.clone(),
74 None => Pattern::var(x.clone())
75 }
76 })),
77 MaybeBottom::Bottom => MaybeBottom::Bottom
78 }
79 }).collect()), self.1.clone())
80 }
81}
82
83impl<F: Symbol + fmt::Display, X: Family + Ord + Clone + fmt::Display, Q: State + fmt::Display> fmt::Display for Atom<F, X, Q> {
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 write!(f, "{}:{}", self.0, self.1)
93 }
94}
95
96pub struct Atoms<'a, F: Symbol, X: Family + Ord + Clone, Q: State> {
97 context: Option<&'a SearchContextData<F, Q, X>>,
98 context_atoms: Iter<'a, Atom<F, X, Q>>,
99 map: HashMap<X, Pattern<F, X>>
100}
101
102impl<'a, F: Symbol, X: Family + Ord + Clone, Q: State> Iterator for Atoms<'a, F, X, Q> {
103 type Item = Atom<F, X, Q>;
104
105 fn next(&mut self) -> Option<Atom<F, X, Q>> {
106 loop {
107 match self.context {
108 Some(context) => {
109 match self.context_atoms.next() {
110 Some(atom) => return Some(atom.instanciated(&self.map)),
111 None => {
112 self.context = match &context.parent {
113 Some(parent) if parent.depth == context.depth => {
114 self.context_atoms = parent.atoms.iter();
115 Some(parent.as_ref())
116 },
117 _ => None
118 };
119 }
120 }
121 },
122 None => return None
123 }
124 }
125 }
126}
127
128fn convoluted_pattern_renaming<F: PartialEq, X: Family + Ord + Clone>(a: &Convoluted<Pattern<F, X>>, b: &Convoluted<Pattern<F, X>>, renaming: &Renaming<X>) -> Option<Renaming<X>> {
129 if a.0.len() == b.0.len() {
130 let mut renaming = Renaming::clone(renaming);
131 for i in 0..(a.0.len()) {
132 match (&a[i], &b[i]) {
133 (MaybeBottom::Some(a), MaybeBottom::Some(b)) => {
134 match pattern_renaming(a, b, &renaming) {
135 Some(r) => renaming = r,
136 None => return None
137 }
138 },
139 (MaybeBottom::Bottom, MaybeBottom::Bottom) => (),
140 _ => return None
141 }
142 }
143
144 Some(renaming)
145 } else {
146 None
147 }
148}
149
150fn pattern_renaming<F: PartialEq, X: Family + Ord + Clone>(a: &Pattern<F, X>, b: &Pattern<F, X>, renaming: &Renaming<X>) -> Option<Renaming<X>> {
151 match (a.kind(), b.kind()) {
152 (PatternKind::Var(x), PatternKind::Var(y)) => {
153 match renaming.get(x) {
154 Some(z) => {
155 if z == y {
156 Some(Renaming::clone(renaming))
157 } else {
158 None
159 }
160 },
161 None => {
162 Some(renaming.insert(x.clone(), y.clone()))
163 }
164 }
165 },
166 (PatternKind::Cons(fa, la), PatternKind::Cons(fb, lb)) => {
167 if fa == fb {
168 let mut renaming = Renaming::clone(renaming);
169 for i in 0..la.len() {
170 match pattern_renaming(la.get(i).unwrap(), lb.get(i).unwrap(), &renaming) {
171 Some(r) => renaming = r,
172 None => return None
173 }
174 }
175
176 Some(renaming)
177 } else {
178 None
179 }
180 },
181 _ => None
182 }
183}
184
185pub struct SubProblem<F: Symbol, X: Family + Ord + Clone, Q: State> {
193 atoms: Vec<Atom<F, X, Q>>,
194 variables: HashSet<X>
195}
196
197impl<F: Symbol + fmt::Display, X: Family + Ord + Clone + fmt::Display, Q: State + fmt::Display> fmt::Display for SubProblem<F, X, Q> {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 write!(f, "<")?;
200 for item in self.atoms.iter() {
201 write!(f, "({})", item)?;
202 }
203 write!(f, ">")
204 }
205}
206
207impl<F: Symbol + fmt::Display, X: Family + Ord + Clone + fmt::Display, Q: State + fmt::Display> fmt::Debug for SubProblem<F, X, Q> {
208 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209 fmt::Display::fmt(self, f)
210 }
211}
212
213struct IndexedAtom<X: Hash + Eq>(usize, HashSet<X>);
214
215impl<F: Symbol, X: Family + Ord + Clone, Q: State> SubProblem<F, X, Q> {
216 pub fn new() -> SubProblem<F, X, Q> {
217 SubProblem {
218 atoms: Vec::new(),
219 variables: HashSet::new()
220 }
221 }
222
223 pub fn singleton(atom: Atom<F, X, Q>, vars: HashSet<X>) -> SubProblem<F, X, Q> {
224 SubProblem {
225 atoms: vec![atom],
226 variables: vars
227 }
228 }
229
230 fn add(&mut self, atom: Atom<F, X, Q>) -> bool {
232 if !self.atoms.contains(&atom) {
233 self.atoms.push(atom);
234 true
235 } else {
236 false
237 }
238 }
239
240 pub fn from_atoms<Atoms: Iterator<Item = Atom<F, X, Q>>>(atoms: Atoms) -> Vec<SubProblem<F, X, Q>> {
241 use union_find::UnionFind;
243 let mut uf: union_find::QuickFindUf<SubProblem<F, X, Q>> = union_find::QuickFindUf::new(8);
244 let atoms: Vec<_> = atoms.filter_map(|atom| {
245 let vars = atom.variables();
246 if vars.is_empty() {
247 None
248 } else {
249 let key = uf.insert(SubProblem::singleton(atom, vars.clone()));
251 Some(IndexedAtom(key, vars))
252 }
253 }).collect();
254
255 for IndexedAtom(x, xvars) in &atoms {
258 for IndexedAtom(y, yvars) in &atoms {
259 if !xvars.is_disjoint(yvars) {
260 uf.union(*x, *y);
261 }
262 }
263 }
264
265 let mut sub_problems = Vec::with_capacity(atoms.len());
266 for IndexedAtom(i, _) in &atoms {
267 let mut p = SubProblem::new();
268 std::mem::swap(uf.get_mut(*i), &mut p);
269 if !p.atoms.is_empty() {
270 sub_problems.push(p)
272 }
273 }
274
275 sub_problems
276 }
277
278 fn is_descendent_of(&self, other: &SubProblem<F, X, Q>) -> bool {
279 self.variables.iter().all(|x| other.variables.iter().any(|y| x.is_descendent_of(y)))
280 }
281
282 pub fn matches(&self, other: &SubProblem<F, X, Q>) -> bool {
284 if self.atoms.len() == other.atoms.len() {
285 let renaming = Renaming::new();
286 let mut map = Vec::new();
287 map.resize(self.atoms.len(), None);
288 let r = self.find_renaming(&renaming, 0, &other.atoms, &mut map).is_some();
289 if r {
290 } else {
292 }
294 r
295 } else {
296 false
297 }
298 }
299
300 pub fn is_descendent_matching(&self, other: &SubProblem<F, X, Q>) -> bool {
301 self.is_descendent_of(other) && self.matches(other)
302 }
303
304 pub fn find_renaming(&self, renaming: &Renaming<X>, i: usize, atoms: &Vec<Atom<F, X, Q>>, map: &mut Vec<Option<usize>>) -> Option<Renaming<X>> {
305 if i >= self.atoms.len() {
306 Some(renaming.clone())
307 } else {
308 let a = &self.atoms[i];
309
310 for (j, b) in atoms.iter().enumerate() {
311 if map[j].is_none() {
312 if let Some(new_renaming) = a.matches(b, renaming) {
313 map[j] = Some(i);
314
315 if let Some(final_renaming) = self.find_renaming(&new_renaming, i+1, atoms, map) {
316 return Some(final_renaming)
317 }
318
319 map[j] = None
320 }
321 }
322 }
323
324 None
325 }
326 }
327}
328
329impl<F: Symbol, X: Family + Ord + Clone, Q: State> union_find::Union for SubProblem<F, X, Q> {
330 fn union(mut lval: Self, rval: Self) -> union_find::UnionResult<Self> {
331 let ll = lval.atoms.len();
332 let rl = rval.atoms.len();
333
334 for atom in rval.atoms {
335 lval.add(atom);
336 }
337
338 lval.variables.extend(rval.variables);
339
340 if ll > rl {
341 union_find::UnionResult::Left(lval)
342 } else {
343 union_find::UnionResult::Right(lval)
344 }
345 }
346}
347
348impl<F: Symbol, X: Family + Ord + Clone, Q: State> Default for SubProblem<F, X, Q> {
349 fn default() -> Self {
350 SubProblem::new()
351 }
352}
353
354pub struct SearchContextData<F: Symbol, Q: State, X: Family + Ord + Clone> {
355 depth: usize,
356 parent: Option<Rc<SearchContextData<F, Q, X>>>,
357 atoms: Vec<Atom<F, X, Q>>,
358 bindings: HashMap<X, (F, Vec<X>)>,
359 sub_problems: OnceCell<Vec<SubProblem<F, X, Q>>>
360}
361
362struct PList<'a, T: fmt::Display>(&'a [T]);
363
364impl<'a, T: fmt::Display> fmt::Display for PList<'a, T> {
365 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
366 match self.0.split_first() {
367 Some((head, tail)) => {
368 head.fmt(f)?;
369 for e in tail.iter() {
370 write!(f, ",")?;
371 e.fmt(f)?;
372 }
373 Ok(())
374 },
375 None => Ok(())
376 }
377 }
378}
379
380impl<F: Symbol + fmt::Display, Q: State + fmt::Display, X: Family + Ord + Clone + fmt::Display> fmt::Display for SearchContextData<F, Q, X> {
381 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
382 for (x, (sym, vars)) in self.bindings.iter() {
383 write!(f, "{}: {}({}), ", x, sym, PList(vars))?;
384 }
385
386 if let Some(parent) = &self.parent {
387 write!(f, " < {}", parent)?;
388 }
389
390 Ok(())
391 }
392}
393
394impl<F: Symbol + fmt::Display, Q: State + fmt::Display, X: Family + Ord + Clone + fmt::Display> fmt::Debug for SearchContextData<F, Q, X> {
395 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
396 fmt::Display::fmt(self, f)
397 }
398}
399
400#[derive(Clone)]
401pub struct SearchContext<F: Symbol, Q: State, X: Family + Ord + Clone> {
402 p: Rc<SearchContextData<F, Q, X>>
403}
404
405impl<F: Symbol, Q: State, X: Family + Ord + Clone> ta::bottom_up::width_search::SearchContext for SearchContext<F, Q, X> {
406 fn looping(&self) -> bool {
407 if self.p.sub_problems.get().is_some() {
408 false
409 } else {
410 let sub_problems = SubProblem::from_atoms(self.p.depth_atoms());
411 if self.p.looping_sub_problems(&sub_problems) {
412 true
413 } else {
414 match self.p.sub_problems.set(sub_problems) {
415 Ok(_) => false,
416 Err(_) => unreachable!()
417 }
418 }
419 }
420 }
421}
422
423impl<F: Symbol + fmt::Display, Q: State + fmt::Display, X: Family + Ord + Clone + fmt::Display> fmt::Debug for SearchContext<F, Q, X> {
424 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425 fmt::Debug::fmt(&self.p, f)
426 }
427}
428
429impl<F: Symbol + fmt::Display, Q: State + fmt::Display, X: Family + Ord + Clone + fmt::Display> fmt::Display for SearchContext<F, Q, X> {
430 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
431 fmt::Display::fmt(&self.p, f)
432 }
433}
434
435impl<F: Symbol, Q: State, X: Family + Ord + Clone> SearchContext<F, Q, X> {
436 fn new(depth: usize) -> SearchContext<F, Q, X> {
437 let data = SearchContextData {
438 depth,
439 parent: None,
440 atoms: Vec::new(),
441 bindings: HashMap::new(),
442 sub_problems: OnceCell::new()
443 };
444 SearchContext::from(data)
445 }
446
447 fn from(data: SearchContextData<F, Q, X>) -> SearchContext<F, Q, X> {
448 SearchContext {
449 p: Rc::new(data)
450 }
451 }
452}
453
454impl<F: Symbol, Q: State, X: Family + Ord + Clone> SearchContextData<F, Q, X> {
455 fn from(depth: usize, parent: &SearchContext<F, Q, X>) -> SearchContextData<F, Q, X> {
456 SearchContextData {
457 depth,
458 parent: Some(parent.p.clone()),
459 atoms: Vec::new(),
460 bindings: HashMap::new(),
461 sub_problems: OnceCell::new()
462 }
463 }
464
465 fn instance(&self, x: &X) -> Option<&(F, Vec<X>)> {
466 if let Some(instance) = self.bindings.get(x) {
467 Some(instance)
468 } else {
469 if let Some(parent) = &self.parent {
470 parent.instance(x)
471 } else {
472 None
473 }
474 }
475 }
476
477 fn instanciate(&mut self, x: &X, _q: &Q, f: &F, arity: usize) -> &(F, Vec<X>) {
478 let vars = (0..arity).map(|_| {
479 let y = x.generate();
480 y
482 }).collect();
483 self.bindings.insert(x.clone(), (f.clone(), vars));
484 self.bindings.get(x).as_ref().unwrap()
485 }
486
487 fn looping_sub_problems(&self, sub_problems: &[SubProblem<F, X, Q>]) -> bool {
488 if let Some(sps) = self.sub_problems.get() {
489 let r1 = sub_problems.iter().any(|sub_problem| sps.iter().any(|sp| sub_problem.is_descendent_matching(sp)));
491 if r1 { return true
497 }
498 }
500
501 if let Some(parent) = self.parent.as_ref() {
502 parent.looping_sub_problems(sub_problems)
503 } else {
504 false
505 }
506 }
507
508 fn depth_bindings(&self, map: &mut HashMap<X, Pattern<F, X>>) {
509 if let Some(parent) = &self.parent {
510 if self.depth == parent.depth {
511 parent.depth_bindings(map)
512 }
513 }
514
515 for (x, (f, subs)) in &self.bindings {
516 let xp = Pattern::cons(f.clone(), subs.iter().map(|x| Pattern::var(x.clone())).collect());
517
518 for (_y, yp) in map.iter_mut() {
519 *yp = yp.map_variables(&|z| {
520 if z == x {
521 xp.clone()
522 } else {
523 Pattern::var(z.clone())
524 }
525 })
526 }
527
528 map.insert(x.clone(), xp);
529 }
530 }
531
532 fn depth_atoms(&self) -> Atoms<F, X, Q> {
533 let mut map = HashMap::new();
534 self.depth_bindings(&mut map);
535 Atoms {
536 context: Some(self),
537 context_atoms: self.atoms.iter(),
538 map
539 }
540 }
541}
542
543impl<F: Symbol + Ranked, Q: State, X: Family + Ord + Clone> SearchPattern<Rank<Convoluted<F>>, Q, SearchContext<F, Q, X>> for Convoluted<Pattern<F, X>> {
544 fn matches(&self, depth: usize, ctx: &SearchContext<F, Q, X>, current_state: &Q, current_conf: &Configuration<Rank<Convoluted<F>>, Q>) -> Option<(SearchContext<F, Q, X>, Vec<Convoluted<Pattern<F, X>>>)> {
545 let conf_f = current_conf.symbol();
546 let mut new_context = SearchContextData::from(depth, ctx);
552 let mut patterns_builder = ConvolutedPatternBuilder::new();
553 for (k, pattern) in self.0.iter().enumerate() {
556 if let MaybeBottom::Some(pattern) = pattern {
557 match pattern.kind() {
558 PatternKind::Cons(f, sub_patterns) => {
559 if conf_f.0[k] != *f {
560 return None
562 }
563
564 for (i, sub_pattern) in sub_patterns.iter().enumerate() {
565 patterns_builder.set(k, i, sub_pattern.clone())
566 }
567 },
568 PatternKind::Var(x) => {
569 if let Some((f, sub_variables)) = new_context.instance(x) {
582 if conf_f.0[k] != *f {
583 return None
585 }
586
587 for (i, y) in sub_variables.iter().enumerate() {
588 patterns_builder.set(k, i, Pattern::var(y.clone()))
589 }
590 } else {
591 if let MaybeBottom::Some(f) = &conf_f.0[k] {
594 let (_, sub_variables) = new_context.instanciate(x, current_state, f, f.arity());
596
597 for (i, y) in sub_variables.iter().enumerate() {
598 patterns_builder.set(k, i, Pattern::var(y.clone()))
599 }
600 } else {
601 return None
604 }
605 }
606 }
608 }
609 } else {
610 if conf_f.0[k] != MaybeBottom::Bottom {
611 return None
613 }
614 }
615 }
616
617 let patterns = patterns_builder.unwrap();
618 new_context.atoms.push(Atom(self.clone(), current_state.clone()));
619
620 let new_context = SearchContext::from(new_context);
623 Some((new_context, patterns))
627 }
629}
630
631pub fn search<'a, F: Symbol + Ranked, Q: State, X: Family + Ord + Clone>(aut: &'a Automaton<Rank<Convoluted<F>>, Q, NoLabel>, patterns: Vec<Convoluted<Pattern<F, X>>>, kill_signal: Option<crossbeam_channel::Receiver<()>>) -> Mux<TermFragment<'a, Automaton<Rank<Convoluted<F>>, Q, NoLabel>, Rank<Convoluted<F>>, Q, SearchContext<F, Q, X>, Convoluted<Pattern<F, X>>>> {
632 let mut iterators = Vec::new();
648 for leaves in combinations(&patterns, |_| aut.final_states().map(|q| (*q).clone())) {
649 iterators.push(TermFragment::new(aut, 0, SearchContext::new(0), leaves, patterns.clone(), kill_signal.clone()))
651 }
652
653 Mux::new(iterators)
654}
655
656pub fn multi_search<'a, 'e, F: Symbol + Ranked, Q: State, X: Family + Ord + Clone>(automata: &'a [&'e Automaton<Rank<Convoluted<F>>, Q, NoLabel>], patterns: Vec<Convoluted<Pattern<F, X>>>, kill_signal: Option<crossbeam_channel::Receiver<()>>) -> Mux<TermFragment<'a, [&'e Automaton<Rank<Convoluted<F>>, Q, NoLabel>], Rank<Convoluted<F>>, Indexed<Q>, SearchContext<F, Indexed<Q>, X>, Convoluted<Pattern<F, X>>>> {
657 let mut iterators = Vec::new();
673 let indexes: Vec<usize> = (0..patterns.len()).collect();
674 for leaves in combinations(&indexes, |i| automata[*i].final_states().map(move |q| Indexed((*q).clone(), *i))) {
675 iterators.push(TermFragment::new(automata, 0, SearchContext::new(0), leaves, patterns.clone(), kill_signal.clone()))
677 }
678
679 Mux::new(iterators)
680}