1
use std::fmt;
2

            
3
use merc_aterm::Protected;
4
use merc_aterm::Term;
5
use merc_aterm::storage::ThreadTermPool;
6
use merc_data::DataExpression;
7
use merc_data::DataExpressionRef;
8

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

            
17
use super::DataPosition;
18
use super::DataPositionIndexed;
19
use super::DataSubstitutionBuilder;
20
use super::TermStack;
21
use super::create_var_map;
22
use super::data_substitute_with;
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
    /// The right hand side stored such that it can be substituted easily.
34
    pub rhs_term_stack: TermStack,
35

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

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

            
49
42417
        let is_duplicating = sctt_rhs.contains_duplicate_var_references();
50

            
51
42417
        AnnouncementSabre {
52
42417
            conditions: extend_conditions(rule),
53
42417
            equivalence_classes: derive_equivalence_classes(rule),
54
42417
            rhs_term_stack: sctt_rhs,
55
42417
            is_duplicating,
56
42417
        }
57
42417
    }
58
}
59

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

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

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

            
144
    /// A reusable substitution builder, borrowed from the caller so that a
145
    /// protected container is not registered on every rewrite call.
146
    pub substitution_builder: &'b mut DataSubstitutionBuilder,
147
}
148

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

            
166
716538
        let mut write_conf_list = conf_list.terms.write();
167
716538
        write_conf_list.push(term.copy());
168
716538
        drop(write_conf_list);
169

            
170
716538
        conf_list
171
716538
    }
172

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

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

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

            
188
        // If there are more transitions store the remaining on the side stack
189
17969934
        let tr_slice: &[(DataPosition, usize)] = &(tr_slice)[1..];
190
17969934
        if !tr_slice.is_empty() {
191
1756551
            self.side_branch_stack.push(SideInfo {
192
1756551
                corresponding_configuration: c,
193
1756551
                info: SideInfoType::SideBranch(tr_slice),
194
1756551
            })
195
16213383
        }
196

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

            
204
        // Push the term belonging to the leaf.
205
17969934
        let mut write_terms = self.terms.write();
206
        // Safety: t is pushed into the container on the next line.
207
17969934
        let t = unsafe { write_terms.protect(&write_terms[c].get_data_position(pos)) };
208
17969934
        write_terms.push(t.into());
209

            
210
17969934
        self.current_node = Some(c + 1);
211
17969934
    }
212

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

            
224
        // Reroll the configuration stack by truncating the Vec (which is a constant time operation)
225
3502932
        self.stack.truncate(depth + 1);
226
3502932
        self.terms.write().truncate(depth + 1);
227

            
228
        // Remove side info for the deleted configurations
229
3502932
        self.roll_back_side_info_stack(depth, true);
230

            
231
        // Update the subterm stored at the prune point.
232
        // Note that the subterm stored earlier may not have been up to date. We replace it with a term that is up to date
233
3502932
        let mut write_terms = self.terms.write();
234
        // Safety: subterm is stored in the container on the next line.
235
3502932
        let subterm = unsafe {
236
3502932
            write_terms.protect(&data_substitute_with(
237
3502932
                self.substitution_builder,
238
3502932
                tp,
239
3502932
                &write_terms[depth],
240
3502932
                new_subterm,
241
3502932
                automaton.states()[self.stack[depth].state].label(),
242
3502932
            ))
243
        };
244
3502932
        write_terms[depth] = subterm.into();
245

            
246
3502932
        self.oldest_reliable_subterm = depth;
247
3502932
    }
248

            
249
    /// Removes side info for configurations beyond configuration index 'end'.
250
    /// If 'including' is true the side info for the configuration with index 'end' is also deleted.
251
6377136
    pub(crate) fn roll_back_side_info_stack(&mut self, end: usize, including: bool) {
252
        loop {
253
6453579
            match self.side_branch_stack.last() {
254
                None => {
255
3654126
                    break;
256
                }
257
2799453
                Some(sbi) => {
258
2799453
                    if sbi.corresponding_configuration < end || (sbi.corresponding_configuration <= end && !including) {
259
2723010
                        break;
260
76443
                    } else {
261
76443
                        self.side_branch_stack.pop();
262
76443
                    }
263
                }
264
            }
265
        }
266
6377136
    }
267

            
268
    /// Roll back the configuration stack to level 'depth'.
269
    /// This function is used exclusively when a subtree has been explored and no matches have been found.
270
2874204
    pub(crate) fn jump_back(&mut self, depth: usize, tp: &ThreadTermPool) {
271
        // Updated subterms may have to be propagated up the configuration tree.
272
        // Only the subterm at 'depth' needs to be correct: everything below it is
273
        // truncated away immediately afterwards.
274
2874204
        self.integrate_updated_subterms(depth, tp);
275
2874204
        self.current_node = Some(depth);
276
2874204
        self.stack.truncate(depth + 1);
277
2874204
        self.terms.write().truncate(depth + 1);
278

            
279
2874204
        self.roll_back_side_info_stack(depth, false);
280
2874204
    }
