1
#![allow(clippy::arc_with_non_send_sync)]
2

            
3
use std::{collections::BTreeSet, fmt::Display};
4

            
5
use crate::bug_assert;
6
use conjure_cp_core::ast::ReturnType;
7
use itertools::Itertools as _;
8
use parking_lot::RwLockReadGuard;
9
use serde::{Deserialize, Serialize};
10
use serde_with::serde_as;
11
use uniplate::{Biplate, Uniplate};
12

            
13
use super::{
14
    DeclarationPtr, Domain, DomainPtr, Expression, Model, Name, Range, SymbolTable, SymbolTablePtr,
15
    Typeable,
16
    ac_operators::ACOperatorKind,
17
    categories::{Category, CategoryOf},
18
    serde::{AsId, PtrAsInner},
19
};
20

            
21
#[serde_as]
22
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug, Uniplate)]
23
#[biplate(to=Expression)]
24
#[biplate(to=Name)]
25
#[biplate(to=DeclarationPtr)]
26
pub enum ComprehensionQualifier {
27
    ExpressionGenerator {
28
        #[serde_as(as = "AsId")]
29
        ptr: DeclarationPtr,
30
    },
31
    Generator {
32
        #[serde_as(as = "AsId")]
33
        ptr: DeclarationPtr,
34
    },
35
    Condition(Expression),
36
}
37

            
38
/// A comprehension.
39
#[serde_as]
40
#[derive(Clone, PartialEq, Eq, Hash, Uniplate, Serialize, Deserialize, Debug)]
41
#[biplate(to=Expression)]
42
#[biplate(to=SymbolTable)]
43
#[biplate(to=SymbolTablePtr)]
44
#[non_exhaustive]
45
pub struct Comprehension {
46
    pub return_expression: Expression,
47
    pub qualifiers: Vec<ComprehensionQualifier>,
48
    /// When this comprehension appears inside an AC operator, records which operator so
49
    /// expansion can apply the correct skip semantics for symbolic guards.
50
    #[serde(default)]
51
    pub skip_operator: Option<ACOperatorKind>,
52
    #[doc(hidden)]
53
    #[serde_as(as = "PtrAsInner")]
54
    pub symbols: SymbolTablePtr,
55
}
56

            
57
impl Comprehension {
58
251766
    pub fn domain_of(&self) -> Option<DomainPtr> {
59
251766
        let return_expr_domain = self.return_expression.domain_of()?;
60

            
61
        // return a list (matrix with index domain int(1..)) of return_expr elements
62
251650
        Some(Domain::matrix(
63
251650
            return_expr_domain,
64
251650
            vec![Domain::int(vec![Range::UnboundedR(1)])],
65
251650
        ))
66
251766
    }
67

            
68
1043065
    pub fn return_expression(self) -> Expression {
69
1043065
        self.return_expression
70
1043065
    }
71

            
72
    pub fn replace_return_expression(&mut self, new_expr: Expression) {
73
        self.return_expression = new_expr;
74
    }
75

            
76
9316281
    pub fn symbols(&self) -> RwLockReadGuard<'_, SymbolTable> {
77
9316281
        self.symbols.read()
78
9316281
    }
79

            
80
1118018
    pub fn quantified_vars(&self) -> Vec<Name> {
81
1118018
        self.qualifiers
82
1118018
            .iter()
83
2101386
            .filter_map(|q| match q {
84
253706
                ComprehensionQualifier::ExpressionGenerator { ptr } => Some(ptr.name().clone()),
85
1472613
                ComprehensionQualifier::Generator { ptr } => Some(ptr.name().clone()),
86
375067
                ComprehensionQualifier::Condition(_) => None,
87
2101386
            })
88
            .collect()
89
1118018
    }
90

            
91
6276
    pub fn generator_conditions(&self) -> Vec<Expression> {
92
6276
        self.qualifiers
93
6276
            .iter()
94
8794
            .filter_map(|q| match q {
95
                ComprehensionQualifier::Condition(c) => Some(c.clone()),
96
8794
                ComprehensionQualifier::Generator { .. } => None,
97
                ComprehensionQualifier::ExpressionGenerator { .. } => None,
98
8794
            })
99
            .collect()
100
6276
    }
