Skip to main content

vj_sim/constraints/
real_domain.rs

1//! Real-valued constraints (IEEE 1800-2023 18.5.3 and 18.5.13; plan Task C9).
2//!
3//! # Why a separate solver
4//!
5//! The BDD solver ([`super::solve`]) is a bit-vector engine; a `real` is a
6//! 64-bit IEEE pattern, and bit-blasting a floating-point comparison is both
7//! enormous and meaningless for sampling (nearly every bit pattern is a
8//! number far from any bound). Real random variables therefore live in their
9//! own namespace and are solved here, by interval arithmetic on the float
10//! grid, after the integral solve and with the same threaded PRNG seed.
11//!
12//! # The domain
13//!
14//! A real variable ranges over the finite values of its type (`real`: f64,
15//! `shortreal`: f32; `-0.0` is the same grid point as `+0.0`). A set of
16//! values is a [`RealSet`]: sorted disjoint closed intervals of *grid indices*
17//! (the order-preserving integer numbering of the floats), so open and closed
18//! bounds, `!=` holes and adjacent floats need no special cases. An
19//! unconstrained variable is uniform over `[-MAX, MAX]`.
20//!
21//! # What a constraint may be
22//!
23//! A conjunct reads at most one random real variable (affinely: a product
24//! has at most one side that reads it, a quotient's denominator reads none),
25//! or it is a guard (`->`/`if`) whose predicate reads one variable and whose
26//! arms constrain other variables one at a time. Each atom `l op r` is turned
27//! into the exact set of grid values for which the IEEE evaluation of the
28//! atom is true: the sign of the slope is read off the affine form and the
29//! boundary is found by binary search on the grid with the real evaluator, so
30//! `x / 3.0 < 1.0` includes exactly the floats for which that rounded
31//! comparison holds. (The one inexactness: when *both* sides read the
32//! variable and rounding makes `l < r` non-monotone within an ulp of the
33//! boundary the search lands on one boundary of that zone.)
34//!
35//! Anything else (two variables in one non-guard conjunct, a real conjunct
36//! reading an integral random variable, a non-affine product) is a loud
37//! [`RealError`] or a build error, never a silently wrong distribution.
38//!
39//! # Sampling
40//!
41//! A variable is drawn *uniformly by length* over its feasible set (a point
42//! set, such as `x == 3.0 || x == 5.0`, uniformly over its points; an
43//! isolated point inside a set of positive length has probability zero), or
44//! by `dist` weights (a `:/` range's weight is spread over its length, so
45//! the part of it other constraints exclude loses weight in proportion, the
46//! real analogue of 18.5.3's per-value weights). A guard that couples two
47//! variables (`a inside [..] -> b == X`) is solved by the staged rule of
48//! 18.5.9: it requires `solve a before b`, `a` is drawn from exactly the
49//! values for which some `b` completes the solution, and `b` is then drawn
50//! from what remains.
51
52use super::guard::{self, GuardValue};
53use super::ir::{Assignment, ConstraintEvalError, Expr, StateId, Ty, VarId};
54use super::rng;
55use num_bigint::{BigInt, BigUint};
56use num_traits::{ToPrimitive, Zero};
57
58/// Index of a random real variable (its own numbering, separate from the
59/// integral [`super::ir::VarId`]s).
60pub type RealVarId = u32;
61
62/// The two floating-point types a real random variable can have.
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
64pub enum FloatKind {
65    /// `real` / `realtime`: IEEE 754 binary64.
66    F64,
67    /// `shortreal`: IEEE 754 binary32.
68    F32,
69}
70
71impl FloatKind {
72    /// The grid index of the largest finite value.
73    pub fn max_idx(self) -> i64 {
74        match self {
75            FloatKind::F64 => 0x7fef_ffff_ffff_ffff,
76            FloatKind::F32 => 0x7f7f_ffff,
77        }
78    }
79
80    /// The largest finite value.
81    pub fn max_val(self) -> f64 {
82        self.val(self.max_idx())
83    }
84
85    /// The grid index of the finite or infinite grid value `x` (`-0.0` is 0).
86    /// `x` must already be a value of the type ([`Self::round`]).
87    pub fn idx(self, x: f64) -> i64 {
88        match self {
89            FloatKind::F64 => {
90                let b = x.to_bits();
91                let m = (b & 0x7fff_ffff_ffff_ffff) as i64;
92                if b >> 63 == 1 { -m } else { m }
93            }
94            FloatKind::F32 => {
95                let b = (x as f32).to_bits();
96                let m = i64::from(b & 0x7fff_ffff);
97                if b >> 31 == 1 { -m } else { m }
98            }
99        }
100    }
101
102    /// The value of grid index `i` (never `-0.0`).
103    pub fn val(self, i: i64) -> f64 {
104        match self {
105            FloatKind::F64 => {
106                if i >= 0 {
107                    f64::from_bits(i as u64)
108                } else {
109                    f64::from_bits(i.unsigned_abs() | (1 << 63))
110                }
111            }
112            FloatKind::F32 => {
113                if i >= 0 {
114                    f64::from(f32::from_bits(i as u32))
115                } else {
116                    f64::from(f32::from_bits(i.unsigned_abs() as u32 | (1 << 31)))
117                }
118            }
119        }
120    }
121
122    /// `x` rounded to the type (a no-op for `real`).
123    pub fn round(self, x: f64) -> f64 {
124        match self {
125            FloatKind::F64 => x,
126            FloatKind::F32 => f64::from(x as f32),
127        }
128    }
129
130    /// The smallest grid index whose value is `>= x`, or `None` if there is
131    /// none (`x` above the largest finite value, or NaN).
132    pub fn ceil_idx(self, x: f64) -> Option<i64> {
133        if x.is_nan() || x > self.max_val() {
134            return None;
135        }
136        if x <= -self.max_val() {
137            return Some(-self.max_idx());
138        }
139        let r = self.round(x);
140        let i = self.idx(r);
141        Some(if r < x { i + 1 } else { i })
142    }
143
144    /// The largest grid index whose value is `<= x`, or `None`.
145    pub fn floor_idx(self, x: f64) -> Option<i64> {
146        if x.is_nan() || x < -self.max_val() {
147            return None;
148        }
149        if x >= self.max_val() {
150            return Some(self.max_idx());
151        }
152        let r = self.round(x);
153        let i = self.idx(r);
154        Some(if r > x { i - 1 } else { i })
155    }
156
157    /// The value held by the low bits of a field (`words` of an object
158    /// property: the f64 pattern, or the f32 pattern in the low 32 bits).
159    pub fn from_bits(self, w: &BigUint) -> f64 {
160        let b = w.to_u64().unwrap_or(0);
161        match self {
162            FloatKind::F64 => f64::from_bits(b),
163            FloatKind::F32 => f64::from(f32::from_bits(b as u32)),
164        }
165    }
166
167    /// The field bits of the grid value `x`.
168    pub fn to_bits(self, x: f64) -> u64 {
169        match self {
170            FloatKind::F64 => x.to_bits(),
171            FloatKind::F32 => u64::from((x as f32).to_bits()),
172        }
173    }
174
175    /// The field width in bits.
176    pub fn width(self) -> u32 {
177        match self {
178            FloatKind::F64 => 64,
179            FloatKind::F32 => 32,
180        }
181    }
182}
183
184/// A set of grid values: sorted, disjoint, non-adjacent closed intervals of
185/// grid indices (module docs).
186#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
187pub struct RealSet {
188    kind: FloatKind,
189    iv: Vec<(i64, i64)>,
190}
191
192/// `[0, 1)` from a draw.
193fn unit(seed: &mut u64) -> f64 {
194    (rng::draw(seed) >> 11) as f64 / (1u64 << 53) as f64
195}
196
197impl RealSet {
198    /// The empty set.
199    pub fn empty(kind: FloatKind) -> RealSet {
200        RealSet {
201            kind,
202            iv: Vec::new(),
203        }
204    }
205
206    /// Every finite value.
207    pub fn full(kind: FloatKind) -> RealSet {
208        RealSet {
209            kind,
210            iv: vec![(-kind.max_idx(), kind.max_idx())],
211        }
212    }
213
214    /// The grid indices `lo..=hi` (empty if `lo > hi`), clipped to the
215    /// finite values.
216    pub fn from_idx(kind: FloatKind, lo: i64, hi: i64) -> RealSet {
217        let (lo, hi) = (lo.max(-kind.max_idx()), hi.min(kind.max_idx()));
218        RealSet {
219            kind,
220            iv: if lo <= hi { vec![(lo, hi)] } else { Vec::new() },
221        }
222    }
223
224    /// The values `v` of the type with `lo <= v <= hi` (compared as exact
225    /// numbers); `lo` or `hi` may be infinite. Empty if `lo > hi` or NaN.
226    pub fn range(kind: FloatKind, lo: f64, hi: f64) -> RealSet {
227        match (kind.ceil_idx(lo), kind.floor_idx(hi)) {
228            (Some(a), Some(b)) => RealSet::from_idx(kind, a, b),
229            _ => RealSet::empty(kind),
230        }
231    }
232
233    /// The single value `x` if it is a value of the type, else the empty set
234    /// (a `shortreal` never equals a `real` that needs more than 24 bits).
235    pub fn point(kind: FloatKind, x: f64) -> RealSet {
236        if x.is_nan() || x.abs() > kind.max_val() || kind.round(x) != x {
237            return RealSet::empty(kind);
238        }
239        let i = kind.idx(x);
240        RealSet::from_idx(kind, i, i)
241    }
242
243    /// The type of the values.
244    pub fn kind(&self) -> FloatKind {
245        self.kind
246    }
247
248    /// Whether no value is in the set.
249    pub fn is_empty(&self) -> bool {
250        self.iv.is_empty()
251    }
252
253    /// Whether `x` (a value of the type) is in the set.
254    pub fn contains(&self, x: f64) -> bool {
255        if x.is_nan() || x.abs() > self.kind.max_val() {
256            return false;
257        }
258        let i = self.kind.idx(x);
259        self.iv.iter().any(|&(a, b)| a <= i && i <= b)
260    }
261
262    /// The intervals, as inclusive grid-index pairs.
263    pub fn intervals(&self) -> &[(i64, i64)] {
264        &self.iv
265    }
266
267    fn normalised(kind: FloatKind, mut iv: Vec<(i64, i64)>) -> RealSet {
268        iv.sort_unstable();
269        let mut out: Vec<(i64, i64)> = Vec::with_capacity(iv.len());
270        for (a, b) in iv {
271            match out.last_mut() {
272                Some(last) if a <= last.1 + 1 => last.1 = last.1.max(b),
273                _ => out.push((a, b)),
274            }
275        }
276        RealSet { kind, iv: out }
277    }
278
279    /// The union.
280    pub fn union(&self, o: &RealSet) -> RealSet {
281        let mut iv = self.iv.clone();
282        iv.extend_from_slice(&o.iv);
283        RealSet::normalised(self.kind, iv)
284    }
285
286    /// The intersection.
287    pub fn intersect(&self, o: &RealSet) -> RealSet {
288        let mut out = Vec::new();
289        let (mut i, mut j) = (0, 0);
290        while i < self.iv.len() && j < o.iv.len() {
291            let (a, b) = (self.iv[i], o.iv[j]);
292            let (lo, hi) = (a.0.max(b.0), a.1.min(b.1));
293            if lo <= hi {
294                out.push((lo, hi));
295            }
296            if a.1 < b.1 {
297                i += 1;
298            } else {
299                j += 1;
300            }
301        }
302        RealSet {
303            kind: self.kind,
304            iv: out,
305        }
306    }
307
308    /// Every value of the type not in the set.
309    pub fn complement(&self) -> RealSet {
310        let m = self.kind.max_idx();
311        let mut out = Vec::new();
312        let mut next = -m;
313        for &(a, b) in &self.iv {
314            if a > next {
315                out.push((next, a - 1));
316            }
317            next = b + 1;
318        }
319        if next <= m {
320            out.push((next, m));
321        }
322        RealSet {
323            kind: self.kind,
324            iv: out,
325        }
326    }
327
328    /// The Lebesgue measure in half-units (so the full range does not
329    /// overflow): `0.5 * hi - 0.5 * lo` summed over the intervals. A single
330    /// point has measure 0.
331    pub fn measure(&self) -> f64 {
332        self.iv
333            .iter()
334            .map(|&(a, b)| 0.5 * self.kind.val(b) - 0.5 * self.kind.val(a))
335            .sum()
336    }
337
338    /// The number of grid points.
339    fn points(&self) -> BigUint {
340        self.iv
341            .iter()
342            .map(|&(a, b)| BigUint::from((i128::from(b) - i128::from(a) + 1) as u128))
343            .sum()
344    }
345
346    /// One value drawn uniformly by length (module docs); the set must not be
347    /// empty.
348    pub fn sample(&self, seed: &mut u64) -> f64 {
349        debug_assert!(!self.is_empty(), "sampling an empty set");
350        let k = self.kind;
351        let total = self.measure();
352        if !total.is_finite() || total <= 0.0 {
353            // Only points (or a measure that underflows): uniform over the
354            // grid points.
355            let n = self.points();
356            let mut r = rng::below(seed, &n);
357            for &(a, b) in &self.iv {
358                let len = BigUint::from((i128::from(b) - i128::from(a) + 1) as u128);
359                if r < len {
360                    let off = r.to_i64().unwrap_or(0);
361                    return k.val(a + off);
362                }
363                r -= len;
364            }
365            return k.val(self.iv[self.iv.len() - 1].1);
366        }
367        let mut target = unit(seed) * total;
368        let mut chosen = self.iv[self.iv.len() - 1];
369        for &(a, b) in &self.iv {
370            let len = 0.5 * k.val(b) - 0.5 * k.val(a);
371            if target < len {
372                chosen = (a, b);
373                break;
374            }
375            target -= len;
376        }
377        let (lo, hi) = (k.val(chosen.0), k.val(chosen.1));
378        let (h_lo, h_hi) = (0.5 * lo, 0.5 * hi);
379        let x = 2.0 * (h_lo + unit(seed) * (h_hi - h_lo));
380        let x = k.round(x.clamp(lo, hi));
381        x.clamp(lo, hi)
382    }
383}
384
385/// A real value with bitwise equality, so IR containing reals can derive
386/// `Eq`.
387#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
388pub struct Real(u64);
389
390impl Real {
391    /// Wraps `x`.
392    pub fn new(x: f64) -> Real {
393        Real(x.to_bits())
394    }
395    /// The value.
396    pub fn get(self) -> f64 {
397        f64::from_bits(self.0)
398    }
399}
400
401impl PartialEq for Real {
402    fn eq(&self, o: &Real) -> bool {
403        self.0 == o.0
404    }
405}
406impl Eq for Real {}
407
408/// Why a real solve failed.
409#[derive(Clone, Debug, PartialEq, Eq)]
410pub enum RealError {
411    /// The constraints have no solution (`randomize()` returns 0).
412    Unsatisfiable,
413    /// A constraint guard evaluated to `ERROR` (18.5.12).
414    GuardError,
415    /// A constraint reads a state variable with no value (X/Z bits, or a
416    /// null handle on the way to it).
417    StateUnreadable(StateId),
418    /// An expression produced NaN, or divided by zero.
419    NonFinite(&'static str),
420    /// A `dist` weight is negative.
421    BadWeight,
422    /// The problem is not one this solver handles (a malformed program, or
423    /// a constraint shape [`RealProblem::check`] would have rejected).
424    Shape(String),
425}
426
427impl core::fmt::Display for RealError {
428    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
429        match self {
430            RealError::Unsatisfiable => f.write_str("the real constraints have no solution"),
431            RealError::GuardError => f.write_str("a real constraint guard evaluated to ERROR"),
432            RealError::StateUnreadable(s) => {
433                write!(f, "state variable #{s} of a real constraint cannot be read")
434            }
435            RealError::NonFinite(what) => write!(f, "real constraint evaluation: {what}"),
436            RealError::BadWeight => f.write_str("a real `dist` weight is negative"),
437            RealError::Shape(what) => write!(f, "unsupported real constraint: {what}"),
438        }
439    }
440}
441
442impl std::error::Error for RealError {}
443
444/// A real-valued expression over real random variables, state variables and
445/// constants. `shortreal` operands compute in binary32, anything involving a
446/// `real` in binary64.
447#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
448pub enum RealExpr {
449    /// A constant of the given type.
450    Const(Real, FloatKind),
451    /// A random real variable.
452    Var(RealVarId),
453    /// A state variable holding the bits of a real of the given type.
454    State(StateId, FloatKind),
455    /// An integral, variable-free expression converted to `real` (signed or
456    /// unsigned by its own type).
457    FromInt(Box<Expr>),
458    /// Unary minus.
459    Neg(Box<RealExpr>),
460    /// `+`.
461    Add(Box<RealExpr>, Box<RealExpr>),
462    /// `-`.
463    Sub(Box<RealExpr>, Box<RealExpr>),
464    /// `*`.
465    Mul(Box<RealExpr>, Box<RealExpr>),
466    /// `/`.
467    Div(Box<RealExpr>, Box<RealExpr>),
468    /// Conversion to `shortreal` (rounds to binary32).
469    ToShort(Box<RealExpr>),
470}
471
472/// A comparison operator.
473#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
474pub enum CmpOp {
475    /// `<`
476    Lt,
477    /// `<=`
478    Le,
479    /// `>`
480    Gt,
481    /// `>=`
482    Ge,
483    /// `==`
484    Eq,
485    /// `!=`
486    Ne,
487}
488
489/// A boolean condition over real expressions.
490#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
491pub enum RealCond {
492    /// A constant.
493    Const(bool),
494    /// An integral condition over state variables only (true when nonzero).
495    Int(Expr),
496    /// A comparison.
497    Cmp(CmpOp, RealExpr, RealExpr),
498    /// `!`.
499    Not(Box<RealCond>),
500    /// `&&` of any number of conditions.
501    And(Vec<RealCond>),
502    /// `||` of any number of conditions.
503    Or(Vec<RealCond>),
504    /// `if (pred) then else els` (an implication has the constant-true else
505    /// arm): a constraint guard (18.5.12).
506    Guard(Box<RealCond>, Box<RealCond>, Box<RealCond>),
507}
508
509/// The values of one real `dist` item.
510#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
511pub enum RealSpan {
512    /// A single value.
513    Point(RealExpr),
514    /// `[lo:hi]`; `None` is `$`, the type's own extreme.
515    Range {
516        /// The low bound.
517        lo: Option<RealExpr>,
518        /// The high bound.
519        hi: Option<RealExpr>,
520    },
521}
522
523/// One explicit item of a real `dist`.
524#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
525pub struct RealDistItem {
526    /// The item's values.
527    pub span: RealSpan,
528    /// The weight (integral, over states and constants). A range's weight is
529    /// the item's whole weight (`:/`, the only operator a real range may
530    /// use, 18.5.3); a point's weight is that of the value.
531    pub weight: Expr,
532}
533
534/// `var dist { ... }` on a real random variable.
535#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
536pub struct RealDist {
537    /// The distributed variable.
538    pub var: RealVarId,
539    /// The explicit items.
540    pub items: Vec<RealDistItem>,
541    /// The weight of `default :/ w`, spread over the values no item names.
542    pub default: Option<Expr>,
543}
544
545/// The whole real problem of one class template.
546#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
547pub struct RealProblem {
548    /// The type of each real random variable.
549    pub kinds: Vec<FloatKind>,
550    /// Conditions that must all hold.
551    pub conds: Vec<RealCond>,
552    /// The `dist` constraints, at most one per variable.
553    pub dists: Vec<RealDist>,
554    /// `solve a before b` edges between real variables.
555    pub before: Vec<(RealVarId, RealVarId)>,
556}
557
558impl RealExpr {
559    fn map_int(&self, f: &mut dyn FnMut(&Expr) -> Expr) -> RealExpr {
560        let mut b = |x: &RealExpr| Box::new(x.map_int(f));
561        match self {
562            RealExpr::Const(..) | RealExpr::Var(_) | RealExpr::State(..) => self.clone(),
563            RealExpr::FromInt(x) => RealExpr::FromInt(Box::new(f(x))),
564            RealExpr::Neg(x) => RealExpr::Neg(b(x)),
565            RealExpr::ToShort(x) => RealExpr::ToShort(b(x)),
566            RealExpr::Add(x, y) => RealExpr::Add(b(x), b(y)),
567            RealExpr::Sub(x, y) => RealExpr::Sub(b(x), b(y)),
568            RealExpr::Mul(x, y) => RealExpr::Mul(b(x), b(y)),
569            RealExpr::Div(x, y) => RealExpr::Div(b(x), b(y)),
570        }
571    }
572
573    fn for_each_int(&self, f: &mut dyn FnMut(&Expr)) {
574        match self {
575            RealExpr::Const(..) | RealExpr::Var(_) | RealExpr::State(..) => {}
576            RealExpr::FromInt(x) => f(x),
577            RealExpr::Neg(x) | RealExpr::ToShort(x) => x.for_each_int(f),
578            RealExpr::Add(x, y)
579            | RealExpr::Sub(x, y)
580            | RealExpr::Mul(x, y)
581            | RealExpr::Div(x, y) => {
582                x.for_each_int(f);
583                y.for_each_int(f);
584            }
585        }
586    }
587}
588
589impl RealCond {
590    fn map_int(&self, f: &mut dyn FnMut(&Expr) -> Expr) -> RealCond {
591        match self {
592            RealCond::Const(_) => self.clone(),
593            RealCond::Int(e) => RealCond::Int(f(e)),
594            RealCond::Cmp(op, l, r) => RealCond::Cmp(*op, l.map_int(f), r.map_int(f)),
595            RealCond::Not(x) => RealCond::Not(Box::new(x.map_int(f))),
596            RealCond::And(v) => RealCond::And(v.iter().map(|x| x.map_int(f)).collect()),
597            RealCond::Or(v) => RealCond::Or(v.iter().map(|x| x.map_int(f)).collect()),
598            RealCond::Guard(p, t, e) => RealCond::Guard(
599                Box::new(p.map_int(f)),
600                Box::new(t.map_int(f)),
601                Box::new(e.map_int(f)),
602            ),
603        }
604    }
605
606    fn for_each_int(&self, f: &mut dyn FnMut(&Expr)) {
607        match self {
608            RealCond::Const(_) => {}
609            RealCond::Int(e) => f(e),
610            RealCond::Cmp(_, l, r) => {
611                l.for_each_int(f);
612                r.for_each_int(f);
613            }
614            RealCond::Not(x) => x.for_each_int(f),
615            RealCond::And(v) | RealCond::Or(v) => v.iter().for_each(|x| x.for_each_int(f)),
616            RealCond::Guard(p, t, e) => {
617                p.for_each_int(f);
618                t.for_each_int(f);
619                e.for_each_int(f);
620            }
621        }
622    }
623}
624
625impl RealProblem {
626    /// Whether any integral sub-expression of a condition or a `dist` bound
627    /// reads an integral *random* variable (IEEE 1800-2023 18.5.13: a real
628    /// constraint over an integral `rand` variable is solved after it).
629    pub fn reads_int_vars(&self) -> bool {
630        let mut any = false;
631        let mut f = |e: &Expr| e.for_each_var(&mut |_, _| any = true);
632        for c in &self.conds {
633            c.for_each_int(&mut f);
634        }
635        for d in &self.dists {
636            for it in &d.items {
637                match &it.span {
638                    RealSpan::Point(x) => x.for_each_int(&mut f),
639                    RealSpan::Range { lo, hi } => {
640                        lo.iter().chain(hi).for_each(|x| x.for_each_int(&mut f));
641                    }
642                }
643            }
644        }
645        any
646    }
647
648    /// Whether some top-level condition compares an integral random variable
649    /// with real values and reads no real random variable: it only filters the
650    /// integral draw, which rejection sampling cannot do reliably (a 32-bit
651    /// variable against a real bound), so the builder refuses it by name.
652    pub fn has_integral_only_filter(&self) -> bool {
653        self.conds.iter().any(|c| {
654            let (mut int_var, mut real_var) = (false, false);
655            c.for_each_int(&mut |e| e.for_each_var(&mut |_, _| int_var = true));
656            c.for_each_var(&mut |_| real_var = true);
657            int_var && !real_var
658        })
659    }
660
661    /// This problem with every integral random variable `v` of type `t` that a
662    /// condition or `dist` bound reads replaced by `f(v, t)` (a state variable
663    /// holding the value the integral solve drew).
664    pub fn with_int_vars(&self, mut f: impl FnMut(VarId, Ty) -> Expr) -> RealProblem {
665        let mut m = |e: &Expr| e.substitute_vars(&mut f);
666        let mut p = self.clone();
667        p.conds = self.conds.iter().map(|c| c.map_int(&mut m)).collect();
668        for d in &mut p.dists {
669            for it in &mut d.items {
670                it.span = match &it.span {
671                    RealSpan::Point(x) => RealSpan::Point(x.map_int(&mut m)),
672                    RealSpan::Range { lo, hi } => RealSpan::Range {
673                        lo: lo.as_ref().map(|x| x.map_int(&mut m)),
674                        hi: hi.as_ref().map(|x| x.map_int(&mut m)),
675                    },
676                };
677            }
678        }
679        p
680    }
681}
682
683impl RealExpr {
684    /// The type the expression computes in.
685    pub fn kind(&self, kinds: &[FloatKind]) -> FloatKind {
686        match self {
687            RealExpr::Const(_, k) | RealExpr::State(_, k) => *k,
688            RealExpr::Var(v) => kinds.get(*v as usize).copied().unwrap_or(FloatKind::F64),
689            RealExpr::FromInt(_) => FloatKind::F64,
690            RealExpr::ToShort(_) => FloatKind::F32,
691            RealExpr::Neg(x) => x.kind(kinds),
692            RealExpr::Add(a, b)
693            | RealExpr::Sub(a, b)
694            | RealExpr::Mul(a, b)
695            | RealExpr::Div(a, b) => {
696                if a.kind(kinds) == FloatKind::F32 && b.kind(kinds) == FloatKind::F32 {
697                    FloatKind::F32
698                } else {
699                    FloatKind::F64
700                }
701            }
702        }
703    }
704
705    /// Calls `f` on every random variable read.
706    pub fn for_each_var(&self, f: &mut impl FnMut(RealVarId)) {
707        match self {
708            RealExpr::Var(v) => f(*v),
709            RealExpr::Const(..) | RealExpr::State(..) | RealExpr::FromInt(_) => {}
710            RealExpr::Neg(x) | RealExpr::ToShort(x) => x.for_each_var(f),
711            RealExpr::Add(a, b)
712            | RealExpr::Sub(a, b)
713            | RealExpr::Mul(a, b)
714            | RealExpr::Div(a, b) => {
715                a.for_each_var(f);
716                b.for_each_var(f);
717            }
718        }
719    }
720
721    /// Calls `f` on every state variable read (including those of an
722    /// integral sub-expression).
723    pub fn for_each_state(&self, f: &mut impl FnMut(StateId)) {
724        match self {
725            RealExpr::State(s, _) => f(*s),
726            RealExpr::FromInt(e) => e.for_each_state(f),
727            RealExpr::Const(..) | RealExpr::Var(_) => {}
728            RealExpr::Neg(x) | RealExpr::ToShort(x) => x.for_each_state(f),
729            RealExpr::Add(a, b)
730            | RealExpr::Sub(a, b)
731            | RealExpr::Mul(a, b)
732            | RealExpr::Div(a, b) => {
733                a.for_each_state(f);
734                b.for_each_state(f);
735            }
736        }
737    }
738}
739
740impl RealCond {
741    /// Calls `f` on every random variable read.
742    pub fn for_each_var(&self, f: &mut impl FnMut(RealVarId)) {
743        match self {
744            RealCond::Const(_) | RealCond::Int(_) => {}
745            RealCond::Cmp(_, l, r) => {
746                l.for_each_var(f);
747                r.for_each_var(f);
748            }
749            RealCond::Not(x) => x.for_each_var(f),
750            RealCond::And(xs) | RealCond::Or(xs) => xs.iter().for_each(|x| x.for_each_var(f)),
751            RealCond::Guard(p, t, e) => {
752                p.for_each_var(f);
753                t.for_each_var(f);
754                e.for_each_var(f);
755            }
756        }
757    }
758
759    /// The distinct random variables read, ascending.
760    pub fn vars(&self) -> Vec<RealVarId> {
761        let mut v = Vec::new();
762        self.for_each_var(&mut |x| v.push(x));
763        v.sort_unstable();
764        v.dedup();
765        v
766    }
767}
768
769/// What an evaluation reads: the type of every variable, the values of the
770/// variables fixed for this evaluation, and the state values.
771struct Ctx<'a> {
772    kinds: &'a [FloatKind],
773    /// `Some` for a variable with a value (inactive, or the one being probed).
774    vars: Vec<Option<f64>>,
775    states: &'a [Option<BigUint>],
776}
777
778fn int_value(e: &Expr, states: &[Option<BigUint>]) -> Result<BigUint, RealError> {
779    let a = Assignment {
780        vars: Vec::new(),
781        states: states.to_vec(),
782    };
783    super::ir::eval(e, &a).map_err(|err| match err {
784        ConstraintEvalError::StateUnknown(s) => RealError::StateUnreadable(s),
785        ConstraintEvalError::DivideByZero => RealError::NonFinite("integer division by zero"),
786        ConstraintEvalError::UnboundVar(_)
787        | ConstraintEvalError::UnboundState(_)
788        | ConstraintEvalError::Unexpanded => {
789            RealError::Shape("an integral sub-expression reads an unbound variable".into())
790        }
791    })
792}
793
794/// A two's-complement `v` of `e`'s type as a number.
795fn signed_number(v: &BigUint, e: &Expr) -> BigInt {
796    let t = e.ty();
797    if t.signed && v.bit(u64::from(t.width) - 1) {
798        BigInt::from(v.clone()) - (BigInt::from(1) << t.width)
799    } else {
800        BigInt::from(v.clone())
801    }
802}
803
804impl Ctx<'_> {
805    fn state(&self, s: StateId) -> Result<&BigUint, RealError> {
806        match self.states.get(s as usize) {
807            Some(Some(v)) => Ok(v),
808            _ => Err(RealError::StateUnreadable(s)),
809        }
810    }
811
812    /// The value of `e` (module docs for the NaN and division rules).
813    fn eval(&self, e: &RealExpr) -> Result<f64, RealError> {
814        let finite = |x: f64, k: FloatKind| -> Result<f64, RealError> {
815            let x = k.round(x);
816            if x.is_nan() {
817                Err(RealError::NonFinite("a real expression is NaN"))
818            } else {
819                Ok(x)
820            }
821        };
822        Ok(match e {
823            RealExpr::Const(r, _) => r.get(),
824            RealExpr::Var(v) => self
825                .vars
826                .get(*v as usize)
827                .copied()
828                .flatten()
829                .ok_or_else(|| RealError::Shape(format!("real variable #{v} has no value here")))?,
830            RealExpr::State(s, k) => k.from_bits(self.state(*s)?),
831            RealExpr::FromInt(x) => {
832                let v = int_value(x, self.states)?;
833                signed_number(&v, x).to_f64().unwrap_or(f64::NAN)
834            }
835            RealExpr::Neg(x) => -self.eval(x)?,
836            RealExpr::ToShort(x) => finite(self.eval(x)?, FloatKind::F32)?,
837            RealExpr::Add(a, b)
838            | RealExpr::Sub(a, b)
839            | RealExpr::Mul(a, b)
840            | RealExpr::Div(a, b) => {
841                let (x, y) = (self.eval(a)?, self.eval(b)?);
842                let k = e.kind(self.kinds);
843                let r = match e {
844                    RealExpr::Add(..) => x + y,
845                    RealExpr::Sub(..) => x - y,
846                    RealExpr::Mul(..) => x * y,
847                    _ => {
848                        if y == 0.0 {
849                            return Err(RealError::NonFinite("division by zero"));
850                        }
851                        x / y
852                    }
853                };
854                finite(r, k)?
855            }
856        })
857    }
858
859    /// The truth of one comparison.
860    fn cmp(&self, op: CmpOp, l: &RealExpr, r: &RealExpr) -> Result<bool, RealError> {
861        let (x, y) = (self.eval(l)?, self.eval(r)?);
862        Ok(match op {
863            CmpOp::Lt => x < y,
864            CmpOp::Le => x <= y,
865            CmpOp::Gt => x > y,
866            CmpOp::Ge => x >= y,
867            CmpOp::Eq => x == y,
868            CmpOp::Ne => x != y,
869        })
870    }
871
872    fn expr_reads_free(&self, e: &RealExpr) -> bool {
873        let mut any = false;
874        e.for_each_var(&mut |v| any |= self.vars.get(v as usize).copied().flatten().is_none());
875        any
876    }
877
878    /// The Figure 18-3 value of the predicate `p` (module docs of
879    /// [`super::guard`]): `Random` exactly when it reads a variable with no
880    /// value yet and nothing decides it.
881    fn pred_value(&self, p: &RealCond) -> GuardValue {
882        match p {
883            RealCond::Const(b) => {
884                if *b {
885                    GuardValue::True
886                } else {
887                    GuardValue::False
888                }
889            }
890            RealCond::Int(e) => guard::value(
891                e,
892                &Assignment {
893                    vars: Vec::new(),
894                    states: self.states.to_vec(),
895                },
896            ),
897            RealCond::Cmp(op, l, r) => {
898                let mut bad = false;
899                let mut note = |s: StateId| bad |= self.state(s).is_err();
900                l.for_each_state(&mut note);
901                r.for_each_state(&mut note);
902                if bad {
903                    GuardValue::Error
904                } else if self.expr_reads_free(l) || self.expr_reads_free(r) {
905                    GuardValue::Random
906                } else {
907                    match self.cmp(*op, l, r) {
908                        Ok(true) => GuardValue::True,
909                        Ok(false) => GuardValue::False,
910                        Err(_) => GuardValue::Error,
911                    }
912                }
913            }
914            RealCond::Not(x) => self.pred_value(x).not(),
915            RealCond::And(xs) => xs
916                .iter()
917                .fold(GuardValue::True, |acc, x| acc.and(self.pred_value(x))),
918            RealCond::Or(xs) => xs
919                .iter()
920                .fold(GuardValue::False, |acc, x| acc.or(self.pred_value(x))),
921            RealCond::Guard(c, t, e) => match self.pred_value(c) {
922                GuardValue::True => self.pred_value(t),
923                GuardValue::False => self.pred_value(e),
924                GuardValue::Error => GuardValue::Error,
925                GuardValue::Random => GuardValue::Random,
926            },
927        }
928    }
929
930    /// Strict truth of a condition that reads no free variable.
931    fn truth(&self, c: &RealCond) -> Result<bool, RealError> {
932        Ok(match c {
933            RealCond::Const(b) => *b,
934            RealCond::Int(e) => !int_value(e, self.states)?.is_zero(),
935            RealCond::Cmp(op, l, r) => self.cmp(*op, l, r)?,
936            RealCond::Not(x) => !self.truth(x)?,
937            RealCond::And(xs) => {
938                let mut all = true;
939                for x in xs {
940                    all &= self.truth(x)?;
941                }
942                all
943            }
944            RealCond::Or(xs) => {
945                let mut any = false;
946                for x in xs {
947                    any |= self.truth(x)?;
948                }
949                any
950            }
951            RealCond::Guard(p, t, e) => match self.pred_value(p) {
952                GuardValue::True => self.truth(t)?,
953                GuardValue::False => self.truth(e)?,
954                GuardValue::Error => return Err(RealError::GuardError),
955                GuardValue::Random => {
956                    return Err(RealError::Shape("a guard reads a free variable".into()));
957                }
958            },
959        })
960    }
961}
962
963/// The values of a monotone predicate over the whole grid: `pred` is true on
964/// a prefix (`prefix_true`) or a suffix of the indices, and the boundary is
965/// found by bisection.
966fn monotone_set(
967    kind: FloatKind,
968    prefix_true: bool,
969    mut pred: impl FnMut(f64) -> Result<bool, RealError>,
970) -> Result<RealSet, RealError> {
971    let m = kind.max_idx();
972    let mut at = |i: i64| pred(kind.val(i));
973    let (lo_true, hi_true) = (at(-m)?, at(m)?);
974    let (first, last) = if prefix_true {
975        (lo_true, hi_true)
976    } else {
977        (hi_true, lo_true)
978    };
979    // `first` is the predicate at the end where it is expected to hold.
980    if !first {
981        return Ok(RealSet::empty(kind));
982    }
983    if last {
984        return Ok(RealSet::full(kind));
985    }
986    // Invariant: `good` satisfies the predicate, `bad` does not.
987    let (mut good, mut bad) = if prefix_true { (-m, m) } else { (m, -m) };
988    while good.abs_diff(bad) > 1 {
989        let mid = ((i128::from(good) + i128::from(bad)) / 2) as i64;
990        if at(mid)? {
991            good = mid;
992        } else {
993            bad = mid;
994        }
995    }
996    Ok(if prefix_true {
997        RealSet::from_idx(kind, -m, good)
998    } else {
999        RealSet::from_idx(kind, good, m)
1000    })
1001}
1002
1003impl<'a> Ctx<'a> {
1004    /// This context with the free variable `v` bound to `x`.
1005    fn bind(&self, v: RealVarId, x: f64) -> Ctx<'a> {
1006        let mut vars = self.vars.clone();
1007        vars[v as usize] = Some(x);
1008        Ctx {
1009            kinds: self.kinds,
1010            vars,
1011            states: self.states,
1012        }
1013    }
1014
1015    /// The free random variables `c` reads.
1016    fn free_vars(&self, c: &RealCond) -> Vec<RealVarId> {
1017        c.vars()
1018            .into_iter()
1019            .filter(|v| self.vars.get(*v as usize).copied().flatten().is_none())
1020            .collect()
1021    }
1022
1023    /// The `(slope, offset)` of `e` as an affine function of the free
1024    /// variable `v`; only the slope's sign and finiteness are used.
1025    fn lin(&self, e: &RealExpr, v: RealVarId) -> Result<(f64, f64), RealError> {
1026        let non_affine =
1027            || RealError::Shape("a product or quotient is not affine in the variable".into());
1028        Ok(match e {
1029            RealExpr::Const(r, _) => (0.0, r.get()),
1030            RealExpr::Var(x) if *x == v => (1.0, 0.0),
1031            RealExpr::Var(_) | RealExpr::State(..) | RealExpr::FromInt(_) => (0.0, self.eval(e)?),
1032            RealExpr::Neg(x) => {
1033                let (c, d) = self.lin(x, v)?;
1034                (-c, -d)
1035            }
1036            RealExpr::ToShort(x) => self.lin(x, v)?,
1037            RealExpr::Add(a, b) => {
1038                let ((c1, d1), (c2, d2)) = (self.lin(a, v)?, self.lin(b, v)?);
1039                (c1 + c2, d1 + d2)
1040            }
1041            RealExpr::Sub(a, b) => {
1042                let ((c1, d1), (c2, d2)) = (self.lin(a, v)?, self.lin(b, v)?);
1043                (c1 - c2, d1 - d2)
1044            }
1045            RealExpr::Mul(a, b) => {
1046                let ((c1, d1), (c2, d2)) = (self.lin(a, v)?, self.lin(b, v)?);
1047                if c1 == 0.0 {
1048                    (d1 * c2, d1 * d2)
1049                } else if c2 == 0.0 {
1050                    (d2 * c1, d2 * d1)
1051                } else {
1052                    return Err(non_affine());
1053                }
1054            }
1055            RealExpr::Div(a, b) => {
1056                let ((c1, d1), (c2, d2)) = (self.lin(a, v)?, self.lin(b, v)?);
1057                if c2 != 0.0 {
1058                    return Err(non_affine());
1059                }
1060                if d2 == 0.0 {
1061                    return Err(RealError::NonFinite("division by zero"));
1062                }
1063                (c1 / d2, d1 / d2)
1064            }
1065        })
1066    }
1067
1068    /// The set of values of the free variable `v` for which the comparison
1069    /// `l op r` is true under IEEE evaluation (module docs).
1070    fn cmp_set(
1071        &self,
1072        op: CmpOp,
1073        l: &RealExpr,
1074        r: &RealExpr,
1075        v: RealVarId,
1076    ) -> Result<RealSet, RealError> {
1077        let kind = self.kinds[v as usize];
1078        let mut others = false;
1079        for e in [l, r] {
1080            e.for_each_var(&mut |x| {
1081                others |= x != v && self.vars.get(x as usize).copied().flatten().is_none();
1082            });
1083        }
1084        if others {
1085            return Err(RealError::Shape(
1086                "a comparison reads two random real variables".into(),
1087            ));
1088        }
1089        let (c, _) = {
1090            let ((c1, d1), (c2, d2)) = (self.lin(l, v)?, self.lin(r, v)?);
1091            (c1 - c2, d1 - d2)
1092        };
1093        if !c.is_finite() {
1094            return Err(RealError::NonFinite("a real coefficient is not finite"));
1095        }
1096        let (full, empty) = (RealSet::full(kind), RealSet::empty(kind));
1097        if c == 0.0 {
1098            return Ok(if self.bind(v, 0.0).cmp(op, l, r)? {
1099                full
1100            } else {
1101                empty
1102            });
1103        }
1104        let prefix = c > 0.0;
1105        let lt = || monotone_set(kind, prefix, |x| self.bind(v, x).cmp(CmpOp::Lt, l, r));
1106        let le = || monotone_set(kind, prefix, |x| self.bind(v, x).cmp(CmpOp::Le, l, r));
1107        Ok(match op {
1108            CmpOp::Lt => lt()?,
1109            CmpOp::Le => le()?,
1110            CmpOp::Gt => le()?.complement(),
1111            CmpOp::Ge => lt()?.complement(),
1112            CmpOp::Eq => le()?.intersect(&lt()?.complement()),
1113            CmpOp::Ne => le()?.intersect(&lt()?.complement()).complement(),
1114        })
1115    }
1116
1117    /// The values of `v` that satisfy `c` (which reads no free variable
1118    /// other than `v`), strictly: an unreadable state is an error, but a
1119    /// guard predicate is evaluated by Figure 18-3.
1120    fn set_of(&self, c: &RealCond, v: RealVarId) -> Result<RealSet, RealError> {
1121        let kind = self.kinds[v as usize];
1122        let all = |b: bool| {
1123            if b {
1124                RealSet::full(kind)
1125            } else {
1126                RealSet::empty(kind)
1127            }
1128        };
1129        Ok(match c {
1130            RealCond::Const(b) => all(*b),
1131            RealCond::Int(_) => all(self.truth(c)?),
1132            RealCond::Cmp(op, l, r) => {
1133                if self.expr_reads_free(l) || self.expr_reads_free(r) {
1134                    self.cmp_set(*op, l, r, v)?
1135                } else {
1136                    all(self.cmp(*op, l, r)?)
1137                }
1138            }
1139            RealCond::Not(x) => self.set_of(x, v)?.complement(),
1140            RealCond::And(xs) => {
1141                let mut s = all(true);
1142                for x in xs {
1143                    s = s.intersect(&self.set_of(x, v)?);
1144                }
1145                s
1146            }
1147            RealCond::Or(xs) => {
1148                let mut s = all(false);
1149                for x in xs {
1150                    s = s.union(&self.set_of(x, v)?);
1151                }
1152                s
1153            }
1154            RealCond::Guard(p, t, e) => match self.pred_value(p) {
1155                GuardValue::True => self.set_of(t, v)?,
1156                GuardValue::False => self.set_of(e, v)?,
1157                GuardValue::Error => return Err(RealError::GuardError),
1158                GuardValue::Random => {
1159                    let ps = self.pred_set(p, v)?;
1160                    ps.intersect(&self.set_of(t, v)?)
1161                        .union(&ps.complement().intersect(&self.set_of(e, v)?))
1162                }
1163            },
1164        })
1165    }
1166
1167    /// The values of `v` for which the guard predicate `p` is true (Figure
1168    /// 18-3 decides what it can; the rest reads `v`).
1169    fn pred_set(&self, p: &RealCond, v: RealVarId) -> Result<RealSet, RealError> {
1170        let kind = self.kinds[v as usize];
1171        match self.pred_value(p) {
1172            GuardValue::True => Ok(RealSet::full(kind)),
1173            GuardValue::False => Ok(RealSet::empty(kind)),
1174            GuardValue::Error => Err(RealError::GuardError),
1175            GuardValue::Random => match p {
1176                RealCond::Not(x) => Ok(self.pred_set(x, v)?.complement()),
1177                RealCond::And(xs) => {
1178                    let mut s = RealSet::full(kind);
1179                    for x in xs {
1180                        s = s.intersect(&self.pred_set(x, v)?);
1181                    }
1182                    Ok(s)
1183                }
1184                RealCond::Or(xs) => {
1185                    let mut s = RealSet::empty(kind);
1186                    for x in xs {
1187                        s = s.union(&self.pred_set(x, v)?);
1188                    }
1189                    Ok(s)
1190                }
1191                RealCond::Cmp(op, l, r) => self.cmp_set(*op, l, r, v),
1192                RealCond::Guard(..) => self.set_of(p, v),
1193                RealCond::Const(_) | RealCond::Int(_) => {
1194                    unreachable!("a state-only predicate is never RANDOM")
1195                }
1196            },
1197        }
1198    }
1199}
1200
1201/// A non-negative weight (module docs of [`super::dist`]).
1202fn weight_of(e: &Expr, states: &[Option<BigUint>]) -> Result<f64, RealError> {
1203    let n = signed_number(&int_value(e, states)?, e);
1204    if n < BigInt::zero() {
1205        return Err(RealError::BadWeight);
1206    }
1207    Ok(n.to_f64().unwrap_or(f64::INFINITY))
1208}
1209
1210/// A real `dist` evaluated under the current state values.
1211struct DistPlan {
1212    /// Each explicit item's values and weight.
1213    items: Vec<(RealSet, f64)>,
1214    /// The default's values (everything no item names) and weight.
1215    default: Option<(RealSet, f64)>,
1216    /// The values with a positive weight: where the variable may land.
1217    support: RealSet,
1218}
1219
1220fn dist_plan(ctx: &Ctx<'_>, d: &RealDist, kind: FloatKind) -> Result<DistPlan, RealError> {
1221    let mut items = Vec::with_capacity(d.items.len());
1222    let mut named = RealSet::empty(kind);
1223    let mut support = RealSet::empty(kind);
1224    for it in &d.items {
1225        let set = match &it.span {
1226            RealSpan::Point(e) => RealSet::point(kind, ctx.eval(e)?),
1227            RealSpan::Range { lo, hi } => {
1228                let lo = lo.as_ref().map_or(Ok(f64::NEG_INFINITY), |e| ctx.eval(e))?;
1229                let hi = hi.as_ref().map_or(Ok(f64::INFINITY), |e| ctx.eval(e))?;
1230                RealSet::range(kind, lo, hi)
1231            }
1232        };
1233        let w = weight_of(&it.weight, ctx.states)?;
1234        named = named.union(&set);
1235        if w > 0.0 {
1236            support = support.union(&set);
1237        }
1238        items.push((set, w));
1239    }
1240    let default = match &d.default {
1241        Some(e) => {
1242            let w = weight_of(e, ctx.states)?;
1243            let region = named.complement();
1244            if w > 0.0 {
1245                support = support.union(&region);
1246            }
1247            Some((region, w))
1248        }
1249        None => None,
1250    };
1251    Ok(DistPlan {
1252        items,
1253        default,
1254        support,
1255    })
1256}
1257
1258/// The share of `item` that `f` leaves (module docs: a `:/` weight is spread
1259/// over the item's length).
1260fn share(item: &RealSet, f: &RealSet) -> f64 {
1261    let inter = item.intersect(f);
1262    if inter.is_empty() {
1263        return 0.0;
1264    }
1265    let m = item.measure();
1266    if m > 0.0 {
1267        (inter.measure() / m).min(1.0)
1268    } else {
1269        1.0
1270    }
1271}
1272
1273impl DistPlan {
1274    /// A value of `f` (a non-empty subset of the support) drawn by the
1275    /// items' weights.
1276    fn sample(&self, f: &RealSet, seed: &mut u64) -> Result<f64, RealError> {
1277        let all: Vec<(&RealSet, f64)> = self
1278            .items
1279            .iter()
1280            .map(|(s, w)| (s, *w))
1281            .chain(self.default.iter().map(|(s, w)| (s, *w)))
1282            .collect();
1283        let pick = |weights: &[f64], seed: &mut u64| -> Option<usize> {
1284            let total: f64 = weights.iter().sum();
1285            if !total.is_finite() || total <= 0.0 {
1286                return None;
1287            }
1288            let mut t = unit(seed) * total;
1289            let mut last = None;
1290            for (i, w) in weights.iter().enumerate() {
1291                if *w > 0.0 {
1292                    last = Some(i);
1293                    if t < *w {
1294                        return Some(i);
1295                    }
1296                    t -= *w;
1297                }
1298            }
1299            last
1300        };
1301        let eff: Vec<f64> = all.iter().map(|(s, w)| w * share(s, f)).collect();
1302        let i = match pick(&eff, seed) {
1303            Some(i) => i,
1304            None => {
1305                // Every positive-weight item is cut to a point: weigh by the
1306                // nominal weights of the items that still hold a value.
1307                let nominal: Vec<f64> = all
1308                    .iter()
1309                    .map(|(s, w)| if s.intersect(f).is_empty() { 0.0 } else { *w })
1310                    .collect();
1311                pick(&nominal, seed).ok_or(RealError::Unsatisfiable)?
1312            }
1313        };
1314        Ok(all[i].0.intersect(f).sample(seed))
1315    }
1316}
1317
1318/// One guard that couples its predicate variable `a` to other variables.
1319struct Coupled {
1320    a: RealVarId,
1321    pred: RealSet,
1322    then: Vec<(RealVarId, RealSet)>,
1323    other: Vec<(RealVarId, RealSet)>,
1324}
1325
1326impl Coupled {
1327    fn arm(&self, then: bool) -> &[(RealVarId, RealSet)] {
1328        if then { &self.then } else { &self.other }
1329    }
1330}
1331
1332/// The conjuncts of a problem, classified by the variables they involve.
1333#[derive(Default)]
1334struct Lowered {
1335    unary: Vec<(RealVarId, RealSet)>,
1336    coupled: Vec<Coupled>,
1337    /// Conjuncts reading two or more free random reals, solved together by
1338    /// [`joint_draw`].
1339    joint: Vec<RealCond>,
1340    infeasible: bool,
1341}
1342
1343impl Ctx<'_> {
1344    /// Classifies `c` into `out` (see [`solve`]).
1345    fn lower(&self, c: &RealCond, out: &mut Lowered) -> Result<(), RealError> {
1346        let free = self.free_vars(c);
1347        match c {
1348            RealCond::Guard(p, t, e) => match self.pred_value(p) {
1349                GuardValue::True => self.lower(t, out),
1350                GuardValue::False => self.lower(e, out),
1351                GuardValue::Error => Err(RealError::GuardError),
1352                GuardValue::Random => {
1353                    if free.len() == 1 {
1354                        out.unary.push((free[0], self.set_of(c, free[0])?));
1355                        return Ok(());
1356                    }
1357                    let pf = self.free_vars(p);
1358                    let [a] = pf[..] else {
1359                        out.joint.push(c.clone());
1360                        return Ok(());
1361                    };
1362                    let pred = self.pred_set(p, a)?;
1363                    let mut arms: [Vec<(RealVarId, RealSet)>; 2] = [Vec::new(), Vec::new()];
1364                    for (k, arm) in [t, e].into_iter().enumerate() {
1365                        let mut l = Lowered::default();
1366                        self.lower(arm, &mut l)?;
1367                        if !l.coupled.is_empty() {
1368                            return Err(RealError::Shape(
1369                                "a guard nested in a coupling guard couples two more variables"
1370                                    .into(),
1371                            ));
1372                        }
1373                        // The arm applies where the predicate holds (then) or
1374                        // fails (else); a requirement on `a` itself or a
1375                        // contradiction is a restriction of `a` alone.
1376                        let applies = if k == 0 {
1377                            pred.clone()
1378                        } else {
1379                            pred.complement()
1380                        };
1381                        let off = applies.complement();
1382                        if l.infeasible {
1383                            out.unary.push((a, off.clone()));
1384                        }
1385                        for (v, s) in l.unary {
1386                            if v == a {
1387                                out.unary.push((a, off.union(&s)));
1388                            } else {
1389                                arms[k].push((v, s));
1390                            }
1391                        }
1392                    }
1393                    let [then, other] = arms;
1394                    if !then.is_empty() || !other.is_empty() {
1395                        out.coupled.push(Coupled {
1396                            a,
1397                            pred,
1398                            then,
1399                            other,
1400                        });
1401                    }
1402                    Ok(())
1403                }
1404            },
1405            _ => match free.len() {
1406                0 => {
1407                    if !self.truth(c)? {
1408                        out.infeasible = true;
1409                    }
1410                    Ok(())
1411                }
1412                1 => {
1413                    out.unary.push((free[0], self.set_of(c, free[0])?));
1414                    Ok(())
1415                }
1416                _ => match c {
1417                    RealCond::And(xs) => xs.iter().try_for_each(|x| self.lower(x, out)),
1418                    _ => {
1419                        out.joint.push(c.clone());
1420                        Ok(())
1421                    }
1422                },
1423            },
1424        }
1425    }
1426}
1427
1428/// Draws attempted by [`joint_draw`] before it gives up.
1429const JOINT_TRIES: usize = 20_000;
1430
1431/// Solves the conjuncts that read several free random reals together (LRM
1432/// 18.5: every legal solution is equally likely) by rejection sampling: each
1433/// variable is drawn from its own feasible set (its unary constraints and
1434/// `dist`), and the draw is kept only when every joint conjunct holds, so the
1435/// accepted tuple is uniform over the joint feasible region. Any expression
1436/// shape is accepted (`f < g`, `f + g < 1.0`, `f * g > 2.0`); a region so thin
1437/// that no draw lands in it (an equality between two random reals has measure
1438/// zero) is refused loudly, never reported as a solution.
1439fn joint_draw(
1440    ctx: &Ctx<'_>,
1441    joint: &[RealCond],
1442    own: &[RealSet],
1443    plans: &[Option<DistPlan>],
1444    seed: &mut u64,
1445) -> Result<Vec<(usize, f64)>, RealError> {
1446    let mut vars: Vec<usize> = joint
1447        .iter()
1448        .flat_map(|c| ctx.free_vars(c))
1449        .map(|v| v as usize)
1450        .collect();
1451    vars.sort_unstable();
1452    vars.dedup();
1453    if vars.iter().any(|&v| own[v].is_empty()) {
1454        return Err(RealError::Unsatisfiable);
1455    }
1456    let mut probe = Ctx {
1457        kinds: ctx.kinds,
1458        vars: ctx.vars.clone(),
1459        states: ctx.states,
1460    };
1461    'tries: for _ in 0..JOINT_TRIES {
1462        for &v in &vars {
1463            let x = match &plans[v] {
1464                Some(plan) => plan.sample(&own[v], seed)?,
1465                None => own[v].sample(seed),
1466            };
1467            probe.vars[v] = Some(x);
1468        }
1469        for c in joint {
1470            match probe.truth(c) {
1471                Ok(true) => {}
1472                // A draw whose expression is NaN or divides by zero is not a
1473                // solution; try another.
1474                Ok(false) | Err(RealError::NonFinite(_)) => continue 'tries,
1475                Err(e) => return Err(e),
1476            }
1477        }
1478        return Ok(vars
1479            .iter()
1480            .map(|&v| (v, probe.vars[v].unwrap_or(0.0)))
1481            .collect());
1482    }
1483    Err(RealError::Shape(format!(
1484        "no draw satisfied the constraints that relate several random reals in {JOINT_TRIES} \
1485         tries (an equality between random reals, or a region too thin to sample)"
1486    )))
1487}
1488
1489/// Whether `a` is ordered before `b` by the transitive closure of `before`.
1490fn ordered_before(before: &[(RealVarId, RealVarId)], a: RealVarId, b: RealVarId) -> bool {
1491    let mut seen = vec![a];
1492    let mut work = vec![a];
1493    while let Some(x) = work.pop() {
1494        for &(p, q) in before {
1495            if p == x && !seen.contains(&q) {
1496                if q == b {
1497                    return true;
1498                }
1499                seen.push(q);
1500                work.push(q);
1501            }
1502        }
1503    }
1504    false
1505}
1506
1507/// Solves the real variables of `p` (module docs; `Ok` has `Some` for every
1508/// variable that was free, i.e. `known[v]` is `None`).
1509///
1510/// `known[v]` is the fixed value of an inactive (`rand_mode(0)`) variable.
1511/// All draws come from `seed`, and none are made when no variable is free.
1512pub fn solve(
1513    p: &RealProblem,
1514    known: &[Option<f64>],
1515    states: &[Option<BigUint>],
1516    seed: &mut u64,
1517) -> Result<Vec<Option<f64>>, RealError> {
1518    let n = p.kinds.len();
1519    if known.len() != n {
1520        return Err(RealError::Shape("one known value per real variable".into()));
1521    }
1522    let ctx = Ctx {
1523        kinds: &p.kinds,
1524        vars: known.to_vec(),
1525        states,
1526    };
1527    let mut low = Lowered::default();
1528    for c in &p.conds {
1529        ctx.lower(c, &mut low)?;
1530    }
1531    let mut plans: Vec<Option<DistPlan>> = (0..n).map(|_| None).collect();
1532    for d in &p.dists {
1533        let v = d.var as usize;
1534        if v >= n || plans[v].is_some() {
1535            return Err(RealError::Shape(
1536                "a `dist` names an unknown variable, or a variable has two".into(),
1537            ));
1538        }
1539        let plan = dist_plan(&ctx, d, p.kinds[v])?;
1540        if let Some(x) = known[v]
1541            && !plan.support.contains(x)
1542        {
1543            low.infeasible = true;
1544        }
1545        plans[v] = Some(plan);
1546    }
1547    if low.infeasible {
1548        return Err(RealError::Unsatisfiable);
1549    }
1550
1551    let active: Vec<usize> = (0..n).filter(|&v| known[v].is_none()).collect();
1552    let mut own: Vec<RealSet> = (0..n).map(|v| RealSet::full(p.kinds[v])).collect();
1553    for (v, s) in &low.unary {
1554        own[*v as usize] = own[*v as usize].intersect(s);
1555    }
1556    for &v in &active {
1557        if let Some(plan) = &plans[v] {
1558            own[v] = own[v].intersect(&plan.support);
1559        }
1560    }
1561
1562    // Joint conjuncts (several free random reals) are drawn first; their
1563    // variables may not also take part in a coupling guard.
1564    let mut out: Vec<Option<f64>> = vec![None; n];
1565    if !low.joint.is_empty() {
1566        let drawn = joint_draw(&ctx, &low.joint, &own, &plans, seed)?;
1567        for g in &low.coupled {
1568            let mut touched = vec![g.a as usize];
1569            touched.extend(g.then.iter().chain(&g.other).map(|(b, _)| *b as usize));
1570            if touched.iter().any(|t| drawn.iter().any(|(v, _)| v == t)) {
1571                return Err(RealError::Shape(
1572                    "a random real that a constraint relates to another random real also takes \
1573                     part in a coupling guard"
1574                        .into(),
1575                ));
1576            }
1577        }
1578        for (v, x) in drawn {
1579            out[v] = Some(x);
1580        }
1581    }
1582
1583    // The coupling forest: each variable has at most one parent.
1584    let mut parent: Vec<Option<RealVarId>> = vec![None; n];
1585    for g in &low.coupled {
1586        for arm in [&g.then, &g.other] {
1587            for (b, _) in arm {
1588                match parent[*b as usize] {
1589                    Some(q) if q != g.a => {
1590                        return Err(RealError::Shape(format!(
1591                            "real variable #{b} is constrained by guards on two other variables"
1592                        )));
1593                    }
1594                    _ => parent[*b as usize] = Some(g.a),
1595                }
1596                if !ordered_before(&p.before, g.a, *b) {
1597                    return Err(RealError::Shape(format!(
1598                        "a guard on real variable #{} constrains #{b}; add `solve` the first \
1599                         `before` the second",
1600                        g.a
1601                    )));
1602                }
1603            }
1604        }
1605    }
1606    let depth = |mut v: usize| -> Result<usize, RealError> {
1607        let mut d = 0;
1608        while let Some(q) = parent[v] {
1609            d += 1;
1610            v = q as usize;
1611            if d > n {
1612                return Err(RealError::Shape("a cycle of coupled real guards".into()));
1613            }
1614        }
1615        Ok(d)
1616    };
1617    let mut order: Vec<(usize, usize)> = Vec::with_capacity(active.len());
1618    for &v in &active {
1619        order.push((depth(v)?, v));
1620    }
1621    order.sort_unstable();
1622    let order: Vec<usize> = order.into_iter().map(|(_, v)| v).collect();
1623
1624    // Backward: the values of each variable some completion exists for.
1625    let mut down: Vec<RealSet> = own.clone();
1626    for &v in order.iter().rev() {
1627        let guards: Vec<&Coupled> = low.coupled.iter().filter(|g| g.a as usize == v).collect();
1628        if guards.is_empty() {
1629            if down[v].is_empty() {
1630                return Err(RealError::Unsatisfiable);
1631            }
1632            continue;
1633        }
1634        let mut cells: Vec<(RealSet, Vec<bool>)> = vec![(own[v].clone(), Vec::new())];
1635        for g in &guards {
1636            let mut next = Vec::new();
1637            for (s, sig) in &cells {
1638                for (then, pset) in [(true, g.pred.clone()), (false, g.pred.complement())] {
1639                    let t = s.intersect(&pset);
1640                    if !t.is_empty() {
1641                        let mut sg = sig.clone();
1642                        sg.push(then);
1643                        next.push((t, sg));
1644                    }
1645                }
1646            }
1647            cells = next;
1648        }
1649        let mut children: Vec<usize> = guards
1650            .iter()
1651            .flat_map(|g| g.then.iter().chain(&g.other).map(|(b, _)| *b as usize))
1652            .collect();
1653        children.sort_unstable();
1654        children.dedup();
1655        let mut feasible = RealSet::empty(p.kinds[v]);
1656        for (s, sig) in &cells {
1657            let ok = children.iter().all(|&b| {
1658                let mut need = down[b].clone();
1659                for (g, &then) in guards.iter().zip(sig) {
1660                    for (c, set) in g.arm(then) {
1661                        if *c as usize == b {
1662                            need = need.intersect(set);
1663                        }
1664                    }
1665                }
1666                !need.is_empty()
1667            });
1668            if ok {
1669                feasible = feasible.union(s);
1670            }
1671        }
1672        if feasible.is_empty() {
1673            return Err(RealError::Unsatisfiable);
1674        }
1675        down[v] = feasible;
1676    }
1677
1678    // Forward: each variable in turn, parents first.
1679    let mut extra: Vec<RealSet> = (0..n).map(|v| RealSet::full(p.kinds[v])).collect();
1680    for &v in &order {
1681        if out[v].is_some() {
1682            continue;
1683        }
1684        let f = down[v].intersect(&extra[v]);
1685        if f.is_empty() {
1686            return Err(RealError::Shape(
1687                "a real variable has no value left after its parents were drawn".into(),
1688            ));
1689        }
1690        let x = match &plans[v] {
1691            Some(plan) => plan.sample(&f, seed)?,
1692            None => f.sample(seed),
1693        };
1694        out[v] = Some(x);
1695        for g in low.coupled.iter().filter(|g| g.a as usize == v) {
1696            for (b, s) in g.arm(g.pred.contains(x)) {
1697                extra[*b as usize] = extra[*b as usize].intersect(s);
1698            }
1699        }
1700    }
1701    Ok(out)
1702}
1703
1704impl RealProblem {
1705    /// Checks the shape rules that do not depend on a call's state values or
1706    /// `rand_mode`s (module docs), so the builder can reject a class whose
1707    /// real constraints this solver cannot handle at compile time. [`solve`]
1708    /// re-checks everything it relies on, with the same messages.
1709    pub fn check(&self) -> Result<(), String> {
1710        let n = self.kinds.len() as u32;
1711        let mut edges: Vec<(RealVarId, RealVarId)> = Vec::new();
1712        let mut joint: Vec<RealVarId> = Vec::new();
1713        for c in &self.conds {
1714            check_cond(c, &mut edges, &mut joint)?;
1715            check_affine(c)?;
1716        }
1717        if let Some(&(a, b)) = edges
1718            .iter()
1719            .find(|&&(a, b)| joint.contains(&a) || joint.contains(&b))
1720        {
1721            return Err(format!(
1722                "real variable #{a} or #{b} is both related to another random real and part of \
1723                 a coupling guard"
1724            ));
1725        }
1726        let mut seen = vec![false; self.kinds.len()];
1727        for d in &self.dists {
1728            if d.var >= n || std::mem::replace(&mut seen[d.var as usize], true) {
1729                return Err("a real variable has two `dist` constraints".into());
1730            }
1731        }
1732        if let Some(&(a, b)) = self.before.iter().find(|&&(a, b)| a >= n || b >= n) {
1733            return Err(format!(
1734                "`solve` names real variable #{a} or #{b} that does not exist"
1735            ));
1736        }
1737        let mut parent: Vec<Option<RealVarId>> = vec![None; self.kinds.len()];
1738        for &(a, b) in &edges {
1739            match parent[b as usize] {
1740                Some(q) if q != a => {
1741                    return Err(format!(
1742                        "real variable #{b} is constrained by guards on two other variables"
1743                    ));
1744                }
1745                _ => parent[b as usize] = Some(a),
1746            }
1747            if !ordered_before(&self.before, a, b) {
1748                return Err(format!(
1749                    "a guard on real variable #{a} constrains #{b}; add `solve` the first \
1750                     `before` the second"
1751                ));
1752            }
1753        }
1754        Ok(())
1755    }
1756
1757    /// Validates every integral expression embedded in the problem (the
1758    /// builder's last line of defence against building a malformed tree).
1759    pub fn validate_ir(&self) -> Result<(), super::ir::IrError> {
1760        fn expr(e: &RealExpr) -> Result<(), super::ir::IrError> {
1761            match e {
1762                RealExpr::FromInt(x) => x.validate(),
1763                RealExpr::Const(..) | RealExpr::Var(_) | RealExpr::State(..) => Ok(()),
1764                RealExpr::Neg(x) | RealExpr::ToShort(x) => expr(x),
1765                RealExpr::Add(a, b)
1766                | RealExpr::Sub(a, b)
1767                | RealExpr::Mul(a, b)
1768                | RealExpr::Div(a, b) => {
1769                    expr(a)?;
1770                    expr(b)
1771                }
1772            }
1773        }
1774        fn cond(c: &RealCond) -> Result<(), super::ir::IrError> {
1775            match c {
1776                RealCond::Const(_) => Ok(()),
1777                RealCond::Int(x) => x.validate(),
1778                RealCond::Cmp(_, l, r) => {
1779                    expr(l)?;
1780                    expr(r)
1781                }
1782                RealCond::Not(x) => cond(x),
1783                RealCond::And(xs) | RealCond::Or(xs) => xs.iter().try_for_each(cond),
1784                RealCond::Guard(p, t, e) => {
1785                    cond(p)?;
1786                    cond(t)?;
1787                    cond(e)
1788                }
1789            }
1790        }
1791        self.conds.iter().try_for_each(cond)?;
1792        for d in &self.dists {
1793            for it in &d.items {
1794                it.weight.validate()?;
1795                match &it.span {
1796                    RealSpan::Point(x) => expr(x)?,
1797                    RealSpan::Range { lo, hi } => {
1798                        lo.iter().chain(hi).try_for_each(expr)?;
1799                    }
1800                }
1801            }
1802            if let Some(w) = &d.default {
1803                w.validate()?;
1804            }
1805        }
1806        Ok(())
1807    }
1808}
1809
1810fn check_cond(
1811    c: &RealCond,
1812    edges: &mut Vec<(RealVarId, RealVarId)>,
1813    joint: &mut Vec<RealVarId>,
1814) -> Result<(), String> {
1815    if c.vars().len() <= 1 {
1816        return Ok(());
1817    }
1818    match c {
1819        RealCond::And(xs) => xs.iter().try_for_each(|x| check_cond(x, edges, joint)),
1820        RealCond::Guard(p, t, e) => match p.vars()[..] {
1821            [] => {
1822                check_cond(t, edges, joint)?;
1823                check_cond(e, edges, joint)
1824            }
1825            [a] => {
1826                check_arm(t, a, edges)?;
1827                check_arm(e, a, edges)
1828            }
1829            _ => {
1830                joint.extend(c.vars());
1831                Ok(())
1832            }
1833        },
1834        _ => {
1835            joint.extend(c.vars());
1836            Ok(())
1837        }
1838    }
1839}
1840
1841/// Rejects, at build time, an atom that reads one random variable
1842/// non-affinely (`x * x`, `1.0 / x`): the structural form of the rule
1843/// [`Ctx::lin`] applies at solve time. The arms of a guard are skipped: the
1844/// variable they read may already be bound by then, which makes the product
1845/// affine in the one that is not.
1846fn check_affine(c: &RealCond) -> Result<(), String> {
1847    fn reads(e: &RealExpr, v: RealVarId) -> bool {
1848        let mut any = false;
1849        e.for_each_var(&mut |x| any |= x == v);
1850        any
1851    }
1852    fn affine(e: &RealExpr, v: RealVarId) -> Result<(), String> {
1853        match e {
1854            RealExpr::Const(..) | RealExpr::Var(_) | RealExpr::State(..) | RealExpr::FromInt(_) => {
1855                Ok(())
1856            }
1857            RealExpr::Neg(x) | RealExpr::ToShort(x) => affine(x, v),
1858            RealExpr::Add(a, b) | RealExpr::Sub(a, b) => {
1859                affine(a, v)?;
1860                affine(b, v)
1861            }
1862            RealExpr::Mul(a, b) => {
1863                if reads(a, v) && reads(b, v) {
1864                    return Err(format!(
1865                        "a product of two factors that both read real variable #{v} is not \
1866                         affine in it"
1867                    ));
1868                }
1869                affine(a, v)?;
1870                affine(b, v)
1871            }
1872            RealExpr::Div(a, b) => {
1873                if reads(b, v) {
1874                    return Err(format!(
1875                        "a quotient whose denominator reads real variable #{v} is not affine \
1876                         in it"
1877                    ));
1878                }
1879                affine(a, v)?;
1880                affine(b, v)
1881            }
1882        }
1883    }
1884    match c {
1885        RealCond::Cmp(_, l, r) => match c.vars()[..] {
1886            [v] => {
1887                affine(l, v)?;
1888                affine(r, v)
1889            }
1890            _ => Ok(()),
1891        },
1892        RealCond::Not(x) => check_affine(x),
1893        RealCond::And(xs) | RealCond::Or(xs) => xs.iter().try_for_each(check_affine),
1894        RealCond::Guard(p, _, _) => check_affine(p),
1895        RealCond::Const(_) | RealCond::Int(_) => Ok(()),
1896    }
1897}
1898
1899fn check_arm(
1900    c: &RealCond,
1901    a: RealVarId,
1902    edges: &mut Vec<(RealVarId, RealVarId)>,
1903) -> Result<(), String> {
1904    let vs = c.vars();
1905    if vs.len() <= 1 {
1906        if let [b] = vs[..]
1907            && b != a
1908        {
1909            edges.push((a, b));
1910        }
1911        return Ok(());
1912    }
1913    match c {
1914        RealCond::And(xs) => xs.iter().try_for_each(|x| check_arm(x, a, edges)),
1915        RealCond::Guard(p, t, e) if p.vars().is_empty() => {
1916            check_arm(t, a, edges)?;
1917            check_arm(e, a, edges)
1918        }
1919        RealCond::Guard(..) => {
1920            Err("a guard nested in a coupling guard couples two more variables".into())
1921        }
1922        _ => Err("a conjunct reads two random real variables".into()),
1923    }
1924}
1925
1926#[cfg(test)]
1927mod tests {
1928    use super::*;
1929
1930    const D: FloatKind = FloatKind::F64;
1931    const S: FloatKind = FloatKind::F32;
1932
1933    #[test]
1934    fn the_grid_is_order_preserving_and_round_trips() {
1935        for k in [D, S] {
1936            let xs = [
1937                -k.max_val(),
1938                -1.5,
1939                -1e-30,
1940                0.0,
1941                1e-30,
1942                1.0,
1943                2.5,
1944                k.max_val(),
1945            ]
1946            .map(|x| k.round(x));
1947            for w in xs.windows(2) {
1948                assert!(k.idx(w[0]) < k.idx(w[1]), "{k:?} {w:?}");
1949            }
1950            for x in xs {
1951                assert_eq!(k.val(k.idx(x)), x, "{k:?} {x}");
1952            }
1953            assert_eq!(k.idx(-0.0), 0);
1954            assert_eq!(k.val(k.idx(1.0)), 1.0);
1955        }
1956        // Adjacent floats are adjacent indices.
1957        assert_eq!(D.idx(f64::from_bits(1.0f64.to_bits() + 1)), D.idx(1.0) + 1);
1958        assert_eq!(
1959            S.val(S.idx(1.0) + 1),
1960            f64::from(f32::from_bits(1.0f32.to_bits() + 1))
1961        );
1962    }
1963
1964    #[test]
1965    fn ceil_and_floor_round_the_right_way_for_shortreal() {
1966        let up = f64::from(f32::from_bits(1.0f32.to_bits() + 1));
1967        let mid = (1.0 + up) / 2.0;
1968        assert_eq!(S.ceil_idx(mid), Some(S.idx(1.0) + 1));
1969        assert_eq!(S.floor_idx(mid), Some(S.idx(1.0)));
1970        assert_eq!(S.ceil_idx(1.0), Some(S.idx(1.0)));
1971        assert_eq!(S.floor_idx(1.0), Some(S.idx(1.0)));
1972        assert_eq!(S.ceil_idx(1e300), None);
1973        assert_eq!(S.floor_idx(1e300), Some(S.max_idx()));
1974        assert_eq!(S.ceil_idx(f64::NEG_INFINITY), Some(-S.max_idx()));
1975        assert_eq!(D.floor_idx(f64::NAN), None);
1976    }
1977
1978    #[test]
1979    fn set_algebra() {
1980        let a = RealSet::range(D, 0.0, 2.0);
1981        let b = RealSet::range(D, 1.0, 3.0);
1982        assert!(a.intersect(&b).contains(1.5) && !a.intersect(&b).contains(2.5));
1983        assert!(a.union(&b).contains(2.5) && !a.union(&b).contains(3.5));
1984        let c = a.complement();
1985        assert!(!c.contains(1.0) && c.contains(-1.0) && c.contains(2.5));
1986        assert_eq!(c.complement(), a);
1987        assert!(RealSet::full(D).complement().is_empty());
1988        assert_eq!(RealSet::empty(D).complement(), RealSet::full(D));
1989        // Adjacent floats merge into one interval.
1990        let p = RealSet::point(D, 1.0);
1991        let q = RealSet::point(D, f64::from_bits(1.0f64.to_bits() + 1));
1992        assert_eq!(p.union(&q).intervals().len(), 1);
1993        // A hole.
1994        let hole = RealSet::point(D, 1.0).complement();
1995        assert!(!hole.contains(1.0) && hole.contains(0.999) && hole.contains(1.001));
1996        // A shortreal never equals a real it cannot represent.
1997        assert!(RealSet::point(S, 0.1).is_empty());
1998        assert!(!RealSet::point(S, 0.5).is_empty());
1999        assert!(RealSet::range(D, 2.0, 1.0).is_empty());
2000    }
2001
2002    #[test]
2003    fn measure_does_not_overflow_on_the_full_range() {
2004        let m = RealSet::full(D).measure();
2005        assert!(m.is_finite() && m > 1e308);
2006        assert_eq!(RealSet::point(D, 3.0).measure(), 0.0);
2007        assert_eq!(RealSet::range(D, 1.0, 3.0).measure(), 1.0);
2008    }
2009
2010    #[test]
2011    fn sampling_stays_in_the_set_and_is_uniform_by_length() {
2012        let set = RealSet::range(D, 0.0, 2.0).union(&RealSet::range(D, 10.0, 12.0));
2013        let mut seed = 7u64;
2014        let (mut lo, mut hi, mut n_a) = (0u32, 0u32, 0u32);
2015        for _ in 0..4000 {
2016            let x = set.sample(&mut seed);
2017            assert!(set.contains(x), "{x}");
2018            if x < 5.0 {
2019                n_a += 1;
2020            }
2021            if x % 10.0 < 1.0 {
2022                lo += 1;
2023            } else {
2024                hi += 1;
2025            }
2026        }
2027        // Two equal-length intervals, and each is uniform inside.
2028        assert!((1850..=2150).contains(&n_a), "{n_a}");
2029        assert!((1850..=2150).contains(&lo), "{lo} vs {hi}");
2030        // Full range: finite and both signs.
2031        let full = RealSet::full(D);
2032        let xs: Vec<f64> = (0..200).map(|_| full.sample(&mut seed)).collect();
2033        assert!(xs.iter().all(|x| x.is_finite()));
2034        assert!(xs.iter().any(|&x| x < 0.0) && xs.iter().any(|&x| x > 0.0));
2035    }
2036
2037    #[test]
2038    fn a_point_set_is_sampled_over_its_points() {
2039        let set = RealSet::point(D, 3.0).union(&RealSet::point(D, 5.0));
2040        let mut seed = 1u64;
2041        let mut n3 = 0;
2042        for _ in 0..1000 {
2043            let x = set.sample(&mut seed);
2044            assert!(x == 3.0 || x == 5.0);
2045            n3 += usize::from(x == 3.0);
2046        }
2047        assert!((400..=600).contains(&n3), "{n3}");
2048        // A shortreal range is sampled on the f32 grid.
2049        let sr = RealSet::range(S, 0.0, 1.0);
2050        for _ in 0..200 {
2051            let x = sr.sample(&mut seed);
2052            assert_eq!(f64::from(x as f32), x);
2053            assert!((0.0..=1.0).contains(&x));
2054        }
2055    }
2056
2057    // ---- the constraint solver ------------------------------------------
2058
2059    fn v(i: u32) -> RealExpr {
2060        RealExpr::Var(i)
2061    }
2062    fn k(x: f64) -> RealExpr {
2063        RealExpr::Const(Real::new(x), D)
2064    }
2065    fn bx(e: RealExpr) -> Box<RealExpr> {
2066        Box::new(e)
2067    }
2068    fn cmp(op: CmpOp, l: RealExpr, r: RealExpr) -> RealCond {
2069        RealCond::Cmp(op, l, r)
2070    }
2071    fn imp(p: RealCond, t: RealCond) -> RealCond {
2072        RealCond::Guard(Box::new(p), Box::new(t), Box::new(RealCond::Const(true)))
2073    }
2074    fn problem(n: usize, conds: Vec<RealCond>) -> RealProblem {
2075        RealProblem {
2076            kinds: vec![D; n],
2077            conds,
2078            ..RealProblem::default()
2079        }
2080    }
2081    fn run(p: &RealProblem, seed: &mut u64) -> Result<Vec<f64>, RealError> {
2082        let known = vec![None; p.kinds.len()];
2083        solve(p, &known, &[], seed).map(|v| v.into_iter().map(|x| x.unwrap()).collect())
2084    }
2085    fn set_of(p: &RealProblem, c: &RealCond) -> RealSet {
2086        let ctx = Ctx {
2087            kinds: &p.kinds,
2088            vars: vec![None; p.kinds.len()],
2089            states: &[],
2090        };
2091        ctx.set_of(c, 0).unwrap()
2092    }
2093
2094    #[test]
2095    fn an_atom_is_exactly_the_floats_for_which_the_rounded_comparison_holds() {
2096        // x / 3.0 < 1.0: the boundary is where the *rounded* quotient reaches
2097        // 1.0, not at 3.0.
2098        let p = problem(1, vec![]);
2099        let c = cmp(CmpOp::Lt, RealExpr::Div(bx(v(0)), bx(k(3.0))), k(1.0));
2100        let s = set_of(&p, &c);
2101        let (_, top) = *s.intervals().last().unwrap();
2102        assert!(D.val(top) / 3.0 < 1.0);
2103        assert!(D.val(top + 1) / 3.0 >= 1.0);
2104        assert_eq!(s.intervals().len(), 1);
2105        // A decreasing form: 5.0 - x <= 2.0 holds for x >= 3.0.
2106        let c = cmp(CmpOp::Le, RealExpr::Sub(bx(k(5.0)), bx(v(0))), k(2.0));
2107        let s = set_of(&p, &c);
2108        assert!(s.contains(3.0) && !s.contains(f64::from_bits(3.0f64.to_bits() - 1)));
2109        // Eq is a single point, Ne its complement, Gt/Ge the open/closed tails.
2110        let eq = set_of(&p, &cmp(CmpOp::Eq, v(0), k(2.5)));
2111        assert_eq!(eq, RealSet::point(D, 2.5));
2112        assert_eq!(set_of(&p, &cmp(CmpOp::Ne, v(0), k(2.5))), eq.complement());
2113        let gt = set_of(&p, &cmp(CmpOp::Gt, v(0), k(2.5)));
2114        let ge = set_of(&p, &cmp(CmpOp::Ge, v(0), k(2.5)));
2115        assert!(!gt.contains(2.5) && gt.contains(f64::from_bits(2.5f64.to_bits() + 1)));
2116        assert!(ge.contains(2.5) && !ge.contains(f64::from_bits(2.5f64.to_bits() - 1)));
2117        // A negative coefficient flips the direction: -2.0 * x > 4.0 is x < -2.0.
2118        let c = cmp(CmpOp::Gt, RealExpr::Mul(bx(k(-2.0)), bx(v(0))), k(4.0));
2119        let s = set_of(&p, &c);
2120        assert!(s.contains(-2.0000000000000004) && !s.contains(-2.0));
2121    }
2122
2123    #[test]
2124    fn a_shortreal_atom_lives_on_the_binary32_grid() {
2125        let p = RealProblem {
2126            kinds: vec![S],
2127            ..RealProblem::default()
2128        };
2129        let ctx = Ctx {
2130            kinds: &p.kinds,
2131            vars: vec![None],
2132            states: &[],
2133        };
2134        let s = ctx.set_of(&cmp(CmpOp::Lt, v(0), k(1.0)), 0).unwrap();
2135        let below = f64::from(f32::from_bits(1.0f32.to_bits() - 1));
2136        assert!(s.contains(below) && !s.contains(1.0));
2137        // Values handed out by the sampler are binary32 numbers.
2138        let mut seed = 3;
2139        for _ in 0..50 {
2140            let x = s.sample(&mut seed);
2141            assert_eq!(f64::from(x as f32), x);
2142        }
2143    }
2144
2145    #[test]
2146    fn an_open_interval_is_uniform() {
2147        // 0.0 < v < 2.0
2148        let p = problem(
2149            1,
2150            vec![RealCond::And(vec![
2151                cmp(CmpOp::Gt, v(0), k(0.0)),
2152                cmp(CmpOp::Lt, v(0), k(2.0)),
2153            ])],
2154        );
2155        let mut seed = 42u64;
2156        let mut bins = [0u32; 8];
2157        for _ in 0..8000 {
2158            let x = run(&p, &mut seed).unwrap()[0];
2159            assert!(x > 0.0 && x < 2.0, "{x}");
2160            bins[(x * 4.0) as usize] += 1;
2161        }
2162        for (i, b) in bins.iter().enumerate() {
2163            assert!((850..=1150).contains(b), "bin {i}: {bins:?}");
2164        }
2165    }
2166
2167    #[test]
2168    fn an_unsatisfiable_set_is_reported_and_draws_nothing() {
2169        let p = problem(
2170            1,
2171            vec![cmp(CmpOp::Gt, v(0), k(2.0)), cmp(CmpOp::Lt, v(0), k(1.0))],
2172        );
2173        let mut seed = 5;
2174        assert_eq!(run(&p, &mut seed), Err(RealError::Unsatisfiable));
2175        assert_eq!(seed, 5);
2176        // No free variable: nothing is drawn and the result is empty.
2177        let q = problem(1, vec![cmp(CmpOp::Lt, v(0), k(9.0))]);
2178        let r = solve(&q, &[Some(1.0)], &[], &mut seed).unwrap();
2179        assert_eq!(r, vec![None]);
2180        assert_eq!(seed, 5);
2181        // ... and a fixed value that breaks the constraint is unsatisfiable.
2182        assert_eq!(
2183            solve(&q, &[Some(10.0)], &[], &mut seed),
2184            Err(RealError::Unsatisfiable)
2185        );
2186    }
2187
2188    #[test]
2189    fn a_state_bound_is_read_per_call_and_an_unreadable_one_is_an_error() {
2190        let p = problem(1, vec![cmp(CmpOp::Lt, v(0), RealExpr::State(0, D))]);
2191        let mut seed = 1;
2192        let st = [Some(BigUint::from((-4.5f64).to_bits()))];
2193        let r = solve(&p, &[None], &st, &mut seed).unwrap();
2194        assert!(r[0].unwrap() < -4.5);
2195        assert_eq!(
2196            solve(&p, &[None], &[None], &mut seed),
2197            Err(RealError::StateUnreadable(0))
2198        );
2199    }
2200
2201    #[test]
2202    fn nan_and_division_by_zero_are_errors_not_silent_values() {
2203        let p = problem(
2204            1,
2205            vec![cmp(CmpOp::Lt, RealExpr::Div(bx(v(0)), bx(k(0.0))), k(1.0))],
2206        );
2207        let mut seed = 1;
2208        assert!(matches!(run(&p, &mut seed), Err(RealError::NonFinite(_))));
2209    }
2210
2211    #[test]
2212    fn a_non_affine_or_two_variable_conjunct_is_rejected() {
2213        let sq = cmp(CmpOp::Lt, RealExpr::Mul(bx(v(0)), bx(v(0))), k(4.0));
2214        let mut seed = 1;
2215        assert!(matches!(
2216            run(&problem(1, vec![sq.clone()]), &mut seed),
2217            Err(RealError::Shape(_))
2218        ));
2219        // `check` (the build-time gate) rejects it too, by name.
2220        let why = problem(1, vec![sq]).check().unwrap_err();
2221        assert!(why.contains("not affine") && why.contains("#0"), "{why}");
2222        let recip = cmp(CmpOp::Gt, RealExpr::Div(bx(k(1.0)), bx(v(0))), k(2.0));
2223        assert!(
2224            problem(1, vec![recip])
2225                .check()
2226                .unwrap_err()
2227                .contains("denominator")
2228        );
2229        // A product with one constant factor, and one whose other factor is
2230        // another variable (bound by the time it is read), stay legal.
2231        let scaled = cmp(CmpOp::Lt, RealExpr::Mul(bx(k(2.0)), bx(v(0))), k(4.0));
2232        assert!(problem(1, vec![scaled]).check().is_ok());
2233    }
2234
2235    #[test]
2236    fn conjuncts_relating_two_random_reals_are_solved_jointly() {
2237        // LRM 18.5: `f < g` and `f + g < 1.0` over unit ranges. Every draw
2238        // satisfies both; the draws spread over the whole feasible triangle
2239        // (f gets near 0, g gets near 1).
2240        let unit_range = |i: u32| [cmp(CmpOp::Ge, v(i), k(0.0)), cmp(CmpOp::Le, v(i), k(1.0))];
2241        let mut conds: Vec<RealCond> = unit_range(0).into_iter().chain(unit_range(1)).collect();
2242        conds.push(cmp(CmpOp::Lt, v(0), v(1)));
2243        conds.push(cmp(CmpOp::Lt, RealExpr::Add(bx(v(0)), bx(v(1))), k(1.0)));
2244        let p = problem(2, conds);
2245        assert!(p.check().is_ok());
2246        let mut seed = 5;
2247        let (mut lo_f, mut hi_g) = (f64::MAX, f64::MIN);
2248        for _ in 0..500 {
2249            let r = run(&p, &mut seed).unwrap();
2250            assert!(r[0] < r[1] && r[0] + r[1] < 1.0, "{r:?}");
2251            assert!((0.0..=1.0).contains(&r[0]) && (0.0..=1.0).contains(&r[1]));
2252            lo_f = lo_f.min(r[0]);
2253            hi_g = hi_g.max(r[1]);
2254        }
2255        assert!(lo_f < 0.05 && hi_g > 0.8, "{lo_f} {hi_g}");
2256        // An equality between random reals has measure zero: refused by name.
2257        let eq = problem(2, vec![cmp(CmpOp::Eq, v(0), v(1))]);
2258        assert!(eq.check().is_ok());
2259        let mut seed = 1;
2260        assert!(matches!(run(&eq, &mut seed), Err(RealError::Shape(_))));
2261        // A contradiction is unsatisfiable only through exhausting the draws,
2262        // which is also a loud Shape error, never a solution.
2263        let bad = problem(
2264            2,
2265            vec![cmp(CmpOp::Lt, v(0), v(1)), cmp(CmpOp::Gt, v(0), v(1))],
2266        );
2267        assert!(matches!(run(&bad, &mut seed), Err(RealError::Shape(_))));
2268    }
2269
2270    #[test]
2271    fn a_guard_with_a_state_predicate_follows_figure_18_3() {
2272        // (state == 1) -> v > 5.0
2273        let g = |st: Option<u64>| {
2274            let p = problem(
2275                1,
2276                vec![imp(
2277                    RealCond::Int(Expr::Binary(
2278                        super::super::ir::BinOp::Eq,
2279                        Box::new(Expr::State(0, super::super::ir::Ty::unsigned(8))),
2280                        Box::new(Expr::constant(super::super::ir::Ty::unsigned(8), 1u64)),
2281                        super::super::ir::Ty::BIT,
2282                    )),
2283                    cmp(CmpOp::Gt, v(0), k(5.0)),
2284                )],
2285            );
2286            let mut seed = 9;
2287            solve(&p, &[None], &[st.map(BigUint::from)], &mut seed)
2288        };
2289        assert!(g(Some(1)).unwrap()[0].unwrap() > 5.0);
2290        assert!(g(Some(2)).is_ok());
2291        assert_eq!(g(None), Err(RealError::GuardError));
2292    }
2293
2294    /// `a` in [0, 10]; `(a < 5) -> b > 20`; `b < 10`.
2295    fn coupled() -> RealProblem {
2296        RealProblem {
2297            kinds: vec![D, D],
2298            conds: vec![
2299                RealCond::And(vec![
2300                    cmp(CmpOp::Ge, v(0), k(0.0)),
2301                    cmp(CmpOp::Le, v(0), k(10.0)),
2302                ]),
2303                imp(cmp(CmpOp::Lt, v(0), k(5.0)), cmp(CmpOp::Gt, v(1), k(20.0))),
2304                cmp(CmpOp::Lt, v(1), k(10.0)),
2305            ],
2306            dists: vec![],
2307            before: vec![(0, 1)],
2308        }
2309    }
2310
2311    #[test]
2312    fn a_coupled_guard_needs_its_order_and_is_solved_staged() {
2313        let mut p = coupled();
2314        assert!(p.check().is_ok());
2315        let mut seed = 11;
2316        // `a < 5` would force `b > 20` against `b < 10`, so only `a >= 5`
2317        // completes: `a` is drawn from exactly those values.
2318        for _ in 0..300 {
2319            let r = run(&p, &mut seed).unwrap();
2320            assert!((5.0..=10.0).contains(&r[0]), "{r:?}");
2321            assert!(r[1] < 10.0);
2322        }
2323        p.before.clear();
2324        let e = p.check().unwrap_err();
2325        assert!(e.contains("before"), "{e}");
2326        assert!(matches!(run(&p, &mut seed), Err(RealError::Shape(_))));
2327    }
2328
2329    #[test]
2330    fn a_coupled_guard_draws_the_child_from_the_arm_the_parent_chose() {
2331        // a in [0,10]; (a < 5) -> b in [20,30]; !(a < 5) -> b in [-30,-20]
2332        let both = |lo: f64, hi: f64| {
2333            RealCond::And(vec![
2334                cmp(CmpOp::Ge, v(1), k(lo)),
2335                cmp(CmpOp::Le, v(1), k(hi)),
2336            ])
2337        };
2338        let p = RealProblem {
2339            kinds: vec![D, D],
2340            conds: vec![
2341                RealCond::And(vec![
2342                    cmp(CmpOp::Ge, v(0), k(0.0)),
2343                    cmp(CmpOp::Le, v(0), k(10.0)),
2344                ]),
2345                RealCond::Guard(
2346                    Box::new(cmp(CmpOp::Lt, v(0), k(5.0))),
2347                    Box::new(both(20.0, 30.0)),
2348                    Box::new(both(-30.0, -20.0)),
2349                ),
2350            ],
2351            dists: vec![],
2352            before: vec![(0, 1)],
2353        };
2354        assert!(p.check().is_ok());
2355        let mut seed = 77;
2356        let mut low = 0;
2357        for _ in 0..2000 {
2358            let r = run(&p, &mut seed).unwrap();
2359            assert_eq!(r[0] < 5.0, r[1] > 0.0, "{r:?}");
2360            assert!(r[1].abs() >= 20.0 && r[1].abs() <= 30.0);
2361            low += usize::from(r[0] < 5.0);
2362        }
2363        // Table 18-2's rule: `a` is drawn first, so it is not skewed by `b`.
2364        assert!((900..=1100).contains(&low), "{low}");
2365    }
2366
2367    #[test]
2368    fn coupling_shapes_the_solver_cannot_do_are_named() {
2369        // a -> b and c -> b: b has two parents.
2370        let p = RealProblem {
2371            kinds: vec![D, D, D],
2372            conds: vec![
2373                imp(cmp(CmpOp::Lt, v(0), k(5.0)), cmp(CmpOp::Gt, v(1), k(0.0))),
2374                imp(cmp(CmpOp::Lt, v(2), k(5.0)), cmp(CmpOp::Gt, v(1), k(1.0))),
2375            ],
2376            dists: vec![],
2377            before: vec![(0, 1), (2, 1)],
2378        };
2379        assert!(p.check().unwrap_err().contains("two other variables"));
2380        // A predicate over two variables is a joint conjunct (rejection
2381        // sampling), not a coupling guard.
2382        let q = problem(
2383            3,
2384            vec![imp(
2385                RealCond::And(vec![
2386                    cmp(CmpOp::Lt, v(0), k(5.0)),
2387                    cmp(CmpOp::Lt, v(1), k(5.0)),
2388                ]),
2389                cmp(CmpOp::Gt, v(2), k(0.0)),
2390            )],
2391        );
2392        assert!(q.check().is_ok());
2393        let mut seed = 3;
2394        for _ in 0..100 {
2395            let s = run(&q, &mut seed).unwrap();
2396            assert!(!(s[0] < 5.0 && s[1] < 5.0) || s[2] > 0.0, "{s:?}");
2397        }
2398        // A nested coupling guard.
2399        let r = RealProblem {
2400            before: vec![(0, 1), (1, 2)],
2401            ..problem(
2402                3,
2403                vec![imp(
2404                    cmp(CmpOp::Lt, v(0), k(5.0)),
2405                    imp(cmp(CmpOp::Lt, v(1), k(5.0)), cmp(CmpOp::Gt, v(2), k(0.0))),
2406                )],
2407            )
2408        };
2409        assert!(r.check().unwrap_err().contains("nested"));
2410    }
2411
2412    fn weight(n: u64) -> Expr {
2413        Expr::constant(super::super::ir::Ty::unsigned(8), n)
2414    }
2415
2416    #[test]
2417    fn a_real_dist_weights_points_and_spreads_ranges_over_their_length() {
2418        // a dist { 1.0 := 1, [2.0:4.0] :/ 3 }, a < 3.0: the range keeps half
2419        // its length, so P(a == 1.0) = 1 / (1 + 1.5).
2420        let dist = RealDist {
2421            var: 0,
2422            items: vec![
2423                RealDistItem {
2424                    span: RealSpan::Point(k(1.0)),
2425                    weight: weight(1),
2426                },
2427                RealDistItem {
2428                    span: RealSpan::Range {
2429                        lo: Some(k(2.0)),
2430                        hi: Some(k(4.0)),
2431                    },
2432                    weight: weight(3),
2433                },
2434            ],
2435            default: None,
2436        };
2437        let mut p = problem(1, vec![cmp(CmpOp::Lt, v(0), k(3.0))]);
2438        p.dists.push(dist);
2439        let mut seed = 21;
2440        let (mut ones, n) = (0, 6000);
2441        for _ in 0..n {
2442            let x = run(&p, &mut seed).unwrap()[0];
2443            assert!(x == 1.0 || (2.0..3.0).contains(&x), "{x}");
2444            ones += usize::from(x == 1.0);
2445        }
2446        let want = f64::from(n as u32) * 0.4;
2447        assert!((ones as f64 - want).abs() < 150.0, "{ones} vs {want}");
2448    }
2449
2450    #[test]
2451    fn a_dist_default_covers_the_complement_and_zero_weights_are_excluded() {
2452        // a dist { [0.0:1.0] :/ 0, default :/ 1 }, a in [0, 2]: never in [0, 1].
2453        let dist = RealDist {
2454            var: 0,
2455            items: vec![RealDistItem {
2456                span: RealSpan::Range {
2457                    lo: Some(k(0.0)),
2458                    hi: Some(k(1.0)),
2459                },
2460                weight: weight(0),
2461            }],
2462            default: Some(weight(1)),
2463        };
2464        let mut p = problem(
2465            1,
2466            vec![RealCond::And(vec![
2467                cmp(CmpOp::Ge, v(0), k(0.0)),
2468                cmp(CmpOp::Le, v(0), k(2.0)),
2469            ])],
2470        );
2471        p.dists.push(dist);
2472        let mut seed = 2;
2473        for _ in 0..500 {
2474            let x = run(&p, &mut seed).unwrap()[0];
2475            assert!(x > 1.0 && x <= 2.0, "{x}");
2476        }
2477        // A `$` bound is the type's extreme.
2478        let dollar = RealDist {
2479            var: 0,
2480            items: vec![RealDistItem {
2481                span: RealSpan::Range {
2482                    lo: Some(k(5.0)),
2483                    hi: None,
2484                },
2485                weight: weight(1),
2486            }],
2487            default: None,
2488        };
2489        let mut q = problem(1, vec![]);
2490        q.dists.push(dollar);
2491        for _ in 0..50 {
2492            assert!(run(&q, &mut seed).unwrap()[0] >= 5.0);
2493        }
2494        // A fixed value outside every positive-weight item breaks the dist.
2495        assert_eq!(
2496            solve(&q, &[Some(1.0)], &[], &mut seed),
2497            Err(RealError::Unsatisfiable)
2498        );
2499    }
2500
2501    #[test]
2502    fn a_point_in_a_range_item_falls_back_to_the_nominal_weights() {
2503        // a == 3.0 with a dist over [0:10]: the range keeps zero length but
2504        // its one point, so the nominal weight decides and the call succeeds.
2505        let dist = RealDist {
2506            var: 0,
2507            items: vec![RealDistItem {
2508                span: RealSpan::Range {
2509                    lo: Some(k(0.0)),
2510                    hi: Some(k(10.0)),
2511                },
2512                weight: weight(1),
2513            }],
2514            default: None,
2515        };
2516        let mut p = problem(1, vec![cmp(CmpOp::Eq, v(0), k(3.0))]);
2517        p.dists.push(dist);
2518        let mut seed = 4;
2519        assert_eq!(run(&p, &mut seed).unwrap(), vec![3.0]);
2520    }
2521
2522    #[test]
2523    fn a_negative_dist_weight_is_an_error() {
2524        let neg = Expr::constant(super::super::ir::Ty::signed(8), 255u64);
2525        let mut p = problem(1, vec![]);
2526        p.dists.push(RealDist {
2527            var: 0,
2528            items: vec![RealDistItem {
2529                span: RealSpan::Point(k(1.0)),
2530                weight: neg,
2531            }],
2532            default: None,
2533        });
2534        let mut seed = 1;
2535        assert_eq!(run(&p, &mut seed), Err(RealError::BadWeight));
2536    }
2537
2538    #[test]
2539    fn an_integral_state_converts_to_real_signed_or_unsigned() {
2540        use super::super::ir::Ty;
2541        let zstate = RealExpr::FromInt(Box::new(Expr::State(0, Ty::signed(32))));
2542        let ctx = Ctx {
2543            kinds: &[],
2544            vars: vec![],
2545            states: &[Some(BigUint::from(0xffff_ff9cu64))],
2546        };
2547        assert_eq!(ctx.eval(&zstate).unwrap(), -100.0);
2548        let unsigned = RealExpr::FromInt(Box::new(Expr::State(0, Ty::unsigned(32))));
2549        assert_eq!(ctx.eval(&unsigned).unwrap(), f64::from(0xffff_ff9cu32));
2550    }
2551
2552    #[test]
2553    fn the_lrm_real_constraint_example_is_solved_with_its_ordering() {
2554        // IEEE 1800-2023 18.5.13: with ZSTATE=-100, LOW=0.7, MIN=1.43,
2555        // NOM=3.3, MAX=3.65.
2556        let (zs, low, min, nom, max) = (-100.0, 0.7, 1.43, 3.3, 3.65);
2557        let range = |lo: f64, hi: f64, w: u64| RealDistItem {
2558            span: RealSpan::Range {
2559                lo: Some(k(lo)),
2560                hi: Some(k(hi)),
2561            },
2562            weight: weight(w),
2563        };
2564        let point = |x: f64, w: u64| RealDistItem {
2565            span: RealSpan::Point(k(x)),
2566            weight: weight(w),
2567        };
2568        let p = RealProblem {
2569            kinds: vec![D, D],
2570            conds: vec![imp(
2571                RealCond::And(vec![
2572                    cmp(CmpOp::Ge, v(0), k(low)),
2573                    cmp(CmpOp::Le, v(0), k(min)),
2574                ]),
2575                cmp(CmpOp::Eq, v(1), k(zs)),
2576            )],
2577            dists: vec![
2578                RealDist {
2579                    var: 0,
2580                    items: vec![
2581                        point(zs, 5),
2582                        range(low, min, 1),
2583                        range(nom - nom * 0.01, nom + nom * 0.01, 13),
2584                        range(min, max, 1),
2585                    ],
2586                    default: None,
2587                },
2588                RealDist {
2589                    var: 1,
2590                    items: vec![point(zs, 1), range(min, max, 20)],
2591                    default: None,
2592                },
2593            ],
2594            before: vec![(0, 1)],
2595        };
2596        assert!(p.check().is_ok());
2597        let mut seed = 99;
2598        let n = 12_000;
2599        let (mut a_z, mut b_z_when_low) = (0, (0, 0));
2600        for _ in 0..n {
2601            let r = run(&p, &mut seed).unwrap();
2602            let (a, b) = (r[0], r[1]);
2603            assert!(a == zs || (low..=max).contains(&a), "{a}");
2604            assert!(b == zs || (min..=max).contains(&b), "{b}");
2605            if (low..=min).contains(&a) {
2606                assert_eq!(b, zs, "{a} {b}");
2607                b_z_when_low.0 += 1;
2608            } else if b == zs {
2609                b_z_when_low.1 += 1;
2610            }
2611            a_z += usize::from(a == zs);
2612        }
2613        // a == ZSTATE has weight 5 of 5 + (1.43-0.7)/... : lengths enter as
2614        // shares, and the items overlap, so only the point's weight is exact.
2615        let total = 5.0 + 1.0 + 13.0 + 1.0;
2616        let want = f64::from(n as u32) * 5.0 / total;
2617        assert!((a_z as f64 - want).abs() < 250.0, "{a_z} vs {want}");
2618        // Outside the guarded range b is ZSTATE with weight 1 against 20.
2619        assert!(b_z_when_low.0 > 0 && b_z_when_low.1 > 0);
2620    }
2621
2622    #[test]
2623    fn check_rejects_a_second_dist_on_one_variable() {
2624        let d = |var| RealDist {
2625            var,
2626            items: vec![],
2627            default: None,
2628        };
2629        let mut p = problem(1, vec![]);
2630        p.dists = vec![d(0), d(0)];
2631        assert!(p.check().is_err());
2632        p.dists = vec![d(0)];
2633        assert!(p.check().is_ok());
2634    }
2635}