1
use crate::utils::rewrite_children;
2
use conjure_cp::ast::{Domain, Expression as Expr, IntVal, Range, SymbolTable};
3
use conjure_cp::into_matrix_expr;
4
use conjure_cp::rule_engine::{
5
    ApplicationError::RuleNotApplicable, ApplicationResult, Reduction, register_rule,
6
};
7

            
8
/// Converts a matrix to a list if possible.
9
///
10
/// A list is a matrix with the unbounded domain `int(1..)`. Unlike matrices in general, lists can
11
/// be resized; consequently, a lot more rules apply to them.
12
///
13
/// A matrix can be converted to a list if:
14
///
15
///  1. It has some contiguous domain `int(1..n)`.
16
///
17
///  2. It is a matrix literal (i.e. not a reference to a decision variable).
18
///
19
///  3. Its direct parent is a constraint, not another matrix or `AbstractLiteral`.
20
///
21
///    This prevents the conversion of rows in a 2d matrix from being turned into lists. If were
22
///    to happen, the rows of the matrix might become different lengths, which is invalid!
23
///
24
///  4. The matrix is stored as `Expression` type inside (i.e. not as an `Atom` inside a Minion
25
///     constraint)
26
///
27
/// Because of condition 4, and this rules low priority, this rule will not run post-flattening, so
28
/// matrices that do not need to be converted to lists in order to get them ready for Minion will
29
/// be left alone.
30
#[register_rule("Base", 2000)]
31
169872
fn matrix_to_list(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
32
    // match on the parent: do not apply this rule to things descended from abstract literal, or
33
    // special language constructs like bubble.
34
    //
35
    // As Minion/ flat constraints do not have expression children, they are automatically
36
    // excluded.
37

            
38
73703
    if matches!(
39
169872
        expr,
40
        Expr::AbstractLiteral(_, _)
41
            | Expr::Bubble(_, _, _)
42
            | Expr::Atomic(_, _)
43
             // not sure if this needs to be excluded, being cautious.
44
            | Expr::DominanceRelation(_, _)
45
    ) {
46
96169
        return Err(RuleNotApplicable);
47
73703
    }
48

            
49
121058
    let (new_expr, num_changed) = rewrite_children(expr, |child| {
50
        // already a list => no change
51
121058
        if child.unwrap_list().is_some() {
52
10434
            return (child, false);
53
110624
        }
54

            
55
        // not a matrix => no change
56
110624
        let Some((elems, domain)) = child.clone().unwrap_matrix_unchecked() else {
57
108135
            return (child, false);
58
        };
59

            
60
2489
        let Some(ranges) = domain.as_int() else {
61
            return (child, false);
62
        };
63

            
64
        // must be domain int(1..n)
65
2489
        let [Range::Bounded(IntVal::Const(1), _)] = ranges[..] else {
66
            return (child, false);
67
        };
68

            
69
2489
        (
70
2489
            into_matrix_expr![elems;Domain::int_ground(vec![Range::UnboundedR(1)])],
71
2489
            true,
72
2489
        )
73
121058
    });
74

            
75
73703
    if num_changed != 0 {
76
2447
        Ok(Reduction::pure(new_expr))
77
    } else {
78
71256
        Err(RuleNotApplicable)
79
    }
80
169872
}