1
use std::cell::RefCell;
2
use std::rc::Rc;
3

            
4
use log::info;
5
use log::trace;
6
use mcrl2::aterm::ATermRef;
7
use mcrl2::aterm::TermPool;
8
use mcrl2::data::DataApplication;
9
use mcrl2::data::DataExpression;
10
use mcrl2::data::DataExpressionRef;
11

            
12
use crate::matching::conditions::extend_conditions;
13
use crate::matching::conditions::EMACondition;
14
use crate::matching::nonlinear::check_equivalence_classes;
15
use crate::matching::nonlinear::derive_equivalence_classes;
16
use crate::matching::nonlinear::EquivalenceClass;
17
use crate::set_automaton::MatchAnnouncement;
18
use crate::set_automaton::SetAutomaton;
19
use crate::utilities::Config;
20
use crate::utilities::InnermostStack;
21
use crate::utilities::PositionIndexed;
22
use crate::utilities::RHSStack;
23
use crate::utilities::SCCTBuilder;
24
use crate::RewriteEngine;
25
use crate::RewriteSpecification;
26
use crate::RewritingStatistics;
27
use crate::Rule;
28

            
29
impl RewriteEngine for InnermostRewriter {
30
386
    fn rewrite(&mut self, t: DataExpression) -> DataExpression {
31
386
        let mut stats = RewritingStatistics::default();
32
386

            
33
386
        trace!("input: {}", t);
34

            
35
386
        let result = InnermostRewriter::rewrite_aux(
36
386
            &mut self.tp.borrow_mut(),
37
386
            &mut self.stack,
38
386
            &mut self.builder,
39
386
            &mut stats,
40
386
            &self.apma,
41
386
            t,
42
386
        );
43
386
        info!(
44
1
            "{} rewrites, {} single steps and {} symbol comparisons",
45
            stats.recursions, stats.rewrite_steps, stats.symbol_comparisons
46
        );
47
386
        result
48
386
    }
49
}
50

            
51
impl InnermostRewriter {
52
251
    pub fn new(tp: Rc<RefCell<TermPool>>, spec: &RewriteSpecification) -> InnermostRewriter {
53
251
        let apma = SetAutomaton::new(spec, AnnouncementInnermost::new, true);
54
251

            
55
251
        info!("ATerm pool: {}", tp.borrow());
56
251
        InnermostRewriter {
57
251
            apma,
58
251
            tp: tp.clone(),
59
251
            stack: InnermostStack::default(),
60
251
            builder: SCCTBuilder::new(),
61
251
        }
62
251
    }
63

            
64
    /// Function to rewrite a term 't'. The elements of the automaton 'states'
65
    /// and 'tp' are passed as separate parameters to satisfy the borrow
66
    /// checker.
67
    ///
68
    /// # Details
69
    ///
70
    /// Uses a stack of terms and configurations to avoid recursions and to keep
71
    /// track of terms in normal forms without explicit tagging. The configuration
72
    /// stack consists of three different possible values with the following semantics
73
    ///     - Return(): Returns the top of the stack.
74
    ///     - Rewrite(index): Updates the configuration to rewrite the top of the term stack
75
    ///                       and places the result on the given index.
76
    ///     - Construct(arity, index, result):
77
    ///
78
2796176
    pub(crate) fn rewrite_aux(
79
2796176
        tp: &mut TermPool,
80
2796176
        stack: &mut InnermostStack,
81
2796176
        builder: &mut SCCTBuilder,
82
2796176
        stats: &mut RewritingStatistics,
83
2796176
        automaton: &SetAutomaton<AnnouncementInnermost>,
84
2796176
        input_term: DataExpression,
85
2796176
    ) -> DataExpression {
86
2796176
        debug_assert!(!input_term.is_default(), "Cannot rewrite the default term");
87

            
88
2796176
        stats.recursions += 1;
89
2796176
        {
90
2796176
            let mut write_terms = stack.terms.write();
91
2796176
            let mut write_configs = stack.configs.write();
92
2796176

            
93
2796176
            // Push the result term to the stack.
94
2796176
            let top_of_stack = write_terms.len();
95
2796176
            write_configs.push(Config::Return());
96
2796176
            write_terms.push(DataExpressionRef::default());
97
2796176
            InnermostStack::add_rewrite(&mut write_configs, &mut write_terms, input_term.copy(), top_of_stack);
98
2796176
        }
99

            
100
        loop {
101
70327929
            trace!("{}", stack);
102

            
103
70327929
            let mut write_configs = stack.configs.write();
104
70327929
            if let Some(config) = write_configs.pop() {
105
70327929
                match config {
106
25737659
                    Config::Rewrite(result) => {
107
25737659
                        let mut write_terms = stack.terms.write();
108
25737659
                        let term = write_terms.pop().unwrap();
109
25737659

            
110
25737659
                        let symbol = term.data_function_symbol();
111
25737659
                        let arguments = term.data_arguments();
112
25737659

            
113
25737659
                        // For all the argument we reserve space on the stack.
114
25737659
                        let top_of_stack = write_terms.len();
115
25737659
                        for _ in 0..arguments.len() {
116
22941483
                            write_terms.push(Default::default());
117
22941483
                        }
118

            
119
25737659
                        let symbol = write_configs.protect(&symbol.into());
120
25737659
                        InnermostStack::add_result(&mut write_configs, symbol.into(), arguments.len(), result);
121
25737659
                        for (offset, arg) in arguments.into_iter().enumerate() {
122
22941483
                            InnermostStack::add_rewrite(
123
22941483
                                &mut write_configs,
124
22941483
                                &mut write_terms,
125
22941483
                                arg.into(),
126
22941483
                                top_of_stack + offset,
127
22941483
                            );
128
22941483
                        }
129
25737659
                        drop(write_configs);
130
                    }
131
41794094
                    Config::Construct(symbol, arity, index) => {
132
41794094
                        // Take the last arity arguments.
133
41794094
                        let mut write_terms = stack.terms.write();
134
41794094
                        let length = write_terms.len();
135
41794094

            
136
41794094
                        let arguments = &write_terms[length - arity..];
137

            
138
41794094
                        let term: DataExpression = if arguments.is_empty() {
139
11008830
                            symbol.protect().into()
140
                        } else {
141
30785264
                            DataApplication::new(tp, &symbol, arguments).into()
142
                        };
143

            
144
                        // Remove the arguments from the stack.
145
41794094
                        write_terms.drain(length - arity..);
146
41794094
                        drop(write_terms);
147
41794094
                        drop(write_configs);
148
41794094

            
149
41794094
                        match InnermostRewriter::find_match(tp, stack, builder, stats, automaton, &term) {
150
11317515
                            Some((announcement, annotation)) => {
151
11317515
                                trace!(
152
                                    "rewrite {} => {} using rule {}",
153
                                    term,
154
                                    annotation.rhs_stack.evaluate(tp, &term),
155
                                    announcement.rule
156
                                );
157

            
158
                                // Reacquire the write access and add the matching RHSStack.
159
11317515
                                let mut write_terms = stack.terms.write();
160
11317515
                                let mut write_configs = stack.configs.write();
161
11317515
                                InnermostStack::integrate(
162
11317515
                                    &mut write_configs,
163
11317515
                                    &mut write_terms,
164
11317515
                                    &annotation.rhs_stack,
165
11317515
                                    &term,
166
11317515
                                    index,
167
11317515
                                );
168
11317515
                                stats.rewrite_steps += 1;
169
                            }
170
30476579
                            None => {
171
30476579
                                // Add the term on the stack.
172
30476579
                                let mut write_terms = stack.terms.write();
173
30476579
                                let t = write_terms.protect(&term);
174
30476579
                                write_terms[index] = t.into();
175
30476579
                            }
176
                        }
177
                    }
178
                    Config::Return() => {
179
2796176
                        let mut write_terms = stack.terms.write();
180
2796176

            
181
2796176
                        return write_terms
182
2796176
                            .pop()
183
2796176
                            .expect("The result should be the last element on the stack")
184
2796176
                            .protect();
185
                    }
186
                }
187

            
188
67531753
                let read_configs = stack.configs.read();
189
11322794770
                for (index, term) in stack.terms.read().iter().enumerate() {
190
11322794770
                    if term.is_default() {
191
4988316377
                        debug_assert!(
192
4988316377
                            read_configs.iter().any(|x| {
193
4988316377
                                match x {
194
                                    Config::Construct(_, _, result) => index == *result,
195
                                    Config::Rewrite(result) => index == *result,
196
4988316377
                                    Config::Return() => true,
197
                                }
198
4988316377
                            }),
199
                            "The default term at index {index} is not a result of any operation."
200
                        );
201
6334478393
                    }
202
                }
203
            }
204
        }
205
2796176
    }
206

            
207
    /// Use the APMA to find a match for the given term.
208
41794094
    fn find_match<'a>(
209
41794094
        tp: &mut TermPool,
210
41794094
        stack: &mut InnermostStack,
211
41794094
        builder: &mut SCCTBuilder,
212
41794094
        stats: &mut RewritingStatistics,
213
41794094
        automaton: &'a SetAutomaton<AnnouncementInnermost>,
214
41794094
        t: &ATermRef<'_>,
215
41794094
    ) -> Option<(&'a MatchAnnouncement, &'a AnnouncementInnermost)> {
216
41794094
        // Start at the initial state
217
41794094
        let mut state_index = 0;
218
        loop {
219
59695714
            let state = &automaton.states[state_index];
220
59695714

            
221
59695714
            // Get the symbol at the position state.label
222
59695714
            stats.symbol_comparisons += 1;
223
59695714
            let pos: DataExpressionRef<'_> = t.get_position(&state.label).into();
224
59695714
            let symbol = pos.data_function_symbol();
225

            
226
            // Get the transition for the label and check if there is a pattern match
227
59695714
            if let Some(transition) = automaton.transitions.get(&(state_index, symbol.operation_id())) {
228
61395005
                for (announcement, annotation) in &transition.announcements {
229
13016855
                    if check_equivalence_classes(t, &annotation.equivalence_classes)
230
11822425
                        && InnermostRewriter::check_conditions(tp, stack, builder, stats, automaton, annotation, t)
231
                    {
232
                        // We found a matching pattern
233
11317515
                        return Some((announcement, annotation));
234
1699340
                    }
235
                }
236

            
237
                // If there is no pattern match we check if the transition has a destination state
238
48378150
                if transition.destinations.is_empty() {
239
                    // If there is no destination state there is no pattern match
240
30476530
                    return None;
241
17901620
                } else {
242
17901620
                    // Continue matching in the next state
243
17901620
                    state_index = transition.destinations.first().unwrap().1;
244
17901620
                }
245
            } else {
246
49
                return None;
247
            }
248
        }
249
41794094
    }
