1use std::collections::BTreeMap;
8use std::fmt::Write as _;
9
10use anyhow::{Context as _, anyhow, bail};
11use conjure_cp::Model;
12use conjure_cp::ast::{
13 AbstractLiteral, DeclarationPtr, DomainPtr, Field, GroundDomain, Literal, Moo, Name, Range,
14};
15use serde_json::{Map, Number, Value as JsonValue};
16
17use crate::utils::json::sort_json_object;
18
19pub fn solutions_to_simplified_json(
21 solutions: &[BTreeMap<Name, Literal>],
22) -> anyhow::Result<JsonValue> {
23 let mut sorted = solutions.to_vec();
24 sorted.sort_by(solution_key_cmp);
25 let mut items = Vec::with_capacity(sorted.len());
26 for solution in &sorted {
27 items.push(solution_to_simplified_json(solution)?);
28 }
29 Ok(JsonValue::Array(items))
30}
31
32pub fn solutions_to_simplified_json_string(
34 solutions: &[BTreeMap<Name, Literal>],
35) -> anyhow::Result<String> {
36 let value = solutions_to_simplified_json(solutions)?;
37 Ok(format!("{}\n", serde_json::to_string_pretty(&value)?))
38}
39
40pub fn solutions_from_simplified_json(
42 value: &JsonValue,
43 domains: &BTreeMap<Name, DomainPtr>,
44) -> anyhow::Result<Vec<BTreeMap<Name, Literal>>> {
45 match value {
46 JsonValue::Array(items) => items
47 .iter()
48 .map(|item| solution_from_simplified_json(item, domains))
49 .collect(),
50 JsonValue::Object(_) => Ok(vec![solution_from_simplified_json(value, domains)?]),
51 _ => bail!("expected a JSON array or object of solutions"),
52 }
53}
54
55pub fn solutions_from_simplified_json_str(
57 text: &str,
58 domains: &BTreeMap<Name, DomainPtr>,
59) -> anyhow::Result<Vec<BTreeMap<Name, Literal>>> {
60 let value: JsonValue = serde_json::from_str(text).context("parsing solutions JSON")?;
61 solutions_from_simplified_json(&value, domains)
62}
63
64pub fn solution_to_simplified_json(
66 solution: &BTreeMap<Name, Literal>,
67) -> anyhow::Result<JsonValue> {
68 let mut object = Map::new();
69 for (name, value) in solution {
70 object.insert(name.to_string(), literal_to_simplified_json(value)?);
71 }
72 Ok(sort_json_object(&JsonValue::Object(object), false))
73}
74
75pub fn solution_from_simplified_json(
77 value: &JsonValue,
78 domains: &BTreeMap<Name, DomainPtr>,
79) -> anyhow::Result<BTreeMap<Name, Literal>> {
80 let object = value
81 .as_object()
82 .ok_or_else(|| anyhow!("each solution must be a JSON object"))?;
83 let mut solution = BTreeMap::new();
84 for (name, json_value) in object {
85 let name = Name::user(name.as_str());
86 let domain = domains.get(&name);
87 let literal = literal_from_simplified_json(json_value, domain)
88 .with_context(|| format!("parsing value for `{name}`"))?;
89 solution.insert(name, literal);
90 }
91 Ok(solution)
92}
93
94pub fn params_to_simplified_json(params: &BTreeMap<Name, Literal>) -> anyhow::Result<JsonValue> {
96 solution_to_simplified_json(params)
97}
98
99pub fn params_from_simplified_json(
101 value: &JsonValue,
102 given_domains: &BTreeMap<Name, DomainPtr>,
103) -> anyhow::Result<BTreeMap<Name, Literal>> {
104 solution_from_simplified_json(value, given_domains)
105}
106
107pub fn params_from_simplified_json_str(
109 text: &str,
110 given_domains: &BTreeMap<Name, DomainPtr>,
111) -> anyhow::Result<BTreeMap<Name, Literal>> {
112 let value: JsonValue = serde_json::from_str(text).context("parsing parameter JSON")?;
113 params_from_simplified_json(&value, given_domains)
114}
115
116pub fn literal_to_simplified_json(literal: &Literal) -> anyhow::Result<JsonValue> {
118 match literal {
119 Literal::Unnamed(_) => bail!("simplified JSON for unnamed types is not supported yet"),
120 Literal::EnumVariant(_) => {
121 bail!("simplified JSON for enumerated types is not supported yet")
122 }
123 Literal::Bool(b) => Ok(JsonValue::Bool(*b)),
124 Literal::Int(i) => Ok(JsonValue::Number(Number::from(*i))),
125 Literal::AbstractLiteral(abs) => abstract_literal_to_simplified_json(abs),
126 }
127}
128
129fn abstract_literal_to_simplified_json(
130 abs: &AbstractLiteral<Literal>,
131) -> anyhow::Result<JsonValue> {
132 match abs {
133 AbstractLiteral::Set(elems) | AbstractLiteral::MSet(elems) => elems_as_json_array(elems),
134 AbstractLiteral::Tuple(elems) | AbstractLiteral::Sequence(elems) => {
135 elems_as_json_array(elems)
136 }
137 AbstractLiteral::Record(fields) => {
138 let mut object = Map::new();
139 for field in fields {
140 object.insert(
141 field.name.to_string(),
142 literal_to_simplified_json(&field.value)?,
143 );
144 }
145 Ok(JsonValue::Object(object))
146 }
147 AbstractLiteral::Variant(field) => {
148 let mut object = Map::new();
149 object.insert(
150 field.name.to_string(),
151 literal_to_simplified_json(&field.value)?,
152 );
153 Ok(JsonValue::Object(object))
154 }
155 AbstractLiteral::Matrix(elems, index_domain) => {
156 matrix_to_simplified_json(elems, index_domain.as_ref())
157 }
158 AbstractLiteral::Function(pairs) => function_to_simplified_json(pairs),
159 AbstractLiteral::Relation(rows) => {
160 let mut items = Vec::with_capacity(rows.len());
161 for row in rows {
162 items.push(elems_as_json_array(row)?);
163 }
164 Ok(JsonValue::Array(items))
165 }
166 AbstractLiteral::Partition(parts) => {
167 let mut items = Vec::with_capacity(parts.len());
168 for part in parts {
169 items.push(elems_as_json_array(part)?);
170 }
171 Ok(JsonValue::Array(items))
172 }
173 AbstractLiteral::Permutation(cycles) => {
174 let mut items = Vec::with_capacity(cycles.len());
175 for cycle in cycles {
176 items.push(elems_as_json_array(cycle)?);
177 }
178 Ok(JsonValue::Array(items))
179 }
180 }
181}
182
183fn matrix_to_simplified_json(
184 elems: &[Literal],
185 index_domain: &GroundDomain,
186) -> anyhow::Result<JsonValue> {
187 let GroundDomain::Int(_, _) = index_domain else {
188 return elems_as_json_array(elems);
189 };
190
191 let indices: Vec<Literal> = match index_domain.values() {
192 Ok(iter) => iter.collect(),
193 Err(_) => return elems_as_json_array(elems),
194 };
195
196 if indices.len() != elems.len() {
197 return elems_as_json_array(elems);
198 }
199
200 dictionary_from_pairs(indices.into_iter().zip(elems.iter().cloned()))
201}
202
203fn function_to_simplified_json(pairs: &[(Literal, Literal)]) -> anyhow::Result<JsonValue> {
204 dictionary_from_pairs(pairs.iter().cloned())
205}
206
207fn dictionary_from_pairs(
208 pairs: impl IntoIterator<Item = (Literal, Literal)>,
209) -> anyhow::Result<JsonValue> {
210 let mut object_entries = Vec::new();
211 let mut array_entries = Vec::new();
212 let mut all_keys_ok = true;
213
214 for (key, value) in pairs {
215 let key_json = literal_to_simplified_json(&key)?;
216 let value_json = literal_to_simplified_json(&value)?;
217 array_entries.push(JsonValue::Array(vec![key_json.clone(), value_json.clone()]));
218 match &key_json {
219 JsonValue::Bool(_) | JsonValue::Number(_) | JsonValue::String(_) => {
220 object_entries.push((json_key_string(&key_json)?, value_json));
221 }
222 _ => {
223 all_keys_ok = false;
224 }
225 }
226 }
227
228 if all_keys_ok {
229 Ok(JsonValue::Object(object_entries.into_iter().collect()))
230 } else {
231 Ok(JsonValue::Array(array_entries))
232 }
233}
234
235fn elems_as_json_array(elems: &[Literal]) -> anyhow::Result<JsonValue> {
236 let mut items = Vec::with_capacity(elems.len());
237 for elem in elems {
238 items.push(literal_to_simplified_json(elem)?);
239 }
240 Ok(JsonValue::Array(items))
241}
242
243fn json_key_string(value: &JsonValue) -> anyhow::Result<String> {
244 match value {
245 JsonValue::Bool(b) => Ok(b.to_string()),
246 JsonValue::Number(n) => Ok(n.to_string()),
247 JsonValue::String(s) => Ok(s.clone()),
248 _ => bail!("JSON object keys must be bool, number, or string"),
249 }
250}
251
252pub fn literal_from_simplified_json(
254 value: &JsonValue,
255 domain: Option<&DomainPtr>,
256) -> anyhow::Result<Literal> {
257 if let Some(domain) = domain {
258 if let Some(ground) = domain.as_ground() {
259 return literal_from_simplified_json_with_ground(value, ground);
260 }
261 if let Ok(ground) = domain.resolve() {
264 return literal_from_simplified_json_with_ground(value, ground.as_ref());
265 }
266 }
267 literal_from_simplified_json_unguided(value)
268}
269
270fn literal_from_simplified_json_with_ground(
271 value: &JsonValue,
272 domain: &GroundDomain,
273) -> anyhow::Result<Literal> {
274 match domain {
275 GroundDomain::EnumeratedType(_, _) | GroundDomain::UnnamedType(_) => {
276 bail!("simplified JSON for enumerated and unnamed types is not supported yet")
277 }
278 GroundDomain::Empty(_) => bail!("cannot parse a value for an empty domain"),
279 GroundDomain::Bool => match value {
280 JsonValue::Bool(b) => Ok(Literal::Bool(*b)),
281 JsonValue::Number(n) => match n.as_i64() {
282 Some(1) => Ok(Literal::Bool(true)),
283 Some(0) => Ok(Literal::Bool(false)),
284 _ => bail!("expected a boolean"),
285 },
286 _ => bail!("expected a boolean"),
287 },
288 GroundDomain::Int(_, _) => Ok(Literal::Int(json_to_i32(value)?)),
289 GroundDomain::Tuple(inners, _) => {
290 let JsonValue::Array(items) = value else {
291 bail!("expected a JSON array for a tuple");
292 };
293 if items.len() != inners.len() {
294 bail!(
295 "tuple arity mismatch: expected {}, got {}",
296 inners.len(),
297 items.len()
298 );
299 }
300 let mut elems = Vec::with_capacity(items.len());
301 for (item, inner) in items.iter().zip(inners) {
302 elems.push(literal_from_simplified_json_with_ground(item, inner)?);
303 }
304 Ok(Literal::AbstractLiteral(AbstractLiteral::Tuple(elems)))
305 }
306 GroundDomain::Record(fields, _) => {
307 let JsonValue::Object(object) = value else {
308 bail!("expected a JSON object for a record");
309 };
310 let mut entries = Vec::with_capacity(fields.len());
311 for field in fields {
312 let key = field.name.to_string();
313 let Some(item) = object.get(&key) else {
314 bail!("missing record field `{key}`");
315 };
316 entries.push(Field {
317 name: field.name.clone(),
318 value: literal_from_simplified_json_with_ground(item, field.value.as_ref())?,
319 });
320 }
321 Ok(Literal::AbstractLiteral(AbstractLiteral::Record(entries)))
322 }
323 GroundDomain::Variant(fields, _) => {
324 let JsonValue::Object(object) = value else {
325 bail!("expected a JSON object for a variant");
326 };
327 if object.len() != 1 {
328 bail!(
329 "variant value must contain exactly one alternative, got {}",
330 object.len()
331 );
332 }
333 let (key, item) = object.iter().next().expect("object has one entry");
334 let field = fields
335 .iter()
336 .find(|field| field.name.to_string() == *key)
337 .ok_or_else(|| anyhow!("unknown variant alternative `{key}`"))?;
338 Ok(Literal::AbstractLiteral(AbstractLiteral::Variant(
339 Moo::new(Field {
340 name: field.name.clone(),
341 value: literal_from_simplified_json_with_ground(item, field.value.as_ref())?,
342 }),
343 )))
344 }
345 GroundDomain::Matrix(inner, index_domains, _) => {
346 matrix_from_simplified_json(value, inner.as_ref(), index_domains)
347 }
348 GroundDomain::Sequence(_, inner) => match value {
349 JsonValue::Array(items) => {
350 let mut elems = Vec::with_capacity(items.len());
351 for item in items {
352 elems.push(literal_from_simplified_json_with_ground(item, inner)?);
353 }
354 Ok(Literal::AbstractLiteral(AbstractLiteral::Sequence(elems)))
355 }
356 JsonValue::Object(object) => sequence_from_object(object, inner),
357 _ => bail!("expected a JSON array for a sequence"),
358 },
359 GroundDomain::Set(_, inner) => {
360 let JsonValue::Array(items) = value else {
361 bail!("expected a JSON array for a set");
362 };
363 let mut elems = Vec::with_capacity(items.len());
364 for item in items {
365 elems.push(literal_from_simplified_json_with_ground(item, inner)?);
366 }
367 Ok(Literal::AbstractLiteral(AbstractLiteral::Set(elems)))
368 }
369 GroundDomain::MSet(_, inner) => {
370 let JsonValue::Array(items) = value else {
371 bail!("expected a JSON array for a multiset");
372 };
373 let mut elems = Vec::with_capacity(items.len());
374 for item in items {
375 elems.push(literal_from_simplified_json_with_ground(item, inner)?);
376 }
377 Ok(Literal::AbstractLiteral(AbstractLiteral::MSet(elems)))
378 }
379 GroundDomain::Function(_, from, to) => {
380 function_from_simplified_json(value, from.as_ref(), to.as_ref())
381 }
382 GroundDomain::Relation(_, inner_doms) => relation_from_simplified_json(value, inner_doms),
383 GroundDomain::Partition(_, inner) => partition_from_simplified_json(value, inner),
384 GroundDomain::Permutation(_, inner) => permutation_from_simplified_json(value, inner),
385 }
386}
387
388fn partition_from_simplified_json(
389 value: &JsonValue,
390 inner: &GroundDomain,
391) -> anyhow::Result<Literal> {
392 let JsonValue::Array(items) = value else {
393 bail!("expected a JSON array for a partition");
394 };
395 let mut parts = Vec::with_capacity(items.len());
396 for item in items {
397 let JsonValue::Array(elems) = item else {
398 bail!("expected a JSON array for a partition part");
399 };
400 let mut part = Vec::with_capacity(elems.len());
401 for elem in elems {
402 part.push(literal_from_simplified_json_with_ground(elem, inner)?);
403 }
404 parts.push(part);
405 }
406 Ok(Literal::AbstractLiteral(AbstractLiteral::Partition(parts)))
407}
408
409fn permutation_from_simplified_json(
410 value: &JsonValue,
411 inner: &GroundDomain,
412) -> anyhow::Result<Literal> {
413 let JsonValue::Array(items) = value else {
414 bail!("expected a JSON array for a permutation");
415 };
416 let mut cycles = Vec::with_capacity(items.len());
417 for item in items {
418 let JsonValue::Array(elems) = item else {
419 bail!("expected a JSON array for a permutation cycle");
420 };
421 let mut cycle = Vec::with_capacity(elems.len());
422 for elem in elems {
423 cycle.push(literal_from_simplified_json_with_ground(elem, inner)?);
424 }
425 cycles.push(cycle);
426 }
427 Ok(Literal::AbstractLiteral(AbstractLiteral::Permutation(
428 cycles,
429 )))
430}
431
432fn relation_from_simplified_json(
433 value: &JsonValue,
434 inner_doms: &[Moo<GroundDomain>],
435) -> anyhow::Result<Literal> {
436 let JsonValue::Array(items) = value else {
437 bail!("expected a JSON array for a relation");
438 };
439 let mut tuples = Vec::with_capacity(items.len());
440 for item in items {
441 let JsonValue::Array(fields) = item else {
442 bail!("expected a JSON array for a relation tuple");
443 };
444 if fields.len() != inner_doms.len() {
445 bail!(
446 "relation tuple arity mismatch: expected {}, got {}",
447 inner_doms.len(),
448 fields.len()
449 );
450 }
451 let mut tuple = Vec::with_capacity(fields.len());
452 for (field, dom) in fields.iter().zip(inner_doms) {
453 tuple.push(literal_from_simplified_json_with_ground(field, dom)?);
454 }
455 tuples.push(tuple);
456 }
457 Ok(Literal::AbstractLiteral(AbstractLiteral::Relation(tuples)))
458}
459
460fn matrix_from_simplified_json(
461 value: &JsonValue,
462 inner: &GroundDomain,
463 index_domains: &[Moo<GroundDomain>],
464) -> anyhow::Result<Literal> {
465 let (first_index, rest) = index_domains
466 .split_first()
467 .ok_or_else(|| anyhow!("matrix domain has no index domains"))?;
468
469 let elem_domain: GroundDomain = if rest.is_empty() {
470 inner.clone()
471 } else {
472 GroundDomain::Matrix(Moo::new(inner.clone()), rest.to_vec(), None)
473 };
474
475 match value {
476 JsonValue::Object(object) => {
477 let mut pairs = Vec::with_capacity(object.len());
478 for (key, item) in object {
479 let index = index_key_to_literal(key, first_index)?;
480 let elem = literal_from_simplified_json_with_ground(item, &elem_domain)?;
481 pairs.push((index, elem));
482 }
483 pairs.sort_by(|a, b| a.0.essence_cmp(&b.0));
484 let keys: Vec<i32> = object
485 .keys()
486 .map(|k| k.parse::<i32>())
487 .collect::<Result<Vec<_>, _>>()
488 .unwrap_or_default();
489 let elems: Vec<Literal> = pairs.into_iter().map(|(_, v)| v).collect();
490 let index_domain = if let Ok(expected) = first_index.values() {
491 let expected: Vec<_> = expected.collect();
492 if expected.len() == elems.len() {
493 first_index.as_ref().clone()
494 } else {
495 infer_int_index_domain(&keys)
496 }
497 } else {
498 infer_int_index_domain(&keys)
499 };
500 Ok(Literal::AbstractLiteral(AbstractLiteral::Matrix(
501 elems,
502 index_domain.into(),
503 )))
504 }
505 JsonValue::Array(items) => {
506 let mut elems = Vec::with_capacity(items.len());
507 for item in items {
508 elems.push(literal_from_simplified_json_with_ground(
509 item,
510 &elem_domain,
511 )?);
512 }
513 let n = i32::try_from(elems.len()).context("matrix too large")?;
514 let index_domain = GroundDomain::Int(vec![Range::Bounded(1, n)], None);
515 Ok(Literal::AbstractLiteral(AbstractLiteral::Matrix(
516 elems,
517 index_domain.into(),
518 )))
519 }
520 _ => bail!("expected a JSON object or array for a matrix"),
521 }
522}
523
524fn infer_int_index_domain(keys: &[i32]) -> GroundDomain {
525 if keys.is_empty() {
526 return GroundDomain::Int(vec![], None);
527 }
528 let mut ints = keys.to_vec();
529 ints.sort_unstable();
530 let min = ints[0];
531 let max = *ints.last().expect("non-empty");
532 if max - min + 1 == ints.len() as i32 {
533 GroundDomain::Int(vec![Range::Bounded(min, max)], None)
534 } else {
535 GroundDomain::Int(ints.into_iter().map(Range::Single).collect(), None)
536 }
537}
538
539fn index_key_to_literal(key: &str, index_domain: &GroundDomain) -> anyhow::Result<Literal> {
540 match index_domain {
541 GroundDomain::Bool => match key {
542 "false" => Ok(Literal::Bool(false)),
543 "true" => Ok(Literal::Bool(true)),
544 _ => bail!("expected boolean matrix index key"),
545 },
546 _ => Ok(Literal::Int(key.parse::<i32>().with_context(|| {
547 format!("expected integer matrix index key, got `{key}`")
548 })?)),
549 }
550}
551
552fn sequence_from_object(
553 object: &Map<String, JsonValue>,
554 inner: &GroundDomain,
555) -> anyhow::Result<Literal> {
556 let mut pairs = Vec::with_capacity(object.len());
557 for (key, item) in object {
558 let index: i32 = key
559 .parse()
560 .with_context(|| format!("sequence index `{key}` is not an integer"))?;
561 pairs.push((
562 index,
563 literal_from_simplified_json_with_ground(item, inner)?,
564 ));
565 }
566 pairs.sort_by_key(|(i, _)| *i);
567 Ok(Literal::AbstractLiteral(AbstractLiteral::Sequence(
568 pairs.into_iter().map(|(_, v)| v).collect(),
569 )))
570}
571
572fn function_from_simplified_json(
573 value: &JsonValue,
574 from: &GroundDomain,
575 to: &GroundDomain,
576) -> anyhow::Result<Literal> {
577 match value {
578 JsonValue::Object(object) => {
579 let mut pairs = Vec::with_capacity(object.len());
580 for (key, item) in object {
581 let domain_key = index_key_to_literal(key, from)?;
582 let mapped = literal_from_simplified_json_with_ground(item, to)?;
583 pairs.push((domain_key, mapped));
584 }
585 Ok(Literal::AbstractLiteral(AbstractLiteral::Function(pairs)))
586 }
587 JsonValue::Array(items) => {
588 let mut pairs = Vec::with_capacity(items.len());
589 for item in items {
590 let JsonValue::Array(pair) = item else {
591 bail!("function array entries must be [from, to] pairs");
592 };
593 if pair.len() != 2 {
594 bail!("function array entries must be [from, to] pairs");
595 }
596 pairs.push((
597 literal_from_simplified_json_with_ground(&pair[0], from)?,
598 literal_from_simplified_json_with_ground(&pair[1], to)?,
599 ));
600 }
601 Ok(Literal::AbstractLiteral(AbstractLiteral::Function(pairs)))
602 }
603 _ => bail!("expected a JSON object or array for a function"),
604 }
605}
606
607fn literal_from_simplified_json_unguided(value: &JsonValue) -> anyhow::Result<Literal> {
608 match value {
609 JsonValue::Bool(b) => Ok(Literal::Bool(*b)),
610 JsonValue::Number(_) | JsonValue::String(_) => Ok(Literal::Int(json_to_i32(value)?)),
611 JsonValue::Array(items) => {
612 let mut elems = Vec::with_capacity(items.len());
613 for item in items {
614 elems.push(literal_from_simplified_json_unguided(item)?);
615 }
616 Ok(Literal::AbstractLiteral(AbstractLiteral::Set(elems)))
617 }
618 JsonValue::Object(object) => {
619 let all_int_keys = object.keys().all(|key| key.parse::<i32>().is_ok());
620 if all_int_keys {
621 let mut pairs = Vec::with_capacity(object.len());
622 for (key, item) in object {
623 let index: i32 = key.parse().with_context(|| {
624 format!("unguided object key `{key}` is not an integer")
625 })?;
626 pairs.push((index, literal_from_simplified_json_unguided(item)?));
627 }
628 pairs.sort_by_key(|(i, _)| *i);
629 let keys: Vec<i32> = pairs.iter().map(|(i, _)| *i).collect();
630 let elems: Vec<_> = pairs.into_iter().map(|(_, v)| v).collect();
631 return Ok(Literal::AbstractLiteral(AbstractLiteral::Matrix(
632 elems,
633 infer_int_index_domain(&keys).into(),
634 )));
635 }
636
637 let mut entries = Vec::with_capacity(object.len());
639 for (key, item) in object {
640 entries.push(Field {
641 name: Name::user(key.as_str()),
642 value: literal_from_simplified_json_unguided(item)?,
643 });
644 }
645 entries.sort_by(|a, b| a.name.cmp(&b.name));
646 Ok(Literal::AbstractLiteral(AbstractLiteral::Record(entries)))
647 }
648 JsonValue::Null => bail!("null is not a valid Essence literal"),
649 }
650}
651
652fn json_to_i32(value: &JsonValue) -> anyhow::Result<i32> {
653 match value {
654 JsonValue::Number(n) => n
655 .as_i64()
656 .and_then(|i| i32::try_from(i).ok())
657 .ok_or_else(|| anyhow!("expected a 32-bit integer")),
658 JsonValue::String(s) => s
659 .parse::<i32>()
660 .with_context(|| format!("expected an integer string, got `{s}`")),
661 _ => bail!("expected an integer"),
662 }
663}
664
665fn solution_key_cmp(
666 lhs: &BTreeMap<Name, Literal>,
667 rhs: &BTreeMap<Name, Literal>,
668) -> std::cmp::Ordering {
669 lhs.iter()
670 .zip(rhs)
671 .find_map(|((lhs_name, lhs_value), (rhs_name, rhs_value))| {
672 let ordering = lhs_name.cmp(rhs_name);
673 (ordering != std::cmp::Ordering::Equal)
674 .then_some(ordering)
675 .or_else(|| {
676 let ordering = lhs_value.essence_cmp(rhs_value);
677 (ordering != std::cmp::Ordering::Equal).then_some(ordering)
678 })
679 })
680 .unwrap_or_else(|| lhs.len().cmp(&rhs.len()))
681}
682
683pub fn domains_from_model(model: &Model) -> BTreeMap<Name, DomainPtr> {
685 let mut domains = BTreeMap::new();
686 for (name, decl) in model.symbols().clone().into_iter() {
687 if let Some(domain) = decl.domain() {
688 domains.insert(name, domain);
689 }
690 }
691 domains
692}
693
694pub fn param_model_from_assignments(
696 params: BTreeMap<Name, Literal>,
697 given_domains: &BTreeMap<Name, DomainPtr>,
698 context: std::sync::Arc<std::sync::RwLock<conjure_cp::context::Context<'static>>>,
699) -> Model {
700 let mut model = Model::new(context);
701 for (name, literal) in params {
702 let decl = match given_domains.get(&name) {
703 Some(domain) => {
704 DeclarationPtr::new_value_letting_with_domain(name, literal.into(), domain.clone())
705 }
706 None => DeclarationPtr::new_value_letting(name, literal.into()),
707 };
708 model.symbols_mut().update_insert(decl);
709 }
710 model
711}
712
713pub fn canonical_simplified_json_string(value: &JsonValue) -> anyhow::Result<String> {
715 let sorted = sort_json_object(value, true);
716 Ok(format!("{}\n", serde_json::to_string_pretty(&sorted)?))
717}
718
719pub fn write_simplified_json(value: &JsonValue, out: &mut String) -> anyhow::Result<()> {
721 write!(out, "{}", serde_json::to_string_pretty(value)?)?;
722 out.push('\n');
723 Ok(())
724}
725
726#[cfg(test)]
727mod tests {
728 use super::*;
729 use conjure_cp::ast::{Domain, SetAttr};
730
731 fn int_dom(lo: i32, hi: i32) -> DomainPtr {
732 Domain::int(vec![Range::Bounded(lo, hi)])
733 }
734
735 #[test]
736 fn round_trips_scalars_and_set() {
737 let mut domains = BTreeMap::new();
738 domains.insert(Name::user("x"), int_dom(1, 3));
739 domains.insert(
740 Name::user("s"),
741 Domain::set(SetAttr::<i32>::default(), int_dom(1, 3)),
742 );
743
744 let json = serde_json::json!([{"s": [1, 3], "x": 2}]);
745 let solutions = solutions_from_simplified_json(&json, &domains).unwrap();
746 assert_eq!(solutions.len(), 1);
747 assert_eq!(solutions[0].get(&Name::user("x")), Some(&Literal::Int(2)));
748 let Literal::AbstractLiteral(AbstractLiteral::Set(elems)) =
749 solutions[0].get(&Name::user("s")).unwrap()
750 else {
751 panic!("expected set");
752 };
753 assert_eq!(elems, &vec![Literal::Int(1), Literal::Int(3)]);
754
755 let rendered = solutions_to_simplified_json(&solutions).unwrap();
756 let again = solutions_from_simplified_json(&rendered, &domains).unwrap();
757 assert_eq!(again, solutions);
758 }
759
760 #[test]
761 fn round_trips_int_indexed_matrix() {
762 let matrix_dom = Domain::matrix(int_dom(1, 3), vec![int_dom(1, 2)]);
763 let mut domains = BTreeMap::new();
764 domains.insert(Name::user("m"), matrix_dom);
765
766 let json = serde_json::json!([{"m": {"1": 1, "2": 2}}]);
767 let solutions = solutions_from_simplified_json(&json, &domains).unwrap();
768 let rendered = solutions_to_simplified_json(&solutions).unwrap();
769 let again = solutions_from_simplified_json(&rendered, &domains).unwrap();
770 assert_eq!(again, solutions);
771 }
772
773 #[test]
774 fn guided_variant_object_parses_as_variant() {
775 let mut domains = BTreeMap::new();
776 domains.insert(
777 Name::user("x"),
778 Domain::variant(vec![
779 Field {
780 name: Name::user("flag"),
781 value: Domain::bool(),
782 },
783 Field {
784 name: Name::user("value"),
785 value: int_dom(1, 3),
786 },
787 ]),
788 );
789
790 let json = serde_json::json!([{"x": {"value": 2}}]);
791 let solutions = solutions_from_simplified_json(&json, &domains).unwrap();
792 let expected = Literal::AbstractLiteral(AbstractLiteral::Variant(Moo::new(Field {
793 name: Name::user("value"),
794 value: Literal::Int(2),
795 })));
796 assert_eq!(solutions[0].get(&Name::user("x")), Some(&expected));
797
798 let rendered = solutions_to_simplified_json(&solutions).unwrap();
799 assert_eq!(
800 solutions_from_simplified_json(&rendered, &domains).unwrap(),
801 solutions
802 );
803 }
804
805 #[test]
806 fn unguided_object_with_named_fields_parses_as_record() {
807 let json = serde_json::json!({"a": true, "b": 3});
808 let literal = literal_from_simplified_json(&json, None).unwrap();
809 let Literal::AbstractLiteral(AbstractLiteral::Record(fields)) = literal else {
810 panic!("expected record, got {literal:?}");
811 };
812 assert_eq!(fields.len(), 2);
813 assert_eq!(fields[0].name, Name::user("a"));
814 assert_eq!(fields[0].value, Literal::Bool(true));
815 assert_eq!(fields[1].name, Name::user("b"));
816 assert_eq!(fields[1].value, Literal::Int(3));
817 }
818}