1
use conjure_cp::ast::Expression as Expr;
2
use conjure_cp::ast::GroundDomain;
3
use conjure_cp::ast::Moo;
4
use conjure_cp::ast::SymbolTable;
5
use conjure_cp::into_matrix_expr;
6
use conjure_cp::matrix_expr;
7
use conjure_cp::rule_engine::{
8
    ApplicationError::RuleNotApplicable, ApplicationResult, Reduction, register_rule,
9
};
10

            
11
use conjure_cp::ast::Atom;
12
use conjure_cp::ast::Expression;
13
use conjure_cp::ast::Literal;
14
use conjure_cp::ast::Metadata;
15
use conjure_cp::ast::Name;
16
use conjure_cp::rule_engine::ApplicationError;
17

            
18
//TODO: largely copied from the matrix rules, This should be possible to simplify
19
#[register_rule("Base", 2000, [SafeIndex])]
20
168892
fn index_tuple_to_atom(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
21
    // i assume the MkOpIndexing is the same as matrix indexing
22
168892
    let Expr::SafeIndex(_, subject, indices) = expr else {
23
160226
        return Err(RuleNotApplicable);
24
    };
25

            
26
8666
    let Expr::Atomic(_, Atom::Reference(decl)) = &**subject else {
27
570
        return Err(RuleNotApplicable);
28
    };
29

            
30
8096
    let Name::WithRepresentation(name, reprs) = &decl.name() as &Name else {
31
7832
        return Err(RuleNotApplicable);
32
    };
33

            
34
264
    if reprs.first().is_none_or(|x| x.as_str() != "tuple_to_atom") {
35
96
        return Err(RuleNotApplicable);
36
168
    }
37

            
38
    // tuples are always one dimensional
39
168
    if indices.len() != 1 {
40
        return Err(RuleNotApplicable);
41
168
    }
42

            
43
168
    let repr = symbols
44
168
        .get_representation(name, &["tuple_to_atom"])
45
168
        .unwrap()[0]
46
168
        .clone();
47

            
48
    // let decl = symbols.lookup(name).unwrap();
49

            
50
168
    let Some(GroundDomain::Tuple(_)) = decl.resolved_domain().as_deref() else {
51
        return Err(RuleNotApplicable);
52
    };
53

            
54
168
    let mut indices_as_lit: Literal = Literal::Bool(false);
55

            
56
168
    for index in indices {
57
168
        let Some(index) = index.clone().into_literal() else {
58
            return Err(RuleNotApplicable); // we don't support non-literal indices
59
        };
60
168
        indices_as_lit = index;
61
    }
62

            
63
168
    let indices_as_name = Name::Represented(Box::new((
64
168
        name.as_ref().clone(),
65
168
        "tuple_to_atom".into(),
66
168
        indices_as_lit.into(),
67
168
    )));
68

            
69
168
    let subject = repr.expression_down(symbols)?[&indices_as_name].clone();
70

            
71
168
    Ok(Reduction::pure(subject))
72
168892
}
73

            
74
#[register_rule("Bubble", 8000, [UnsafeIndex])]
75
1408020
fn tuple_index_to_bubble(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
76
1408020
    let Expr::UnsafeIndex(_, subject, indices) = expr else {
77
1389454
        return Err(RuleNotApplicable);
78
    };
79

            
80
18566
    let Expr::Atomic(_, Atom::Reference(decl)) = &**subject else {
81
24
        return Err(RuleNotApplicable);
82
    };
83

            
84
18542
    let Name::WithRepresentation(_, reprs) = &decl.name() as &Name else {
85
5664
        return Err(RuleNotApplicable);
86
    };
87

            
88
12878
    if reprs.first().is_none_or(|x| x.as_str() != "tuple_to_atom") {
89
12854
        return Err(RuleNotApplicable);
90
24
    }
91

            
92
24
    let domain = subject.domain_of().ok_or(ApplicationError::DomainError)?;
93

            
94
24
    let Some(elems) = domain.as_tuple() else {
95
        return Err(RuleNotApplicable);
96
    };
97

            
98
24
    assert_eq!(indices.len(), 1, "tuple indexing is always one dimensional");
99
24
    let index = indices[0].clone();
100

            
101
24
    let bubble_constraint = Moo::new(Expression::And(
102
24
        Metadata::new(),
103
24
        Moo::new(matrix_expr![
104
24
            Expression::Leq(
105
24
                Metadata::new(),
106
24
                Moo::new(index.clone()),
107
24
                Moo::new(Expression::Atomic(
108
24
                    Metadata::new(),
109
24
                    Atom::Literal(Literal::Int(elems.len() as i32))
110
24
                ))
111
24
            ),
112
24
            Expression::Geq(
113
24
                Metadata::new(),
114
24
                Moo::new(index),
115
24
                Moo::new(Expression::Atomic(
116
24
                    Metadata::new(),
117
24
                    Atom::Literal(Literal::Int(1))
118
24
                ))
119
24
            )
120
24
        ]),
121
24
    ));
122

            
123
24
    let new_expr = Moo::new(Expression::SafeIndex(
124
24
        Metadata::new(),
125
24
        subject.clone(),
126
24
        indices.clone(),
127
24
    ));
128

            
129
24
    Ok(Reduction::pure(Expression::Bubble(
130
24
        Metadata::new(),
131
24
        new_expr,
132
24
        bubble_constraint,
133
24
    )))
134
1408020
}
135

            
136
// convert equality to tuple equality
137
#[register_rule("Base", 2000, [Eq])]
138
168892
fn tuple_equality(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
139
168892
    let Expr::Eq(_, left, right) = expr else {
140
163259
        return Err(RuleNotApplicable);
141
    };
142

            
143
5633
    let Expr::Atomic(_, Atom::Reference(decl)) = &**left else {
144
2674
        return Err(RuleNotApplicable);
145
    };
146

            
147
2959
    let Name::WithRepresentation(_, reprs) = &decl.name() as &Name else {
148
2899
        return Err(RuleNotApplicable);
149
    };
150

            
151
60
    let Expr::Atomic(_, Atom::Reference(decl2)) = &**right else {
152
36
        return Err(RuleNotApplicable);
153
    };
154

            
155
24
    let Name::WithRepresentation(_, reprs2) = &decl2.name() as &Name else {
156
        return Err(RuleNotApplicable);
157
    };
158

            
159
24
    if reprs.first().is_none_or(|x| x.as_str() != "tuple_to_atom") {
160
12
        return Err(RuleNotApplicable);
161
12
    }
162

            
163
12
    if reprs2.first().is_none_or(|x| x.as_str() != "tuple_to_atom") {
164
        return Err(RuleNotApplicable);
165
12
    }
166

            
167
    // let decl = symbols.lookup(name).unwrap();
168
    // let decl2 = symbols.lookup(name2).unwrap();
169

            
170
12
    let domain = decl
171
12
        .resolved_domain()
172
12
        .ok_or(ApplicationError::DomainError)?;
173
12
    let domain2 = decl2
174
12
        .resolved_domain()
175
12
        .ok_or(ApplicationError::DomainError)?;
176

            
177
12
    let GroundDomain::Tuple(elems) = domain.as_ref() else {
178
        return Err(RuleNotApplicable);
179
    };
180

            
181
12
    let GroundDomain::Tuple(elems2) = domain2.as_ref() else {
182
        return Err(RuleNotApplicable);
183
    };
184

            
185
12
    if elems.len() != elems2.len() {
186
        return Err(RuleNotApplicable);
187
12
    }
188

            
189
12
    let mut equality_constraints = vec![];
190
24
    for i in 0..elems.len() {
191
24
        let left_elem = Expression::SafeIndex(
192
24
            Metadata::new(),
193
24
            Moo::clone(left),
194
24
            vec![Expression::Atomic(
195
24
                Metadata::new(),
196
24
                Atom::Literal(Literal::Int((i + 1) as i32)),
197
24
            )],
198
24
        );
199
24
        let right_elem = Expression::SafeIndex(
200
24
            Metadata::new(),
201
24
            Moo::clone(right),
202
24
            vec![Expression::Atomic(
203
24
                Metadata::new(),
204
24
                Atom::Literal(Literal::Int((i + 1) as i32)),
205
24
            )],
206
24
        );
207
24

            
208
24
        equality_constraints.push(Expression::Eq(
209
24
            Metadata::new(),
210
24
            Moo::new(left_elem),
211
24
            Moo::new(right_elem),
212
24
        ));
213
24
    }
214

            
215
12
    let new_expr = Expression::And(
216
12
        Metadata::new(),
217
12
        Moo::new(into_matrix_expr!(equality_constraints)),
218
12
    );
219

            
220
12
    Ok(Reduction::pure(new_expr))
221
168892
}
222

            
223
//tuple equality where the left is a variable and the right is a tuple literal
224
#[register_rule("Base", 2000, [Eq])]
225
168892
fn tuple_to_constant(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
226
168892
    let Expr::Eq(_, left, right) = expr else {
227
163259
        return Err(RuleNotApplicable);
228
    };
229

            
230
5633
    let Expr::Atomic(_, Atom::Reference(decl)) = &**left else {
231
2674
        return Err(RuleNotApplicable);
232
    };
233

            
234
2959
    let Name::WithRepresentation(name, reprs) = &decl.name() as &Name else {
235
2899
        return Err(RuleNotApplicable);
236
    };
237

            
238
60
    let Some(rhs_tuple_len) = crate::utils::constant_tuple_len(right.as_ref()) else {
239
36
        return Err(RuleNotApplicable);
240
    };
241

            
242
24
    if reprs.first().is_none_or(|x| x.as_str() != "tuple_to_atom") {
243
        return Err(RuleNotApplicable);
244
24
    }
245

            
246
24
    let decl = symbols.lookup(name).unwrap();
247

            
248
24
    let domain = decl
249
24
        .resolved_domain()
250
24
        .ok_or(ApplicationError::DomainError)?;
251

            
252
24
    let GroundDomain::Tuple(elems) = domain.as_ref() else {
253
        return Err(RuleNotApplicable);
254
    };
255

            
256
24
    if elems.len() != rhs_tuple_len {
257
        return Err(RuleNotApplicable);
258
24
    }
259

            
260
24
    let mut equality_constraints = vec![];
261
48
    for i in 0..elems.len() {
262
48
        let left_elem = Expression::SafeIndex(
263
48
            Metadata::new(),
264
48
            Moo::clone(left),
265
48
            vec![Expression::Atomic(
266
48
                Metadata::new(),
267
48
                Atom::Literal(Literal::Int((i + 1) as i32)),
268
48
            )],
269
48
        );
270
48
        let right_elem = Expression::SafeIndex(
271
48
            Metadata::new(),
272
48
            Moo::clone(right),
273
48
            vec![Expression::Atomic(
274
48
                Metadata::new(),
275
48
                Atom::Literal(Literal::Int((i + 1) as i32)),
276
48
            )],
277
48
        );
278
48

            
279
48
        equality_constraints.push(Expression::Eq(
280
48
            Metadata::new(),
281
48
            Moo::new(left_elem),
282
48
            Moo::new(right_elem),
283
48
        ));
284
48
    }
285

            
286
24
    let new_expr = Expression::And(
287
24
        Metadata::new(),
288
24
        Moo::new(into_matrix_expr!(equality_constraints)),
289
24
    );
290

            
291
24
    Ok(Reduction::pure(new_expr))
292
168892
}
293

            
294
// convert equality to tuple inequality
295
#[register_rule("Base", 2000, [Neq])]
296
168892
fn tuple_inequality(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
297
168892
    let Expr::Neq(_, left, right) = expr else {
298
167086
        return Err(RuleNotApplicable);
299
    };
300

            
301
1806
    let Expr::Atomic(_, Atom::Reference(decl)) = &**left else {
302
813
        return Err(RuleNotApplicable);
303
    };
304

            
305
993
    let Name::WithRepresentation(_, reprs) = &decl.name() as &Name else {
306
981
        return Err(RuleNotApplicable);
307
    };
308

            
309
12
    let Expr::Atomic(_, Atom::Reference(decl2)) = &**right else {
310
        return Err(RuleNotApplicable);
311
    };
312

            
313
12
    let Name::WithRepresentation(_, reprs2) = &decl2.name() as &Name else {
314
        return Err(RuleNotApplicable);
315
    };
316

            
317
12
    if reprs.first().is_none_or(|x| x.as_str() != "tuple_to_atom") {
318
        return Err(RuleNotApplicable);
319
12
    }
320

            
321
12
    if reprs2.first().is_none_or(|x| x.as_str() != "tuple_to_atom") {
322
        return Err(RuleNotApplicable);
323
12
    }
324

            
325
12
    let domain = decl
326
12
        .resolved_domain()
327
12
        .ok_or(ApplicationError::DomainError)?;
328

            
329
12
    let domain2 = decl2
330
12
        .resolved_domain()
331
12
        .ok_or(ApplicationError::DomainError)?;
332

            
333
12
    let GroundDomain::Tuple(elems) = domain.as_ref() else {
334
        return Err(RuleNotApplicable);
335
    };
336

            
337
12
    let GroundDomain::Tuple(elems2) = domain2.as_ref() else {
338
        return Err(RuleNotApplicable);
339
    };
340

            
341
12
    assert_eq!(
342
12
        elems.len(),
343
12
        elems2.len(),
344
        "tuple inequality requires same length domains"
345
    );
346

            
347
12
    let mut equality_constraints = vec![];
348
24
    for i in 0..elems.len() {
349
24
        let left_elem = Expression::SafeIndex(
350
24
            Metadata::new(),
351
24
            Moo::clone(left),
352
24
            vec![Expression::Atomic(
353
24
                Metadata::new(),
354
24
                Atom::Literal(Literal::Int((i + 1) as i32)),
355
24
            )],
356
24
        );
357
24
        let right_elem = Expression::SafeIndex(
358
24
            Metadata::new(),
359
24
            Moo::clone(right),
360
24
            vec![Expression::Atomic(
361
24
                Metadata::new(),
362
24
                Atom::Literal(Literal::Int((i + 1) as i32)),
363
24
            )],
364
24
        );
365
24

            
366
24
        equality_constraints.push(Expression::Eq(
367
24
            Metadata::new(),
368
24
            Moo::new(left_elem),
369
24
            Moo::new(right_elem),
370
24
        ));
371
24
    }
372

            
373
    // Just copied from Conjure, would it be better to DeMorgan this?
374
12
    let new_expr = Expression::Not(
375
12
        Metadata::new(),
376
12
        Moo::new(Expression::And(
377
12
            Metadata::new(),
378
12
            Moo::new(into_matrix_expr!(equality_constraints)),
379
12
        )),
380
12
    );
381

            
382
12
    Ok(Reduction::pure(new_expr))
383
168892
}