1
use std::fmt;
2
use std::ops::Deref;
3

            
4
use crate::matching::conditions::extend_conditions;
5
use crate::matching::conditions::EMACondition;
6
use crate::matching::nonlinear::derive_equivalence_classes;
7
use crate::matching::nonlinear::EquivalenceClass;
8
use crate::set_automaton::MatchAnnouncement;
9
use crate::set_automaton::SetAutomaton;
10
use crate::utilities::ExplicitPosition;
11
use crate::Rule;
12

            
13
use mcrl2::aterm::Protected;
14
use mcrl2::aterm::TermPool;
15
use mcrl2::data::DataExpression;
16
use mcrl2::data::DataExpressionRef;
17

            
18
use super::create_var_map;
19
use super::substitute_with;
20
use super::PositionIndexed;
21
use super::SemiCompressedTermTree;
22
use super::SubstitutionBuilder;
23

            
24
/// This is the announcement for Sabre, which stores additional information about the rewrite rules.
25
#[derive(Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
26
pub struct AnnouncementSabre {
27
    /// Positions in the pattern with the same variable, for non-linear patterns
28
    pub equivalence_classes: Vec<EquivalenceClass>,
29

            
30
    /// Conditions for applying the rule.
31
    pub conditions: Vec<EMACondition>,
32

            
33
    /// Right hand side is stored in the term pool as much as possible with a SemiCompressedTermTree
34
    pub semi_compressed_rhs: SemiCompressedTermTree,
35
    /// Whether the rewrite rule duplicates subterms, e.g. times(s(x), y) = plus(y, times(x, y))
36
    pub is_duplicating: bool,
37
}
38

            
39
impl AnnouncementSabre {
40
71420
    pub fn new(rule: &Rule) -> AnnouncementSabre {
41
71420
        // Compute the extra information for the InnermostRewriter.
42
71420
        // Create a mapping of where the variables are and derive SemiCompressedTermTrees for the
43
71420
        // rhs of the rewrite rule and for lhs and rhs of each condition.
44
71420
        // Also see the documentation of SemiCompressedTermTree
45
71420
        let var_map = create_var_map(&rule.lhs.clone().into());
46
71420
        let sctt_rhs = SemiCompressedTermTree::from_term(&rule.rhs.copy().into(), &var_map);
47
71420

            
48
71420
        let is_duplicating = sctt_rhs.contains_duplicate_var_references();
49
71420

            
50
71420
        AnnouncementSabre {
51
71420
            conditions: extend_conditions(rule),
52
71420
            equivalence_classes: derive_equivalence_classes(rule),
53
71420
            semi_compressed_rhs: sctt_rhs,
54
71420
            is_duplicating,
55
71420
        }
56
71420
    }
57
}
58

            
59
/// A Configuration is part of the configuration stack/tree
60
/// It contains:
61
///     1. the index of a state of the set automaton
62
///     2. The subterm at the position of the configuration.
63
///     3. The difference of position compared to the parent configuration (None for the root).
64
///         Note that it stores a reference to a position. It references the position listed on
65
///         a transition of the set automaton.
66
#[derive(Debug)]
67
pub(crate) struct Configuration<'a> {
68
    pub state: usize,
69
    pub position: Option<&'a ExplicitPosition>,
70
}
71

            
72
/// SideInfo stores additional information of a configuration. It stores an
73
/// index of the corresponding configuration on the configuration stack.
74
#[derive(Debug)]
75
pub(crate) struct SideInfo<'a> {
76
    pub corresponding_configuration: usize,
77
    pub info: SideInfoType<'a>,