101

            
102
    /// Builds a temporary model containing generator qualifiers and guards.
103
6276
    pub fn to_generator_model(&self) -> Model {
104
6276
        let mut model = self.empty_model_with_symbols();
105
6276
        model.add_constraints(self.generator_conditions());
106
6276
        model
107
6276
    }
108

            
109
    /// Builds a temporary model containing the return expression only.
110
6276
    pub fn to_return_expression_model(&self) -> Model {
111
6276
        let mut model = self.empty_model_with_symbols();
112
6276
        model.add_constraint(self.return_expression.clone());
113
6276
        model
114
6276
    }
115

            
116
12552
    fn empty_model_with_symbols(&self) -> Model {
117
12552
        let parent = self.symbols.read().parent().clone();
118
12552
        let mut model = if let Some(parent) = parent {
119
12552
            Model::new_in_parent_scope(parent)
120
        } else {
121
            Model::default()
122
        };
123
12552
        *model.symbols_ptr_unchecked_mut() = self.symbols.clone();
124
12552
        model
125
12552
    }
126

            
127
    /// Adds a guard to the comprehension.
128
    ///
129
    /// Returns false if the guard references non-quantified decision variables.
130
    pub fn add_quantified_guard(&mut self, guard: Expression) -> bool {
131
        if self.is_quantified_guard(&guard) {
132
            self.qualifiers
133
                .push(ComprehensionQualifier::Condition(guard));
134
            true
135
        } else {
136
            false
137
        }
138
    }
139

            
140
    /// True iff expr does not reference non-quantified decision variables.
141
1665
    pub fn is_quantified_guard(&self, expr: &Expression) -> bool {
142
1665
        let quantified: BTreeSet<Name> = self.quantified_vars().into_iter().collect();
143
1665
        is_quantified_guard(&self.symbols.read(), &quantified, expr)
144
1665
    }
145
}
146

            
147
impl Typeable for Comprehension {
148
    /// A comprehension is a collection, so its type is a matrix of the return expression's type.
149
    ///
150
    /// This agrees with [`Comprehension::domain_of`], which reports a matrix domain. Reporting the
151
    /// element type here instead made every "is this a collection?" test quietly miss
152
    /// comprehensions.
153
174
    fn return_type(&self) -> ReturnType {
154
174
        ReturnType::Matrix(Box::new(self.return_expression.return_type()))
155
174
    }
156
}
157

            
158
impl Display for Comprehension {
159
75625
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160
75625
        let generators_and_guards = self
161
75625
            .qualifiers
162
75625
            .iter()
163
123869
            .map(|qualifier| match qualifier {
164
                ComprehensionQualifier::Generator { ptr } => {
165
97020
                    let domain = ptr.domain().expect("generator declaration has domain");
166
97020
                    format!("{} : {domain}", ptr.name())
167
                }
168
                ComprehensionQualifier::ExpressionGenerator { ptr } => {
169
10402
                    let name = ptr.name();
170
10402
                    if let Some(expr) = ptr.as_quantified_expr() {
171
10402
                        format!("{name} <- {expr}")
172
                    } else {
173
                        panic!("Oh nein! Dat is nicht gut!")
174
                    }
175
                }
176
16447
                ComprehensionQualifier::Condition(expr) => format!("{expr}"),
177
123869
            })
178
75625
            .join(", ");
179

            
180
75625
        write!(
181
75625
            f,
182
75625
            "[ {} | {generators_and_guards} ]",
183
75625
            self.return_expression
184
        )
185
75625
    }
