1
//! Canonical modelling collections for table constraints.
2
use crate::shared::utils::{
3
    short_table_row_entries, table_operand, table_ordered_entries, table_rows,
4
};
5
use conjure_cp::ast::{
6
    AbstractLiteral, Atom, Expression as Expr, Literal, Metadata, Moo, SymbolTable,
7
};
8
use conjure_cp::rule_engine::{
9
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
10
};
11

            
12
1875762
fn is_collection(expr: &Expr, set: bool) -> bool {
13
1875762
    matches!(
14
1875762
        (expr, set),
15
        (Expr::AbstractLiteral(_, AbstractLiteral::Set(_)), true)
16
            | (
17
                Expr::Atomic(
18
                    _,
19
                    Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Set(_)))
20
                ),
21
                true
22
            )
23
            | (
24
                Expr::AbstractLiteral(_, AbstractLiteral::Sequence(_)),
25
                false
26
            )
27
            | (
28
                Expr::Atomic(
29
                    _,
30
                    Atom::Literal(Literal::AbstractLiteral(AbstractLiteral::Sequence(_)))
31
                ),
32
                false
33
            )
34
    )
35
1875762
}
36

            
37
/// Inputs are sequences; relations are sets of sequence rows or sparse sets of tuple pairs.
38
#[register_rule("Base", 25000, [Table, NegativeTable, ShortTable])]
39
430556
fn normalise_table_collections(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
40
430556
    let (meta, inputs, rows, sparse) = match expr {
41
        Expr::Table(meta, inputs, rows) | Expr::NegativeTable(meta, inputs, rows) => {
42
405258
            (meta, inputs, rows, false)
43
        }
44
19460
        Expr::ShortTable(meta, inputs, rows) => (meta, inputs, rows, true),
45
5838
        _ => return Err(RuleNotApplicable),
46
    };
47
424718
    let input_value = table_operand(inputs);
48
424718
    let changed_inputs = !is_collection(&input_value, false);
49
424718
    let inputs = if !changed_inputs {
50
399392
        inputs.clone()
51
    } else {
52
25326
        Moo::new(Expr::AbstractLiteral(
53
25326
            Metadata::new(),
54
            AbstractLiteral::Sequence(
55
25326
                table_ordered_entries(&input_value).ok_or(RuleNotApplicable)?,
56
            ),
57
        ))
58
    };
59
414848
    let row_value = table_operand(rows);
60
414848
    let entries = table_rows(&row_value).ok_or(RuleNotApplicable)?;
61
414848
    let mut changed_rows = !is_collection(&row_value, true);
62
414848
    let entries = entries
63
414848
        .into_iter()
64
1036196
        .map(|row| {
65
1036196
            let value = table_operand(&row);
66
1036196
            if is_collection(&value, sparse) {
67
945070
                return Ok(row);
68
91126
            }
69
91126
            changed_rows = true;
70
91126
            let entries = if sparse {
71
                short_table_row_entries(&value)
72
            } else {
73
91126
                table_ordered_entries(&value)
74
            }
75
91126
            .ok_or(RuleNotApplicable)?;
76
            Ok(Expr::AbstractLiteral(
77
91126
                Metadata::new(),
78
91126
                if sparse {
79
                    AbstractLiteral::Set(entries)
80
                } else {
81
91126
                    AbstractLiteral::Sequence(entries)
82
                },
83
            ))
84
1036196
        })
85
        .collect::<Result<Vec<_>, _>>()?;
86
414848
    let new_rows = if changed_rows {
87
31136
        Moo::new(Expr::AbstractLiteral(
88
31136
            Metadata::new(),
89
31136
            AbstractLiteral::Set(entries),
90
31136
        ))
91
    } else {
92
383712
        rows.clone()
93
    };
94
414848
    if !changed_inputs && !changed_rows {
95
383712
        return Err(RuleNotApplicable);
96
31136
    }
97
31136
    Ok(RuleEffect::pure(match expr {
98
29666
        Expr::Table(..) => Expr::Table(meta.clone(), inputs, new_rows),
99
1470
        Expr::NegativeTable(..) => Expr::NegativeTable(meta.clone(), inputs, new_rows),
100
        Expr::ShortTable(..) => Expr::ShortTable(meta.clone(), inputs, new_rows),
101
        _ => unreachable!(),
102
    }))
103
430556
}