1
//! Generic normalising rules for associative-commutative operators.
2

            
3
use std::mem::Discriminant;
4

            
5
use crate::shared::utils::{single_vec_child, with_single_vec_child};
6
use conjure_cp::ast::{AbstractLiteral, Expression as Expr, SymbolTable};
7
use conjure_cp::rule_engine::{
8
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
9
};
10

            
11
/// Normalises associative_commutative operations.
12
///
13
/// For now, this just removes nested expressions by associativity.
14
///
15
/// ```text
16
/// v(v(a,b,...),c,d,...) ~> v(a,b,c,d)
17
/// v(a,b,[c,d],...)      ~> v(a,b,c,d)
18
/// where v is an AC vector operator
19
/// ```
20
///
21
/// The second form arises when a comprehension standing for several terms is expanded in place:
22
/// the expansion lands as a matrix literal among the operands, and an AC operator's operand list
23
/// is flat by definition.
24
#[register_rule("Base", 8900, [And, Or, Product, Sum])]
25
7299348
fn normalise_associative_commutative(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
26
7299348
    if !expr.is_associative_commutative_operator() {
27
4825
        return Err(RuleNotApplicable);
28
7294523
    }
29

            
30
7294523
    if !has_direct_nested_ac_child(expr) {
31
7118561
        return Err(RuleNotApplicable);
32
175962
    }
33

            
34
    // remove nesting deeply
35
948052
    fn recurse_deeply(
36
948052
        root_discriminant: Discriminant<Expr>,
37
948052
        expr: Expr,
38
948052
        changed: &mut bool,
39
948052
    ) -> Vec<Expr> {
40
        // A matrix literal among the operands is spliced in, whatever the operator.
41
948052
        if let Expr::AbstractLiteral(_, AbstractLiteral::Matrix(children, _)) = &expr {
42
            *changed = true;
43
            return children
44
                .clone()
45
                .into_iter()
46
                .flat_map(|child| recurse_deeply(root_discriminant, child, changed))
47
                .collect();
48
948052
        }
49

            
50
        // if expr a different expression type, stop recursing
51
948052
        if std::mem::discriminant(&expr) != root_discriminant {
52
568718
            return vec![expr];
53
379334
        }
54

            
55
379334
        let Some(children) = single_vec_child(&expr) else {
56
182
            return vec![expr];
57
        };
58
379152
        let old_len = children.len();
59

            
60
379152
        let new_children = children
61
379152
            .into_iter()
62
772090
            .flat_map(|child| recurse_deeply(root_discriminant, child, changed))
63
            .collect::<Vec<_>>();
64
379152
        if new_children.len() != old_len {
65
153694
            *changed = true;
66
225458
        }
67

            
68
379152
        new_children
69
948052
    }
70

            
71
175962
    if single_vec_child(expr).is_none() {
72
        return Err(RuleNotApplicable);
73
175962
    }
74

            
75
175962
    let mut changed = false;
76
175962
    let new_children = recurse_deeply(std::mem::discriminant(expr), expr.clone(), &mut changed);
77

            
78
175962
    if !changed {
79
35079
        return Err(RuleNotApplicable);
80
140883
    }
81

            
82
140883
    let new_expr = with_single_vec_child(expr, new_children);
83

            
84
140883
    Ok(RuleEffect::pure(new_expr))
85
7299348
}
86

            
87
7294523
fn has_direct_nested_ac_child(expr: &Expr) -> bool {
88
7294523
    let root_discriminant = std::mem::discriminant(expr);
89
7294523
    ac_children(expr).is_some_and(|children| {
90
153395055
        children.iter().any(|child| {
91
153395055
            std::mem::discriminant(child) == root_discriminant
92
153219093
                || matches!(
93
153219093
                    child,
94
                    Expr::AbstractLiteral(_, AbstractLiteral::Matrix(_, _))
95
                )
96
153395055
        })
97
7046927
    })
98
7294523
}
99

            
100
7294523
fn ac_children(expr: &Expr) -> Option<&[Expr]> {
101
7294523
    let matrix = match expr {
102
        Expr::And(_, matrix)
103
        | Expr::Or(_, matrix)
104
        | Expr::Product(_, matrix)
105
7294523
        | Expr::Sum(_, matrix) => matrix.as_ref(),
106
        _ => return None,
107
    };
108

            
109
7294523
    match matrix {
110
7046927
        Expr::AbstractLiteral(_, AbstractLiteral::Matrix(children, _)) => Some(children),
111
247596
        _ => None,
112
    }
113
7294523
}