78
}
79

            
80
/// A "side stack" is used besides the configuration stack to
81
/// remember a couple of things. There are 4 options.
82
///
83
/// 1. There is nothing on the side stack for this
84
///    configuration. This means we have never seen this
85
///    configuration before. It is a bud that needs to be
86
///    explored.
87
///
88
/// In the remaining three cases we have seen the
89
/// configuration before and have pruned back, either because
90
/// of applying a rewrite rule or just because our depth
91
/// first search has hit the bottom and needs to explore a
92
/// new branch.
93
///
94
/// 2. There is a side branch. That means we had a hyper
95
///    transition. The configuration has multiple children in
96
///    the overall tree. We have already explored some of these
97
///    child configurations and parked the remaining on the side
98
///    stack. We are going to explore the next child
99
///    configuration.
100
///
101
/// 3. There is a delayed rewrite rule. We had found a
102
///    matching rewrite rule the first time visiting this
103
///    configuration but did not want to apply it yet. At the
104
///    moment this is the case for "duplicating" rewrite rules
105
///    that copy some subterms. We first examine side branches
106
///    on the side stack, meaning that we have explored all
107
///    child configurations. Which, in turn, means that the
108
///    subterms of the term in the current configuration are in
109
///    normal form. Thus the duplicating rewrite rule only
110
///    duplicates terms that are in normal form.
111
///
112
/// 4. There is another type of delayed rewrite rule: one
113
///    that is non-linear or has a condition. We had found a
114
///    matching rewrite rule the first time visiting this
115
///    configuration but our strategy dictates that we only
116
///    perform the condition check and check on the equivalence
117
///    of positions when the subterms are in normal form. We
118
///    perform the checks and apply the rewrite rule if it
119
///    indeed matches.
120
#[derive(Debug)]
121
pub(crate) enum SideInfoType<'a> {
122
    SideBranch(&'a [(ExplicitPosition, usize)]),
123
    DelayedRewriteRule(&'a MatchAnnouncement, &'a AnnouncementSabre),
124
    EquivalenceAndConditionCheck(&'a MatchAnnouncement, &'a AnnouncementSabre),
125
}
126

            
127
/// A configuration stack. The first element is the root of the configuration tree.
128
#[derive(Debug)]
129
pub(crate) struct ConfigurationStack<'a> {
130
    pub stack: Vec<Configuration<'a>>,
131
    pub terms: Protected<Vec<DataExpressionRef<'static>>>,
132

            
133
    /// Separate stack with extra information on some configurations
134
    pub side_branch_stack: Vec<SideInfo<'a>>,
135

            
136
    /// Current node. Becomes None when the configuration tree is completed
137
    pub current_node: Option<usize>,
138

            
139
    /// Upon applying a rewrite rule we do not immediately update the subterm stored in every configuration on the stack.
140
    /// That would be very expensive. Instead we ensure that the subterm in the current_node is always up to date.
141
    /// oldest_reliable_subterm is an index to the highest configuration in the tree that is up to date.
142
    pub oldest_reliable_subterm: usize,
143
    pub substitution_builder: SubstitutionBuilder,
144
}
145

            
146
impl<'a> ConfigurationStack<'a> {
147
    /// Initialise the stack with one Configuration containing 'term' and the initial state of the set automaton
148
1289000
    pub fn new(state: usize, term: DataExpression) -> ConfigurationStack<'a> {
149
1289000
        let mut conf_list = ConfigurationStack {
150
1289000
            stack: Vec::with_capacity(8),
151
1289000
            side_branch_stack: vec![],
152
1289000
            terms: Protected::new(vec![]),
153
1289000
            current_node: Some(0),
154
1289000
            oldest_reliable_subterm: 0,
155
1289000
            substitution_builder: SubstitutionBuilder::default(),
156
1289000
        };
157
1289000
        conf_list.stack.push(Configuration { state, position: None });
158
1289000

            
159
1289000
        let mut write_conf_list = conf_list.terms.write();
160
1289000
        let term = write_conf_list.protect(&term.copy().into());
161
1289000
        write_conf_list.push(term.into());
162
1289000
        drop(write_conf_list);
163
1289000

            
164
1289000
        conf_list
165
1289000
    }
166

            
167
    /// Obtain the first unexplored node of the stack, which is just the top of the stack.
168
93099545
    pub(crate) fn get_unexplored_leaf(&self) -> Option<usize> {
169
93099545
        self.current_node
170
93099545
    }
171

            
172
    /// Returns the lowest configuration in the tree with SideInfo
173
5943990
    pub(crate) fn get_prev_with_side_info(&self) -> Option<usize> {
174
5943990
        self.side_branch_stack.last().map(|si| si.corresponding_configuration)
175
5943990
    }
176

            
177
    /// Grow a Configuration with index c. tr_slice contains the hypertransition to possibly multiple states
