1use std::collections::VecDeque;
8
9use indexmap::IndexMap;
10
11use super::{StateId, Transition, TransitionType, NFA};
12
13pub fn intersect(n: &NFA, m: &NFA) -> NFA {
17 let mut result = NFA::new();
18 let mut state_map = IndexMap::new();
20 let mut queue = VecDeque::new();
22
23 if let Some((n_start, m_start)) = n.initial().zip(m.initial()) {
25 let start = result.new_state();
26 result.set_initial(start).unwrap(); state_map.insert((n_start, m_start), start);
28 queue.push_back((n_start, m_start));
29 }
30
31 while let Some((n_state, m_state)) = queue.pop_front() {
33 let mapped_state = *state_map.get(&(n_state, m_state)).unwrap();
35 if n.is_final(n_state) && m.is_final(m_state) {
37 result.add_final(mapped_state).unwrap();
38 }
39 for t1 in n.transitions_from(n_state).unwrap() {
40 for t2 in m.transitions_from(m_state).unwrap() {
41 let intersections = intersect_transitions(n_state, t1, m_state, t2);
42 for (label, (p1, p2)) in intersections {
43 let new_dest = state_map.entry((p1, p2)).or_insert_with(|| {
44 let new_state = result.new_state();
45 queue.push_back((p1, p2));
46 new_state
47 });
48 result
49 .add_transition(mapped_state, *new_dest, label)
50 .unwrap();
51 }
52 }
53 }
54 }
55 result.trim()
56}
57
58fn intersect_transitions(
67 q1: StateId,
68 t1: &Transition,
69 q2: StateId,
70 t2: &Transition,
71) -> Vec<(TransitionType, (StateId, StateId))> {
72 let type1 = t1.label;
73 let type2 = t2.label;
74 let p1 = t1.destination;
75 let p2 = t2.destination;
76
77 let mut res = Vec::new();
78
79 match (type1, type2) {
80 (TransitionType::Range(r1), TransitionType::Range(r2)) => {
81 let inter = r1.intersect(&r2);
82 if !inter.is_empty() {
83 res.push((TransitionType::Range(inter), (p1, p2)));
84 }
85 }
86 (TransitionType::Range(r2), TransitionType::NotRange(r))
87 | (TransitionType::NotRange(r), TransitionType::Range(r2)) => {
88 let comp = r.complement();
89 for c in comp {
90 let inter = c.intersect(&r2);
91 if !inter.is_empty() {
92 res.push((TransitionType::Range(inter), (p1, p2)));
93 }
94 }
95 }
96 (TransitionType::NotRange(r1), TransitionType::NotRange(r2)) => {
97 for r1 in r1.complement() {
98 for r2 in r2.complement() {
99 let inter = r1.intersect(&r2);
100 if !inter.is_empty() {
101 res.push((TransitionType::Range(inter), (p1, p2)));
102 }
103 }
104 }
105 }
106 (TransitionType::Epsilon, _) => {
107 res.push((TransitionType::Epsilon, (p1, q2)));
109 }
110 (_, TransitionType::Epsilon) => {
111 res.push((TransitionType::Epsilon, (q1, p2)));
113 }
114 };
115 res
116}
117
118#[cfg(test)]
119mod tests {
120
121 use quickcheck_macros::quickcheck;
122
123 use crate::alphabet::CharRange;
124
125 use super::*;
126
127 #[test]
128 fn test_intersect_transitions_disjoint() {
129 let q1 = 0;
130 let q2 = 1;
131 let p1 = 2;
132 let p2 = 3;
133 let t1 = Transition::new(TransitionType::Range(CharRange::new('a', 'c')), p1);
134 let t2 = Transition::new(TransitionType::Range(CharRange::new('d', 'e')), p2);
135
136 let res = intersect_transitions(q1, &t1, q2, &t2);
137 assert_eq!(res.len(), 0);
138 }
139
140 #[test]
141 fn test_intersect_transitions_overlap() {
142 let q1 = 0;
143 let q2 = 1;
144 let p1 = 2;
145 let p2 = 3;
146 let t1 = Transition::new(TransitionType::Range(CharRange::new('a', 'g')), p1);
147 let t2 = Transition::new(TransitionType::Range(CharRange::new('d', 'k')), p2);
148
149 let res = intersect_transitions(q1, &t1, q2, &t2);
150 assert_eq!(res.len(), 1);
151 assert_eq!(res[0].0, TransitionType::Range(CharRange::new('d', 'g')));
152 }
153
154 #[test]
155 fn test_intersect_transitions_equal() {
156 let q1 = 0;
157 let q2 = 1;
158 let p1 = 2;
159 let p2 = 3;
160 let t1 = Transition::new(TransitionType::Range(CharRange::new('a', 'g')), p1);
161 let t2 = Transition::new(TransitionType::Range(CharRange::new('a', 'g')), p2);
162
163 let res = intersect_transitions(q1, &t1, q2, &t2);
164 assert_eq!(res.len(), 1);
165 assert_eq!(res[0].0, TransitionType::Range(CharRange::new('a', 'g')));
166 }
167
168 #[quickcheck]
169 fn test_intersect_transitions_ranges(r1: CharRange, r2: CharRange) {
170 let q1 = 0;
171 let q2 = 1;
172 let p1 = 2;
173 let p2 = 3;
174 let t1 = Transition::new(TransitionType::Range(r1), p1);
175 let t2 = Transition::new(TransitionType::Range(r2), p2);
176
177 let res = intersect_transitions(q1, &t1, q2, &t2);
178 if r1.intersect(&r2).is_empty() {
179 assert_eq!(res.len(), 0);
180 } else {
181 assert_eq!(res.len(), 1);
182 assert_eq!(res[0].0, TransitionType::Range(r1.intersect(&r2)));
183 assert_eq!(res[0].1, (p1, p2));
184 }
185 }
186
187 #[test]
188 fn test_intersect_transitions_epsilon() {
189 let q1 = 0;
190 let q2 = 1;
191 let p1 = 2;
192 let p2 = 3;
193 let t1 = Transition::new(TransitionType::Epsilon, p1);
194 let t2 = Transition::new(TransitionType::Range(CharRange::new('a', 'z')), p2);
195
196 let res = intersect_transitions(q1, &t1, q2, &t2);
197 assert_eq!(res.len(), 1);
198 assert_eq!(res[0].0, TransitionType::Epsilon);
199 assert_eq!(res[0].1, (p1, q2));
200 }
201
202 #[test]
203 fn test_intersect_empty() {
204 let a1: NFA = NFA::new();
205 let a2: NFA = NFA::new();
206 let result = intersect(&a1, &a2);
207 assert_eq!(result.states.len(), 0);
208 assert!(result.initial.is_none());
209 assert_eq!(result.finals.len(), 0);
210 }
211
212 #[test]
213 fn test_intersection_simple() {
214 let mut nfa1 = NFA::new();
215 let q0 = nfa1.new_state();
216 let q1 = nfa1.new_state();
217 nfa1.set_initial(q0).unwrap();
218 nfa1.add_final(q1).unwrap();
219 nfa1.add_transition(q0, q1, TransitionType::Range(CharRange::new('a', 'b')))
220 .unwrap();
221
222 let mut nfa2 = NFA::new();
223 let p0 = nfa2.new_state();
224 let p1 = nfa2.new_state();
225 nfa2.set_initial(p0).unwrap();
226 nfa2.add_final(p1).unwrap();
227 nfa2.add_transition(p0, p1, TransitionType::Range(CharRange::new('b', 'c')))
228 .unwrap();
229
230 let result = intersect(&nfa1, &nfa2);
231
232 assert!(result.accepts(&"b".into()));
233 assert!(!result.accepts(&"a".into()));
234 assert!(!result.accepts(&"c".into()));
235 }
236
237 #[test]
238 fn test_intersection_epsilon() {
239 let mut nfa1 = NFA::new();
240 let q0 = nfa1.new_state();
241 let q1 = nfa1.new_state();
242 nfa1.set_initial(q0).unwrap();
243 nfa1.add_final(q1).unwrap();
244 nfa1.add_transition(q0, q1, TransitionType::Epsilon)
245 .unwrap();
246
247 let mut nfa2 = NFA::new();
248 let p0 = nfa2.new_state();
249 let p1 = nfa2.new_state();
250 nfa2.set_initial(p0).unwrap();
251 nfa2.add_final(p1).unwrap();
252 nfa2.add_transition(p0, p1, TransitionType::Range(CharRange::new('a', 'z')))
253 .unwrap();
254
255 let result = intersect(&nfa1, &nfa2);
256
257 assert!(!result.accepts(&"a".into()));
258 assert!(!result.accepts(&"".into()));
259 }
260
261 #[test]
262 fn test_intersection_negated_range() {
263 let mut nfa1 = NFA::new();
264 let q0 = nfa1.new_state();
265 let q1 = nfa1.new_state();
266 nfa1.set_initial(q0).unwrap();
267 nfa1.add_final(q1).unwrap();
268 nfa1.add_transition(q0, q1, TransitionType::NotRange(CharRange::new('a', 'z')))
269 .unwrap();
270
271 let mut nfa2 = NFA::new();
272 let p0 = nfa2.new_state();
273 let p1 = nfa2.new_state();
274 nfa2.set_initial(p0).unwrap();
275 nfa2.add_final(p1).unwrap();
276 nfa2.add_transition(p0, p1, TransitionType::Range(CharRange::new('0', '9')))
277 .unwrap();
278
279 let result = intersect(&nfa1, &nfa2);
280
281 assert!(result.accepts(&"0".into()));
282 assert!(result.accepts(&"9".into()));
283 assert!(!result.accepts(&"a".into()));
284 assert!(!result.accepts(&"z".into()));
285 }
286
287 #[test]
288 fn test_intersection_disjoint() {
289 let mut nfa1 = NFA::new();
290 let q0 = nfa1.new_state();
291 let q1 = nfa1.new_state();
292 nfa1.set_initial(q0).unwrap();
293 nfa1.add_final(q1).unwrap();
294 nfa1.add_transition(q0, q1, TransitionType::Range(CharRange::new('a', 'b')))
295 .unwrap();
296
297 let mut nfa2 = NFA::new();
298 let p0 = nfa2.new_state();
299 let p1 = nfa2.new_state();
300 nfa2.set_initial(p0).unwrap();
301 nfa2.add_final(p1).unwrap();
302 nfa2.add_transition(p0, p1, TransitionType::Range(CharRange::new('c', 'd')))
303 .unwrap();
304
305 let result = intersect(&nfa1, &nfa2);
306
307 assert!(!result.accepts(&"a".into()));
308 assert!(!result.accepts(&"b".into()));
309 assert!(!result.accepts(&"c".into()));
310 assert!(!result.accepts(&"d".into()));
311 }
312
313 #[test]
314 fn test_intersection_multiple_steps() {
315 let mut nfa1 = NFA::new();
316 let q0 = nfa1.new_state();
317 let q1 = nfa1.new_state();
318 let q2 = nfa1.new_state();
319 nfa1.set_initial(q0).unwrap();
320 nfa1.add_final(q2).unwrap();
321 nfa1.add_transition(q0, q1, TransitionType::Range(CharRange::new('a', 'a')))
322 .unwrap();
323 nfa1.add_transition(q1, q2, TransitionType::Range(CharRange::new('b', 'b')))
324 .unwrap();
325
326 let mut nfa2 = NFA::new();
327 let p0 = nfa2.new_state();
328 let p1 = nfa2.new_state();
329 let p2 = nfa2.new_state();
330 nfa2.set_initial(p0).unwrap();
331 nfa2.add_final(p2).unwrap();
332 nfa2.add_transition(p0, p1, TransitionType::Range(CharRange::new('a', 'a')))
333 .unwrap();
334 nfa2.add_transition(p1, p2, TransitionType::Range(CharRange::new('b', 'b')))
335 .unwrap();
336
337 let result = intersect(&nfa1, &nfa2);
338
339 assert!(result.accepts(&"ab".into()));
340 assert!(!result.accepts(&"a".into()));
341 assert!(!result.accepts(&"b".into()));
342 assert!(!result.accepts(&"ba".into()));
343 }
344}