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