1
//! Retain allDifferent as a semantic decision until library clause generation.
2
use conjure_cp::ast::{
3
    Expression as Expr, Metadata, Moo, ReturnType, SatEncodingDecision, SymbolTable, Typeable,
4
};
5
use conjure_cp::rule_engine::{
6
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
7
};
8

            
9
/// Preserve ready numeric operands, including nested Boolean uses.
10
#[register_rule("SAT", 18500, [AllDiff, AllDifferentExcept])]
11
338198
fn select_alldifferent(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
12
338198
    let (matrix, except) = match expr {
13
187537
        Expr::AllDiff(_, matrix) => (matrix, None),
14
        Expr::AllDifferentExcept(_, matrix, except) => {
15
150640
            let value = super::pseudo_boolean::integer_view(except).ok_or(RuleNotApplicable)?;
16
97195
            (matrix, Some(value))
17
        }
18
21
        _ => return Err(RuleNotApplicable),
19
    };
20
284732
    let elements = super::counting::matrix_entries(matrix).ok_or(RuleNotApplicable)?;
21
162302
    let inputs = elements
22
162302
        .iter()
23
162302
        .map(super::pseudo_boolean::integer_view)
24
162302
        .collect::<Option<Vec<_>>>()
25
162302
        .ok_or(RuleNotApplicable)?;
26
23135
    let mut symbols = symbols.clone();
27
23135
    let output = super::boolean::create_bool_aux(&mut symbols);
28
23135
    Ok(RuleEffect::sat(
29
23135
        output.clone(),
30
23135
        vec![SatEncodingDecision::AllDifferent {
31
23135
            output,
32
23135
            inputs,
33
23135
            comparisons: None,
34
23135
            except,
35
23135
            encoding: None,
36
23135
            amo_encoding: None,
37
23135
            pb_encoding: None,
38
23135
        }],
39
23135
        symbols,
40
23135
    ))
41
338198
}
42

            
43
/// Construct whole-value comparisons before their representation-specific lowering.
44
#[register_rule("SAT", 18400, [AllDiff, AllDifferentExcept])]
45
301567
fn introduce_compound_alldifferent(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
46
301567
    let (matrix, except) = match expr {
47
167951
        Expr::AllDiff(_, matrix) => (matrix, None),
48
133595
        Expr::AllDifferentExcept(_, matrix, except) => (matrix, Some(except)),
49
21
        _ => return Err(RuleNotApplicable),
50
    };
51
301546
    let inputs = super::counting::matrix_entries(matrix).ok_or(RuleNotApplicable)?;
52
180495
    if inputs.is_empty()
53
196175
        || inputs.iter().any(|input| {
54
196175
            matches!(
55
196175
                input.return_type(),
56
                ReturnType::Int | ReturnType::Bool | ReturnType::Unknown
57
            )
58
196175
        })
59
    {
60
167755
        return Err(RuleNotApplicable);
61
12740
    }
62
12740
    let mut symbols = symbols.clone();
63
12740
    let mut definitions = Vec::new();
64
12740
    let mut conditions = Vec::new();
65
12740
    for (index, left) in inputs.iter().enumerate() {
66
28420
        let exempt = except.filter(|_| index + 1 < inputs.len()).map(|except| {
67
11270
            let output = super::boolean::create_bool_aux(&mut symbols);
68
11270
            definitions.push(Expr::Iff(
69
11270
                Metadata::new(),
70
11270
                Moo::new(output.clone()),
71
11270
                Moo::new(Expr::Eq(
72
11270
                    Metadata::new(),
73
11270
                    Moo::new(left.clone()),
74
11270
                    except.clone(),
75
11270
                )),
76
11270
            ));
77
11270
            output
78
11270
        });
79
28420
        for right in &inputs[index + 1..] {
80
18620
            let unequal = Expr::Neq(
81
18620
                Metadata::new(),
82
18620
                Moo::new(left.clone()),
83
18620
                Moo::new(right.clone()),
84
18620
            );
85
18620
            conditions.push(if let Some(exempt) = &exempt {
86
14210
                Expr::Or(
87
14210
                    Metadata::new(),
88
14210
                    Moo::new(conjure_cp::into_matrix_expr!(vec![unequal, exempt.clone()])),
89
14210
                )
90
            } else {
91
4410
                unequal
92
            });
93
        }
94
    }
95
12740
    Ok(RuleEffect::new(
96
12740
        Expr::SatAllDifferentComparisons(
97
12740
            Metadata::new(),
98
12740
            Moo::new(conjure_cp::into_matrix_expr!(conditions)),
99
12740
        ),
100
12740
        definitions,
101
12740
        symbols,
102
12740
    ))
103
301567
}
104

            
105
/// Keep the allDifferent family choice after whole-value comparisons become Boolean literals.
106
#[register_rule("SAT", 18500, [SatAllDifferentComparisons])]
107
333676
fn select_compound_alldifferent(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
108
333676
    let Expr::SatAllDifferentComparisons(_, comparisons) = expr else {
109
21
        return Err(RuleNotApplicable);
110
    };
111
333655
    let comparisons = comparisons.unwrap_list_cow().ok_or(RuleNotApplicable)?;
112
352590
    if !comparisons.iter().all(|condition| {
113
352590
        condition.return_type() == ReturnType::Bool && crate::shared::utils::is_literal(condition)
114
352590
    }) {
115
320915
        return Err(RuleNotApplicable);
116
12740
    }
117
12740
    let mut symbols = symbols.clone();
118
12740
    let output = super::boolean::create_bool_aux(&mut symbols);
119
12740
    Ok(RuleEffect::sat(
120
12740
        output.clone(),
121
12740
        vec![SatEncodingDecision::AllDifferent {
122
12740
            output,
123
12740
            inputs: vec![],
124
12740
            comparisons: Some(comparisons.into_owned()),
125
12740
            except: None,
126
12740
            encoding: None,
127
12740
            amo_encoding: None,
128
12740
            pb_encoding: None,
129
12740
        }],
130
12740
        symbols,
131
12740
    ))
132
333676
}