1#![allow(clippy::arc_with_non_send_sync)]
2
3use std::{collections::BTreeSet, fmt::Display};
4
5use crate::bug_assert;
6use conjure_cp_core::ast::ReturnType;
7use itertools::Itertools as _;
8use parking_lot::RwLockReadGuard;
9use serde::{Deserialize, Serialize};
10use serde_with::serde_as;
11use uniplate::{Biplate, Uniplate};
12
13use 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)]
26pub 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#[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]
45pub struct Comprehension {
46 pub return_expression: Expression,
47 pub qualifiers: Vec<ComprehensionQualifier>,
48 #[serde(default)]
51 pub skip_operator: Option<ACOperatorKind>,
52 #[doc(hidden)]
53 #[serde_as(as = "PtrAsInner")]
54 pub symbols: SymbolTablePtr,
55}
56
57impl Comprehension {
58 pub fn domain_of(&self) -> Option<DomainPtr> {
59 let return_expr_domain = self.return_expression.domain_of()?;
60
61 Some(Domain::matrix(
63 return_expr_domain,
64 vec![Domain::int(vec![Range::UnboundedR(1)])],
65 ))
66 }
67
68 pub fn return_expression(self) -> Expression {
69 self.return_expression
70 }
71
72 pub fn replace_return_expression(&mut self, new_expr: Expression) {
73 self.return_expression = new_expr;
74 }
75
76 pub fn symbols(&self) -> RwLockReadGuard<'_, SymbolTable> {
77 self.symbols.read()
78 }
79
80 pub fn quantified_vars(&self) -> Vec<Name> {
81 self.qualifiers
82 .iter()
83 .filter_map(|q| match q {
84 ComprehensionQualifier::ExpressionGenerator { ptr } => Some(ptr.name().clone()),
85 ComprehensionQualifier::Generator { ptr } => Some(ptr.name().clone()),
86 ComprehensionQualifier::Condition(_) => None,
87 })
88 .collect()
89 }
90
91 pub fn generator_conditions(&self) -> Vec<Expression> {
92 self.qualifiers
93 .iter()
94 .filter_map(|q| match q {
95 ComprehensionQualifier::Condition(c) => Some(c.clone()),
96 ComprehensionQualifier::Generator { .. } => None,
97 ComprehensionQualifier::ExpressionGenerator { .. } => None,
98 })
99 .collect()
100 }
101
102 pub fn to_generator_model(&self) -> Model {
104 let mut model = self.empty_model_with_symbols();
105 model.add_constraints(self.generator_conditions());
106 model
107 }
108
109 pub fn to_return_expression_model(&self) -> Model {
111 let mut model = self.empty_model_with_symbols();
112 model.add_constraint(self.return_expression.clone());
113 model
114 }
115
116 fn empty_model_with_symbols(&self) -> Model {
117 let parent = self.symbols.read().parent().clone();
118 let mut model = if let Some(parent) = parent {
119 Model::new_in_parent_scope(parent)
120 } else {
121 Model::default()
122 };
123 *model.symbols_ptr_unchecked_mut() = self.symbols.clone();
124 model
125 }
126
127 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 pub fn is_quantified_guard(&self, expr: &Expression) -> bool {
142 let quantified: BTreeSet<Name> = self.quantified_vars().into_iter().collect();
143 is_quantified_guard(&self.symbols.read(), &quantified, expr)
144 }
145}
146
147impl Typeable for Comprehension {
148 fn return_type(&self) -> ReturnType {
154 ReturnType::Matrix(Box::new(self.return_expression.return_type()))
155 }
156}
157
158impl Display for Comprehension {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 let generators_and_guards = self
161 .qualifiers
162 .iter()
163 .map(|qualifier| match qualifier {
164 ComprehensionQualifier::Generator { ptr } => {
165 let domain = ptr.domain().expect("generator declaration has domain");
166 format!("{} : {domain}", ptr.name())
167 }
168 ComprehensionQualifier::ExpressionGenerator { ptr } => {
169 let name = ptr.name();
170 if let Some(expr) = ptr.as_quantified_expr() {
171 format!("{name} <- {expr}")
172 } else {
173 panic!("Oh nein! Dat is nicht gut!")
174 }
175 }
176 ComprehensionQualifier::Condition(expr) => format!("{expr}"),
177 })
178 .join(", ");
179
180 write!(
181 f,
182 "[ {} | {generators_and_guards} ]",
183 self.return_expression
184 )
185 }
186}
187
188#[derive(Clone, Debug, PartialEq, Eq)]
190pub struct ComprehensionBuilder {
191 qualifiers: Vec<ComprehensionQualifier>,
192 symbols: SymbolTablePtr,
194 quantified_variables: BTreeSet<Name>,
195}
196
197impl ComprehensionBuilder {
198 pub fn new(symbol_table_ptr: SymbolTablePtr) -> Self {
199 ComprehensionBuilder {
200 qualifiers: vec![],
201 symbols: SymbolTablePtr::with_parent(symbol_table_ptr),
202 quantified_variables: BTreeSet::new(),
203 }
204 }
205
206 pub fn generator_symboltable(&mut self) -> SymbolTablePtr {
208 self.symbols.clone()
209 }
210
211 pub fn return_expr_symboltable(&mut self) -> SymbolTablePtr {
213 self.symbols.clone()
214 }
215
216 pub fn guard(mut self, guard: Expression) -> Self {
217 self.qualifiers
218 .push(ComprehensionQualifier::Condition(guard));
219 self
220 }
221
222 pub fn generator(mut self, declaration: DeclarationPtr) -> Self {
223 let name = declaration.name().clone();
224 bug_assert!(!self.quantified_variables.contains(&name));
225
226 self.quantified_variables.insert(name.clone());
227
228 let quantified_decl = DeclarationPtr::new_quantified(name, declaration.domain().unwrap());
230 self.symbols.write().insert(quantified_decl.clone());
231
232 self.qualifiers.push(ComprehensionQualifier::Generator {
233 ptr: quantified_decl,
234 });
235
236 self
237 }
238
239 pub fn expression_generator(mut self, name: Name, expr: Expression) -> Self {
240 bug_assert!(!self.quantified_variables.contains(&name));
241
242 self.quantified_variables.insert(name.clone());
243
244 let quantified_decl = DeclarationPtr::new_quantified_expr(name, expr);
246 self.symbols.write().insert(quantified_decl.clone());
247
248 self.qualifiers
249 .push(ComprehensionQualifier::ExpressionGenerator {
250 ptr: quantified_decl,
251 });
252
253 self
254 }
255
256 pub fn with_return_value(self, expression: Expression) -> Comprehension {
262 Comprehension {
263 return_expression: expression,
264 qualifiers: self.qualifiers,
265 skip_operator: None,
266 symbols: self.symbols,
267 }
268 }
269}
270
271fn is_quantified_guard(
273 symbols: &SymbolTable,
274 quantified_variables: &BTreeSet<Name>,
275 guard: &Expression,
276) -> bool {
277 guard.universe_bi().iter().all(|name| {
278 quantified_variables.contains(name)
279 || symbols
280 .lookup(name)
281 .is_some_and(|decl| decl.category_of() != Category::Decision)
282 })
283}