178
78825180
    pub fn grow(&mut self, c: usize, tr_slice: &'a [(ExplicitPosition, usize)]) {
179
78825180
        // Pick the first transition to grow the stack
180
78825180
        let (pos, des) = tr_slice.first().unwrap();
181
78825180

            
182
78825180
        // If there are more transitions store the remaining on the side stack
183
78825180
        let tr_slice: &[(ExplicitPosition, usize)] = &(tr_slice)[1..];
184
78825180
        if !tr_slice.is_empty() {
185
4231695
            self.side_branch_stack.push(SideInfo {
186
4231695
                corresponding_configuration: c,
187
4231695
                info: SideInfoType::SideBranch(tr_slice),
188
4231695
            })
189
74593485
        }
190

            
191
        // Create a new configuration and push it onto the stack
192
78825180
        let new_leaf = Configuration {
193
78825180
            state: *des,
194
78825180
            position: Some(pos),
195
78825180
        };
196
78825180
        self.stack.push(new_leaf);
197
78825180

            
198
78825180
        // Push the term belonging to the leaf.
199
78825180
        let mut write_terms = self.terms.write();
200
78825180
        let t = write_terms.protect(&write_terms[c].get_position(pos));
201
78825180
        write_terms.push(t.into());
202
78825180

            
203
78825180
        self.current_node = Some(c + 1);
204
78825180
    }
205

            
206
    /// When rewriting prune "prunes" the configuration stack to the place where the first symbol
207
    /// of the matching rewrite rule was observed (at index 'depth').
208
6777690
    pub fn prune(
209
6777690
        &mut self,
210
6777690
        tp: &mut TermPool,
211
6777690
        automaton: &SetAutomaton<AnnouncementSabre>,
212
6777690
        depth: usize,
213
6777690
        new_subterm: DataExpression,
214
6777690
    ) {
215
6777690
        self.current_node = Some(depth);
216
6777690

            
217
6777690
        // Reroll the configuration stack by truncating the Vec (which is a constant time operation)
218
6777690
        self.stack.truncate(depth + 1);
219
6777690
        self.terms.write().truncate(depth + 1);
220
6777690

            
221
6777690
        // Remove side info for the deleted configurations
222
6777690
        self.roll_back_side_info_stack(depth, true);
223
6777690

            
224
6777690
        // Update the subterm stored at the prune point.
225
6777690
        // Note that the subterm stored earlier may not have been up to date. We replace it with a term that is up to date
226
6777690
        let mut write_terms = self.terms.write();
227
6777690
        let subterm = write_terms.protect(
228
6777690
            substitute_with(
229
6777690
                &mut self.substitution_builder,
230
6777690
                tp,
231
6777690
                write_terms[depth].deref(),
232
6777690
                new_subterm.into(),
233
6777690
                &automaton.states[self.stack[depth].state].label.indices,
234
6777690
            )
235
6777690
            .deref(),
236
6777690
        );
237
6777690
        write_terms[depth] = subterm.into();
238
6777690

            
239
6777690
        self.oldest_reliable_subterm = depth;
240
6777690
    }
241

            
242
    /// Removes side info for configurations beyond configuration index 'end'.
243
    /// If 'including' is true the side info for the configuration with index 'end' is also deleted.
244
12721680
    pub fn roll_back_side_info_stack(&mut self, end: usize, including: bool) {
245
        loop {
246
12903665
            match self.side_branch_stack.last() {
247
                None => {
248
7061195
                    break;
249
                }
250
5842470
                Some(sbi) => {
251
5842470
                    if sbi.corresponding_configuration < end || (sbi.corresponding_configuration <= end && !including) {
252
5660485
                        break;
253
181985
                    } else {
254
181985
                        self.side_branch_stack.pop();
255
181985
                    }
256
                }
257
            }
258
        }
259
12721680
    }
260

            
261
    /// Roll back the configuration stack to level 'depth'.
262
    /// This function is used exclusively when a subtree has been explored and no matches have been found.
263
5943990
    pub fn jump_back(&mut self, depth: usize, tp: &mut TermPool) {
264
5943990
        // Updated subterms may have to be propagated up the configuration tree
265
5943990
        self.integrate_updated_subterms(depth, tp, true);
266
5943990
        self.current_node = Some(depth);
267
5943990
        self.stack.truncate(depth + 1);
268
5943990
        self.terms.write().truncate(depth + 1);
269
5943990

            
270
5943990
        self.roll_back_side_info_stack(depth, false);
271
5943990
    }
272

            
273
    /// When going back up the configuration tree the subterms stored in the configuration tree must be updated
274
    /// This function ensures that the Configuration at depth 'end' is made up to date.
275
    /// If store_intermediate is true, all configurations below 'end' are also up to date.
