Skip to main content

xcsp3_serde/
lib.rs

1//! Serialization of the XCSP3 (core) format
2//!
3//! XCSP3 is an integrated format for representing combinatorial constrained
4//! problems, which can deal with mono/multi optimization, many types of
5//! variables, cost functions, reification, views, annotations, variable
6//! quantification, distributed, probabilistic and qualitative reasoning. It is
7//! also compact, and easy to read and to parse. The objective of XCSP3 is to
8//! ease the effort required to test and compare different algorithms by
9//! providing a common test-bed of combinatorial constrained instances.
10//!
11//! This crate focuses on the (de-)serializeation of the XCSP3 format. It can be
12//! used to parse an XCSP3 XML file into the provided rust types, or writing the
13//! provided rust types to an XCSP3 XML file.
14//!
15//! # Getting Started
16//!
17//! Install `xcsp3-serde` and `quick-xml` for your package:
18//!
19//! ```bash
20//! cargo add xcsp3-serde quick-xml
21//! ```
22//!
23//! Once these dependencies have been installed to your crate, you could
24//! deserialize a XCSP3 XML file as follows:
25//!
26//! ```
27//! # use xcsp3_serde::Instance;
28//! # use std::{fs::File, io::BufReader, path::Path};
29//! # let path = Path::new("corpus/xcsp3_ex_001.xml");
30//! // let path = Path::new("/lorem/ipsum/instance.xml");
31//! let rdr = BufReader::new(File::open(path).unwrap());
32//! let instance: Instance = quick_xml::de::from_reader(rdr).unwrap();
33//! // ... process XCSP3 ...
34//! ```
35//!
36//! If, however, you want to serialize a XCSP3 instance you could follow the
37//! following fragment:
38//!
39//! ```
40//! # use xcsp3_serde::Instance;
41//! let instance = Instance::<String>::default();
42//! // ... create XCSP3 instance ...
43//! let xml_str = quick_xml::se::to_string(&instance).unwrap();
44//! ```
45//! Note that `quick_xml::se::to_writer`, using a buffered file writer, would be
46//! preferred when writing larger instances.
47//!
48//! # Limitations
49//!
50//! Not all XCSP3 features are currently implemented, the functionality of
51//! XCSP3-core is generally implemented and supported. This allows users to work
52//! with the most common constraint types and representations. Future updates
53//! will focus on expanding the range of supported XCSP3 features.
54
55pub mod constraint;
56pub mod error;
57pub mod expression;
58
59use std::{
60	borrow::Cow,
61	collections::{HashMap, VecDeque},
62	fmt::{self, Display},
63	hash::Hash,
64	marker::PhantomData,
65	ops::RangeInclusive,
66};
67
68use itertools::Itertools;
69use nom::{
70	branch::alt,
71	bytes::streaming::tag,
72	character::complete::{char, digit1},
73	combinator::{all_consuming, map, map_res, opt, recognize},
74	multi::many0,
75	sequence::{delimited, pair, preceded},
76	IResult, Parser,
77};
78pub use rangelist::RangeList;
79use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
80
81use crate::{
82	constraint::{Constraint, MetaConstraint},
83	error::UnrollError,
84	expression::{identifier, int, range, sequence, whitespace_seperated, Exp, IntExp},
85};
86
87/// Definition of a k-dimensional arrays of variables
88#[derive(Clone, Debug, PartialEq, Hash)]
89pub struct Array<Identifier = String, Var = VarRef<Identifier>> {
90	/// Name used to refer to the array
91	pub identifier: Identifier,
92	/// Comment by the user
93	pub note: Option<String>,
94	/// Dimensions of the array
95	pub size: Vec<usize>,
96	/// Domains of the variables contained within the array
97	///
98	/// Note that when several subsets of variables of an array have different
99	/// domains, a rangelist is provided for each of these subsets. The first
100	/// member of the tuple indicates the list of variables to which the domain
101	/// definition applies. The special identifier `others` is used to declare a
102	/// default domain for all other variables contained in the array.
103	pub domains: Vec<(Vec<Var>, RangeList<IntVal>)>,
104}
105
106/// The way in which combinations of objectives are to be evaluated
107#[derive(Clone, Debug, Default, PartialEq, Hash, Deserialize, Serialize)]
108#[serde(rename_all = "camelCase")]
109pub enum CombinationType {
110	/// Objectives are lexicographically ordered
111	///
112	/// A solution is superceeded if it is better in the first objective, or if it
113	/// is equal in the first objective and better in the second objective, and so
114	/// on.
115	#[default]
116	Lexico,
117	/// No objective is more important than another one
118	///
119	/// A solution is better than another if it is better in at least one
120	/// objective and not worse in any other objective.
121	Pareto,
122}
123
124/// The framework of an XCSP3 instance
125///
126/// The framework of an XCSP3 instance is used to determine the types of
127/// constraints and variables that can be used in the instance. Different
128/// frameworks correspond to different types of problems that can be expressed
129/// in XCSP3.
130#[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Debug, Deserialize, Serialize)]
131#[serde(rename_all = "UPPERCASE")]
132pub enum FrameworkType {
133	/// Constraint Satisfaction Problem
134	///
135	/// A discrete Constraint Network that constains a finite set of variables and
136	/// a finite set of constraints.
137	#[default]
138	Csp,
139	/// Constraint Optimization Problem
140	///
141	/// An instance is defined by a set of variables, a set of constraints, as for
142	/// [`FrameworkType::Csp`], together with a set of objective functions.
143	/// Mono-objective optimization is when only one objective function is
144	/// present. Otherwise, this is multi-objective optimization.
145	Cop,
146	/// Weighted Constraint Satisfaction Problem
147	///
148	/// An extension to [`FrameworkType::Csp`] that relies on a valuation
149	/// structure using weighted constraints.
150	Wcsp,
151	/// Fuzzy Constraint Satisfaction Problem
152	///
153	/// An extension of [`FrameworkType::Csp`] with fuzzy constraints. Each fuzzy
154	/// constraint represents a fuzzy relation on its scope: it associates a value
155	/// in \[0,1\], called membership degree, with each constraint tuple,
156	/// indicating to what extent the tuple belongs to the relation and therefore
157	/// satisfies the constraint.
158	Fcsp,
159	/// Quantified Constraint Satisfaction Problem
160	///
161	/// An extension of [`FrameworkType::Csp`] in which variables may be
162	/// quantified universally or existentially.
163	Qcsp,
164	/// Extended Quantified Constraint Optimization Problem
165	///
166	/// An extension of [`FrameworkType::Qcsp`] to overcome some difficulties that
167	/// may occur when modeling real problems with classical QCSP.
168	QcspPlus,
169	/// Quantified Constraint Optimization Problem
170	///
171	/// An extesion of [`FrameworkType::Qcsp`] that allows us to formally express
172	/// preferences over [`FrameworkType::Qcsp`] strategies
173	Qcop,
174	/// Extended Quantified Constraint Optimization Problem
175	///
176	/// An extesion of [`FrameworkType::QcspPlus`] that allows us to formally
177	/// express preferences over [`FrameworkType::QcspPlus`] strategies
178	QcopPlus,
179	/// Stochastic Constraint Satisfaction Problem
180	Scsp,
181	/// Stochastic Constraint Optimization Problem
182	Scop,
183	/// Qualitative Spatial Temporal Reasoning
184	Qstr,
185	/// Temporal Constraint Satisfaction Problem
186	///
187	/// In this framework, variables represent time points and temporal
188	/// information is represented by a set of unary and binary constraints, each
189	/// specifying a set of permitted intervals.
190	Tcsp,
191	/// Numerical Constraint Satisfaction Problem
192	///
193	/// An extension of [`FrameworkType::Csp`] in which variables are real numbers
194	/// and constraints are relations between these variables.
195	Ncsp,
196	/// Numerical Constraint Optimization Problem
197	///
198	/// An extension of [`FrameworkType::Ncsp`] that includes objective functions.
199	Ncop,
200	/// Distributed Constraint Satisfaction Problem
201	DisCsp,
202	/// Distributed Weighted Constraint Satisfaction Problem
203	DisWcsp,
204}
205
206/// An expression used to access a single element or a larger part of an array
207#[derive(Clone, Debug, PartialEq, Hash, Eq)]
208pub enum Indexing {
209	/// Accessing a single index of a dimension in an array
210	Single(usize),
211	/// Accessing a slice of a dimension in an array
212	Range(usize, usize),
213	/// Accessing the full range of an array
214	Full,
215}
216
217/// XCSP3 problem instance
218#[derive(Clone, PartialEq, Debug, Hash)]
219pub struct Instance<Identifier = String, Var = VarRef<Identifier>> {
220	/// The type of the framework used to express the instance.
221	pub ty: FrameworkType,
222	/// Definitions of the single decision variables
223	pub variables: Vec<Variable<Identifier>>,
224	/// Definitions of the arrays of decision variables
225	pub arrays: Vec<Array<Identifier, Var>>,
226	/// Constraints that must be satisfied for a solution to be valid
227	pub constraints: Vec<MetaConstraint<Identifier, Var>>,
228	/// The objectives to be optimized
229	pub objectives: Objectives<Identifier, Var>,
230}
231
232/// An assignment from a list of variables to a list of values
233///
234/// This structure is used both to represent an elementary constraint in an
235/// instance, and to represent the solution to an instance.
236#[derive(Clone, Debug, PartialEq, Hash, Deserialize)]
237#[serde(bound(deserialize = "Identifier: From<String>, Var: IntoVar"))]
238pub struct Instantiation<Identifier = String, Var = VarRef<Identifier>> {
239	/// Optional metadata for the constraint
240	#[serde(flatten)]
241	pub info: MetaInfo<Identifier>,
242	/// The type of instantiation
243	///
244	/// This field is used to distinguish between different types of solutions,
245	/// and signal whether the solution is optimal or not. When this type is used
246	/// as a constraint, then this field is ignore and can be set to `None`.
247	#[serde(rename = "@type", default, skip_serializing_if = "Option::is_none")]
248	pub ty: Option<InstantiationType>,
249	/// The objective cost of the instantiation
250	///
251	/// This field is used to represent the cost of a solution, and is only used
252	/// when the instantiation type is used to represent a solution. When this
253	/// type is used as a constraint, then this field is ignore and can be set to
254	/// `None`.
255	#[serde(rename = "@cost", default, skip_serializing_if = "Option::is_none")]
256	pub cost: Option<IntVal>,
257	#[serde(
258		deserialize_with = "VarRef::parse_vec",
259		serialize_with = "serialize_list"
260	)]
261	/// List of variables that are assigned values
262	pub list: Vec<Var>,
263	/// List of values assigned to the variables
264	///
265	/// A [`None`] entry represents the star `*`, marking a variable that is
266	/// allowed to take any value.
267	#[serde(
268		deserialize_with = "deserialize_opt_int_vals",
269		serialize_with = "serialize_opt_list"
270	)]
271	pub values: Vec<Option<IntVal>>,
272}
273
274/// The type of instantiation
275#[derive(Clone, Debug, PartialEq, Hash, Deserialize, Serialize)]
276#[serde(rename_all = "camelCase")]
277pub enum InstantiationType {
278	/// A solution that satisfies all constraints
279	Solution,
280	/// A solution that satisfies all constraints and is optimal with regards to
281	/// the objective function(s)
282	Optimum,
283}
284
285/// Trait used to construct variable references during deserialization
286pub trait IntoVar {
287	/// Constructs a variable reference from a string-based representation
288	fn into_var(var: VarRef) -> Self;
289}
290
291/// Type used to represent integer values
292pub type IntVal = i64;
293
294/// Type used to capture optional metadata that can be attached to most XCSP3
295/// elements
296#[derive(Clone, Debug, PartialEq, Hash, Deserialize, Serialize)]
297#[serde(bound(
298	deserialize = "Identifier: From<String>",
299	serialize = "Identifier: Display"
300))]
301pub struct MetaInfo<Identifier> {
302	/// Name assigned to the element
303	#[serde(
304		rename = "@id",
305		default,
306		skip_serializing_if = "Option::is_none",
307		deserialize_with = "deserialize_ident",
308		serialize_with = "serialize_ident"
309	)]
310	pub identifier: Option<Identifier>,
311	/// Comment from the user about the element
312	#[serde(rename = "@note", default, skip_serializing_if = "Option::is_none")]
313	pub note: Option<String>,
314}
315
316/// Objective function
317#[derive(Clone, Debug, PartialEq, Hash, Deserialize, Serialize)]
318#[serde(
319	rename_all = "camelCase",
320	bound(
321		deserialize = "Identifier: From<String>, Var: IntoVar",
322		serialize = "Identifier: Display, Var: Display"
323	)
324)]
325pub enum Objective<Identifier = String, Var = VarRef<Identifier>> {
326	/// An objective function where the goal is to find the smallest possible
327	/// value.
328	#[serde(rename = "minimize")]
329	Minimize(ObjExp<Identifier, Var>),
330	/// An objective function where the goal is to find the largest possible
331	/// value.
332	#[serde(rename = "maximize")]
333	Maximize(ObjExp<Identifier, Var>),
334}
335
336/// Collection of objective functions
337#[derive(Clone, Debug, PartialEq, Hash, Deserialize, Serialize)]
338#[serde(bound(
339	deserialize = "Identifier: From<String>, Var: IntoVar",
340	serialize = "Identifier: Display, Var: Display"
341))]
342pub struct Objectives<Identifier = String, Var = VarRef<Identifier>> {
343	/// Combinator to aggregate multiple objectives
344	#[serde(default, rename = "@combination")]
345	pub combination: CombinationType,
346	/// List of objectives functions
347	#[serde(rename = "$value")]
348	pub objectives: Vec<Objective<Identifier, Var>>,
349}
350
351/// Expression used to represent an objective function
352#[derive(Clone, Debug, PartialEq, Hash, Deserialize, Serialize)]
353#[serde(bound(
354	deserialize = "Identifier: From<String>, Var: IntoVar",
355	serialize = "Identifier: Display, Var: Display"
356))]
357pub struct ObjExp<Identifier = String, Var = VarRef<Identifier>> {
358	/// Optional metadata for the objective
359	#[serde(flatten)]
360	pub info: MetaInfo<Identifier>,
361	/// Evaluation method for the list of expressions
362	#[serde(alias = "@type", default)]
363	pub ty: ObjType,
364	/// List of expressions
365	#[serde(
366		alias = "$text",
367		deserialize_with = "IntExp::parse_vec",
368		serialize_with = "serialize_list"
369	)]
370	pub list: Vec<IntExp<Var>>,
371	/// List of coefficients to apply to the expressions
372	#[serde(
373		default,
374		skip_serializing_if = "Vec::is_empty",
375		deserialize_with = "deserialize_int_vals",
376		serialize_with = "serialize_list"
377	)]
378	pub coeffs: Vec<IntVal>,
379}
380
381/// Evaluation method for the list of expressions in an objective function
382#[derive(Clone, Debug, Default, PartialEq, Hash, Deserialize, Serialize)]
383#[serde(rename_all = "camelCase")]
384pub enum ObjType {
385	/// Sum of the expressions
386	#[default]
387	Sum,
388	/// Minimum value of the expressions
389	Minimum,
390	/// Maximum value of the expressions
391	Maximum,
392	/// Number of different values among the expressions
393	NValues,
394	/// Lexico order of the expressions
395	Lex,
396}
397
398/// Representation of a placeholder to be replaced in a meta-constraint.
399#[derive(Clone, Debug, Eq, PartialEq, Hash)]
400pub enum Placeholder {
401	/// Placeholder replaced by the argument at the given position.
402	Position(usize),
403	/// Placeholder replaced by all arguments larger than the largest given
404	/// position.
405	Remainder,
406}
407
408/// Reference to a variable or array element
409#[derive(Clone, Debug, PartialEq, Hash, Eq)]
410pub enum SimpleRef<Identifier> {
411	/// Reference to a variable
412	Ident(Identifier),
413	/// Reference to an array element
414	ArrayAccess(Identifier, Vec<usize>),
415}
416
417/// Definition of a variable
418#[derive(Clone, Debug, PartialEq, Hash, Deserialize, Serialize)]
419#[serde(bound(
420	deserialize = "Identifier: From<String>",
421	serialize = "Identifier: Display"
422))]
423pub struct Variable<Identifier = String> {
424	/// Name of the variable
425	#[serde(
426		rename = "@id",
427		deserialize_with = "from_string",
428		serialize_with = "as_str"
429	)]
430	pub identifier: Identifier,
431	/// Comment by the user about the variable
432	#[serde(rename = "@note", default, skip_serializing_if = "Option::is_none")]
433	pub note: Option<String>,
434	/// List of possible values the variable can take
435	#[serde(
436		rename = "$text",
437		deserialize_with = "deserialize_range_list",
438		serialize_with = "serialize_range_list"
439	)]
440	pub domain: RangeList<IntVal>,
441}
442
443/// Reference to a variable, array element, array slice, or placeholder in a
444/// group.
445#[derive(Clone, Debug, PartialEq, Hash, Eq)]
446pub enum VarRef<Identifier = String> {
447	/// Reference to a variable
448	Ident(Identifier),
449	/// Reference to an array element or slice
450	ArrayAccess(Identifier, Vec<Indexing>),
451	/// Placeholders to be replaced by other references
452	Placeholder(Placeholder),
453}
454
455/// Serialize the value by converting it to a string
456fn as_str<S: Serializer, I: Display>(value: &I, serializer: S) -> Result<S::Ok, S::Error> {
457	serializer.serialize_str(&value.to_string())
458}
459
460/// Combine a list of integer ranges into a single range list
461fn collect_range_list<I: IntoIterator<Item = RangeInclusive<IntVal>>>(
462	iter: I,
463) -> RangeList<IntVal> {
464	let mut r: Vec<_> = iter.into_iter().collect();
465	r.sort_by_key(|i| *i.start());
466	let mut it = r.into_iter();
467	let mut ranges = Vec::new();
468	let mut cur = it.next().unwrap();
469	for next in it {
470		if *cur.end() >= (next.start() - 1) {
471			cur = *cur.start()..=*next.end()
472		} else {
473			ranges.push(cur);
474			cur = next;
475		}
476	}
477	ranges.push(cur);
478	ranges.into_iter().collect()
479}
480
481/// Deserialize a string as an identifier
482fn deserialize_ident<'de, D: Deserializer<'de>, Identifier: From<String>>(
483	deserializer: D,
484) -> Result<Option<Identifier>, D::Error> {
485	/// Visitor to deserialize a string as an identifier
486	struct V<X>(PhantomData<X>);
487	impl<X: From<String>> Visitor<'_> for V<X> {
488		type Value = Option<X>;
489
490		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
491			formatter.write_str("an identfier")
492		}
493
494		fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
495			Ok(Some(s.trim().to_owned().into()))
496		}
497	}
498	let visitor = V::<Identifier>(PhantomData);
499	deserializer.deserialize_str(visitor)
500}
501
502/// Deserialize a string as a list of integers
503fn deserialize_int_vals<'de, D: Deserializer<'de>>(
504	deserializer: D,
505) -> Result<Vec<IntVal>, D::Error> {
506	/// Visitor to parse a list of integers
507	struct V;
508	impl Visitor<'_> for V {
509		type Value = Vec<IntVal>;
510
511		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
512			formatter.write_str("a list of integers")
513		}
514
515		fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
516			let v = v.trim();
517			let (_, vals) = all_consuming(whitespace_seperated(repeated(int)))
518				.parse(v)
519				.map_err(|_| E::custom(format!("invalid list of integers {v}")))?;
520			Ok(vals.into_iter().flatten().collect())
521		}
522	}
523	deserializer.deserialize_str(V)
524}
525
526/// Deserialize a string as a list of integers, where `*` denotes that any value
527/// is allowed
528fn deserialize_opt_int_vals<'de, D: Deserializer<'de>>(
529	deserializer: D,
530) -> Result<Vec<Option<IntVal>>, D::Error> {
531	/// Visitor to parse a list of integers
532	struct V;
533	impl Visitor<'_> for V {
534		type Value = Vec<Option<IntVal>>;
535
536		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
537			formatter.write_str("a list of integers")
538		}
539
540		fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
541			let v = v.trim();
542			let (_, vals) = all_consuming(whitespace_seperated(repeated(alt((
543				map(char('*'), |_| None),
544				map(int, Some),
545			)))))
546			.parse(v)
547			.map_err(|_| E::custom(format!("invalid list of integers {v}")))?;
548			Ok(vals.into_iter().flatten().collect())
549		}
550	}
551	deserializer.deserialize_str(V)
552}
553
554/// Parser combinator for a value that can be followed by `x<count>` to indicate
555/// that it occurs `count` times in a row
556fn repeated<'a, O: Clone>(
557	p: impl Parser<&'a str, Output = O, Error = nom::error::Error<&'a str>>,
558) -> impl Parser<&'a str, Output = Vec<O>> {
559	map(pair(p, opt(preceded(char('x'), idx_int))), |(v, n)| {
560		vec![v; n.unwrap_or(1)]
561	})
562}
563
564/// Deserialize a string as a range list
565fn deserialize_range_list<'de, D: Deserializer<'de>>(
566	deserializer: D,
567) -> Result<RangeList<IntVal>, D::Error> {
568	/// Visitor for deserializing a range list
569	struct V;
570	impl Visitor<'_> for V {
571		type Value = RangeList<IntVal>;
572
573		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
574			formatter.write_str("a list of ranges")
575		}
576
577		fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
578			let v = v.trim();
579			let (_, r) = all_consuming(whitespace_seperated(range))
580				.parse(v)
581				.map_err(|_| E::custom(format!("invalid list of ranges `{v}")))?;
582			Ok(collect_range_list(r))
583		}
584	}
585	let visitor = V;
586	deserializer.deserialize_str(visitor)
587}
588
589/// Deserialize a string as a size expression
590fn deserialize_size<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<usize>, D::Error> {
591	/// Visitor for deserializing a size expression
592	struct V;
593	impl Visitor<'_> for V {
594		type Value = Vec<usize>;
595
596		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
597			formatter.write_str("an array size expression")
598		}
599
600		fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
601			let v = v.trim();
602			let (_, r) = all_consuming(sequence(delimited(
603				char::<_, nom::error::Error<&str>>('['),
604				map_res(recognize(digit1), str::parse),
605				char(']'),
606			)))
607			.parse(v)
608			.map_err(|_| E::custom(format!("invalid array size expression `{v}'")))?;
609			Ok(r)
610		}
611	}
612	let visitor = V;
613	deserializer.deserialize_str(visitor)
614}
615
616/// Deserialize a string and call the `FromStr` implementation
617fn from_string<'de, D: Deserializer<'de>, I: From<String>>(deserializer: D) -> Result<I, D::Error> {
618	let s: Cow<'_, str> = Deserialize::deserialize(deserializer)?;
619	Ok(s.trim().to_owned().into())
620}
621
622/// Parser combinator that parses an integer from a string
623fn idx_int(input: &str) -> IResult<&str, usize> {
624	let (input, i): (_, usize) = map_res(recognize(digit1), str::parse).parse(input)?;
625	Ok((input, i))
626}
627
628/// Parser combinator that parses a range of integers from a string
629fn idx_range(input: &str) -> IResult<&str, RangeInclusive<usize>> {
630	let (input, lb) = idx_int(input)?;
631	if let (input, Some(_)) = opt(tag("..")).parse(input)? {
632		let (input, ub) = idx_int(input)?;
633		Ok((input, lb..=ub))
634	} else {
635		Ok((input, lb..=lb))
636	}
637}
638
639/// Serialize a list of values by printing them to strings and joining them with
640/// spaces
641fn serialize_list<S: Serializer, T: Display>(exps: &[T], serializer: S) -> Result<S::Ok, S::Error> {
642	serializer.serialize_str(
643		&exps
644			.iter()
645			.map(|e| format!("{}", e))
646			.collect::<Vec<_>>()
647			.join(" "),
648	)
649}
650
651/// Serialize a list of optional values as a string, writing [`None`] as `*`
652fn serialize_opt_list<S: Serializer, T: Display>(
653	exps: &[Option<T>],
654	serializer: S,
655) -> Result<S::Ok, S::Error> {
656	serializer.serialize_str(
657		&exps
658			.iter()
659			.map(|e| match e {
660				Some(e) => e.to_string(),
661				None => "*".to_owned(),
662			})
663			.collect::<Vec<_>>()
664			.join(" "),
665	)
666}
667
668/// Serialize an optional identifier as a string
669fn serialize_ident<S: Serializer, Identifier: Display>(
670	identifier: &Option<Identifier>,
671	serializer: S,
672) -> Result<S::Ok, S::Error> {
673	serializer.serialize_str(&format!("{}", identifier.as_ref().unwrap()))
674}
675
676/// Serialize a list of integers as a string of ranges separated by spaces
677fn serialize_range_list<S: Serializer>(
678	exps: &RangeList<IntVal>,
679	serializer: S,
680) -> Result<S::Ok, S::Error> {
681	serializer.serialize_str(
682		&exps
683			.into_iter()
684			.map(|e| {
685				if e.start() == e.end() {
686					e.start().to_string()
687				} else {
688					format!("{}..{}", e.start(), e.end())
689				}
690			})
691			.collect::<Vec<_>>()
692			.join(" "),
693	)
694}
695
696/// Serialize a list of dimensions as a string size expression
697fn serialize_size<S: Serializer>(exps: &[usize], serializer: S) -> Result<S::Ok, S::Error> {
698	serializer.serialize_str(
699		&exps
700			.iter()
701			.map(|e| format!("[{}]", e))
702			.collect::<Vec<_>>()
703			.join(""),
704	)
705}
706
707impl<Identifier: Clone + Hash + Eq + ToString> Array<Identifier, VarRef<Identifier>> {
708	/// Expand the domain definitions of the array domain defintiions into
709	/// [`SimpleRef`].
710	pub fn unroll(&self) -> Result<Array<Identifier, SimpleRef<Identifier>>, UnrollError> {
711		let size_wrap: HashMap<_, _> = Some((self.identifier.clone(), &self.size[..]))
712			.into_iter()
713			.collect();
714		let mut domains = Vec::with_capacity(self.domains.len());
715		for (v, d) in &self.domains {
716			let mut res: Vec<SimpleRef<_>> = Vec::new();
717			for x in v {
718				res.extend(
719					x.unroll(&size_wrap, &[], &[])?
720						.into_iter()
721						.map(|x| match x {
722							Exp::Var(v) => v,
723							_ => unreachable!(),
724						}),
725				);
726			}
727			domains.push((res, d.clone()));
728		}
729		Ok(Array {
730			identifier: self.identifier.clone(),
731			note: self.note.clone(),
732			size: self.size.clone(),
733			domains,
734		})
735	}
736}
737
738impl<'de, Identifier: From<String>, Var: IntoVar> Deserialize<'de> for Array<Identifier, Var> {
739	fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
740		/// Helper struct to deserialize the content of the <domain> element
741		#[derive(Deserialize)]
742		#[serde(bound = "Var: IntoVar")]
743		struct DomainStruct<Var> {
744			/// for attribute
745			#[serde(rename = "@for", deserialize_with = "VarRef::parse_vec")]
746			vars: Vec<Var>,
747			/// content of element
748			#[serde(rename = "$text", deserialize_with = "deserialize_range_list")]
749			domain: RangeList<IntVal>,
750		}
751		/// Helper enum to deserialize the content of the <array> element
752		#[derive(Deserialize)]
753		#[serde(bound = " Var: IntoVar")]
754		enum Domain<'a, Var> {
755			/// multiple <domain> elements
756			#[serde(rename = "domain")]
757			Domain(Vec<DomainStruct<Var>>),
758			/// single string content
759			#[serde(rename = "$text")]
760			Direct(Cow<'a, str>),
761		}
762		/// Helper struct to deserialize an <array> element
763		#[derive(Deserialize)]
764		#[serde(bound = "Identifier: From<String>, Var: IntoVar")]
765		struct Array<'a, Identifier, Var> {
766			/// id attribute
767			#[serde(rename = "@id", deserialize_with = "from_string")]
768			identifier: Identifier,
769			/// optional note attribute
770			#[serde(rename = "@note", default, skip_serializing_if = "Option::is_none")]
771			note: Option<String>,
772			/// size attribute
773			#[serde(rename = "@size", deserialize_with = "deserialize_size")]
774			size: Vec<usize>,
775			/// content of the element
776			#[serde(rename = "$value")]
777			domain: Domain<'a, Var>,
778		}
779		let x = Array::deserialize(deserializer)?;
780		let domains = match x.domain {
781			Domain::Domain(v) => v.into_iter().map(|d| (d.vars, d.domain)).collect(),
782			Domain::Direct(s) => {
783				let s = s.trim();
784				let s = all_consuming(whitespace_seperated(range))
785					.parse(s.as_ref())
786					.map_err(|_| {
787						serde::de::Error::custom(format!("unable to parse ranges from `{s}'"))
788					})?;
789				vec![(
790					vec![Var::into_var(VarRef::Ident("others".to_owned()))],
791					collect_range_list(s.1),
792				)]
793			}
794		};
795		Ok(Self {
796			identifier: x.identifier,
797			note: x.note,
798			size: x.size,
799			domains,
800		})
801	}
802}
803
804impl<Identifier: Display> Serialize for Array<Identifier> {
805	fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
806		/// Helper struct to serialize the domain expression
807		#[derive(Serialize)]
808		#[serde(bound = "Identifier: Display")]
809		struct DomainStruct<'a, Identifier: Display> {
810			/// Variable references serialized as the for attribute
811			#[serde(rename = "@for", serialize_with = "serialize_list")]
812			vars: &'a Vec<VarRef<Identifier>>,
813			/// RangeList serialized as the string content of the element
814			#[serde(rename = "$text", serialize_with = "serialize_range_list")]
815			domain: &'a RangeList<IntVal>,
816		}
817		/// Domain expression serialized as the <domain> elements
818		#[derive(Serialize)]
819		#[serde(bound = "Identifier: Display")]
820		enum Domain<'a, Identifier: Display> {
821			/// Domain expression serialized as the <domain> elements
822			#[serde(rename = "domain")]
823			Domain(DomainStruct<'a, Identifier>),
824		}
825		#[derive(Serialize)]
826		#[serde(bound = "Identifier: Display")]
827		struct Array<'a, Identifier: Display> {
828			/// Identifier serialized as the id attribute
829			#[serde(rename = "@id", serialize_with = "as_str")]
830			identifier: &'a Identifier,
831			/// String serialized as the note attribute
832			#[serde(rename = "@note", default, skip_serializing_if = "Option::is_none")]
833			note: &'a Option<String>,
834			/// Size expression serialized as the size attribute
835			#[serde(rename = "@size", serialize_with = "serialize_size")]
836			size: &'a Vec<usize>,
837			/// Domain expressions serialized as the element content
838			#[serde(rename = "$value")]
839			domain: Vec<Domain<'a, Identifier>>,
840		}
841		let domain = self
842			.domains
843			.iter()
844			.map(|(v, d)| Domain::Domain(DomainStruct { vars: v, domain: d }))
845			.collect();
846		let x = Array {
847			identifier: &self.identifier,
848			note: &self.note,
849			size: &self.size,
850			domain,
851		};
852		x.serialize(serializer)
853	}
854}
855
856impl<Identifier: Clone + Eq + Hash + ToString> Instance<Identifier, VarRef<Identifier>> {
857	/// Create a flat list of constraints, instantiating all
858	/// [`Group`](constraint::Group)s and [`Slide`](constraint::Slide)s,
859	/// extracting constraints from [`Block`](constraint::Block)s, and expanding
860	/// all slicing operations.
861	pub fn unroll_constraints(
862		&self,
863	) -> Result<Vec<Constraint<Identifier, SimpleRef<Identifier>>>, UnrollError> {
864		let arrays: HashMap<Identifier, &[usize]> = self
865			.arrays
866			.iter()
867			.map(|arr| (arr.identifier.clone(), &arr.size[..]))
868			.collect();
869
870		let mut flat = Vec::new();
871		let mut metas = VecDeque::new();
872		metas.push_back(&self.constraints);
873		while let Some(cons) = metas.pop_front() {
874			for con in cons {
875				match con {
876					MetaConstraint::Group(group) => flat.extend(group.unroll(&arrays)?),
877					MetaConstraint::Slide(slide) => flat.extend(slide.unroll(&arrays)?),
878					MetaConstraint::Block(block) => metas.push_back(&block.constraints),
879					MetaConstraint::Constraint(c) => flat.push(c.unroll(&arrays, &[], &[])?),
880				}
881			}
882		}
883		Ok(flat)
884	}
885}
886
887impl<Identifier> Default for Instance<Identifier> {
888	fn default() -> Self {
889		Self {
890			ty: Default::default(),
891			variables: Default::default(),
892			arrays: Default::default(),
893			constraints: Default::default(),
894			objectives: Default::default(),
895		}
896	}
897}
898
899impl<'de, Identifier: From<String>, Var: IntoVar> Deserialize<'de> for Instance<Identifier, Var> {
900	fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
901		/// Deserialized content of <variables> element
902		#[derive(Deserialize)]
903		#[serde(bound(deserialize = "Identifier: From<String>, Var: IntoVar"))]
904		enum V<Identifier, Var> {
905			/// Deserialized <var> element
906			#[serde(rename = "var")]
907			Variable(Variable<Identifier>),
908			/// Deserialized <array> element
909			#[serde(rename = "array")]
910			Array(Array<Identifier, Var>),
911		}
912		/// Deserialized <variables> element
913		#[derive(Deserialize)]
914		#[serde(bound(deserialize = "Identifier: From<String>, Var: IntoVar"))]
915		struct Variables<Identifier, Var> {
916			/// Deserialized content of <variables> element
917			#[serde(rename = "$value")]
918			vars: Vec<V<Identifier, Var>>,
919		}
920		/// Deserialized <constraints> element
921		#[derive(Deserialize)]
922		#[serde(bound(deserialize = "Identifier: From<String>, Var: IntoVar"))]
923		struct Constraints<Identifier, Var> {
924			/// Deserialized content of <constraints> element
925			#[serde(rename = "$value")]
926			content: Vec<MetaConstraint<Identifier, Var>>,
927		}
928		/// Deserialized <instance> element
929		#[derive(Deserialize)]
930		#[serde(bound(deserialize = "Identifier: From<String>, Var: IntoVar"))]
931		struct Instance<Identifier, Var> {
932			/// Deserialized type attribute
933			#[serde(rename = "@type")]
934			ty: FrameworkType,
935			/// Deserialized <variables> element
936			variables: Option<Variables<Identifier, Var>>,
937			/// Deserialized <constraints> element
938			constraints: Option<Constraints<Identifier, Var>>,
939			/// Deserialized <objectives> element
940			#[serde(default = "Objectives::default")]
941			objectives: Objectives<Identifier, Var>,
942		}
943		let inst: Instance<Identifier, Var> = Deserialize::deserialize(deserializer)?;
944		let mut variables = Vec::new();
945		let mut arrays = Vec::new();
946		for v in inst.variables.map(|v| v.vars).into_iter().flatten() {
947			match v {
948				V::Variable(var) => variables.push(var),
949				V::Array(arr) => arrays.push(arr),
950			}
951		}
952		Ok(Self {
953			ty: inst.ty,
954			variables,
955			arrays,
956			constraints: inst.constraints.map_or_else(Vec::new, |c| c.content),
957			objectives: inst.objectives,
958		})
959	}
960}
961
962impl<Identifier: Serialize + Display> Serialize for Instance<Identifier> {
963	fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
964		/// Helper struct to serialize the <variables> element
965		#[derive(Serialize)]
966		struct Variables<'a, Identifier: Display> {
967			/// Values serialized as <var> elements
968			var: &'a Vec<Variable<Identifier>>,
969			/// Values serialized as <array> elements
970			array: &'a Vec<Array<Identifier>>,
971		}
972		impl<Identifier: Display> Variables<'_, Identifier> {
973			/// Check whether there are any variables or arrays to serialize
974			fn is_empty(&self) -> bool {
975				self.var.is_empty() && self.array.is_empty()
976			}
977		}
978		/// Helper struct to serialize the <constraints> element
979		#[derive(Serialize)]
980		struct Constraints<'a, Identifier: Display> {
981			/// Constraints to be serialized
982			#[serde(rename = "$value")]
983			content: &'a Vec<MetaConstraint<Identifier>>,
984		}
985		impl<Identifier: Display> Constraints<'_, Identifier> {
986			/// Check whether there are any constraints to serialize
987			fn is_empty(&self) -> bool {
988				self.content.is_empty()
989			}
990		}
991		/// Helper struct to serialize the <instance> element
992		#[derive(Serialize)]
993		#[serde(rename = "instance")]
994		struct Instance<'a, Identifier: Display> {
995			/// Value serialized as the type attribute
996			#[serde(rename = "@type")]
997			ty: FrameworkType,
998			/// Value serialized as the <variables> element
999			#[serde(skip_serializing_if = "Variables::is_empty")]
1000			variables: Variables<'a, Identifier>,
1001			/// Value serialized as the <constraints> element
1002			#[serde(skip_serializing_if = "Constraints::is_empty")]
1003			constraints: Constraints<'a, Identifier>,
1004			/// Value serialized as the <objectives> element
1005			#[serde(skip_serializing_if = "Objectives::is_empty")]
1006			objectives: &'a Objectives<Identifier>,
1007		}
1008		let x = Instance {
1009			ty: self.ty,
1010			variables: Variables {
1011				var: &self.variables,
1012				array: &self.arrays,
1013			},
1014			constraints: Constraints {
1015				content: &self.constraints,
1016			},
1017			objectives: &self.objectives,
1018		};
1019		Serialize::serialize(&x, serializer)
1020	}
1021}
1022
1023// Note: flatten of MetaInfo does not seem to work here
1024// (https://github.com/tafia/quick-xml/issues/761)
1025impl<Identifier: Display, Var: Display> Serialize for Instantiation<Identifier, Var> {
1026	fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1027		/// Helper struct to serialize the instantiation element
1028		#[derive(Serialize)]
1029		#[serde(
1030			rename = "instantiation",
1031			bound(serialize = "Identifier: Display, Var: Display")
1032		)]
1033		struct Instantiation<'a, Identifier, Var> {
1034			/// Value serialized as the id attribute
1035			#[serde(
1036				rename = "@id",
1037				skip_serializing_if = "Option::is_none",
1038				serialize_with = "serialize_ident"
1039			)]
1040			identifier: &'a Option<Identifier>,
1041			/// Value serialized as the note attribute
1042			#[serde(rename = "@note", skip_serializing_if = "Option::is_none")]
1043			note: &'a Option<String>,
1044			/// Value serialized as the type attribute
1045			#[serde(rename = "@type", skip_serializing_if = "Option::is_none")]
1046			ty: &'a Option<InstantiationType>,
1047			/// Value serialized as the cost attribute
1048			#[serde(rename = "@cost", skip_serializing_if = "Option::is_none")]
1049			cost: &'a Option<IntVal>,
1050			/// Variable references serialized as <list>
1051			#[serde(serialize_with = "serialize_list")]
1052			list: &'a Vec<Var>,
1053			/// Values serialized as <values>
1054			#[serde(serialize_with = "serialize_opt_list")]
1055			values: &'a Vec<Option<IntVal>>,
1056		}
1057		Instantiation {
1058			identifier: &self.info.identifier,
1059			note: &self.info.note,
1060			ty: &self.ty,
1061			cost: &self.cost,
1062			list: &self.list,
1063			values: &self.values,
1064		}
1065		.serialize(serializer)
1066	}
1067}
1068
1069impl<Identifier> Objectives<Identifier> {
1070	/// Check whether there are no objectives.
1071	pub fn is_empty(&self) -> bool {
1072		self.objectives.is_empty()
1073	}
1074}
1075
1076impl<Identifier: Clone + Hash + Eq + ToString> ObjExp<Identifier, VarRef<Identifier>> {
1077	/// Expand the domain definitions of the array domain defintiions into
1078	/// [`SimpleRef`].
1079	pub fn unroll(
1080		&self,
1081		instance: &Instance<Identifier, VarRef<Identifier>>,
1082	) -> Result<ObjExp<Identifier, SimpleRef<Identifier>>, UnrollError> {
1083		let arrays: HashMap<Identifier, &[usize]> = instance
1084			.arrays
1085			.iter()
1086			.map(|arr| (arr.identifier.clone(), &arr.size[..]))
1087			.collect();
1088
1089		let list = self
1090			.list
1091			.iter()
1092			.map(|v| v.unroll(&arrays, &[], &[]))
1093			.collect::<Result<Vec<_>, _>>()?
1094			.into_iter()
1095			.flatten()
1096			.collect();
1097
1098		Ok(ObjExp {
1099			info: self.info.clone(),
1100			ty: self.ty.clone(),
1101			list,
1102			coeffs: self.coeffs.clone(),
1103		})
1104	}
1105}
1106
1107impl<Identifier, Var> Default for Objectives<Identifier, Var> {
1108	fn default() -> Self {
1109		Self {
1110			combination: CombinationType::default(),
1111			objectives: Vec::new(),
1112		}
1113	}
1114}
1115
1116impl Display for Placeholder {
1117	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1118		match self {
1119			Placeholder::Position(i) => write!(f, "%{}", i),
1120			Placeholder::Remainder => write!(f, "%..."),
1121		}
1122	}
1123}
1124
1125impl<Identifier: Clone + Hash + Eq + ToString> VarRef<Identifier> {
1126	/// Expand the reference into the list of expressions it denotes.
1127	///
1128	/// Placeholders are resolved using `args` (one entry per `<args>` token, so
1129	/// a single placeholder can stand for a whole list) and `remainder` (the
1130	/// flattened tokens matched by `%...`). Array slices are expanded using the
1131	/// dimensions given in `arrays`.
1132	pub(crate) fn unroll(
1133		&self,
1134		arrays: &HashMap<Identifier, &[usize]>,
1135		args: &[Vec<Exp<SimpleRef<Identifier>>>],
1136		remainder: &[Exp<SimpleRef<Identifier>>],
1137	) -> Result<Vec<Exp<SimpleRef<Identifier>>>, UnrollError> {
1138		match self {
1139			&VarRef::Placeholder(Placeholder::Position(i)) if i < args.len() => Ok(args[i].clone()),
1140			&VarRef::Placeholder(Placeholder::Position(i)) => Err(UnrollError::ArgMissing {
1141				placeholder: i,
1142				args_len: args.len(),
1143			}),
1144			VarRef::Placeholder(Placeholder::Remainder) => Ok(remainder.to_vec()),
1145			VarRef::Ident(ident) => Ok(vec![Exp::Var(SimpleRef::Ident(ident.clone()))]),
1146			VarRef::ArrayAccess(ident, indexings) => {
1147				let Some(size) = arrays.get(ident) else {
1148					return Err(UnrollError::UnknownIdentifier(ident.to_string()));
1149				};
1150				if indexings.len() != size.len() {
1151					return Err(UnrollError::UnexpectedIndexes {
1152						expected_len: size.len(),
1153						args_len: indexings.len(),
1154					});
1155				}
1156				Ok(indexings
1157					.iter()
1158					.enumerate()
1159					.map(|(i, idx)| match idx {
1160						&Indexing::Single(i) => i..=i,
1161						&Indexing::Range(start, end) => start..=end,
1162						Indexing::Full => 0..=(size[i] - 1),
1163					})
1164					.multi_cartesian_product()
1165					.map(|idxs| Exp::Var(SimpleRef::ArrayAccess(ident.clone(), idxs)))
1166					.collect())
1167			}
1168		}
1169	}
1170
1171	/// Expand an array slice (e.g. `x[][]`) into the rows of the matrix it
1172	/// denotes.
1173	///
1174	/// The width of a row is given by the last indexing operation, since
1175	/// [`Self::unroll`] varies the last index the fastest.
1176	pub(crate) fn unroll_matrix(
1177		&self,
1178		arrays: &HashMap<Identifier, &[usize]>,
1179		args: &[Vec<Exp<SimpleRef<Identifier>>>],
1180		remainder: &[Exp<SimpleRef<Identifier>>],
1181	) -> Result<Vec<Vec<Exp<SimpleRef<Identifier>>>>, UnrollError> {
1182		let VarRef::ArrayAccess(ident, indexings) = self else {
1183			return Err(UnrollError::UnexpectedIndexes {
1184				expected_len: 2,
1185				args_len: 0,
1186			});
1187		};
1188		let Some(size) = arrays.get(ident) else {
1189			return Err(UnrollError::UnknownIdentifier(ident.to_string()));
1190		};
1191		let flat = self.unroll(arrays, args, remainder)?;
1192		let row_len = match indexings.last() {
1193			Some(&Indexing::Single(_)) => 1,
1194			Some(&Indexing::Range(start, end)) => end - start + 1,
1195			// `unroll` has already checked that the number of indexing
1196			// operations matches the number of dimensions of the array.
1197			Some(Indexing::Full) => size[indexings.len() - 1],
1198			None => {
1199				return Err(UnrollError::UnexpectedIndexes {
1200					expected_len: 2,
1201					args_len: 0,
1202				})
1203			}
1204		};
1205		Ok(flat.chunks(row_len).map(<[_]>::to_vec).collect())
1206	}
1207
1208	/// Same as [`Self::unroll`], but requires that the reference denotes exactly
1209	/// one expression.
1210	pub(crate) fn unroll_single(
1211		&self,
1212		arrays: &HashMap<Identifier, &[usize]>,
1213		args: &[Vec<Exp<SimpleRef<Identifier>>>],
1214		remainder: &[Exp<SimpleRef<Identifier>>],
1215	) -> Result<Exp<SimpleRef<Identifier>>, UnrollError> {
1216		let res = self.unroll(arrays, args, remainder)?;
1217		match &res[..] {
1218			[exp] => Ok(exp.clone()),
1219			_ => Err(UnrollError::UnexpectedLength {
1220				expected_len: 1,
1221				args_len: res.len(),
1222			}),
1223		}
1224	}
1225}
1226
1227impl VarRef {
1228	/// Parse a list of variable references.
1229	fn parse_vec<'de, D: Deserializer<'de>, R: IntoVar>(
1230		deserializer: D,
1231	) -> Result<Vec<R>, D::Error> {
1232		/// Visitor for parsing a list of variable references.
1233		struct V<X>(PhantomData<X>);
1234		impl<X: From<String>> Visitor<'_> for V<X> {
1235			type Value = Vec<VarRef<X>>;
1236
1237			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1238				formatter.write_str("a list of variable references")
1239			}
1240
1241			fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
1242				let v = v.trim();
1243				let (_, v) = all_consuming(whitespace_seperated(VarRef::parse))
1244					.parse(v)
1245					.map_err(|_| E::custom(format!("invalid variable references `{v}'")))?;
1246				Ok(v)
1247			}
1248		}
1249		let visitor = V::<String>(PhantomData);
1250		Ok(deserializer
1251			.deserialize_str(visitor)?
1252			.into_iter()
1253			.map(R::into_var)
1254			.collect())
1255	}
1256}
1257
1258impl<Identifier: From<String>> VarRef<Identifier> {
1259	/// Parse a variable reference.
1260	pub(crate) fn parse(input: &str) -> IResult<&str, Self> {
1261		// First try to see whether the variable is a placeholder
1262		let placeholder: IResult<&str, Placeholder> = preceded(
1263			char('%'),
1264			alt((
1265				map(digit1, |p: &str| Placeholder::Position(p.parse().unwrap())),
1266				map(tag("..."), |_| Placeholder::Remainder),
1267			)),
1268		)
1269		.parse(input);
1270		if let Ok((input, placeholder)) = placeholder {
1271			return Ok((input, Self::Placeholder(placeholder)));
1272		}
1273		// Parse a normal identifier
1274		let (input, ident) = identifier(input)?;
1275		// Optionally add an array access tail
1276		let (input, v) = many0(delimited(char('['), opt(idx_range), char(']'))).parse(input)?;
1277		// Create VarRef object
1278		Ok((
1279			input,
1280			if v.is_empty() {
1281				VarRef::Ident(ident)
1282			} else {
1283				let v = v
1284					.into_iter()
1285					.map(|r| {
1286						r.map(|r| {
1287							if r.start() == r.end() {
1288								Indexing::Single(*r.start())
1289							} else {
1290								Indexing::Range(*r.start(), *r.end())
1291							}
1292						})
1293						.unwrap_or(Indexing::Full)
1294					})
1295					.collect();
1296				VarRef::ArrayAccess(ident, v)
1297			},
1298		))
1299	}
1300}
1301
1302impl<Identifier: Display> Display for VarRef<Identifier> {
1303	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1304		match self {
1305			VarRef::Ident(ident) => ident.fmt(f),
1306			VarRef::ArrayAccess(ident, v) => {
1307				write!(
1308					f,
1309					"{}{}",
1310					ident,
1311					v.iter()
1312						.map(|i| format!(
1313							"[{}]",
1314							match i {
1315								Indexing::Single(v) => v.to_string(),
1316								Indexing::Range(a, b) => format!("{}..{}", a, b),
1317								Indexing::Full => String::new(),
1318							}
1319						))
1320						.collect::<Vec<_>>()
1321						.join("")
1322				)
1323			}
1324			VarRef::Placeholder(placeholder) => placeholder.fmt(f),
1325		}
1326	}
1327}
1328
1329impl<I: From<String>> IntoVar for VarRef<I> {
1330	fn into_var(var: VarRef) -> Self {
1331		match var {
1332			VarRef::Ident(s) => VarRef::Ident(I::from(s)),
1333			VarRef::ArrayAccess(s, idxs) => VarRef::ArrayAccess(I::from(s), idxs),
1334			VarRef::Placeholder(p) => VarRef::Placeholder(p),
1335		}
1336	}
1337}
1338
1339#[cfg(test)]
1340mod tests {
1341	use std::{fmt::Debug, fs::File, io::BufReader, path::Path};
1342
1343	use expect_test::ExpectFile;
1344	use serde::{de::DeserializeOwned, Serialize};
1345
1346	use crate::{Instance, Instantiation};
1347
1348	fn test_successful_serialization<T: Debug + DeserializeOwned + Serialize + PartialEq>(
1349		file: &Path,
1350		exp: ExpectFile,
1351	) {
1352		let rdr = BufReader::new(File::open(file).unwrap());
1353		let inst: T = quick_xml::de::from_reader(rdr).unwrap();
1354		exp.assert_debug_eq(&inst);
1355		let output = quick_xml::se::to_string(&inst).unwrap();
1356		let inst2: T = quick_xml::de::from_str(&output).unwrap();
1357		assert_eq!(inst, inst2)
1358	}
1359
1360	/// Round-trip the instance, and additionally check the flat list of
1361	/// constraints produced by [`Instance::unroll_constraints`].
1362	fn test_successful_unroll(file: &Path, exp: ExpectFile, unrolled: ExpectFile) {
1363		test_successful_serialization::<Instance>(file, exp);
1364		let rdr = BufReader::new(File::open(file).unwrap());
1365		let inst: Instance = quick_xml::de::from_reader(rdr).unwrap();
1366		unrolled.assert_debug_eq(&inst.unroll_constraints().unwrap());
1367	}
1368
1369	macro_rules! test_file {
1370		($file:ident) => {
1371			test_file!($file, Instance);
1372		};
1373		($file:ident, $t:ident) => {
1374			#[test]
1375			fn $file() {
1376				test_successful_serialization::<$t>(
1377					std::path::Path::new(&format!("./corpus/{}.xml", stringify!($file))),
1378					expect_test::expect_file![&format!(
1379						"../corpus/{}.debug.txt",
1380						stringify!($file)
1381					)],
1382				)
1383			}
1384		};
1385	}
1386
1387	macro_rules! test_unroll {
1388		($file:ident) => {
1389			#[test]
1390			fn $file() {
1391				test_successful_unroll(
1392					std::path::Path::new(&format!("./corpus/{}.xml", stringify!($file))),
1393					expect_test::expect_file![&format!(
1394						"../corpus/{}.debug.txt",
1395						stringify!($file)
1396					)],
1397					expect_test::expect_file![&format!(
1398						"../corpus/{}.unroll.txt",
1399						stringify!($file)
1400					)],
1401				)
1402			}
1403		};
1404	}
1405
1406	test_file!(knapsack);
1407	// A `<args>` token can expand to a whole list, so `%0` must bind to all of
1408	// `x[0][]` and `%1` to the value that follows it.
1409	test_unroll!(group_list_arg);
1410
1411	test_file!(xcsp3_ex_001);
1412	test_file!(xcsp3_ex_002);
1413	test_file!(xcsp3_ex_003);
1414	test_file!(xcsp3_ex_004);
1415	test_file!(xcsp3_ex_005);
1416	test_file!(xcsp3_ex_006);
1417	test_file!(xcsp3_ex_007);
1418	// test_file!(xcsp3_ex_008);
1419	// test_file!(xcsp3_ex_009);
1420	// test_file!(xcsp3_ex_010);
1421	// test_file!(xcsp3_ex_011);
1422	// test_file!(xcsp3_ex_012);
1423	// test_file!(xcsp3_ex_013);
1424	// test_file!(xcsp3_ex_014);
1425	// test_file!(xcsp3_ex_015);
1426	// test_file!(xcsp3_ex_016);
1427	// test_file!(xcsp3_ex_017);
1428	test_file!(xcsp3_ex_018);
1429	test_file!(xcsp3_ex_019);
1430	// test_file!(xcsp3_ex_020);
1431	test_file!(xcsp3_ex_021);
1432	test_file!(xcsp3_ex_022);
1433	test_file!(xcsp3_ex_023, Instantiation);
1434	test_file!(xcsp3_ex_024);
1435	test_file!(xcsp3_ex_025, Instantiation);
1436	test_file!(xcsp3_ex_026, Instantiation);
1437	test_file!(xcsp3_ex_027, Instantiation);
1438	test_file!(xcsp3_ex_028, Instantiation);
1439	test_file!(xcsp3_ex_029);
1440	test_file!(xcsp3_ex_030);
1441	test_file!(xcsp3_ex_031);
1442	test_file!(xcsp3_ex_032);
1443	test_file!(xcsp3_ex_033);
1444	test_file!(xcsp3_ex_034);
1445	test_file!(xcsp3_ex_035);
1446	test_file!(xcsp3_ex_036);
1447	test_file!(xcsp3_ex_037);
1448	test_file!(xcsp3_ex_038);
1449	test_file!(xcsp3_ex_039);
1450	// test_file!(xcsp3_ex_040);
1451	test_file!(xcsp3_ex_041);
1452	// test_file!(xcsp3_ex_042);
1453	test_file!(xcsp3_ex_043);
1454	test_file!(xcsp3_ex_044);
1455	test_file!(xcsp3_ex_045);
1456	test_file!(xcsp3_ex_046);
1457	test_file!(xcsp3_ex_047);
1458	// test_file!(xcsp3_ex_048);
1459	test_file!(xcsp3_ex_049);
1460	// test_file!(xcsp3_ex_050);
1461	test_file!(xcsp3_ex_051);
1462	test_file!(xcsp3_ex_052);
1463	test_file!(xcsp3_ex_053);
1464	test_file!(xcsp3_ex_054);
1465	test_file!(xcsp3_ex_055);
1466	test_file!(xcsp3_ex_056);
1467	test_file!(xcsp3_ex_057);
1468	test_file!(xcsp3_ex_058);
1469	test_file!(xcsp3_ex_059);
1470	test_file!(xcsp3_ex_060);
1471	// test_file!(xcsp3_ex_061);
1472	// test_file!(xcsp3_ex_062);
1473	test_file!(xcsp3_ex_063);
1474	test_file!(xcsp3_ex_064);
1475	test_file!(xcsp3_ex_065);
1476	test_file!(xcsp3_ex_066);
1477	test_file!(xcsp3_ex_067);
1478	test_file!(xcsp3_ex_068);
1479	test_file!(xcsp3_ex_069);
1480	// test_file!(xcsp3_ex_070);
1481	// test_file!(xcsp3_ex_071);
1482	test_file!(xcsp3_ex_072);
1483	test_unroll!(xcsp3_ex_073);
1484	test_file!(xcsp3_ex_074);
1485	test_file!(xcsp3_ex_075);
1486	test_file!(xcsp3_ex_076);
1487	test_file!(xcsp3_ex_077);
1488	test_file!(xcsp3_ex_078);
1489	// test_file!(xcsp3_ex_079);
1490	// test_file!(xcsp3_ex_080);
1491	// test_file!(xcsp3_ex_081);
1492	// test_file!(xcsp3_ex_082);
1493	// test_file!(xcsp3_ex_083);
1494	test_unroll!(xcsp3_ex_084);
1495	test_file!(xcsp3_ex_085);
1496	test_file!(xcsp3_ex_086);
1497	// test_file!(xcsp3_ex_087);
1498	// test_file!(xcsp3_ex_088);
1499	test_file!(xcsp3_ex_089);
1500	// test_file!(xcsp3_ex_090);
1501	test_file!(xcsp3_ex_091);
1502	// test_file!(xcsp3_ex_092);
1503	// test_file!(xcsp3_ex_093);
1504	// test_file!(xcsp3_ex_094);
1505	// test_file!(xcsp3_ex_095);
1506	// test_file!(xcsp3_ex_096);
1507	test_file!(xcsp3_ex_097);
1508	// test_file!(xcsp3_ex_098);
1509	// test_file!(xcsp3_ex_099);
1510	test_file!(xcsp3_ex_100);
1511	test_file!(xcsp3_ex_101);
1512	// test_file!(xcsp3_ex_102);
1513	// test_file!(xcsp3_ex_103);
1514	// test_file!(xcsp3_ex_104);
1515	// test_file!(xcsp3_ex_105);
1516	// test_file!(xcsp3_ex_106);
1517	// test_file!(xcsp3_ex_107);
1518	// test_file!(xcsp3_ex_108);
1519	// test_file!(xcsp3_ex_109);
1520	// test_file!(xcsp3_ex_110);
1521	// test_file!(xcsp3_ex_111);
1522	// test_file!(xcsp3_ex_112);
1523	test_unroll!(xcsp3_ex_113);
1524	// test_file!(xcsp3_ex_114);
1525	test_unroll!(xcsp3_ex_115);
1526	test_unroll!(xcsp3_ex_116);
1527	test_unroll!(xcsp3_ex_117);
1528	test_unroll!(xcsp3_ex_118);
1529	// test_file!(xcsp3_ex_119);
1530	// test_file!(xcsp3_ex_120);
1531	// test_file!(xcsp3_ex_121);
1532	// test_file!(xcsp3_ex_122);
1533	// test_file!(xcsp3_ex_123);
1534	// test_file!(xcsp3_ex_124);
1535	// test_file!(xcsp3_ex_125);
1536	// test_file!(xcsp3_ex_126);
1537	test_unroll!(xcsp3_ex_127);
1538	test_unroll!(xcsp3_ex_128);
1539	// test_file!(xcsp3_ex_129);
1540	test_unroll!(xcsp3_ex_130);
1541	test_unroll!(xcsp3_ex_131);
1542	// test_file!(xcsp3_ex_132);
1543	// test_file!(xcsp3_ex_133);
1544	// test_file!(xcsp3_ex_134);
1545	// test_file!(xcsp3_ex_135);
1546	// test_file!(xcsp3_ex_136);
1547	// test_file!(xcsp3_ex_137);
1548	// test_file!(xcsp3_ex_138);
1549	// test_file!(xcsp3_ex_139);
1550	// test_file!(xcsp3_ex_140);
1551	// test_file!(xcsp3_ex_141);
1552	// test_file!(xcsp3_ex_142);
1553	// test_file!(xcsp3_ex_143);
1554	// test_file!(xcsp3_ex_144);
1555	// test_file!(xcsp3_ex_145);
1556	// test_file!(xcsp3_ex_146);
1557	// test_file!(xcsp3_ex_147);
1558	// test_file!(xcsp3_ex_148);
1559	// test_file!(xcsp3_ex_149);
1560	// test_file!(xcsp3_ex_150);
1561	// test_file!(xcsp3_ex_151);
1562	test_unroll!(xcsp3_ex_152);
1563	test_unroll!(xcsp3_ex_153);
1564	test_unroll!(xcsp3_ex_154);
1565	test_unroll!(xcsp3_ex_155);
1566	test_unroll!(xcsp3_ex_156);
1567	test_unroll!(xcsp3_ex_157);
1568	test_unroll!(xcsp3_ex_158);
1569	// test_file!(xcsp3_ex_159);
1570	// test_file!(xcsp3_ex_160);
1571	test_unroll!(xcsp3_ex_161);
1572	// test_file!(xcsp3_ex_162);
1573	// test_file!(xcsp3_ex_163);
1574	// test_file!(xcsp3_ex_164);
1575	// test_file!(xcsp3_ex_165);
1576	test_unroll!(xcsp3_ex_166);
1577	test_file!(xcsp3_ex_167);
1578	// test_file!(xcsp3_ex_168);
1579	// test_file!(xcsp3_ex_169);
1580	// test_file!(xcsp3_ex_170);
1581	// test_file!(xcsp3_ex_171);
1582	// test_file!(xcsp3_ex_172);
1583	// test_file!(xcsp3_ex_173);
1584	// test_file!(xcsp3_ex_174);
1585	// test_file!(xcsp3_ex_175);
1586	// test_file!(xcsp3_ex_176);
1587	// test_file!(xcsp3_ex_177);
1588	// test_file!(xcsp3_ex_178);
1589	// test_file!(xcsp3_ex_179);
1590	// test_file!(xcsp3_ex_180);
1591	// test_file!(xcsp3_ex_181);
1592	// test_file!(xcsp3_ex_182);
1593	// test_file!(xcsp3_ex_183);
1594	// test_file!(xcsp3_ex_184);
1595	// test_file!(xcsp3_ex_185);
1596	// test_file!(xcsp3_ex_186);
1597	// test_file!(xcsp3_ex_187);
1598	// test_file!(xcsp3_ex_188);
1599	// test_file!(xcsp3_ex_189);
1600	// test_file!(xcsp3_ex_190);
1601	// test_file!(xcsp3_ex_191);
1602}