1
//! Identity-default selection shares ordinary indexing and definedness handling.
2
use conjure_cp::ast::{
3
    AbstractLiteral, Atom, Expression as Expr, Literal, Metadata, Moo, SymbolTable,
4
};
5
use conjure_cp::rule_engine::{
6
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
7
};
8

            
9
#[register_rule("Base", 19000, [ElementId])]
10
12677
fn element_id_to_identity_lookup(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
11
12677
    let Expr::ElementId(_, matrix, index) = expr else {
12
5838
        return Err(RuleNotApplicable);
13
    };
14
    // Minion literal lookups must wait for resolved labels and its total element lowering.
15
6839
    if conjure_cp::settings::try_current_solver_family()
16
6839
        == Some(conjure_cp::settings::SolverFamily::Minion)
17
28
        && matches!(
18
28
            matrix.as_ref(),
19
            Expr::AbstractLiteral(_, AbstractLiteral::Matrix(..))
20
                | Expr::Atomic(
21
                    _,
22
                    Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Matrix(..)))
23
                )
24
        )
25
    {
26
        return Err(RuleNotApplicable);
27
6839
    }
28
6839
    if matches!(matrix.as_ref(), Expr::Atomic(_, Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Matrix(entries, _)))) if entries.is_empty())
29
6839
        || matrix
30
6839
            .unwrap_matrix_unchecked_ref()
31
            .is_some_and(|(entries, _)| entries.is_empty())
32
    {
33
        let default = if index.domain_of().is_some_and(|domain| domain.is_bool()) {
34
            Expr::ToInt(Metadata::new(), index.clone())
35
        } else {
36
            (**index).clone()
37
        };
38
        return Ok(RuleEffect::pure(default));
39
6839
    }
40
    // Convert Boolean entries before indexing so Boolean bubbling cannot turn an undefined
41
    // lookup into false before catchUndef sees its numeric definedness condition.
42
6839
    let domain = matrix.domain_of().ok_or(RuleNotApplicable)?;
43
6839
    let (entries_domain, indices) = domain.as_matrix().ok_or(RuleNotApplicable)?;
44
6839
    let subject = if entries_domain.is_bool() {
45
        let (entries, labels) =
46
            if let Some((entries, labels)) = matrix.unwrap_matrix_unchecked_ref() {
47
                (entries.to_vec(), labels.clone())
48
            } else {
49
                let [labels] = indices.as_slice() else {
50
                    return Err(RuleNotApplicable);
51
                };
52
                let entries = labels
53
                    .resolve()
54
                    .map_err(|_| RuleNotApplicable)?
55
                    .values()
56
                    .map_err(|_| RuleNotApplicable)?
57
                    .map(|label| {
58
                        conjure_cp::ast::matrix::safe_index_optimised((**matrix).clone(), label)
59
                    })
60
                    .collect::<Option<Vec<_>>>()
61
                    .ok_or(RuleNotApplicable)?;
62
                (entries, labels.clone())
63
            };
64
        let entries = entries
65
            .into_iter()
66
            .map(|entry| Expr::ToInt(Metadata::new(), Moo::new(entry)))
67
            .collect();
68
        Moo::new(conjure_cp::into_matrix_expr![entries; labels])
69
    } else {
70
6839
        matrix.clone()
71
    };
72
6839
    let lookup = Expr::UnsafeIndex(Metadata::new(), subject, vec![(**index).clone()]);
73
13678
    let numeric = |expression: Expr| {
74
13678
        let domain = expression.domain_of()?;
75
13678
        if domain.is_bool() {
76
            Some(Expr::ToInt(Metadata::new(), Moo::new(expression)))
77
13678
        } else if domain.is_int() {
78
13678
            Some(expression)
79
        } else {
80
            None
81
        }
82
13678
    };
83
6839
    let lookup = numeric(lookup).ok_or(RuleNotApplicable)?;
84
6839
    let default = numeric((**index).clone()).ok_or(RuleNotApplicable)?;
85
6839
    Ok(RuleEffect::pure(Expr::CatchUndef(
86
6839
        Metadata::new(),
87
6839
        Moo::new(lookup),
88
6839
        Moo::new(default),
89
6839
    )))
90
12677
}