automatic_relations/convolution/aligned/
search.rs

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	// AlignedConvolutedPattern
38};
39
40type Renaming<X> = immutable_map::TreeMap<X, X>;
41
42/// Convoluted pattern that must be recognized by the given state.
43#[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		// for x in item.0.iter() {
86		//	 if let Some(x) = x {
87		//		 write!(f, "{}:", x)?;
88		//	 } else {
89		//		 write!(f, "_:")?;
90		//	 }
91		// }
92		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
185/// Sub-problem
186///
187/// The general search problem is broken down into these sub problems that can be used to
188/// detect loops in the search.
189///
190/// Each sub-problem associates convoluted patterns to states.
191/// Convoluted patterns in a given sub-problem are dependents, they share variables.
192pub 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	/// Make sure to also add the variables from the atom.
231	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		// println!("== atoms:");
242		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				// println!("{}", atom);
250				let key = uf.insert(SubProblem::singleton(atom, vars.clone()));
251				Some(IndexedAtom(key, vars))
252			}
253		}).collect();
254
255		// println!("== sub problems:");
256
257		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				// println!("{}", p);
271				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	/// Checks if there exsits a renaming such that this sub-problem and the given one are equals.
283	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				// println!("renaming between: {} and {}", self, other);
291			} else {
292				// println!("no renaming between: {} and {}", self, other);
293			}
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			// println!("{} > {}", x, y);
481			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			// println!("looping in");
490			let r1 = sub_problems.iter().any(|sub_problem| sps.iter().any(|sp| sub_problem.is_descendent_matching(sp)));
491			// println!("looping mid");
492			// let r2 = sps.iter().all(|sp| sub_problems.iter().any(|sub_problem| sub_problem.matches(sp)));
493
494			if r1 { // TODO optimize
495				// println!("loop detected");
496				return true
497			}
498			// println!("OUT");
499		}
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 sub_states = current_conf.states();
547
548		// println!("visiting {}({}) -> {}", conf_f, PList(sub_states), current_state);
549
550		// let mut new_instances = HashMap::new();
551		let mut new_context = SearchContextData::from(depth, ctx);
552		let mut patterns_builder = ConvolutedPatternBuilder::new();
553		// patterns_builder.set_arity(sub_states.len());
554
555		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							// println!("rejected because of symbol inequality ({} != {})", conf_f.0[k], f);
561							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						// Is the variable already instanciated ?
570						// if let Some((f, sub_variables)) = ctx.instance(x) {
571						// 	if conf_f.0[k] != *f {
572						// 		// println!("rejected because of symbol inequality ({} != {})", conf_f.0[k], f);
573						// 		return None
574						// 	}
575						//
576						// 	for (i, y) in sub_variables.iter().enumerate() {
577						// 		patterns_builder.set(k, i, Pattern::var(y.clone()))
578						// 	}
579						// } else {
580							// Have we instanciated it in a previous loop iteration (in the new context) ?
581							if let Some((f, sub_variables)) = new_context.instance(x) {
582								if conf_f.0[k] != *f {
583									// println!("rejected because of symbol inequality ({} != {})", conf_f.0[k], f);
584									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								// Can we instanciate the variable ?
592								// We cannot instanciate a variable with bottom.
593								if let MaybeBottom::Some(f) = &conf_f.0[k] {
594									// println!("instanciating {} with {}", x, Configuration(conf_f.clone(), sub_states.clone()));
595									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									// println!("rejected because symbol is not some");
602									// println!("{}", self);
603									return None
604								}
605							}
606						// }
607					}
608				}
609			} else {
610				if conf_f.0[k] != MaybeBottom::Bottom {
611					// println!("rejected because symbol is not bottom (but {})", conf_f.0[k]);
612					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		// println!("size: {}/{}", patterns.len(), sub_states.len());
621
622		let new_context = SearchContext::from(new_context);
623		// if ta::bottom_up::SearchContext::looping(&new_context) {
624		// 	None
625		// } else {
626			Some((new_context, patterns))
627		// }
628	}
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 ctx_patterns: Vec<Convoluted<Pattern<F, X>>> = patterns.iter().map(|ap| {
633	// 	Convoluted(ap.0.iter().map(|p| {
634	// 		match p {
635	// 			MaybeBottom::Bottom => MaybeBottom::Bottom,
636	// 			MaybeBottom::Some(p) => {
637	// 				MaybeBottom::Some(p.map_variables(&|x| {
638	// 					Pattern::var(Variable::from(x.clone()))
639	// 				}))
640	// 			}
641	// 		}
642	// 	}).collect())
643	// }).collect();
644
645	// let leaf = aut.final_states().next().unwrap().clone();
646
647	let mut iterators = Vec::new();
648	for leaves in combinations(&patterns, |_| aut.final_states().map(|q| (*q).clone())) {
649		// println!("leaves: {:?}", leaves);
650		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 ctx_patterns: Vec<Convoluted<Pattern<F, X>>> = patterns.iter().map(|ap| {
658	// 	Convoluted(ap.0.iter().map(|p| {
659	// 		match p {
660	// 			MaybeBottom::Bottom => MaybeBottom::Bottom,
661	// 			MaybeBottom::Some(p) => {
662	// 				MaybeBottom::Some(p.map_variables(&|x| {
663	// 					Pattern::var(Variable::from(x.clone()))
664	// 				}))
665	// 			}
666	// 		}
667	// 	}).collect())
668	// }).collect();
669
670	// let leaf = aut.final_states().next().unwrap().clone();
671
672	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		// println!("leaves: {:?}", leaves);
676		iterators.push(TermFragment::new(automata, 0, SearchContext::new(0), leaves, patterns.clone(), kill_signal.clone()))
677	}
678
679	Mux::new(iterators)
680}