1use crate::complex::Complex;
10use crate::continuous::Family;
11use crate::dist::Dist;
12use rustc_hash::FxHasher;
13use std::cmp::Ordering;
14use std::collections::BTreeMap;
15use std::fmt;
16use std::hash::{Hash, Hasher};
17use std::ops::{Deref, DerefMut};
18use std::sync::Arc;
19use std::sync::atomic::{AtomicU64, Ordering as Atomic};
20
21#[derive(Clone)]
22pub enum Value {
23 Dead,
25 Unit,
26 Bool(bool),
28 Int(probl_number::Integer),
29 Float(f64),
30 Complex(Complex),
31 Prob(f64),
33 Str(Arc<str>),
34 List(Arc<Hashed<Vec<Value>>>),
35 Map(Arc<Hashed<BTreeMap<Value, Value>>>),
36 Bag(Arc<Hashed<Multiset>>),
38 Range(probl_number::Integer, probl_number::Integer),
40 Record(Arc<Hashed<Record>>),
41 Enum(Arc<EnumValue>),
42 Dist(Arc<Dist>),
43 Continuous(Arc<Family>),
45 Closure(Arc<Closure>),
46 Builtin(probl_sema::Builtin),
47 Date(i32),
49 Delayed(Arc<Delayed>),
53 Analytic(Arc<crate::analytic::Analytic>),
55 Event(Arc<crate::analytic::Event>),
57 Counts(Arc<crate::dist::Counts>),
62 Joint(Arc<crate::dist::Dist>),
66}
67
68#[derive(Clone, Copy, Debug, PartialEq)]
71pub struct Delayed {
72 pub family: Family,
73 pub variable: u32,
76}
77
78pub struct Hashed<T> {
84 hash: AtomicU64,
86 value: T,
87}
88
89impl<T> Hashed<T> {
90 pub fn new(value: T) -> Hashed<T> {
91 Hashed {
92 hash: AtomicU64::new(0),
93 value,
94 }
95 }
96
97 fn known_hash(&self) -> u64 {
98 self.hash.load(Atomic::Relaxed)
99 }
100}
101
102impl<T: Hash> Hashed<T> {
103 pub fn hash_code(&self) -> u64 {
105 let known = self.known_hash();
106 if known != 0 {
107 return known;
108 }
109 let mut hasher = FxHasher::default();
110 self.value.hash(&mut hasher);
111 let h = hasher.finish().max(1);
112 self.hash.store(h, Atomic::Relaxed);
113 h
114 }
115}
116
117impl<T> Deref for Hashed<T> {
118 type Target = T;
119
120 fn deref(&self) -> &T {
121 &self.value
122 }
123}
124
125impl<T> DerefMut for Hashed<T> {
126 fn deref_mut(&mut self) -> &mut T {
127 *self.hash.get_mut() = 0;
128 &mut self.value
129 }
130}
131
132impl<T: Clone> Clone for Hashed<T> {
133 fn clone(&self) -> Hashed<T> {
134 Hashed {
135 hash: AtomicU64::new(self.known_hash()),
136 value: self.value.clone(),
137 }
138 }
139}
140
141impl<T: PartialEq> PartialEq for Hashed<T> {
142 fn eq(&self, other: &Hashed<T>) -> bool {
143 let (a, b) = (self.known_hash(), other.known_hash());
144 (a == 0 || b == 0 || a == b) && self.value == other.value
145 }
146}
147
148impl<T: Eq> Eq for Hashed<T> {}
149
150impl<T: Hash> Hash for Hashed<T> {
151 fn hash<H: Hasher>(&self, state: &mut H) {
152 state.write_u64(self.hash_code());
153 }
154}
155
156impl<T: Ord> PartialOrd for Hashed<T> {
157 fn partial_cmp(&self, other: &Hashed<T>) -> Option<Ordering> {
158 Some(self.cmp(other))
159 }
160}
161
162impl<T: Ord> Ord for Hashed<T> {
163 fn cmp(&self, other: &Hashed<T>) -> Ordering {
164 self.value.cmp(&other.value)
165 }
166}
167
168impl<T: fmt::Debug> fmt::Debug for Hashed<T> {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 self.value.fmt(f)
171 }
172}
173
174#[derive(Clone, Debug, Default)]
179pub struct Multiset {
180 entries: Vec<(Value, u64)>,
181 sum: u64,
184}
185
186fn entry_hash(v: &Value, n: u64) -> u64 {
189 let mut hasher = FxHasher::default();
190 v.hash(&mut hasher);
191 n.hash(&mut hasher);
192 crate::continuous::mix(hasher.finish())
193}
194
195impl Multiset {
196 pub fn new(counts: BTreeMap<Value, u64>) -> Multiset {
198 let entries: Vec<(Value, u64)> = counts.into_iter().filter(|(_, n)| *n > 0).collect();
199 let sum = entries
200 .iter()
201 .fold(0u64, |sum, (v, n)| sum.wrapping_add(entry_hash(v, *n)));
202 Multiset { entries, sum }
203 }
204
205 pub fn iter(&self) -> impl Iterator<Item = (&Value, &u64)> + Clone {
206 self.entries.iter().map(|(v, n)| (v, n))
207 }
208
209 pub fn keys(&self) -> impl Iterator<Item = &Value> + Clone {
210 self.entries.iter().map(|(v, _)| v)
211 }
212
213 pub fn values(&self) -> impl Iterator<Item = &u64> + Clone {
214 self.entries.iter().map(|(_, n)| n)
215 }
216
217 pub fn get(&self, v: &Value) -> Option<&u64> {
219 let i = self.entries.binary_search_by(|(k, _)| k.cmp(v)).ok()?;
220 Some(&self.entries[i].1)
221 }
222
223 pub fn without_nth(&self, i: usize) -> Multiset {
225 let mut entries = self.entries.clone();
226 let (v, n) = (&self.entries[i].0, self.entries[i].1);
227 let mut sum = self.sum.wrapping_sub(entry_hash(v, n));
228 if n > 1 {
229 entries[i].1 = n - 1;
230 sum = sum.wrapping_add(entry_hash(v, n - 1));
231 } else {
232 entries.remove(i);
233 }
234 Multiset { entries, sum }
235 }
236
237 pub fn without(&self, v: &Value) -> Option<Multiset> {
239 let i = self.entries.binary_search_by(|(k, _)| k.cmp(v)).ok()?;
240 Some(self.without_nth(i))
241 }
242}
243
244impl PartialEq for Multiset {
245 fn eq(&self, other: &Multiset) -> bool {
246 self.sum == other.sum && self.entries == other.entries
247 }
248}
249
250impl Eq for Multiset {}
251
252impl Hash for Multiset {
253 fn hash<H: Hasher>(&self, state: &mut H) {
254 state.write_u64(self.sum);
255 }
256}
257
258impl PartialOrd for Multiset {
259 fn partial_cmp(&self, other: &Multiset) -> Option<Ordering> {
260 Some(self.cmp(other))
261 }
262}
263
264impl Ord for Multiset {
265 fn cmp(&self, other: &Multiset) -> Ordering {
266 self.entries.cmp(&other.entries)
267 }
268}
269
270#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
271pub struct Record {
272 pub ty: Option<Arc<str>>,
273 pub fields: Vec<(Arc<str>, Value)>,
275}
276
277impl Record {
278 pub fn get(&self, name: &str) -> Option<&Value> {
279 self.fields.iter().find(|(n, _)| &**n == name).map(|(_, v)| v)
280 }
281
282 pub fn get_mut(&mut self, name: &str) -> Option<&mut Value> {
283 self.fields.iter_mut().find(|(n, _)| &**n == name).map(|(_, v)| v)
284 }
285}
286
287#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
288pub struct EnumValue {
289 pub ty: u32,
290 pub variant: u32,
291 pub name: Arc<str>,
292}
293
294#[derive(Clone, Debug)]
295pub struct Closure {
296 pub func: u32,
297 pub captured: Vec<Value>,
298 pub named: bool,
302}
303
304impl Closure {
305 fn identity(&self) -> (u32, &[Value]) {
306 (self.func, if self.named { &[] } else { &self.captured })
307 }
308}
309
310impl PartialEq for Closure {
311 fn eq(&self, other: &Closure) -> bool {
312 self.identity() == other.identity()
313 }
314}
315
316impl Eq for Closure {}
317
318impl Hash for Closure {
319 fn hash<H: Hasher>(&self, state: &mut H) {
320 self.identity().hash(state)
321 }
322}
323
324impl PartialOrd for Closure {
325 fn partial_cmp(&self, other: &Closure) -> Option<Ordering> {
326 Some(self.cmp(other))
327 }
328}
329
330impl Ord for Closure {
331 fn cmp(&self, other: &Closure) -> Ordering {
332 self.identity().cmp(&other.identity())
333 }
334}
335
336impl Value {
337 pub fn str(s: &str) -> Value {
338 Value::Str(Arc::from(s))
339 }
340
341 pub fn list(items: Vec<Value>) -> Value {
342 Value::List(Arc::new(Hashed::new(items)))
343 }
344
345 pub fn map(entries: BTreeMap<Value, Value>) -> Value {
346 Value::Map(Arc::new(Hashed::new(entries)))
347 }
348
349 pub fn bag(counts: BTreeMap<Value, u64>) -> Value {
350 Value::multiset(Multiset::new(counts))
351 }
352
353 pub fn multiset(contents: Multiset) -> Value {
354 Value::Bag(Arc::new(Hashed::new(contents)))
355 }
356
357 pub fn record(record: Record) -> Value {
358 Value::Record(Arc::new(Hashed::new(record)))
359 }
360
361 pub fn unshared(&self) -> Value {
365 self.unshared_with(&mut rustc_hash::FxHashMap::default())
366 }
367
368 fn unshared_with(&self, names: &mut rustc_hash::FxHashMap<String, Arc<str>>) -> Value {
371 fn name(names: &mut rustc_hash::FxHashMap<String, Arc<str>>, n: &str) -> Arc<str> {
372 names.entry(n.to_string()).or_insert_with(|| Arc::from(n)).clone()
373 }
374 match self {
375 Value::Str(s) => Value::str(s),
376 Value::Enum(e) => Value::Enum(Arc::new(EnumValue {
377 name: name(names, &e.name),
378 ..(**e).clone()
379 })),
380 Value::Record(r) => {
381 let ty = r.ty.as_deref().map(|t| name(names, t));
382 let mut fields = Vec::with_capacity(r.fields.len());
383 for (n, v) in &r.fields {
384 fields.push((name(names, n), v.unshared_with(names)));
385 }
386 Value::record(Record { ty, fields })
387 }
388 Value::List(items) => Value::list(items.iter().map(|x| x.unshared_with(names)).collect()),
389 Value::Map(m) => Value::map(
390 m.iter()
391 .map(|(k, v)| (k.unshared_with(names), v.unshared_with(names)))
392 .collect(),
393 ),
394 Value::Bag(b) => Value::multiset(Multiset {
395 entries: b.entries.iter().map(|(v, n)| (v.unshared_with(names), *n)).collect(),
396 sum: b.sum,
397 }),
398 other => other.clone(),
399 }
400 }
401
402 pub fn kind(&self) -> String {
404 match self {
405 Value::Dead => "nothing".into(),
406 Value::Unit => "()".into(),
407 Value::Bool(_) => "bool".into(),
408 Value::Int(_) => "int".into(),
409 Value::Float(_) => "float".into(),
410 Value::Complex(_) => "complex".into(),
411 Value::Prob(_) => "prob".into(),
412 Value::Str(_) => "str".into(),
413 Value::List(_) => "list".into(),
414 Value::Map(_) => "map".into(),
415 Value::Bag(_) => "bag".into(),
416 Value::Range(..) => "range".into(),
417 Value::Record(r) => match &r.ty {
418 Some(t) => t.to_string(),
419 None => "record".into(),
420 },
421 Value::Enum(_) => "enum".into(),
422 Value::Dist(d) if d.outcomes.iter().any(|(v, _)| matches!(v, Value::Continuous(_))) => {
423 "mixture of distributions".into()
424 }
425 Value::Dist(d) => match d.outcomes.first() {
426 Some((v, _)) => format!("distribution over {}", plural_kind(&v.kind())),
427 None => "distribution".into(),
428 },
429 Value::Continuous(f) => format!("{} distribution", f.name()),
430 Value::Closure(_) | Value::Builtin(_) => "function".into(),
431 Value::Date(_) => "date".into(),
432 Value::Delayed(_) => "value not drawn yet".into(),
433 Value::Analytic(a) if a.int => "int".into(),
434 Value::Analytic(_) => "float".into(),
435 Value::Event(_) => "bool".into(),
436 Value::Counts(_) => "distribution over ints".into(),
437 Value::Joint(d) => match d.outcomes.first() {
438 Some((v, _)) => format!("distribution over {}", plural_kind(&v.kind())),
439 None => "distribution".into(),
440 },
441 }
442 }
443
444 pub fn is_dist(&self) -> bool {
446 matches!(self, Value::Dist(_))
447 }
448
449 pub fn is_uncertain(&self) -> bool {
451 matches!(
452 self,
453 Value::Dist(_) | Value::Continuous(_) | Value::Counts(_) | Value::Joint(_)
454 )
455 }
456
457 pub fn as_f64(&self) -> Option<f64> {
459 match self {
460 Value::Int(i) => i.to_f64(),
461 Value::Float(f) | Value::Prob(f) => Some(*f),
462 _ => None,
463 }
464 }
465
466 pub fn as_complex(&self) -> Option<Complex> {
469 match self {
470 Value::Complex(z) => Some(*z),
471 _ => self.as_f64().and_then(|x| Complex::new(x, 0.0).ok()),
472 }
473 }
474
475 fn rank(&self) -> u8 {
477 match self {
478 Value::Dead => 0,
479 Value::Unit => 1,
480 Value::Bool(_) => 2,
481 Value::Int(_) | Value::Float(_) | Value::Prob(_) => 3,
482 Value::Str(_) => 4,
483 Value::Date(_) => 5,
484 Value::Enum(_) => 6,
485 Value::List(_) => 7,
486 Value::Range(..) => 8,
487 Value::Map(_) => 9,
488 Value::Bag(_) => 10,
489 Value::Record(_) => 11,
490 Value::Dist(_) => 12,
491 Value::Closure(_) => 13,
492 Value::Builtin(_) => 19,
493 Value::Continuous(_) => 14,
494 Value::Delayed(_) => 15,
495 Value::Complex(_) => 16,
496 Value::Analytic(_) => 17,
497 Value::Event(_) => 18,
498 Value::Counts(_) => 20,
499 Value::Joint(_) => 21,
500 }
501 }
502
503 fn number_rank(&self) -> u8 {
504 match self {
505 Value::Int(_) => 0,
506 Value::Float(_) => 1,
507 _ => 2,
508 }
509 }
510}
511
512fn plural_kind(kind: &str) -> String {
513 match kind {
514 "str" => "strings".into(),
515 "prob" => "probabilities".into(),
516 "list" => "lists".into(),
517 k => format!("{k}s"),
518 }
519}
520
521pub(crate) fn family_key(f: &Family) -> (&'static str, Vec<u64>) {
523 (f.name(), f.params().into_iter().map(float_key).collect())
524}
525
526pub(crate) fn float_key(f: f64) -> u64 {
528 if f == 0.0 {
529 0
530 } else if f.is_nan() {
531 f64::NAN.to_bits()
532 } else {
533 f.to_bits()
534 }
535}
536
537impl PartialEq for Value {
538 #[inline]
541 fn eq(&self, other: &Value) -> bool {
542 match (self, other) {
543 (Value::Int(a), Value::Int(b)) => a == b,
544 (Value::Bool(a), Value::Bool(b)) => a == b,
545 (Value::Dead, Value::Dead) => true,
546 _ => eq_other(self, other),
547 }
548 }
549}
550
551#[inline(never)]
552fn eq_other(x: &Value, y: &Value) -> bool {
553 match (x, y) {
554 (Value::Unit, Value::Unit) => true,
555 (Value::Float(a), Value::Float(b)) | (Value::Prob(a), Value::Prob(b)) => float_key(*a) == float_key(*b),
556 (Value::Complex(a), Value::Complex(b)) => a == b,
557 (Value::Str(a), Value::Str(b)) => a == b,
558 (Value::List(a), Value::List(b)) => Arc::ptr_eq(a, b) || a == b,
559 (Value::Map(a), Value::Map(b)) => Arc::ptr_eq(a, b) || a == b,
560 (Value::Bag(a), Value::Bag(b)) => Arc::ptr_eq(a, b) || a == b,
561 (Value::Range(a, b), Value::Range(c, d)) => a == c && b == d,
562 (Value::Record(a), Value::Record(b)) => Arc::ptr_eq(a, b) || a == b,
563 (Value::Enum(a), Value::Enum(b)) => a.ty == b.ty && a.variant == b.variant,
564 (Value::Dist(a), Value::Dist(b)) => Arc::ptr_eq(a, b) || a == b,
565 (Value::Closure(a), Value::Closure(b)) => Arc::ptr_eq(a, b) || a == b,
566 (Value::Builtin(a), Value::Builtin(b)) => a == b,
567 (Value::Date(a), Value::Date(b)) => a == b,
568 (Value::Continuous(a), Value::Continuous(b)) => family_key(a) == family_key(b),
569 (Value::Analytic(a), Value::Analytic(b)) => a.key() == b.key(),
570 (Value::Event(a), Value::Event(b)) => a.key() == b.key(),
571 (Value::Counts(a), Value::Counts(b)) => a.key() == b.key(),
572 (Value::Joint(a), Value::Joint(b)) => Arc::ptr_eq(a, b) || a == b,
573 (Value::Delayed(a), Value::Delayed(b)) => {
574 a.variable == b.variable && family_key(&a.family) == family_key(&b.family)
575 }
576 _ => false,
577 }
578}
579
580impl Eq for Value {}
581
582impl Hash for Value {
583 #[inline]
585 fn hash<H: Hasher>(&self, state: &mut H) {
586 std::mem::discriminant(self).hash(state);
587 match self {
588 Value::Dead | Value::Unit => {}
589 Value::Bool(b) => b.hash(state),
590 Value::Int(i) => i.hash(state),
591 other => hash_other(other, state),
592 }
593 }
594}
595
596#[inline(never)]
597fn hash_other<H: Hasher>(value: &Value, state: &mut H) {
598 match value {
599 Value::Dead | Value::Unit | Value::Bool(_) | Value::Int(_) => {}
600 Value::Float(f) | Value::Prob(f) => float_key(*f).hash(state),
601 Value::Complex(z) => (float_key(z.re()), float_key(z.im())).hash(state),
602 Value::Str(s) => s.hash(state),
603 Value::List(items) => items.hash(state),
604 Value::Map(m) => m.hash(state),
605 Value::Bag(b) => b.hash(state),
606 Value::Range(a, b) => (a, b).hash(state),
607 Value::Record(r) => r.hash(state),
608 Value::Enum(e) => (e.ty, e.variant).hash(state),
609 Value::Dist(d) => d.hash(state),
610 Value::Closure(c) => c.hash(state),
611 Value::Builtin(b) => b.hash(state),
612 Value::Date(d) => d.hash(state),
613 Value::Continuous(f) => family_key(f).hash(state),
614 Value::Analytic(a) => a.key().hash(state),
615 Value::Event(a) => a.key().hash(state),
616 Value::Delayed(d) => (d.variable, family_key(&d.family)).hash(state),
617 Value::Counts(c) => c.key().hash(state),
618 Value::Joint(d) => d.hash(state),
619 }
620}
621
622impl Ord for Value {
623 fn cmp(&self, other: &Value) -> Ordering {
624 let (ra, rb) = (self.rank(), other.rank());
625 if ra != rb {
626 return ra.cmp(&rb);
627 }
628 match (self, other) {
629 (Value::Int(a), Value::Int(b)) => a.cmp(b),
630 (Value::Int(a), Value::Float(b) | Value::Prob(b)) => a
631 .cmp_f64(*b)
632 .unwrap_or(Ordering::Less)
633 .then_with(|| self.number_rank().cmp(&other.number_rank())),
634 (Value::Float(a) | Value::Prob(a), Value::Int(b)) => b
635 .cmp_f64(*a)
636 .unwrap_or(Ordering::Less)
637 .reverse()
638 .then_with(|| self.number_rank().cmp(&other.number_rank())),
639 (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
640 (a, b) if ra == 3 => {
641 let (x, y) = (a.as_f64().unwrap(), b.as_f64().unwrap());
643 f64::from_bits(float_key(x))
644 .total_cmp(&f64::from_bits(float_key(y)))
645 .then_with(|| a.number_rank().cmp(&b.number_rank()))
646 .then_with(|| float_key(x).cmp(&float_key(y)))
647 }
648 (Value::Str(a), Value::Str(b)) => a.cmp(b),
649 (Value::Date(a), Value::Date(b)) => a.cmp(b),
650 (Value::Complex(a), Value::Complex(b)) => a.re().total_cmp(&b.re()).then(a.im().total_cmp(&b.im())),
653 (Value::Enum(a), Value::Enum(b)) => (a.ty, a.variant).cmp(&(b.ty, b.variant)),
654 (Value::List(a), Value::List(b)) => a.cmp(b),
655 (Value::Range(a, b), Value::Range(c, d)) => (a, b).cmp(&(c, d)),
656 (Value::Map(a), Value::Map(b)) => a.cmp(b),
657 (Value::Bag(a), Value::Bag(b)) => a.cmp(b),
658 (Value::Record(a), Value::Record(b)) => a.cmp(b),
659 (Value::Dist(a), Value::Dist(b)) => a.cmp(b),
660 (Value::Closure(a), Value::Closure(b)) => a.cmp(b),
661 (Value::Builtin(a), Value::Builtin(b)) => a.cmp(b),
662 (Value::Continuous(a), Value::Continuous(b)) => family_key(a).cmp(&family_key(b)),
663 (Value::Analytic(a), Value::Analytic(b)) => a.key().cmp(&b.key()),
664 (Value::Event(a), Value::Event(b)) => a.key().cmp(&b.key()),
665 (Value::Counts(a), Value::Counts(b)) => a.key().cmp(&b.key()),
666 (Value::Joint(a), Value::Joint(b)) => a.cmp(b),
667 (Value::Delayed(a), Value::Delayed(b)) => {
668 (a.variable, family_key(&a.family)).cmp(&(b.variable, family_key(&b.family)))
669 }
670 _ => Ordering::Equal,
671 }
672 }
673}
674
675impl PartialOrd for Value {
676 fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
677 Some(self.cmp(other))
678 }
679}
680
681pub fn fmt_float(f: f64) -> String {
685 if f.is_nan() {
686 return "nan".into();
687 }
688 if f.is_infinite() {
689 return if f > 0.0 { "inf".into() } else { "-inf".into() };
690 }
691 let a = f.abs();
692 if a != 0.0 && (a >= 1e15 || a < 1e-4) {
693 return format!("{f:e}");
694 }
695 if f == f.trunc() {
696 format!("{f:.1}")
697 } else {
698 format!("{f}")
699 }
700}
701
702pub fn fmt_prob(p: f64) -> String {
704 let pct = p * 100.0;
705 if pct != 0.0 && pct.abs() < 0.01 {
706 return format!("{pct:.1e}%");
707 }
708 let text = format!("{pct:.2}");
709 let text = text.trim_end_matches('0').trim_end_matches('.');
710 format!("{text}%")
711}
712
713impl fmt::Display for Value {
714 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
715 write_value(self, f, false)
716 }
717}
718
719impl fmt::Debug for Value {
720 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
721 write_value(self, f, true)
722 }
723}
724
725fn write_value(v: &Value, f: &mut fmt::Formatter<'_>, nested: bool) -> fmt::Result {
726 match v {
727 Value::Dead => write!(f, "<no value>"),
728 Value::Unit => write!(f, "()"),
729 Value::Int(i) => write!(f, "{i}"),
730 Value::Float(x) => write!(f, "{}", fmt_float(*x)),
731 Value::Complex(z) => write!(f, "complex({}, {})", fmt_float(z.re()), fmt_float(z.im())),
732 Value::Bool(b) => write!(f, "{b}"),
733 Value::Prob(p) => write!(f, "{}", fmt_prob(*p)),
734 Value::Str(s) if nested => write!(f, "{s:?}"),
735 Value::Str(s) => write!(f, "{s}"),
736 Value::List(items) => {
737 write!(f, "[")?;
738 for (i, item) in items.iter().enumerate() {
739 if i > 0 {
740 write!(f, ", ")?;
741 }
742 write_value(item, f, true)?;
743 }
744 write!(f, "]")
745 }
746 Value::Map(m) => {
747 if m.is_empty() {
748 return write!(f, "[:]");
749 }
750 write!(f, "[")?;
751 for (i, (k, v)) in m.iter().enumerate() {
752 if i > 0 {
753 write!(f, ", ")?;
754 }
755 write_value(k, f, true)?;
756 write!(f, ": ")?;
757 write_value(v, f, true)?;
758 }
759 write!(f, "]")
760 }
761 Value::Bag(b) => {
762 write!(f, "bag([")?;
763 for (i, (k, n)) in b.iter().enumerate() {
764 if i > 0 {
765 write!(f, ", ")?;
766 }
767 write_value(k, f, true)?;
768 write!(f, ": {n}")?;
769 }
770 write!(f, "])")
771 }
772 Value::Range(a, b) => write!(f, "{a}..{b}"),
773 Value::Record(r) => {
774 if let Some(t) = &r.ty {
775 write!(f, "{t} ")?;
776 }
777 write!(f, "{{ ")?;
778 for (i, (name, v)) in r.fields.iter().enumerate() {
779 if i > 0 {
780 write!(f, ", ")?;
781 }
782 write!(f, "{name}: ")?;
783 write_value(v, f, true)?;
784 }
785 write!(f, " }}")
786 }
787 Value::Enum(e) => write!(f, "{}", e.name),
788 Value::Dist(d) | Value::Joint(d) => {
789 write!(f, "dist(")?;
790 for (i, (v, p)) in d.outcomes.iter().enumerate() {
791 if i == 8 {
792 write!(f, ", … {} more", d.outcomes.len() - 8)?;
793 break;
794 }
795 if i > 0 {
796 write!(f, ", ")?;
797 }
798 write_value(v, f, true)?;
799 write!(f, ": {}", fmt_prob(*p))?;
800 }
801 write!(f, ")")
802 }
803 Value::Closure(_) | Value::Builtin(_) => write!(f, "<function>"),
804 Value::Date(d) => write!(f, "{}", crate::dates::format(*d)),
805 Value::Continuous(family) => write!(f, "{family}"),
806 Value::Counts(c) => write!(f, "{c}"),
807 Value::Analytic(a) => match a.affine() {
808 Some(g) => write!(f, "<analytic float: {} * {} + {}>", g.scale, a.family, g.offset),
809 None => write!(f, "<analytic float: piecewise in {}>", a.family),
810 },
811 Value::Event(_) => write!(f, "<analytic bool>"),
812 Value::Delayed(d) => write!(f, "<not drawn yet: {}>", d.family),
813 }
814}
815
816#[cfg(test)]
817mod tests {
818 use super::*;
819
820 fn bag(counts: &[(i64, u64)]) -> Multiset {
821 Multiset::new(counts.iter().map(|&(v, n)| (Value::Int(v.into()), n)).collect())
822 }
823
824 #[test]
825 fn a_bag_keeps_its_hash_up_to_date() {
826 let full = bag(&[(1, 2), (2, 1), (3, 4)]);
827 let taken = full
829 .without(&Value::Int(3.into()))
830 .unwrap()
831 .without(&Value::Int(2.into()))
832 .unwrap();
833 let direct = bag(&[(1, 2), (3, 3)]);
834 assert_eq!(taken, direct);
835 assert_eq!(taken.sum, direct.sum);
836 assert!(taken.without(&Value::Int(2.into())).is_none());
837 assert_ne!(bag(&[(1, 2), (2, 3)]).sum, bag(&[(1, 3), (2, 2)]).sum);
839 assert_ne!(bag(&[(1, 1)]).sum, bag(&[(2, 1)]).sum);
840 }
841
842 #[test]
843 fn an_unshared_copy_is_equal_and_separate() {
844 let row = Value::record(Record {
845 ty: Some(Arc::from("Day")),
846 fields: vec![
847 (Arc::from("n"), Value::Int(1.into())),
848 (Arc::from("s"), Value::str("x")),
849 ],
850 });
851 let original = Value::list(vec![row.clone(), row]);
852 let copy = original.unshared();
853 assert_eq!(copy, original);
854 let (Value::List(a), Value::List(b)) = (&original, ©) else {
855 panic!()
856 };
857 assert!(!Arc::ptr_eq(a, b));
858 let (Value::Record(x), Value::Record(y)) = (&a[0], &b[0]) else {
859 panic!()
860 };
861 assert!(!Arc::ptr_eq(x, y));
862 let Value::Record(y2) = &b[1] else { panic!() };
864 assert!(Arc::ptr_eq(&y.fields[0].0, &y2.fields[0].0));
865 assert!(!Arc::ptr_eq(&x.fields[0].0, &y.fields[0].0));
866 }
867
868 #[test]
869 fn a_changed_collection_forgets_its_hash() {
870 let mut list = Hashed::new(vec![Value::Int(1.into())]);
871 let before = list.hash_code();
872 list.push(Value::Int(2.into()));
873 assert_ne!(list.hash_code(), before);
874 assert_eq!(
875 list.hash_code(),
876 Hashed::new(vec![Value::Int(1.into()), Value::Int(2.into())]).hash_code()
877 );
878 assert_ne!(list, Hashed::new(vec![Value::Int(1.into())]));
879 }
880}