276
5943990
    pub fn integrate_updated_subterms(&mut self, end: usize, tp: &mut TermPool, store_intermediate: bool) {
277
5943990
        // Check if there is anything to do. Start updating from self.oldest_reliable_subterm
278
5943990
        let mut up_to_date = self.oldest_reliable_subterm;
279
5943990
        if up_to_date == 0 || end >= up_to_date {
280
5351130
            return;
281
592860
        }
282
592860

            
283
592860
        let mut write_terms = self.terms.write();
284
592860
        let mut subterm = write_terms.protect(&write_terms[up_to_date]);
285

            
286
        // Go over the configurations one by one until we reach 'end'
287
1253160
        while up_to_date > end {
288
            // If the position is not deepened nothing needs to be done, otherwise substitute on the position stored in the configuration.
289
660300
            subterm = match self.stack[up_to_date].position {
290
                None => subterm,
291
660300
                Some(p) => {
292
660300
                    let t = substitute_with(
293
660300
                        &mut self.substitution_builder,
294
660300
                        tp,
295
660300
                        write_terms[up_to_date - 1].deref(),
296
660300
                        subterm.protect(),
297
660300
                        &p.indices,
298
660300
                    );
299
660300
                    write_terms.protect(&t)
300
                }
301
            };
302
660300
            up_to_date -= 1;
303
660300

            
304
660300
            if store_intermediate {
305
660300
                let subterm = write_terms.protect(&subterm);
306
660300
                write_terms[up_to_date] = subterm.into();
307
660300
            }
308
        }
309

            
310
592860
        self.oldest_reliable_subterm = up_to_date;
311
592860

            
312
592860
        let subterm = write_terms.protect(&subterm);
313
592860
        write_terms[up_to_date] = subterm.into();
314
5943990
    }
315

            
316
    /// Final term computed by integrating all subterms up to the root configuration
317
1289000
    pub fn compute_final_term(&mut self, tp: &mut TermPool) -> DataExpression {
318
1289000
        self.jump_back(0, tp);
319
1289000
        self.terms.read()[0].protect()
320
1289000
    }
321

            
322
    /// Returns a SideInfoType object if there is side info for the configuration with index 'leaf_index'
323
91810545
    pub fn pop_side_branch_leaf(stack: &mut Vec<SideInfo<'a>>, leaf_index: usize) -> Option<SideInfoType<'a>> {
324
91810545
        let should_pop = match stack.last() {
325
18901365
            None => false,
326
72909180
            Some(si) => si.corresponding_configuration == leaf_index,
327
        };
328

            
329
91810545
        if should_pop {
330
4918675
            Some(stack.pop().unwrap().info)
331
        } else {
332
86891870
            None
333
        }
334
91810545
    }
335
}
336

            
337
impl fmt::Display for ConfigurationStack<'_> {
338
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339
        writeln!(f, "Current node: {:?}", self.current_node)?;
340
        for (i, c) in self.stack.iter().enumerate() {
341
            writeln!(f, "Configuration {} ", i)?;
342
            writeln!(f, "    State: {:?}", c.state)?;
343
            writeln!(
344
                f,
345
                "    Position: {}",
346
                match c.position {
347
                    Some(x) => x.to_string(),
348
                    None => "None".to_string(),
349
                }
350
            )?;
351
            writeln!(f, "    Subterm: {}", self.terms.read()[i])?;
352

            
353
            for side_branch in &self.side_branch_stack {
354
                if i == side_branch.corresponding_configuration {
355
                    writeln!(f, "    Side branch: {} ", side_branch.info)?;
356
                }
357
            }
358
        }
359

            
360
        Ok(())
361
    }
362
}
363

            
364
impl fmt::Display for SideInfoType<'_> {
365
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366
        match self {
367
            SideInfoType::SideBranch(tr_slice) => {
368
                let mut first = true;
369
                write!(f, "matching: ")?;
370
                for (position, index) in tr_slice.iter() {
371
                    if !first {
372
                        write!(f, ", ")?;
373
                    }
374
                    write!(f, "{} {}", position, *index)?;
375
                    first = false;
376
                }
377
            }
378
            SideInfoType::DelayedRewriteRule(announcement, _) => {
379
                write!(f, "delayed rule: {}", announcement)?;
380
            }
381
            SideInfoType::EquivalenceAndConditionCheck(announcement, _) => {
382
                write!(f, "equivalence {}", announcement)?;
383
            }
384
        }
385

            
386
        Ok(())
387
    }
388
}