1
use conjure_cp::ast::{Expression as Expr, GroundDomain, Moo, SymbolTable};
2
use conjure_cp::matrix_expr;
3
use conjure_cp::rule_engine::{
4
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
5
};
6

            
7
/// Non-scalar operands have no native backend ordering, so route `<`/`>`/`<=`/`>=` through the
8
/// Lex comparison machinery instead -- it already knows how to decompose every abstract type
9
/// (tuple, record, set, mset, ...) into a comparable form, and lex order over the whole value
10
/// coincides with each type's own natural ordering. Runs before `normalise_lex_gt_geq` so a
11
/// promoted `LexGt`/`LexGeq` gets normalised down the same way a user-written one would.
12
///
13
/// A genuine matrix operand *is* already the list `LexLt` compares element-wise, so it is passed
14
/// through unwrapped. Every other abstract type's own ordering rule (e.g. `lex_explicit_sets`,
15
/// the tuple/record comparison rules) instead expects each whole value wrapped as a singleton
16
/// list -- "compare these two values as if they were one-element lists" -- so it is wrapped here
17
/// to match.
18
#[register_rule("Base", 9100, [Lt, Gt, Leq, Geq])]
19
1843824
fn promote_abstract_cmp_to_lex(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
20
1843824
    let (lhs, rhs) = match expr {
21
        Expr::Lt(_, lhs, rhs)
22
        | Expr::Gt(_, lhs, rhs)
23
        | Expr::Leq(_, lhs, rhs)
24
1838488
        | Expr::Geq(_, lhs, rhs) => (lhs, rhs),
25
5336
        _ => return Err(RuleNotApplicable),
26
    };
27
1838488
    let Some(domain) = lhs.domain_of().or_else(|| rhs.domain_of()) else {
28
        return Err(RuleNotApplicable);
29
    };
30
1838488
    let Ok(ground) = domain.resolve() else {
31
        return Err(RuleNotApplicable);
32
    };
33
1838488
    let (is_scalar, is_matrix) = match ground.as_ref() {
34
1838488
        GroundDomain::Bool | GroundDomain::Int(_, _) => (true, false),
35
        GroundDomain::Matrix(_, _, _) => (false, true),
36
        _ => (false, false),
37
    };
38
1838488
    if is_scalar {
39
1838488
        return Err(RuleNotApplicable);
40
    }
41

            
42
    let wrap = |e: &Moo<Expr>| -> Moo<Expr> {
43
        if is_matrix {
44
            e.clone()
45
        } else {
46
            Moo::new(matrix_expr![e.as_ref().clone()])
47
        }
48
    };
49
    let new_expr = match expr {
50
        Expr::Lt(metadata, lhs, rhs) => Expr::LexLt(metadata.clone(), wrap(lhs), wrap(rhs)),
51
        Expr::Gt(metadata, lhs, rhs) => Expr::LexGt(metadata.clone(), wrap(lhs), wrap(rhs)),
52
        Expr::Leq(metadata, lhs, rhs) => Expr::LexLeq(metadata.clone(), wrap(lhs), wrap(rhs)),
53
        Expr::Geq(metadata, lhs, rhs) => Expr::LexGeq(metadata.clone(), wrap(lhs), wrap(rhs)),
54
        _ => unreachable!(),
55
    };
56
    Ok(RuleEffect::pure(new_expr))
57
1843824
}
58

            
59
#[register_rule("Base", 9000, [LexGt, LexGeq])]
60
7503
fn normalise_lex_gt_geq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
61
7503
    match expr {
62
623
        Expr::LexGt(metadata, lhs, rhs) => Ok(RuleEffect::pure(Expr::LexLt(
63
623
            metadata.clone(),
64
623
            rhs.clone(),
65
623
            lhs.clone(),
66
623
        ))),
67
1246
        Expr::LexGeq(metadata, lhs, rhs) => Ok(RuleEffect::pure(Expr::LexLeq(
68
1246
            metadata.clone(),
69
1246
            rhs.clone(),
70
1246
            lhs.clone(),
71
1246
        ))),
72
5634
        _ => Err(RuleNotApplicable),
73
    }
74
7503
}