186
}
187

            
188
/// A builder for a comprehension.
189
#[derive(Clone, Debug, PartialEq, Eq)]
190
pub struct ComprehensionBuilder {
191
    qualifiers: Vec<ComprehensionQualifier>,
192
    // A single scope for generators and return expression.
193
    symbols: SymbolTablePtr,
194
    quantified_variables: BTreeSet<Name>,
195
}
196

            
197
impl ComprehensionBuilder {
198
470598
    pub fn new(symbol_table_ptr: SymbolTablePtr) -> Self {
199
470598
        ComprehensionBuilder {
200
470598
            qualifiers: vec![],
201
470598
            symbols: SymbolTablePtr::with_parent(symbol_table_ptr),
202
470598
            quantified_variables: BTreeSet::new(),
203
470598
        }
204
470598
    }
205

            
206
    /// Backwards-compatible parser API: same table for generators and return expression.
207
154625
    pub fn generator_symboltable(&mut self) -> SymbolTablePtr {
208
154625
        self.symbols.clone()
209
154625
    }
210

            
211
    /// Backwards-compatible parser API: same table for generators and return expression.
212
467445
    pub fn return_expr_symboltable(&mut self) -> SymbolTablePtr {
213
467445
        self.symbols.clone()
214
467445
    }
215

            
216
42716
    pub fn guard(mut self, guard: Expression) -> Self {
217
42716
        self.qualifiers
218
42716
            .push(ComprehensionQualifier::Condition(guard));
219
42716
        self
220
42716
    }
221

            
222
385718
    pub fn generator(mut self, declaration: DeclarationPtr) -> Self {
223
385718
        let name = declaration.name().clone();
224
        bug_assert!(!self.quantified_variables.contains(&name));
225

            
226
385718
        self.quantified_variables.insert(name.clone());
227

            
228
        // insert into comprehension scope as a local quantified variable
229
385718
        let quantified_decl = DeclarationPtr::new_quantified(name, declaration.domain().unwrap());
230
385718
        self.symbols.write().insert(quantified_decl.clone());
231

            
232
385718
        self.qualifiers.push(ComprehensionQualifier::Generator {
233
385718
            ptr: quantified_decl,
234
385718
        });
235

            
236
385718
        self
237
385718
    }
238

            
239
264279
    pub fn expression_generator(mut self, name: Name, expr: Expression) -> Self {
240
        bug_assert!(!self.quantified_variables.contains(&name));
241

            
242
264279
        self.quantified_variables.insert(name.clone());
243

            
244
        // insert into comprehension scope as a local quantified variable
245
264279
        let quantified_decl = DeclarationPtr::new_quantified_expr(name, expr);
246
264279
        self.symbols.write().insert(quantified_decl.clone());
247

            
248
264279
        self.qualifiers
249
264279
            .push(ComprehensionQualifier::ExpressionGenerator {
250
264279
                ptr: quantified_decl,
251
264279
            });
252

            
253
264279
        self
254
264279
    }
255

            
256
    /// Creates a comprehension with the given return expression.
257
    ///
258
    /// Guards are always stored as [`ComprehensionQualifier::Condition`] entries. When a guard
259
    /// references non-quantified decision variables, the enclosing AC operator applies the
260
    /// appropriate skip semantics during comprehension expansion.
261
470569
    pub fn with_return_value(self, expression: Expression) -> Comprehension {
262
470569
        Comprehension {
263
470569
            return_expression: expression,
264
470569
            qualifiers: self.qualifiers,
265
470569
            skip_operator: None,
266
470569
            symbols: self.symbols,
267
470569
        }
268
470569
    }
269
}
270

            
271
/// True iff the guard does not reference non-quantified decision variables.
272
1665
fn is_quantified_guard(
273
1665
    symbols: &SymbolTable,
274
1665
    quantified_variables: &BTreeSet<Name>,
275
1665
    guard: &Expression,
276
1665
) -> bool {
277
3521
    guard.universe_bi().iter().all(|name| {
278
3521
        quantified_variables.contains(name)
279
1491
            || symbols
280
1491
                .lookup(name)
281
186
                .is_some_and(|decl| decl.category_of() != Category::Decision)
282
3521
    })
283
1665
}