1
//! Preserve index definedness through library-backed scalar relations.
2
use conjure_cp::ast::{Expression as Expr, GroundDomain, Metadata, Moo, Range, SymbolTable};
3
use conjure_cp::rule_engine::{
4
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
5
};
6

            
7
#[register_rule("SAT", 18500, [InDomain])]
8
23009
fn scalar_membership(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
9
23009
    let Expr::InDomain(_, value, domain) = expr else {
10
21
        return Err(RuleNotApplicable);
11
    };
12
22988
    let domain = domain.resolve().map_err(|_| RuleNotApplicable)?;
13
22988
    let ranges = match domain.as_ref() {
14
        GroundDomain::Empty(_) => return Ok(RuleEffect::pure(false.into())),
15
        GroundDomain::Bool if value.domain_of().is_some_and(|domain| domain.is_bool()) => {
16
            return Ok(RuleEffect::pure(true.into()));
17
        }
18
22988
        GroundDomain::Int(ranges, _) if value.domain_of().is_some_and(|domain| domain.is_int()) => {
19
22988
            ranges
20
        }
21
        _ => return Err(RuleNotApplicable),
22
    };
23
22988
    let alternatives: Vec<_> = ranges
24
22988
        .iter()
25
38899
        .map(|range| {
26
            let lower =
27
10997
                |bound| Expr::Geq(Metadata::new(), value.clone(), Moo::new(Expr::from(bound)));
28
            let upper =
29
10997
                |bound| Expr::Leq(Metadata::new(), value.clone(), Moo::new(Expr::from(bound)));
30
38899
            match range {
31
                Range::Single(bound) => {
32
27902
                    Expr::Eq(Metadata::new(), value.clone(), Moo::new((*bound).into()))
33
                }
34
10997
                Range::Bounded(low, high) => Expr::And(
35
10997
                    Metadata::new(),
36
10997
                    Moo::new(conjure_cp::into_matrix_expr!(vec![
37
10997
                        lower(*low),
38
10997
                        upper(*high)
39
10997
                    ])),
40
10997
                ),
41
                Range::UnboundedL(high) => upper(*high),
42
                Range::UnboundedR(low) => lower(*low),
43
                Range::Unbounded => true.into(),
44
            }
45
38899
        })
46
        .collect();
47
22988
    Ok(RuleEffect::pure(Expr::Or(
48
22988
        Metadata::new(),
49
22988
        Moo::new(conjure_cp::into_matrix_expr!(alternatives)),
50
22988
    )))
51
23009
}