250

            
251
    /// Checks whether the condition holds for given match announcement.
252
11822425
    fn check_conditions(
253
11822425
        tp: &mut TermPool,
254
11822425
        stack: &mut InnermostStack,
255
11822425
        builder: &mut SCCTBuilder,
256
11822425
        stats: &mut RewritingStatistics,
257
11822425
        automaton: &SetAutomaton<AnnouncementInnermost>,
258
11822425
        announcement: &AnnouncementInnermost,
259
11822425
        t: &ATermRef<'_>,
260
11822425
    ) -> bool {
261
12715410
        for c in &announcement.conditions {
262
1397895
            let rhs: DataExpression = c.semi_compressed_rhs.evaluate_with(builder, t, tp).into();
263
1397895
            let lhs: DataExpression = c.semi_compressed_lhs.evaluate_with(builder, t, tp).into();
264
1397895

            
265
1397895
            let rhs_normal = InnermostRewriter::rewrite_aux(tp, stack, builder, stats, automaton, rhs);
266
1397895
            let lhs_normal = if &lhs == tp.true_term() {
267
                // TODO: Store the conditions in a better way. REC now uses a list of equalities while mCRL2 specifications have a simple condition.
268
                lhs
269
            } else {
270
1397895
                InnermostRewriter::rewrite_aux(tp, stack, builder, stats, automaton, lhs)
271
            };
272

            
273
1397895
            if lhs_normal != rhs_normal && c.equality || lhs_normal == rhs_normal && !c.equality {
274
504910
                return false;
275
892985
            }
276
        }
277

            
278
11317515
        true
279
11822425
    }
