1
#![forbid(unsafe_code)]
2

            
3
use std::fmt;
4

            
5
use merc_aterm::Protected;
6
use merc_aterm::Term;
7
use merc_aterm::storage::ThreadTermPool;
8
use merc_data::DataExpression;
9
use merc_data::DataExpressionRef;
10

            
11
use crate::Rule;
12
use crate::matching::conditions::EMACondition;
13
use crate::matching::conditions::extend_conditions;
14
use crate::matching::nonlinear::EquivalenceClass;
15
use crate::matching::nonlinear::derive_equivalence_classes;
16
use crate::set_automaton::MatchAnnouncement;
17
use crate::set_automaton::SetAutomaton;
18

            
19
use super::DataPosition;
20
use super::DataPositionIndexed;
21
use super::DataSubstitutionBuilder;
22
use super::TermStack;
23
use super::create_var_map;
24
use super::data_substitute_with;
25

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

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

            
35
    /// The right hand side stored such that it can be substituted easily.
36
    pub rhs_term_stack: TermStack,
37

            
38
    /// Whether the rewrite rule duplicates subterms, e.g. times(s(x), y) = plus(y, times(x, y))
39
    pub is_duplicating: bool,
40
}
41

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

            
51
14137
        let is_duplicating = sctt_rhs.contains_duplicate_var_references();
52

            
53
14137
        AnnouncementSabre {
54
14137
            conditions: extend_conditions(rule),
55
14137
            equivalence_classes: derive_equivalence_classes(rule),
56
14137
            rhs_term_stack: sctt_rhs,
57
14137
            is_duplicating,
58
14137
        }
59
14137
    }
60
}
61

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

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

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

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

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

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

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

            
146
    /// A reusable substitution builder for the configuration stack.
147
    pub substitution_builder: DataSubstitutionBuilder,
148
}
149

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

            
163
239528
        let mut write_conf_list = conf_list.terms.write();
164
239528
        write_conf_list.push(term.copy());
165
239528
        drop(write_conf_list);
166

            
167
239528
        conf_list
168
239528
    }
169

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

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

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

            
185
        // If there are more transitions store the remaining on the side stack
186
6000721
        let tr_slice: &[(DataPosition, usize)] = &(tr_slice)[1..];
187
6000721
        if !tr_slice.is_empty() {
188
590334
            self.side_branch_stack.push(SideInfo {
189
590334
                corresponding_configuration: c,
190
590334
                info: SideInfoType::SideBranch(tr_slice),
191
590334
            })
192
5410387
        }
193

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

            
201
        // Push the term belonging to the leaf.
202
6000721
        let mut write_terms = self.terms.write();
203
6000721
        let t = write_terms.protect(&write_terms[c].get_data_position(pos));
204
6000721
        write_terms.push(t.into());
205

            
206
6000721
        self.current_node = Some(c + 1);
207
6000721
    }
208

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

            
220
        // Reroll the configuration stack by truncating the Vec (which is a constant time operation)
221
1167807
        self.stack.truncate(depth + 1);
222
1167807
        self.terms.write().truncate(depth + 1);
223

            
224
        // Remove side info for the deleted configurations
225
1167807
        self.roll_back_side_info_stack(depth, true);
226

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

            
239
1167807
        self.oldest_reliable_subterm = depth;
240
1167807
    }
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
2080841
    pub fn roll_back_side_info_stack(&mut self, end: usize, including: bool) {
245
        loop {
246
2106269
            match self.side_branch_stack.last() {
247
                None => {
248
1218887
                    break;
249
                }
250
887382
                Some(sbi) => {
251
887382
                    if sbi.corresponding_configuration < end || (sbi.corresponding_configuration <= end && !including) {
252
861954
                        break;
253
25428
                    } else {
254
25428
                        self.side_branch_stack.pop();
255
25428
                    }
256
                }
257
            }
258
        }
259
2080841
    }
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
913034
    pub fn jump_back(&mut self, depth: usize, tp: &ThreadTermPool) {
264
        // Updated subterms may have to be propagated up the configuration tree
265
913034
        self.integrate_updated_subterms(depth, tp, true);
266
913034
        self.current_node = Some(depth);
267
913034
        self.stack.truncate(depth + 1);
268
913034
        self.terms.write().truncate(depth + 1);
269

            
270
913034
        self.roll_back_side_info_stack(depth, false);
271
913034
    }
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
913034
    pub fn integrate_updated_subterms(&mut self, end: usize, tp: &ThreadTermPool, store_intermediate: bool) {
277
        // Check if there is anything to do. Start updating from self.oldest_reliable_subterm
278
913034
        let mut up_to_date = self.oldest_reliable_subterm;
279
913034
        if up_to_date == 0 || end >= up_to_date {
280
795331
            return;
281
117703
        }
282

            
283
117703
        let mut write_terms = self.terms.write();
284
117703
        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
242137
        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
124434
            subterm = match self.stack[up_to_date].position {
290
                None => subterm,
291
124434
                Some(position) => {
292
124434
                    let t = data_substitute_with(
293
124434
                        &mut self.substitution_builder,
294
124434
                        tp,
295
124434
                        &write_terms[up_to_date - 1],
296
124434
                        subterm.protect().into(),
297
124434
                        position,
298
                    );
299
124434
                    write_terms.protect(&t)
300
                }
301
            };
302
124434
            up_to_date -= 1;
303

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

            
310
117703
        self.oldest_reliable_subterm = up_to_date;
311

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

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

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

            
329
8132400
        if should_pop {
330
724344
            Some(stack.pop().unwrap().info)
331
        } else {
332
7408056
            None
333
        }
334
8132400
    }
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::Debug 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
}