1
use conjure_cp::ast::{Atom, Expression as Expr, Literal};
2
use conjure_cp::ast::{SATIntEncoding, SymbolTable};
3
use conjure_cp::rule_engine::ApplicationError;
4
use conjure_cp::rule_engine::{
5
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
6
};
7

            
8
use crate::backends::sat::boolean::tseytin_not;
9
use conjure_cp::ast::Metadata;
10
use conjure_cp::ast::Moo;
11
use conjure_cp::into_matrix_expr;
12

            
13
/// This function confirms that all of the input expressions are order SATInts, and returns vectors for each input of their bits
14
/// This function also normalizes order SATInt operands to a common value range.
15
17570
pub fn validate_order_int_operands(
16
17570
    exprs: Vec<Expr>,
17
17570
) -> Result<(Vec<Vec<Expr>>, i32, i32), ApplicationError> {
18
    // Iterate over all inputs
19
    // Check they are order and calulate a lower and upper bound
20
17570
    let mut global_min: i32 = i32::MAX;
21
17570
    let mut global_max: i32 = i32::MIN;
22

            
23
17570
    for operand in &exprs {
24
17570
        let Expr::SATInt(_, SATIntEncoding::Order, _, (local_min, local_max)) = operand else {
25
17500
            return Err(RuleNotApplicable);
26
        };
27
70
        global_min = global_min.min(*local_min);
28
70
        global_max = global_max.max(*local_max);
29
    }
30

            
31
    // build out by iterating over each operand and expanding it to match the new bounds
32
70
    let out: Vec<Vec<Expr>> = exprs
33
70
        .into_iter()
34
70
        .map(|expr| {
35
70
            let Expr::SATInt(_, SATIntEncoding::Order, inner, (local_min, local_max)) = expr else {
36
                return Err(RuleNotApplicable);
37
            };
38

            
39
70
            let Some(v) = inner.as_ref().clone().into_list() else {
40
                return Err(RuleNotApplicable);
41
            };
42

            
43
            // calulcate how many trues/falses to prepend/append
44
70
            let prefix_len = (local_min - global_min) as usize;
45
70
            let postfix_len = (global_max - local_max) as usize;
46

            
47
70
            let mut bits = Vec::with_capacity(v.len() + prefix_len + postfix_len);
48

            
49
            // add `true`s to start
50
70
            bits.extend(std::iter::repeat_n(
51
70
                Expr::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
52
70
                prefix_len,
53
70
            ));
54

            
55
70
            bits.extend(v);
56

            
57
            // add `false`s to end
58
70
            bits.extend(std::iter::repeat_n(
59
70
                Expr::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
60
70
                postfix_len,
61
70
            ));
62

            
63
70
            Ok(bits)
64
70
        })
65
        .collect::<Result<_, _>>()?;
66

            
67
70
    Ok((out, global_min, global_max))
68
17570
}
69

            
70
/// Converts a - expression for a SATInt to a new SATInt
71
///
72
/// ```text
73
/// -SATInt(a) ~> SATInt(b)
74
///
75
/// ```
76
#[register_rule("SAT", 9100, [Neg])]
77
17591
fn neg_sat_order(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
78
17591
    let Expr::Neg(_, value) = expr else {
79
21
        return Err(RuleNotApplicable);
80
    };
81

            
82
17570
    let (binding, old_min, old_max) = validate_order_int_operands(vec![value.as_ref().clone()])?;
83
70
    let [val_bits] = binding.as_slice() else {
84
        return Err(RuleNotApplicable); // consider covered
85
    };
86

            
87
70
    let new_min = -old_max;
88
70
    let new_max = -old_min;
89

            
90
70
    let n = val_bits.len();
91
70
    let mut out: Vec<Expr> = Vec::with_capacity(n);
92

            
93
70
    let mut new_symbols = symbols.clone();
94
70
    let mut new_sat_decisions = vec![];
95

            
96
70
    let ff = Expr::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false)));
97
70
    out.push(tseytin_not(ff, &mut new_sat_decisions, &mut new_symbols));
98

            
99
70
    for i in 1..n {
100
315
        let src = val_bits[n - i].clone();
101
315
        let neg_bit = tseytin_not(src, &mut new_sat_decisions, &mut new_symbols);
102
315
        out.push(neg_bit);
103
    }
104

            
105
70
    Ok(RuleEffect::sat(
106
70
        Expr::SATInt(
107
70
            Metadata::new(),
108
70
            SATIntEncoding::Order,
109
70
            Moo::new(into_matrix_expr!(out)),
110
70
            (new_min, new_max),
111
70
        ),
112
70
        new_sat_decisions,
113
70
        new_symbols,
114
70
    ))
115
17591
}