280
}
281

            
282
/// Innermost Adaptive Pattern Matching Automaton (APMA) rewrite engine.
283
pub struct InnermostRewriter {
284
    tp: Rc<RefCell<TermPool>>,
285
    apma: SetAutomaton<AnnouncementInnermost>,
286
    stack: InnermostStack,
287
    builder: SCCTBuilder,
288
}
289

            
290
pub(crate) struct AnnouncementInnermost {
291
    /// Positions in the pattern with the same variable, for non-linear patterns
292
    equivalence_classes: Vec<EquivalenceClass>,
293

            
294
    /// Conditions for the left hand side.
295
    conditions: Vec<EMACondition>,
296

            
297
    /// The innermost stack for the right hand side of the rewrite rule.
298
    rhs_stack: RHSStack,
299
}
300

            
301
impl AnnouncementInnermost {
302
36080
    fn new(rule: &Rule) -> AnnouncementInnermost {
303
36080
        AnnouncementInnermost {
304
36080
            conditions: extend_conditions(rule),
305
36080
            equivalence_classes: derive_equivalence_classes(rule),
306
36080
            rhs_stack: RHSStack::new(rule),
307
36080
        }
308
36080
    }
309
}
310

            
311
#[cfg(test)]
312
mod tests {
313
    use std::cell::RefCell;
314
    use std::rc::Rc;
315

            
316
    use ahash::AHashSet;
317
    use mcrl2::aterm::random_term;
318
    use mcrl2::aterm::TermPool;
319

            
320
    use rand::rngs::StdRng;
321
    use rand::Rng;
322
    use rand::SeedableRng;
323
    use test_log::test;
324

            
325
    use crate::utilities::to_untyped_data_expression;
326
    use crate::InnermostRewriter;
327
    use crate::RewriteEngine;
328
    use crate::RewriteSpecification;
329

            
330
1
    #[test]
331
    fn test_innermost_simple() {
332
        let tp = Rc::new(RefCell::new(TermPool::new()));
333

            
334
        let spec = RewriteSpecification { rewrite_rules: vec![] };
335
        let mut inner = InnermostRewriter::new(tp.clone(), &spec);
336

            
337
        let seed: u64 = rand::rng().random();
338
        println!("seed: {}", seed);
339
        let mut rng = StdRng::seed_from_u64(seed);
340

            
341
        let term = random_term(
342
            &mut tp.borrow_mut(),
343
            &mut rng,
344
            &[("f".to_string(), 2)],
345
            &["a".to_string(), "b".to_string()],
346
            5,
347
        );
348
        let term = to_untyped_data_expression(&mut tp.borrow_mut(), &term, &AHashSet::new());
349

            
350
        assert_eq!(
351
            inner.rewrite(term.clone().into()),
352
            term.into(),
353
            "Should be in normal form for no rewrite rules"
354
        );
355
    }
356
}