281

            
282
    /// When going back up the configuration tree the subterms stored in the configuration tree must be updated
283
    /// This function ensures that the Configuration at depth 'end' is made up to date.
284
2874204
    pub(crate) fn integrate_updated_subterms(&mut self, end: usize, tp: &ThreadTermPool) {
285
        // Check if there is anything to do. Start updating from self.oldest_reliable_subterm
286
2874204
        let mut up_to_date = self.oldest_reliable_subterm;
287
2874204
        if up_to_date == 0 || end >= up_to_date {
288
2521095
            return;
289
353109
        }
290

            
291
353109
        let mut write_terms = self.terms.write();
292
        // Safety: subterm is kept in write_terms throughout this function.
293
353109
        let mut subterm = unsafe { write_terms.protect(&write_terms[up_to_date]) };
294

            
295
        // Go over the configurations one by one until we reach 'end'. The running
296
        // subterm is stored back into the container at every level rather than
297
        // held as an owned protected term.
298
726411
        while up_to_date > end {
299
            // If the position is not deepened nothing needs to be done, otherwise substitute on the position stored in the configuration.
300
373302
            subterm = match self.stack[up_to_date].position {
301
                None => subterm,
302
373302
                Some(position) => {
303
373302
                    let t = data_substitute_with(
304
373302
                        self.substitution_builder,
305
373302
                        tp,
306
373302
                        &write_terms[up_to_date - 1],
307
373302
                        subterm.protect().into(),
308
373302
                        position,
309
                    );
310
                    // Safety: subterm is kept in write_terms throughout this function.
311
373302
                    unsafe { write_terms.protect(&t) }
312
                }
313
            };
314
373302
            up_to_date -= 1;
315

            
316
            // Safety: subterm is stored in the container on the next line.
317
373302
            let stored = unsafe { write_terms.protect(&subterm) };
318
373302
            write_terms[up_to_date] = stored.into();
319
        }
320

            
321
353109
        self.oldest_reliable_subterm = up_to_date;
322
2874204
    }
323

            
324
    /// Final term computed by integrating all subterms up to the root configuration
325
716538
    pub(crate) fn compute_final_term(&mut self, tp: &ThreadTermPool) -> DataExpression {
326
716538
        self.jump_back(0, tp);
327
716538
        self.terms.read()[0].protect()
328
716538
    }
329

            
330
    /// Returns a SideInfoType object if there is side info for the configuration with index 'leaf_index'
331
24347070
    pub(crate) fn pop_side_branch_leaf(stack: &mut Vec<SideInfo<'a>>, leaf_index: usize) -> Option<SideInfoType<'a>> {
332
24347070
        let should_pop = match stack.last() {
333
9657840
            None => false,
334
14689230
            Some(si) => si.corresponding_configuration == leaf_index,
335
        };
336

            
337
24347070
        if should_pop {
338
2157666
            Some(stack.pop().unwrap().info)
339
        } else {
340
22189404
            None
341
        }
342
24347070
    }
343
}
344

            
345
impl fmt::Display for ConfigurationStack<'_, '_> {
346
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347
        writeln!(f, "Current node: {:?}", self.current_node)?;
348
        for (i, c) in self.stack.iter().enumerate() {
349
            writeln!(f, "Configuration {i} ")?;
350
            writeln!(f, "    State: {:?}", c.state)?;
351
            writeln!(
352
                f,
353
                "    Position: {}",
354
                match c.position {
355
                    Some(x) => x.to_string(),
356
                    None => "None".to_string(),
357
                }
358
            )?;
359
            writeln!(f, "    Subterm: {}", self.terms.read()[i])?;
360

            
361
            for side_branch in &self.side_branch_stack {
362
                if i == side_branch.corresponding_configuration {
363
                    writeln!(f, "    Side branch: {:?} ", side_branch.info)?;
364
                }
365
            }
366
        }
367

            
368
        Ok(())
369
    }
370
}
371

            
372
impl fmt::Debug for SideInfoType<'_> {
373
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374
        match self {
375
            SideInfoType::SideBranch(tr_slice) => {
376
                let mut first = true;
377
                write!(f, "matching: ")?;
378
                for (position, index) in tr_slice.iter() {
379
                    if !first {
380
                        write!(f, ", ")?;
381
                    }
382
                    write!(f, "{} {}", position, *index)?;
383
                    first = false;
384
                }
385
            }
386
            SideInfoType::DelayedRewriteRule(announcement, _) => {
387
                write!(f, "delayed rule: {announcement:?}")?;
388
            }
389
            SideInfoType::EquivalenceAndConditionCheck(announcement, _) => {
390
                write!(f, "equivalence {announcement:?}")?;
391
            }
392
        }
393

            
394
        Ok(())
395
    }
396
}