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, PartialEq, Eq, Hash, PartialOrd, Ord)]
295pub struct Closure {
296 pub func: u32,
297 pub captured: Vec<Value>,
298}
299
300impl Value {
301 pub fn str(s: &str) -> Value {
302 Value::Str(Arc::from(s))
303 }
304
305 pub fn list(items: Vec<Value>) -> Value {
306 Value::List(Arc::new(Hashed::new(items)))
307 }
308
309 pub fn map(entries: BTreeMap<Value, Value>) -> Value {
310 Value::Map(Arc::new(Hashed::new(entries)))
311 }
312
313 pub fn bag(counts: BTreeMap<Value, u64>) -> Value {
314 Value::multiset(Multiset::new(counts))
315 }
316
317 pub fn multiset(contents: Multiset) -> Value {
318 Value::Bag(Arc::new(Hashed::new(contents)))
319 }
320
321 pub fn record(record: Record) -> Value {
322 Value::Record(Arc::new(Hashed::new(record)))
323 }
324
325 pub fn unshared(&self) -> Value {
329 self.unshared_with(&mut rustc_hash::FxHashMap::default())
330 }
331
332 fn unshared_with(&self, names: &mut rustc_hash::FxHashMap<String, Arc<str>>) -> Value {
335 fn name(names: &mut rustc_hash::FxHashMap<String, Arc<str>>, n: &str) -> Arc<str> {
336 names.entry(n.to_string()).or_insert_with(|| Arc::from(n)).clone()
337 }
338 match self {
339 Value::Str(s) => Value::str(s),
340 Value::Enum(e) => Value::Enum(Arc::new(EnumValue {
341 name: name(names, &e.name),
342 ..(**e).clone()
343 })),
344 Value::Record(r) => {
345 let ty = r.ty.as_deref().map(|t| name(names, t));
346 let mut fields = Vec::with_capacity(r.fields.len());
347 for (n, v) in &r.fields {
348 fields.push((name(names, n), v.unshared_with(names)));
349 }
350 Value::record(Record { ty, fields })
351 }
352 Value::List(items) => Value::list(items.iter().map(|x| x.unshared_with(names)).collect()),
353 Value::Map(m) => Value::map(
354 m.iter()
355 .map(|(k, v)| (k.unshared_with(names), v.unshared_with(names)))
356 .collect(),
357 ),
358 Value::Bag(b) => Value::multiset(Multiset {
359 entries: b.entries.iter().map(|(v, n)| (v.unshared_with(names), *n)).collect(),
360 sum: b.sum,
361 }),
362 other => other.clone(),
363 }
364 }
365
366 pub fn kind(&self) -> String {
368 match self {
369 Value::Dead => "nothing".into(),
370 Value::Unit => "()".into(),
371 Value::Bool(_) => "bool".into(),
372 Value::Int(_) => "int".into(),
373 Value::Float(_) => "float".into(),
374 Value::Complex(_) => "complex".into(),
375 Value::Prob(_) => "prob".into(),
376 Value::Str(_) => "str".into(),
377 Value::List(_) => "list".into(),
378 Value::Map(_) => "map".into(),
379 Value::Bag(_) => "bag".into(),
380 Value::Range(..) => "range".into(),
381 Value::Record(r) => match &r.ty {
382 Some(t) => t.to_string(),
383 None => "record".into(),
384 },
385 Value::Enum(_) => "enum".into(),
386 Value::Dist(d) if d.outcomes.iter().any(|(v, _)| matches!(v, Value::Continuous(_))) => {
387 "mixture of distributions".into()
388 }
389 Value::Dist(d) => match d.outcomes.first() {
390 Some((v, _)) => format!("distribution over {}", plural_kind(&v.kind())),
391 None => "distribution".into(),
392 },
393 Value::Continuous(f) => format!("{} distribution", f.name()),
394 Value::Closure(_) | Value::Builtin(_) => "function".into(),
395 Value::Date(_) => "date".into(),
396 Value::Delayed(_) => "value not drawn yet".into(),
397 Value::Analytic(a) if a.int => "int".into(),
398 Value::Analytic(_) => "float".into(),
399 Value::Event(_) => "bool".into(),
400 Value::Counts(_) => "distribution over ints".into(),
401 Value::Joint(d) => match d.outcomes.first() {
402 Some((v, _)) => format!("distribution over {}", plural_kind(&v.kind())),
403 None => "distribution".into(),
404 },
405 }
406 }
407
408 pub fn is_dist(&self) -> bool {
410 matches!(self, Value::Dist(_))
411 }
412
413 pub fn is_uncertain(&self) -> bool {
415 matches!(
416 self,
417 Value::Dist(_) | Value::Continuous(_) | Value::Counts(_) | Value::Joint(_)
418 )
419 }
420
421 pub fn as_f64(&self) -> Option<f64> {
423 match self {
424 Value::Int(i) => i.to_f64(),
425 Value::Float(f) | Value::Prob(f) => Some(*f),
426 _ => None,
427 }
428 }
429
430 pub fn as_complex(&self) -> Option<Complex> {
433 match self {
434 Value::Complex(z) => Some(*z),
435 _ => self.as_f64().and_then(|x| Complex::new(x, 0.0).ok()),
436 }
437 }
438
439 fn rank(&self) -> u8 {
441 match self {
442 Value::Dead => 0,
443 Value::Unit => 1,
444 Value::Bool(_) => 2,
445 Value::Int(_) | Value::Float(_) | Value::Prob(_) => 3,
446 Value::Str(_) => 4,
447 Value::Date(_) => 5,
448 Value::Enum(_) => 6,
449 Value::List(_) => 7,
450 Value::Range(..) => 8,
451 Value::Map(_) => 9,
452 Value::Bag(_) => 10,
453 Value::Record(_) => 11,
454 Value::Dist(_) => 12,
455 Value::Closure(_) => 13,
456 Value::Builtin(_) => 19,
457 Value::Continuous(_) => 14,
458 Value::Delayed(_) => 15,
459 Value::Complex(_) => 16,
460 Value::Analytic(_) => 17,
461 Value::Event(_) => 18,
462 Value::Counts(_) => 20,
463 Value::Joint(_) => 21,
464 }
465 }
466
467 fn number_rank(&self) -> u8 {
468 match self {
469 Value::Int(_) => 0,
470 Value::Float(_) => 1,
471 _ => 2,
472 }
473 }
474}
475
476fn plural_kind(kind: &str) -> String {
477 match kind {
478 "str" => "strings".into(),
479 "prob" => "probabilities".into(),
480 "list" => "lists".into(),
481 k => format!("{k}s"),
482 }
483}
484
485pub(crate) fn family_key(f: &Family) -> (&'static str, Vec<u64>) {
487 (f.name(), f.params().into_iter().map(float_key).collect())
488}
489
490pub(crate) fn float_key(f: f64) -> u64 {
492 if f == 0.0 {
493 0
494 } else if f.is_nan() {
495 f64::NAN.to_bits()
496 } else {
497 f.to_bits()
498 }
499}
500
501impl PartialEq for Value {
502 #[inline]
505 fn eq(&self, other: &Value) -> bool {
506 match (self, other) {
507 (Value::Int(a), Value::Int(b)) => a == b,
508 (Value::Bool(a), Value::Bool(b)) => a == b,
509 (Value::Dead, Value::Dead) => true,
510 _ => eq_other(self, other),
511 }
512 }
513}
514
515#[inline(never)]
516fn eq_other(x: &Value, y: &Value) -> bool {
517 match (x, y) {
518 (Value::Unit, Value::Unit) => true,
519 (Value::Float(a), Value::Float(b)) | (Value::Prob(a), Value::Prob(b)) => float_key(*a) == float_key(*b),
520 (Value::Complex(a), Value::Complex(b)) => a == b,
521 (Value::Str(a), Value::Str(b)) => a == b,
522 (Value::List(a), Value::List(b)) => Arc::ptr_eq(a, b) || a == b,
523 (Value::Map(a), Value::Map(b)) => Arc::ptr_eq(a, b) || a == b,
524 (Value::Bag(a), Value::Bag(b)) => Arc::ptr_eq(a, b) || a == b,
525 (Value::Range(a, b), Value::Range(c, d)) => a == c && b == d,
526 (Value::Record(a), Value::Record(b)) => Arc::ptr_eq(a, b) || a == b,
527 (Value::Enum(a), Value::Enum(b)) => a.ty == b.ty && a.variant == b.variant,
528 (Value::Dist(a), Value::Dist(b)) => Arc::ptr_eq(a, b) || a == b,
529 (Value::Closure(a), Value::Closure(b)) => Arc::ptr_eq(a, b) || a == b,
530 (Value::Builtin(a), Value::Builtin(b)) => a == b,
531 (Value::Date(a), Value::Date(b)) => a == b,
532 (Value::Continuous(a), Value::Continuous(b)) => family_key(a) == family_key(b),
533 (Value::Analytic(a), Value::Analytic(b)) => a.key() == b.key(),
534 (Value::Event(a), Value::Event(b)) => a.key() == b.key(),
535 (Value::Counts(a), Value::Counts(b)) => a.key() == b.key(),
536 (Value::Joint(a), Value::Joint(b)) => Arc::ptr_eq(a, b) || a == b,
537 (Value::Delayed(a), Value::Delayed(b)) => {
538 a.variable == b.variable && family_key(&a.family) == family_key(&b.family)
539 }
540 _ => false,
541 }
542}
543
544impl Eq for Value {}
545
546impl Hash for Value {
547 #[inline]
549 fn hash<H: Hasher>(&self, state: &mut H) {
550 std::mem::discriminant(self).hash(state);
551 match self {
552 Value::Dead | Value::Unit => {}
553 Value::Bool(b) => b.hash(state),
554 Value::Int(i) => i.hash(state),
555 other => hash_other(other, state),
556 }
557 }
558}
559
560#[inline(never)]
561fn hash_other<H: Hasher>(value: &Value, state: &mut H) {
562 match value {
563 Value::Dead | Value::Unit | Value::Bool(_) | Value::Int(_) => {}
564 Value::Float(f) | Value::Prob(f) => float_key(*f).hash(state),
565 Value::Complex(z) => (float_key(z.re()), float_key(z.im())).hash(state),
566 Value::Str(s) => s.hash(state),
567 Value::List(items) => items.hash(state),
568 Value::Map(m) => m.hash(state),
569 Value::Bag(b) => b.hash(state),
570 Value::Range(a, b) => (a, b).hash(state),
571 Value::Record(r) => r.hash(state),
572 Value::Enum(e) => (e.ty, e.variant).hash(state),
573 Value::Dist(d) => d.hash(state),
574 Value::Closure(c) => c.hash(state),
575 Value::Builtin(b) => b.hash(state),
576 Value::Date(d) => d.hash(state),
577 Value::Continuous(f) => family_key(f).hash(state),
578 Value::Analytic(a) => a.key().hash(state),
579 Value::Event(a) => a.key().hash(state),
580 Value::Delayed(d) => (d.variable, family_key(&d.family)).hash(state),
581 Value::Counts(c) => c.key().hash(state),
582 Value::Joint(d) => d.hash(state),
583 }
584}
585
586impl Ord for Value {
587 fn cmp(&self, other: &Value) -> Ordering {
588 let (ra, rb) = (self.rank(), other.rank());
589 if ra != rb {
590 return ra.cmp(&rb);
591 }
592 match (self, other) {
593 (Value::Int(a), Value::Int(b)) => a.cmp(b),
594 (Value::Int(a), Value::Float(b) | Value::Prob(b)) => a
595 .cmp_f64(*b)
596 .unwrap_or(Ordering::Less)
597 .then_with(|| self.number_rank().cmp(&other.number_rank())),
598 (Value::Float(a) | Value::Prob(a), Value::Int(b)) => b
599 .cmp_f64(*a)
600 .unwrap_or(Ordering::Less)
601 .reverse()
602 .then_with(|| self.number_rank().cmp(&other.number_rank())),
603 (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
604 (a, b) if ra == 3 => {
605 let (x, y) = (a.as_f64().unwrap(), b.as_f64().unwrap());
607 f64::from_bits(float_key(x))
608 .total_cmp(&f64::from_bits(float_key(y)))
609 .then_with(|| a.number_rank().cmp(&b.number_rank()))
610 .then_with(|| float_key(x).cmp(&float_key(y)))
611 }
612 (Value::Str(a), Value::Str(b)) => a.cmp(b),
613 (Value::Date(a), Value::Date(b)) => a.cmp(b),
614 (Value::Complex(a), Value::Complex(b)) => a.re().total_cmp(&b.re()).then(a.im().total_cmp(&b.im())),
617 (Value::Enum(a), Value::Enum(b)) => (a.ty, a.variant).cmp(&(b.ty, b.variant)),
618 (Value::List(a), Value::List(b)) => a.cmp(b),
619 (Value::Range(a, b), Value::Range(c, d)) => (a, b).cmp(&(c, d)),
620 (Value::Map(a), Value::Map(b)) => a.cmp(b),
621 (Value::Bag(a), Value::Bag(b)) => a.cmp(b),
622 (Value::Record(a), Value::Record(b)) => a.cmp(b),
623 (Value::Dist(a), Value::Dist(b)) => a.cmp(b),
624 (Value::Closure(a), Value::Closure(b)) => a.cmp(b),
625 (Value::Builtin(a), Value::Builtin(b)) => a.cmp(b),
626 (Value::Continuous(a), Value::Continuous(b)) => family_key(a).cmp(&family_key(b)),
627 (Value::Analytic(a), Value::Analytic(b)) => a.key().cmp(&b.key()),
628 (Value::Event(a), Value::Event(b)) => a.key().cmp(&b.key()),
629 (Value::Counts(a), Value::Counts(b)) => a.key().cmp(&b.key()),
630 (Value::Joint(a), Value::Joint(b)) => a.cmp(b),
631 (Value::Delayed(a), Value::Delayed(b)) => {
632 (a.variable, family_key(&a.family)).cmp(&(b.variable, family_key(&b.family)))
633 }
634 _ => Ordering::Equal,
635 }
636 }
637}
638
639impl PartialOrd for Value {
640 fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
641 Some(self.cmp(other))
642 }
643}
644
645pub fn fmt_float(f: f64) -> String {
649 if f.is_nan() {
650 return "nan".into();
651 }
652 if f.is_infinite() {
653 return if f > 0.0 { "inf".into() } else { "-inf".into() };
654 }
655 let a = f.abs();
656 if a != 0.0 && (a >= 1e15 || a < 1e-4) {
657 return format!("{f:e}");
658 }
659 if f == f.trunc() {
660 format!("{f:.1}")
661 } else {
662 format!("{f}")
663 }
664}
665
666pub fn fmt_prob(p: f64) -> String {
668 let pct = p * 100.0;
669 if pct != 0.0 && pct.abs() < 0.01 {
670 return format!("{pct:.1e}%");
671 }
672 let text = format!("{pct:.2}");
673 let text = text.trim_end_matches('0').trim_end_matches('.');
674 format!("{text}%")
675}
676
677impl fmt::Display for Value {
678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679 write_value(self, f, false)
680 }
681}
682
683impl fmt::Debug for Value {
684 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
685 write_value(self, f, true)
686 }
687}
688
689fn write_value(v: &Value, f: &mut fmt::Formatter<'_>, nested: bool) -> fmt::Result {
690 match v {
691 Value::Dead => write!(f, "<no value>"),
692 Value::Unit => write!(f, "()"),
693 Value::Int(i) => write!(f, "{i}"),
694 Value::Float(x) => write!(f, "{}", fmt_float(*x)),
695 Value::Complex(z) => write!(f, "complex({}, {})", fmt_float(z.re()), fmt_float(z.im())),
696 Value::Bool(b) => write!(f, "{b}"),
697 Value::Prob(p) => write!(f, "{}", fmt_prob(*p)),
698 Value::Str(s) if nested => write!(f, "{s:?}"),
699 Value::Str(s) => write!(f, "{s}"),
700 Value::List(items) => {
701 write!(f, "[")?;
702 for (i, item) in items.iter().enumerate() {
703 if i > 0 {
704 write!(f, ", ")?;
705 }
706 write_value(item, f, true)?;
707 }
708 write!(f, "]")
709 }
710 Value::Map(m) => {
711 if m.is_empty() {
712 return write!(f, "[:]");
713 }
714 write!(f, "[")?;
715 for (i, (k, v)) in m.iter().enumerate() {
716 if i > 0 {
717 write!(f, ", ")?;
718 }
719 write_value(k, f, true)?;
720 write!(f, ": ")?;
721 write_value(v, f, true)?;
722 }
723 write!(f, "]")
724 }
725 Value::Bag(b) => {
726 write!(f, "bag([")?;
727 for (i, (k, n)) in b.iter().enumerate() {
728 if i > 0 {
729 write!(f, ", ")?;
730 }
731 write_value(k, f, true)?;
732 write!(f, ": {n}")?;
733 }
734 write!(f, "])")
735 }
736 Value::Range(a, b) => write!(f, "{a}..{b}"),
737 Value::Record(r) => {
738 if let Some(t) = &r.ty {
739 write!(f, "{t} ")?;
740 }
741 write!(f, "{{ ")?;
742 for (i, (name, v)) in r.fields.iter().enumerate() {
743 if i > 0 {
744 write!(f, ", ")?;
745 }
746 write!(f, "{name}: ")?;
747 write_value(v, f, true)?;
748 }
749 write!(f, " }}")
750 }
751 Value::Enum(e) => write!(f, "{}", e.name),
752 Value::Dist(d) | Value::Joint(d) => {
753 write!(f, "dist(")?;
754 for (i, (v, p)) in d.outcomes.iter().enumerate() {
755 if i == 8 {
756 write!(f, ", … {} more", d.outcomes.len() - 8)?;
757 break;
758 }
759 if i > 0 {
760 write!(f, ", ")?;
761 }
762 write_value(v, f, true)?;
763 write!(f, ": {}", fmt_prob(*p))?;
764 }
765 write!(f, ")")
766 }
767 Value::Closure(_) | Value::Builtin(_) => write!(f, "<function>"),
768 Value::Date(d) => write!(f, "{}", crate::dates::format(*d)),
769 Value::Continuous(family) => write!(f, "{family}"),
770 Value::Counts(c) => write!(f, "{c}"),
771 Value::Analytic(a) => match a.affine() {
772 Some(g) => write!(f, "<analytic float: {} * {} + {}>", g.scale, a.family, g.offset),
773 None => write!(f, "<analytic float: piecewise in {}>", a.family),
774 },
775 Value::Event(_) => write!(f, "<analytic bool>"),
776 Value::Delayed(d) => write!(f, "<not drawn yet: {}>", d.family),
777 }
778}
779
780#[cfg(test)]
781mod tests {
782 use super::*;
783
784 fn bag(counts: &[(i64, u64)]) -> Multiset {
785 Multiset::new(counts.iter().map(|&(v, n)| (Value::Int(v.into()), n)).collect())
786 }
787
788 #[test]
789 fn a_bag_keeps_its_hash_up_to_date() {
790 let full = bag(&[(1, 2), (2, 1), (3, 4)]);
791 let taken = full
793 .without(&Value::Int(3.into()))
794 .unwrap()
795 .without(&Value::Int(2.into()))
796 .unwrap();
797 let direct = bag(&[(1, 2), (3, 3)]);
798 assert_eq!(taken, direct);
799 assert_eq!(taken.sum, direct.sum);
800 assert!(taken.without(&Value::Int(2.into())).is_none());
801 assert_ne!(bag(&[(1, 2), (2, 3)]).sum, bag(&[(1, 3), (2, 2)]).sum);
803 assert_ne!(bag(&[(1, 1)]).sum, bag(&[(2, 1)]).sum);
804 }
805
806 #[test]
807 fn an_unshared_copy_is_equal_and_separate() {
808 let row = Value::record(Record {
809 ty: Some(Arc::from("Day")),
810 fields: vec![
811 (Arc::from("n"), Value::Int(1.into())),
812 (Arc::from("s"), Value::str("x")),
813 ],
814 });
815 let original = Value::list(vec![row.clone(), row]);
816 let copy = original.unshared();
817 assert_eq!(copy, original);
818 let (Value::List(a), Value::List(b)) = (&original, ©) else {
819 panic!()
820 };
821 assert!(!Arc::ptr_eq(a, b));
822 let (Value::Record(x), Value::Record(y)) = (&a[0], &b[0]) else {
823 panic!()
824 };
825 assert!(!Arc::ptr_eq(x, y));
826 let Value::Record(y2) = &b[1] else { panic!() };
828 assert!(Arc::ptr_eq(&y.fields[0].0, &y2.fields[0].0));
829 assert!(!Arc::ptr_eq(&x.fields[0].0, &y.fields[0].0));
830 }
831
832 #[test]
833 fn a_changed_collection_forgets_its_hash() {
834 let mut list = Hashed::new(vec![Value::Int(1.into())]);
835 let before = list.hash_code();
836 list.push(Value::Int(2.into()));
837 assert_ne!(list.hash_code(), before);
838 assert_eq!(
839 list.hash_code(),
840 Hashed::new(vec![Value::Int(1.into()), Value::Int(2.into())]).hash_code()
841 );
842 assert_ne!(list, Hashed::new(vec![Value::Int(1.into())]));
843 }
844}