Skip to main content

conjure_cp_core/ast/domains/
attrs.rs

1use crate::ast::domains::Int;
2use crate::ast::domains::range::Range;
3use funcmap::{FuncMap, TryFuncMap};
4use itertools::Itertools;
5use polyquine::Quine;
6use serde::{Deserialize, Serialize};
7use std::fmt::{Display, Formatter};
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, FuncMap, TryFuncMap, Quine)]
10#[path_prefix(conjure_cp::ast)]
11pub struct SetAttr<A = Int> {
12    pub size: Range<A>,
13    /// Optional user-facing representation preference (short name), e.g. `"packed"`.
14    ///
15    /// Written in Essence as `set (representation packed) of …`. When present, representation selection
16    /// heuristics default to this representation if it is applicable.
17    pub representation: Option<String>,
18}
19
20impl<A> SetAttr<A> {
21    pub fn new(size: Range<A>) -> Self {
22        Self {
23            size,
24            representation: None,
25        }
26    }
27
28    pub fn new_min_max_size(min: A, max: A) -> Self {
29        Self::new(Range::Bounded(min, max))
30    }
31
32    pub fn new_min_size(min: A) -> Self {
33        Self::new(Range::UnboundedR(min))
34    }
35
36    pub fn new_max_size(max: A) -> Self {
37        Self::new(Range::UnboundedL(max))
38    }
39
40    pub fn new_size(sz: A) -> Self {
41        Self::new(Range::Single(sz))
42    }
43
44    /// Set the representation preference (Essence short name), returning the updated attributes.
45    pub fn with_representation(mut self, name: impl Into<String>) -> Self {
46        self.representation = Some(name.into());
47        self
48    }
49}
50
51impl<A> Default for SetAttr<A> {
52    fn default() -> Self {
53        SetAttr {
54            size: Range::Unbounded,
55            representation: None,
56        }
57    }
58}
59
60impl<A: Display> Display for SetAttr<A> {
61    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
62        let mut attrs = Vec::new();
63        if let Some(representation) = &self.representation {
64            attrs.push(format!("representation {representation}"));
65        }
66        let size = fmt_size("size", &self.size);
67        if !size.is_empty() {
68            attrs.push(size);
69        }
70        if attrs.is_empty() {
71            Ok(())
72        } else {
73            write!(f, "({})", attrs.join(", "))
74        }
75    }
76}
77
78#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, FuncMap, TryFuncMap, Quine)]
79#[path_prefix(conjure_cp::ast)]
80pub struct MSetAttr<A = Int> {
81    pub size: Range<A>,
82    pub occurrence: Range<A>,
83    /// Optional user-facing representation preference (short name), e.g. `"repetition"`.
84    ///
85    /// Written in Essence as `mset (representation repetition) of …`. When present, representation selection
86    /// heuristics default to this representation if it is applicable.
87    #[serde(default)]
88    pub representation: Option<String>,
89}
90
91impl<A> MSetAttr<A> {
92    pub fn new(size: Range<A>, occurrence: Range<A>) -> Self {
93        Self {
94            size,
95            occurrence,
96            representation: None,
97        }
98    }
99
100    pub fn new_min_max_size(min: A, max: A) -> Self {
101        Self::new(Range::Bounded(min, max), Range::Unbounded)
102    }
103
104    pub fn new_min_size(min: A) -> Self {
105        Self::new(Range::UnboundedR(min), Range::Unbounded)
106    }
107
108    pub fn new_max_size(max: A) -> Self {
109        Self::new(Range::UnboundedL(max), Range::Unbounded)
110    }
111
112    pub fn new_size(sz: A) -> Self {
113        Self::new(Range::Single(sz), Range::Unbounded)
114    }
115
116    /// Set the representation preference (Essence short name), returning the updated attributes.
117    pub fn with_representation(mut self, name: impl Into<String>) -> Self {
118        self.representation = Some(name.into());
119        self
120    }
121}
122
123impl<A: Display> Display for MSetAttr<A> {
124    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
125        let size_str = fmt_size("size", &self.size);
126
127        // It only makes sense in terms of min and max occurrence for the essence language,
128        // so for single ranges it is still presented as min and max occurrence.
129        let occ_str = match &self.occurrence {
130            Range::Single(x) => format!("minOccur({x}), maxOccur({x})"),
131            Range::Bounded(l, r) => format!("minOccur({l}), maxOccur({r})"),
132            Range::UnboundedL(r) => format!("maxOccur({r})"),
133            Range::UnboundedR(l) => format!("minOccur({l})"),
134            Range::Unbounded => "".to_string(),
135        };
136
137        let representation_str = self
138            .representation
139            .as_ref()
140            .map(|name| format!("representation {name}"))
141            .unwrap_or_default();
142        let mut strs = [representation_str, size_str, occ_str]
143            .iter()
144            .filter(|s| !s.is_empty())
145            .join(", ");
146        if !strs.is_empty() {
147            strs = format!("({})", strs);
148        }
149        write!(f, "{strs}")
150    }
151}
152
153impl<A> Default for MSetAttr<A> {
154    fn default() -> Self {
155        MSetAttr {
156            size: Range::Unbounded,
157            occurrence: Range::Unbounded,
158            representation: None,
159        }
160    }
161}
162
163#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, FuncMap, TryFuncMap, Quine)]
164#[path_prefix(conjure_cp::ast)]
165pub struct FuncAttr<A = Int> {
166    pub size: Range<A>,
167    pub partiality: PartialityAttr,
168    pub jectivity: JectivityAttr,
169}
170
171impl<A> Default for FuncAttr<A> {
172    fn default() -> Self {
173        FuncAttr {
174            size: Range::Unbounded,
175            // Matches Conjure's own default (`Domain.hs`): a bare `function` with no explicit
176            // `total` attribute is partial.
177            partiality: PartialityAttr::Partial,
178            jectivity: JectivityAttr::None,
179        }
180    }
181}
182
183impl<A: Display> Display for FuncAttr<A> {
184    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
185        let size_str = fmt_size("size", &self.size);
186        let mut strs = [
187            size_str,
188            self.partiality.to_string(),
189            self.jectivity.to_string(),
190        ]
191        .iter()
192        .filter(|s| !s.is_empty())
193        .join(", ");
194        if !strs.is_empty() {
195            strs = format!("({})", strs);
196        }
197        write!(f, "{strs}")
198    }
199}
200
201#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, FuncMap, TryFuncMap, Quine)]
202#[path_prefix(conjure_cp::ast)]
203pub struct SequenceAttr<A = Int> {
204    pub size: Range<A>,
205    pub jectivity: JectivityAttr,
206    /// Optional user-facing representation preference (short name), e.g. `"packed"`.
207    ///
208    /// Written in Essence as `sequence{packed} of …`. When present, representation selection
209    /// heuristics default to this representation if it is applicable.
210    pub representation: Option<String>,
211}
212
213impl<A> SequenceAttr<A> {
214    /// Set the representation preference (Essence short name), returning the updated attributes.
215    pub fn with_representation(mut self, name: impl Into<String>) -> Self {
216        self.representation = Some(name.into());
217        self
218    }
219}
220
221impl<A> Default for SequenceAttr<A> {
222    fn default() -> Self {
223        SequenceAttr {
224            size: Range::Unbounded,
225            jectivity: JectivityAttr::None,
226            representation: None,
227        }
228    }
229}
230
231impl<A: Display> Display for SequenceAttr<A> {
232    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
233        let size_str = fmt_size("size", &self.size);
234        let mut strs = [size_str, self.jectivity.to_string()]
235            .iter()
236            .filter(|s| !s.is_empty())
237            .join(", ");
238        if !strs.is_empty() {
239            strs = format!("({})", strs);
240        }
241        write!(f, "{strs}")
242    }
243}
244
245#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, FuncMap, TryFuncMap, Quine)]
246#[path_prefix(conjure_cp::ast)]
247pub struct PartitionAttr<A = Int> {
248    pub num_parts: Range<A>, // i.e. how many parts there are in the partition
249    pub part_len: Range<A>,  // i.e. the size of each constitutent part
250    pub is_regular: bool,
251}
252
253impl<A: Display> Display for PartitionAttr<A> {
254    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
255        let num_parts_str = fmt_size("numParts", &self.num_parts);
256        let part_len_str = fmt_size("partSize", &self.part_len);
257
258        let regular_str = match &self.is_regular {
259            true => "regular".to_string(),
260            false => String::new(),
261        };
262
263        let mut strs = [num_parts_str, part_len_str, regular_str]
264            .iter()
265            .filter(|s| !s.is_empty())
266            .join(", ");
267        if !strs.is_empty() {
268            strs = format!("({})", strs);
269        }
270        write!(f, "{strs}")
271    }
272}
273
274impl<A> Default for PartitionAttr<A> {
275    fn default() -> Self {
276        PartitionAttr {
277            num_parts: Range::Unbounded,
278            part_len: Range::Unbounded,
279            is_regular: false,
280        }
281    }
282}
283
284/// A permutation is inherently total and bijective by definition, so unlike partition or
285/// function, its only attribute is a size constraint on `numMoved`, the number of moved points
286/// (i.e. the points not fixed by the permutation) -- confirmed against real Conjure's own
287/// `numMoved`/`minNumMoved`/`maxNumMoved` keywords (real Conjure does not accept
288/// `size`/`minSize`/`maxSize` here, unlike set/relation/etc).
289#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, FuncMap, TryFuncMap, Quine)]
290#[path_prefix(conjure_cp::ast)]
291pub struct PermutationAttr<A = Int> {
292    pub num_moved: Range<A>,
293}
294
295impl<A: Display> Display for PermutationAttr<A> {
296    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
297        let size_str = fmt_size("numMoved", &self.num_moved);
298        write!(f, "{size_str}")
299    }
300}
301
302impl<A> Default for PermutationAttr<A> {
303    fn default() -> Self {
304        PermutationAttr {
305            num_moved: Range::Unbounded,
306        }
307    }
308}
309
310#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine)]
311pub enum PartialityAttr {
312    Total,
313    Partial,
314}
315
316impl Display for PartialityAttr {
317    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
318        match self {
319            PartialityAttr::Total => write!(f, "total"),
320            PartialityAttr::Partial => write!(f, ""),
321        }
322    }
323}
324
325#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine)]
326pub enum JectivityAttr {
327    None,
328    Injective,
329    Surjective,
330    Bijective,
331}
332
333impl Display for JectivityAttr {
334    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
335        match self {
336            JectivityAttr::None => write!(f, ""),
337            JectivityAttr::Injective => write!(f, "injective"),
338            JectivityAttr::Surjective => write!(f, "surjective"),
339            JectivityAttr::Bijective => write!(f, "bijective"),
340        }
341    }
342}
343
344#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, FuncMap, TryFuncMap, Quine)]
345#[path_prefix(conjure_cp::ast)]
346pub struct RelAttr<A = Int> {
347    pub size: Range<A>,
348    pub binary: Vec<BinaryAttr>,
349}
350
351impl<A> Default for RelAttr<A> {
352    fn default() -> Self {
353        RelAttr {
354            size: Range::Unbounded,
355            binary: Vec::new(),
356        }
357    }
358}
359
360impl<A: Display> Display for RelAttr<A> {
361    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
362        let size_str = fmt_size("size", &self.size);
363        let mut strs = [size_str, self.binary.iter().join(", ")]
364            .iter()
365            .filter(|s| !s.is_empty())
366            .join(", ");
367        if !strs.is_empty() {
368            strs = format!("({})", strs);
369        }
370        write!(f, "{strs}")
371    }
372}
373
374#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Quine)]
375pub enum BinaryAttr {
376    Reflexive,
377    Irreflexive,
378    Coreflexive,
379    Symmetric,
380    AntiSymmetric,
381    ASymmetric,
382    Transitive,
383    Total,
384    Connex,
385    Euclidean,
386    Serial,
387    Equivalence,
388    PartialOrder,
389    LeftTotal,
390    RightTotal,
391    LinearOrder,
392    WeakOrder,
393    PreOrder,
394    StrictPartialOrder,
395}
396
397impl Display for BinaryAttr {
398    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
399        match self {
400            BinaryAttr::Reflexive => write!(f, "reflexive"),
401            BinaryAttr::Irreflexive => write!(f, "irreflexive"),
402            BinaryAttr::Coreflexive => write!(f, "coreflexive"),
403            BinaryAttr::Symmetric => write!(f, "symmetric"),
404            BinaryAttr::AntiSymmetric => write!(f, "antiSymmetric"),
405            BinaryAttr::ASymmetric => write!(f, "aSymmetric"),
406            BinaryAttr::Transitive => write!(f, "transitive"),
407            BinaryAttr::Total => write!(f, "total"),
408            BinaryAttr::Connex => write!(f, "connex"),
409            BinaryAttr::Euclidean => write!(f, "Euclidean"),
410            BinaryAttr::Serial => write!(f, "serial"),
411            BinaryAttr::Equivalence => write!(f, "equivalence"),
412            BinaryAttr::PartialOrder => write!(f, "partialOrder"),
413            BinaryAttr::LeftTotal => write!(f, "leftTotal"),
414            BinaryAttr::RightTotal => write!(f, "rightTotal"),
415            BinaryAttr::LinearOrder => write!(f, "linearOrder"),
416            BinaryAttr::WeakOrder => write!(f, "weakOrder"),
417            BinaryAttr::PreOrder => write!(f, "preOrder"),
418            BinaryAttr::StrictPartialOrder => write!(f, "strictPartialOrder"),
419        }
420    }
421}
422
423impl BinaryAttr {
424    /// Parses the Essence keyword for a binary relation attribute (the inverse of `Display`).
425    /// Used both by native domain-attribute parsing (`relation (reflexive, ...) of ...`) and by
426    /// attribute-as-constraint lifting (`reflexive(r)`).
427    pub fn from_keyword(s: &str) -> Option<Self> {
428        Some(match s {
429            "reflexive" => BinaryAttr::Reflexive,
430            "irreflexive" => BinaryAttr::Irreflexive,
431            "coreflexive" => BinaryAttr::Coreflexive,
432            "symmetric" => BinaryAttr::Symmetric,
433            "antiSymmetric" => BinaryAttr::AntiSymmetric,
434            "aSymmetric" => BinaryAttr::ASymmetric,
435            "transitive" => BinaryAttr::Transitive,
436            "total" => BinaryAttr::Total,
437            "connex" => BinaryAttr::Connex,
438            "Euclidean" => BinaryAttr::Euclidean,
439            "serial" => BinaryAttr::Serial,
440            "equivalence" => BinaryAttr::Equivalence,
441            "partialOrder" => BinaryAttr::PartialOrder,
442            "leftTotal" => BinaryAttr::LeftTotal,
443            "rightTotal" => BinaryAttr::RightTotal,
444            "linearOrder" => BinaryAttr::LinearOrder,
445            "weakOrder" => BinaryAttr::WeakOrder,
446            "preOrder" => BinaryAttr::PreOrder,
447            "strictPartialOrder" => BinaryAttr::StrictPartialOrder,
448            _ => return None,
449        })
450    }
451}
452
453/// Format a range as Essence size attribute
454#[inline]
455fn fmt_size<A: Display>(suffix: &str, sz: &Range<A>) -> String {
456    let cap_suffix = capitalize(suffix);
457    match sz {
458        Range::Single(x) => format!("{suffix} {x}"),
459        Range::Bounded(l, r) => format!("min{cap_suffix} {l}, max{cap_suffix} {r}"),
460        Range::UnboundedL(r) => format!("max{cap_suffix} {r}"),
461        Range::UnboundedR(l) => format!("min{cap_suffix} {l}"),
462        Range::Unbounded => "".to_string(),
463    }
464}
465
466#[inline]
467fn capitalize(s: &str) -> String {
468    let mut c = s.chars();
469    match c.next() {
470        None => String::new(),
471        Some(f) => f.to_uppercase().to_string() + c.as_str(),
472    }
473}