the_q/ext.rs
1//! The extended [`Q`]: a `Rat`, or an explicit non-representable state.
2//!
3//! A layer over the kernel, not a rewrite: `Rat` keeps its invariant and every
4//! kernel obligation keeps its statement. Special values carry no `num`/`den`,
5//! so no operation can read one as a number and an omitted case is a compile
6//! error. There is no `is_finite()`: `PosSat` denotes finite reals above the
7//! budget. Issue #26 holds the design and the propagation tables, which
8//! `tests/extended_q.rs` pins by exhaustive enumeration.
9
10use verus_builtin_macros::verus;
11
12#[allow(unused_imports)]
13use verus_builtin::*;
14#[allow(unused_imports)]
15use vstd::prelude::*;
16
17use crate::types::{Dir, Rat};
18
19verus! {
20
21/// The sign of a value that has one; `signum(Nan)` is `None`.
22#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
23pub enum Sign {
24 /// Strictly less than zero.
25 Negative,
26 /// Exactly zero.
27 Zero,
28 /// Strictly greater than zero.
29 Positive,
30}
31
32/// A bounded rational, or an explicit statement that the value is not one.
33///
34/// | variant | denotes |
35/// |---|---|
36/// | `Number(x)` | `{x}` |
37/// | `PosSat` / `NegSat` | `(MAX_MAG, +∞)` / `(-∞, -MAX_MAG)`, reals only |
38/// | `PosInf` / `NegInf` | `{+∞}` / `{-∞}` |
39/// | `Nan` | no information |
40///
41/// Saturation denotes reals, so `Number(0) · PosSat == Number(0)`. `PartialEq`
42/// is derived and `Nan == Nan`, which keeps `Eq`, `Hash` and the total `Ord`
43/// lawful. `Ord` is hand-written: variant order is not value order.
44#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
45pub enum Q {
46 /// A representable rational.
47 Number(Rat),
48 /// The true magnitude exceeds `MAX_MAG` and the value is positive.
49 PosSat,
50 /// The true magnitude exceeds `MAX_MAG` and the value is negative.
51 NegSat,
52 /// Exactly `+∞`.
53 PosInf,
54 /// Exactly `-∞`.
55 NegInf,
56 /// No information about the value.
57 Nan,
58}
59
60impl Q {
61 /// The invariant: a `Number` payload is `wf`; specials carry nothing.
62 pub open spec fn wf(self) -> bool {
63 match self {
64 Q::Number(x) => x.wf(),
65 _ => true,
66 }
67 }
68
69 // -----------------------------------------------------------------------
70 // Classification
71 //
72 // These four `ensures` clauses restate their own bodies. A discriminant
73 // test has no content beyond the variant that it accepts, thus the
74 // specification and the implementation are the same statement. The clauses
75 // prove nothing. They exist so that a caller in verified code can reason
76 // about the result.
77 //
78 // The predicates below (`is_zero`, `signum` and others) are different. They
79 // have content, and they delegate to the verified kernel.
80 // -----------------------------------------------------------------------
81
82 /// Whether this is a representable rational.
83 pub open spec fn spec_is_number(self) -> bool {
84 match self {
85 Q::Number(_) => true,
86 _ => false,
87 }
88 }
89
90 /// Whether this is a saturation state.
91 pub open spec fn spec_is_saturated(self) -> bool {
92 match self {
93 Q::PosSat => true,
94 Q::NegSat => true,
95 _ => false,
96 }
97 }
98
99 /// Whether this is an infinity.
100 pub open spec fn spec_is_infinite(self) -> bool {
101 match self {
102 Q::PosInf => true,
103 Q::NegInf => true,
104 _ => false,
105 }
106 }
107
108 /// Whether this is `Nan`.
109 pub open spec fn spec_is_nan(self) -> bool {
110 match self {
111 Q::Nan => true,
112 _ => false,
113 }
114 }
115
116 /// The sign of this value in specifications. `Nan` is the only state with
117 /// no sound sign.
118 pub open spec fn spec_signum(self) -> Option<Sign> {
119 match self {
120 Q::Number(x) => {
121 if x.n() < 0 {
122 Some(Sign::Negative)
123 } else if x.n() > 0 {
124 Some(Sign::Positive)
125 } else {
126 Some(Sign::Zero)
127 }
128 },
129 Q::PosSat => Some(Sign::Positive),
130 Q::NegSat => Some(Sign::Negative),
131 Q::PosInf => Some(Sign::Positive),
132 Q::NegInf => Some(Sign::Negative),
133 Q::Nan => None,
134 }
135 }
136
137 /// Whether this is a representable rational.
138 ///
139 /// The four classification predicates are mutually exclusive and jointly
140 /// exhaustive. See `theorem_classification_partitions`.
141 ///
142 /// ```
143 /// use the_q::{Q, Rat};
144 ///
145 /// assert!(Q::number(Rat::one()).is_number());
146 /// assert!(!Q::PosSat.is_number());
147 /// assert!(!Q::Nan.is_number());
148 /// ```
149 pub fn is_number(self) -> (r: bool)
150 ensures
151 r == self.spec_is_number(),
152 {
153 matches!(self, Q::Number(_))
154 }
155
156 /// Whether the true magnitude is known to exceed `MAX_MAG`.
157 ///
158 /// This predicate is not the negation of `is_number`. It separates an
159 /// overflow from a division by zero. That distinction is the purpose of the
160 /// saturation states.
161 ///
162 /// ```
163 /// use the_q::Q;
164 ///
165 /// assert!(Q::PosSat.is_saturated());
166 /// assert!(Q::NegSat.is_saturated());
167 /// assert!(!Q::PosInf.is_saturated());
168 /// ```
169 pub fn is_saturated(self) -> (r: bool)
170 ensures
171 r == self.spec_is_saturated(),
172 {
173 matches!(self, Q::PosSat | Q::NegSat)
174 }
175
176 /// Whether this is exactly `±∞`.
177 ///
178 /// ```
179 /// use the_q::Q;
180 ///
181 /// assert!(Q::PosInf.is_infinite());
182 /// assert!(Q::NegInf.is_infinite());
183 /// assert!(!Q::PosSat.is_infinite());
184 /// ```
185 pub fn is_infinite(self) -> (r: bool)
186 ensures
187 r == self.spec_is_infinite(),
188 {
189 matches!(self, Q::PosInf | Q::NegInf)
190 }
191
192 /// Whether this carries no information about the value.
193 ///
194 /// ```
195 /// use the_q::Q;
196 ///
197 /// assert!(Q::Nan.is_nan());
198 /// assert!(!Q::zero().is_nan());
199 /// ```
200 pub fn is_nan(self) -> (r: bool)
201 ensures
202 r == self.spec_is_nan(),
203 {
204 matches!(self, Q::Nan)
205 }
206
207 // -----------------------------------------------------------------------
208 // Constructors
209 //
210 // Each constructor establishes `wf`. A special value establishes `wf`
211 // trivially. There is no malformed special value, thus these constructors
212 // cannot fail and need no `Option`.
213 // -----------------------------------------------------------------------
214
215 /// Lift a kernel rational into the extended type.
216 ///
217 /// ```
218 /// use the_q::{Q, Rat};
219 ///
220 /// let half = Rat::new(1, 2).unwrap();
221 /// assert_eq!(Q::number(half), Q::Number(half));
222 /// ```
223 pub fn number(x: Rat) -> (r: Q)
224 requires
225 x.wf(),
226 ensures
227 r.wf(),
228 r == Q::Number(x),
229 r.spec_is_number(),
230 {
231 Q::Number(x)
232 }
233
234 /// `0`.
235 ///
236 /// ```
237 /// use the_q::Q;
238 ///
239 /// assert!(Q::zero().is_zero());
240 /// assert!(Q::zero().is_number());
241 /// ```
242 pub fn zero() -> (r: Q)
243 ensures
244 r.wf(),
245 r.spec_is_number(),
246 r.spec_is_value(0, 1),
247 r.spec_is_zero(),
248 r == Q::Number(Rat::from_raw_spec(0, 1)),
249 {
250 Q::Number(Rat::zero())
251 }
252
253 /// `1`.
254 ///
255 /// ```
256 /// use the_q::Q;
257 ///
258 /// assert!(Q::one().is_one());
259 /// ```
260 pub fn one() -> (r: Q)
261 ensures
262 r.wf(),
263 r.spec_is_number(),
264 r.spec_is_value(1, 1),
265 r.spec_is_one(),
266 r == Q::Number(Rat::from_raw_spec(1, 1)),
267 {
268 Q::Number(Rat::one())
269 }
270
271 /// `-1`.
272 ///
273 /// ```
274 /// use the_q::{Q, Rat};
275 ///
276 /// assert_eq!(Q::neg_one(), Q::Number(Rat::new(-1, 1).unwrap()));
277 /// ```
278 pub fn neg_one() -> (r: Q)
279 ensures
280 r.wf(),
281 r.spec_is_number(),
282 r.spec_is_value(-1, 1),
283 {
284 Q::Number(Rat::neg_one())
285 }
286
287 /// The rational `num / den`, total: `x/0` is `±Inf`, `0/0` is `Nan`.
288 /// Saturation is decided on the *value* (`magnitude_fits`), not on the
289 /// components: `1 / i64::MIN` is small and rounds, it does not saturate.
290 /// Where [`Rat::new`] succeeds this returns the same value.
291 ///
292 /// ```
293 /// use the_q::{Q, Rat};
294 ///
295 /// assert_eq!(Q::new(1, 2), Q::Number(Rat::new(1, 2).unwrap()));
296 /// assert_eq!(Q::new(1, 0), Q::PosInf);
297 /// assert_eq!(Q::new(0, 0), Q::Nan);
298 /// ```
299 pub fn new(num: i64, den: i64) -> (r: Q)
300 ensures
301 r.wf(),
302 // Division by zero, per #26 §4, applied uniformly.
303 (den == 0 && num == 0) ==> r == Q::Nan,
304 (den == 0 && num > 0) ==> r == Q::PosInf,
305 (den == 0 && num < 0) ==> r == Q::NegInf,
306 // Away from a zero denominator the result is never an infinity or a
307 // `Nan`: a ratio of two integers is a real number, and the only
308 // question is whether it fits.
309 den != 0 ==> (!r.spec_is_nan() && !r.spec_is_infinite()),
310 // Saturation happens exactly when the *value* leaves the budget...
311 den != 0 ==> (r.spec_is_saturated() <==> !crate::model::magnitude_fits(
312 crate::q::signed_den_num(num as int, den as int),
313 crate::model::abs_int(den as int),
314 )),
315 // ...and when it does, it carries the correct sign.
316 (den != 0 && r == Q::PosSat) ==> crate::q::signed_den_num(
317 num as int,
318 den as int,
319 ) > 0,
320 (den != 0 && r == Q::NegSat) ==> crate::q::signed_den_num(
321 num as int,
322 den as int,
323 ) < 0,
324 // Otherwise the value is pinned completely, to the same nearest-mode
325 // rounding of the same sign-normalised pair that `new_rounded` uses.
326 (den != 0 && crate::model::magnitude_fits(
327 crate::q::signed_den_num(num as int, den as int),
328 crate::model::abs_int(den as int),
329 )) ==> r == Q::Number(
330 crate::round::round_frac(
331 crate::q::signed_den_num(num as int, den as int),
332 crate::model::abs_int(den as int),
333 Dir::Nearest,
334 ),
335 ),
336 {
337 if den == 0 {
338 if num == 0 {
339 Q::Nan
340 } else if num > 0 {
341 Q::PosInf
342 } else {
343 Q::NegInf
344 }
345 } else {
346 // Normalise the sign onto the numerator first, matching
347 // `new_rounded`'s convention, so that the magnitude test and the
348 // rounding agree about which value they are talking about.
349 // `0 - (i64::MIN as i128)` is `2^63`, comfortably inside `i128`.
350 let n: i128 = if den < 0 {
351 0 - (num as i128)
352 } else {
353 num as i128
354 };
355 let d: i128 = if den < 0 {
356 0 - (den as i128)
357 } else {
358 den as i128
359 };
360 proof {
361 // `|n| <= 2^63 < 2^126 = num_input_bound()`, which is
362 // `magnitude_fits_exec`'s precondition.
363 crate::model::lemma_pow2_64();
364 crate::model::lemma_pow2_126();
365 crate::model::lemma_pow2_mono(64, 126);
366 }
367 if crate::q::magnitude_fits_exec(n, d) {
368 match Rat::new_rounded(num, den, Dir::Nearest) {
369 Some(x) => Q::Number(x),
370 // Unreachable: `new_rounded` is `None` iff `den == 0`.
371 None => Q::Nan,
372 }
373 } else if n > 0 {
374 Q::PosSat
375 } else {
376 Q::NegSat
377 }
378 }
379 }
380
381 // -----------------------------------------------------------------------
382 // Value predicates (issue #26 §5)
383 //
384 // Each predicate is `false` on `Nan`. The design states that result.
385 // `Nan` denotes every value, thus no non-trivial predicate holds of it.
386 //
387 // Delegate to the kernel predicates: a reimplementation would mirror its own
388 // postcondition and verify with a shared defect.
389
390 /// Whether this is exactly zero.
391 pub open spec fn spec_is_zero(self) -> bool {
392 match self {
393 Q::Number(x) => x.n() == 0,
394 _ => false,
395 }
396 }
397
398 /// Whether this is exactly one.
399 pub open spec fn spec_is_one(self) -> bool {
400 match self {
401 Q::Number(x) => x.n() == x.d(),
402 _ => false,
403 }
404 }
405
406 /// Whether this is `Number(x)` with `x` the exact rational `n / d`.
407 ///
408 /// A constructor states its result with this predicate and does not name a
409 /// kernel constructor. `Rat::zero()` and the other kernel constructors are
410 /// `exec` functions and thus cannot appear in a specification.
411 pub open spec fn spec_is_value(self, n: int, d: int) -> bool {
412 match self {
413 Q::Number(x) => x.n() == n && x.d() == d,
414 _ => false,
415 }
416 }
417
418 /// Whether this lies in `[0, 1]`.
419 pub open spec fn spec_in_unit_interval(self) -> bool {
420 match self {
421 Q::Number(x) => x.n() >= 0 && x.n() <= x.d(),
422 _ => false,
423 }
424 }
425
426 /// `self == 0`.
427 ///
428 /// The result is false for each special value. `PosSat` denotes
429 /// `(MAX_MAG, +∞)`, which does not contain zero, thus false is the true
430 /// answer there. For `Nan` the result is a convention from the design.
431 ///
432 /// ```
433 /// use the_q::Q;
434 ///
435 /// assert!(Q::zero().is_zero());
436 /// assert!(!Q::PosSat.is_zero());
437 /// assert!(!Q::Nan.is_zero());
438 /// ```
439 pub fn is_zero(self) -> (r: bool)
440 requires
441 self.wf(),
442 ensures
443 r == self.spec_is_zero(),
444 {
445 match self {
446 Q::Number(x) => x.is_zero(),
447 _ => false,
448 }
449 }
450
451 /// `self == 1`.
452 ///
453 /// ```
454 /// use the_q::Q;
455 ///
456 /// assert!(Q::one().is_one());
457 /// assert!(!Q::PosSat.is_one());
458 /// ```
459 pub fn is_one(self) -> (r: bool)
460 requires
461 self.wf(),
462 ensures
463 r == self.spec_is_one(),
464 {
465 match self {
466 Q::Number(x) => x.is_one(),
467 _ => false,
468 }
469 }
470
471 /// `0 <= self <= 1`.
472 ///
473 /// The result is false for each special value. For the saturations false is
474 /// the true answer, because both saturation ranges are outside `[0, 1]`.
475 ///
476 /// ```
477 /// use the_q::{Q, Rat};
478 ///
479 /// assert!(Q::number(Rat::new(1, 2).unwrap()).in_unit_interval());
480 /// assert!(!Q::PosSat.in_unit_interval());
481 /// ```
482 pub fn in_unit_interval(self) -> (r: bool)
483 requires
484 self.wf(),
485 ensures
486 r == self.spec_in_unit_interval(),
487 {
488 match self {
489 Q::Number(x) => x.in_unit_interval(),
490 _ => false,
491 }
492 }
493
494 /// The sign, where one exists.
495 ///
496 /// The result is `None` for `Nan` only. Both saturations and both
497 /// infinities have a definite sign and give `Some`. The discriminant
498 /// carries that sign for this purpose.
499 ///
500 /// ```
501 /// use the_q::{Q, Sign};
502 ///
503 /// assert_eq!(Q::one().signum(), Some(Sign::Positive));
504 /// assert_eq!(Q::zero().signum(), Some(Sign::Zero));
505 /// assert_eq!(Q::PosSat.signum(), Some(Sign::Positive));
506 /// assert_eq!(Q::Nan.signum(), None);
507 /// ```
508 pub fn signum(self) -> (r: Option<Sign>)
509 requires
510 self.wf(),
511 ensures
512 r == self.spec_signum(),
513 {
514 match self {
515 Q::Number(x) => {
516 let s = x.signum();
517 if s < 0 {
518 Some(Sign::Negative)
519 } else if s > 0 {
520 Some(Sign::Positive)
521 } else {
522 Some(Sign::Zero)
523 }
524 },
525 Q::PosSat => Some(Sign::Positive),
526 Q::NegSat => Some(Sign::Negative),
527 Q::PosInf => Some(Sign::Positive),
528 Q::NegInf => Some(Sign::Negative),
529 Q::Nan => None,
530 }
531 }
532}
533
534/// The sign classification is total away from `Nan`, and zero is recognized
535/// exactly rather than merely implied in one direction.
536pub proof fn theorem_signum_complete(q: Q)
537 requires
538 q.wf(),
539 ensures
540 (q.spec_signum() == Some(Sign::Zero)) <==> q.spec_is_zero(),
541 q.spec_signum().is_none() <==> q.spec_is_nan(),
542 !(q.spec_signum() == Some(Sign::Positive)
543 && q.spec_signum() == Some(Sign::Negative)),
544{
545 match q {
546 Q::Number(x) => {
547 if x.n() < 0 {
548 } else if x.n() > 0 {
549 } else {
550 }
551 },
552 _ => {},
553 }
554}
555
556// ---------------------------------------------------------------------------
557// N-ary folds (issue #26 §10.5)
558//
559// The kernel folds `nary::sum`, `nary::product` and `nary::weighted_mean`
560// clamp. For example, `sum(&[M, M, -M])` clamps to `M`, then subtracts, and
561// returns `0`, but the true total is `M`. The folds below use the operations of
562// this enum, thus an overflow at any point in the chain is reported and not
563// absorbed.
564//
565// After a partial fold saturates, `PosSat + Number(-M)` is `Nan` and the fold
566// does not recover; check `is_number()` on the result for the exact-path
567// guarantee.
568
569/// Every element satisfies the type invariant.
570pub open spec fn all_wf_q(s: Seq<Q>) -> bool {
571 forall|i: int| 0 <= i < s.len() ==> (#[trigger] s[i]).wf()
572}
573
574/// Every component of every pair satisfies the type invariant.
575pub open spec fn all_wf_q_pairs(s: Seq<(Q, Q)>) -> bool {
576 forall|i: int|
577 0 <= i < s.len() ==> (#[trigger] s[i]).0.wf() && s[i].1.wf()
578}
579
580impl Q {
581 /// `xs[0] + xs[1] + ...`, left to right. An empty slice gives `0`.
582 ///
583 /// The left-to-right order is part of the contract and not an
584 /// implementation detail. With rounding, addition is not associative, thus
585 /// the order fixes the answer and makes the result reproducible.
586 ///
587 /// ```
588 /// use the_q::Q;
589 ///
590 /// let xs = [Q::one(), Q::one(), Q::one()];
591 /// assert_eq!(Q::sum(&xs), Q::number(the_q::Rat::new(3, 1).unwrap()));
592 /// assert!(Q::sum(&[]).is_zero());
593 /// ```
594 pub fn sum(xs: &[Q]) -> (r: Q)
595 requires
596 all_wf_q(xs@),
597 ensures
598 r.wf(),
599 xs@.len() == 0 ==> r.spec_is_zero(),
600 {
601 let mut acc = Q::zero();
602 let mut i: usize = 0;
603 while i < xs.len()
604 invariant
605 acc.wf(),
606 all_wf_q(xs@),
607 i <= xs.len(),
608 i == 0 ==> acc.spec_is_zero(),
609 decreases xs.len() - i,
610 {
611 acc = Q::add(acc, xs[i]);
612 i = i + 1;
613 }
614 acc
615 }
616
617 /// `xs[0] * xs[1] * ...`, left to right. An empty slice gives `1`.
618 ///
619 /// ```
620 /// use the_q::{Q, Rat};
621 ///
622 /// let xs = [Q::number(Rat::new(2, 1).unwrap()), Q::number(Rat::new(3, 1).unwrap())];
623 /// assert_eq!(Q::product(&xs), Q::number(Rat::new(6, 1).unwrap()));
624 /// assert!(Q::product(&[]).is_one());
625 /// ```
626 pub fn product(xs: &[Q]) -> (r: Q)
627 requires
628 all_wf_q(xs@),
629 ensures
630 r.wf(),
631 xs@.len() == 0 ==> r.spec_is_one(),
632 {
633 let mut acc = Q::one();
634 let mut i: usize = 0;
635 while i < xs.len()
636 invariant
637 acc.wf(),
638 all_wf_q(xs@),
639 i <= xs.len(),
640 i == 0 ==> acc.spec_is_one(),
641 decreases xs.len() - i,
642 {
643 acc = Q::mul(acc, xs[i]);
644 i = i + 1;
645 }
646 acc
647 }
648
649 /// `sum(w_i · x_i) / sum(w_i)`, total: a zero weight sum gives `Nan` (`0/0`)
650 /// or a signed infinity, and an empty slice gives `Nan` the same way.
651 ///
652 /// ```
653 /// use the_q::{Q, Rat};
654 ///
655 /// let one = Q::one();
656 /// let two = Q::number(Rat::new(2, 1).unwrap());
657 /// // weight 1 on `1`, weight 3 on `2`: (1*1 + 3*2) / (1 + 3) == 7/4
658 /// let three = Q::number(Rat::new(3, 1).unwrap());
659 /// let pairs = [(one, one), (three, two)];
660 /// assert_eq!(Q::weighted_mean(&pairs), Q::number(Rat::new(7, 4).unwrap()));
661 /// assert!(Q::weighted_mean(&[]).is_nan());
662 /// ```
663 pub fn weighted_mean(pairs: &[(Q, Q)]) -> (r: Q)
664 requires
665 all_wf_q_pairs(pairs@),
666 ensures
667 r.wf(),
668 {
669 let mut acc_num = Q::zero();
670 let mut acc_w = Q::zero();
671 let mut i: usize = 0;
672 while i < pairs.len()
673 invariant
674 acc_num.wf(),
675 acc_w.wf(),
676 all_wf_q_pairs(pairs@),
677 i <= pairs.len(),
678 decreases pairs.len() - i,
679 {
680 let (w, x) = pairs[i];
681 acc_num = Q::add(acc_num, Q::mul(w, x));
682 acc_w = Q::add(acc_w, w);
683 i = i + 1;
684 }
685 Q::div(acc_num, acc_w)
686 }
687}
688
689// ---------------------------------------------------------------------------
690// Addition, subtraction, multiplication (issue #26 §10.3)
691//
692// Saturation becomes visible in the enum here. The kernel returns `±MAX_MAG/1`
693// when a sum or a product leaves the budget. For example, `Rat::add(M, M)` is
694// `M`, which is wrong by a factor of two and looks like a true result. This
695// layer reports `PosSat` or `NegSat` instead.
696//
697// The precision cliffs below are option (A) from §6. The lattice has no element
698// for "sign known, magnitude unknown", thus some cells give `Nan` where a
699// `PosUnknown` or `NegUnknown` state gives a precise answer. A cliff affects
700// only a computation that continues after an overflow.
701// ---------------------------------------------------------------------------
702
703impl Q {
704 /// Ghost mirror of [`Q::add_numbers`]. Exists only so `Q::add`'s result is
705 /// *nameable* in ghost code, the same role `round_frac` plays for
706 /// `round_frac_exec` (V4): it lets `add`'s `ensures` pin `r ==
707 /// Q::spec_add(a, b)`, which is what makes the algebraic-law theorems in
708 /// `laws_q.rs` statable at all.
709 ///
710 /// This is *not* the specification of soundness. It is shaped like the
711 /// implementation on purpose, because its only job is to *equal* the
712 /// implementation. `denote.rs`'s `denotes`/`add_sound`/`add_honest` are
713 /// the specification, and by construction they never call this function
714 /// (checked by `scripts/check-no-spec-mirror-in-denote.sh`) — a spec
715 /// shaped like the table it specifies verifies with a shared mistake
716 /// (the scar this crate already carries once, noted at `recip` below).
717 pub open spec fn spec_add_numbers(x: Rat, y: Rat) -> Q {
718 let n = crate::q::add_n(x, y);
719 let d = crate::q::prod_d(x, y);
720 if crate::model::magnitude_fits(n, d) {
721 Q::Number(crate::round::round_frac(n, d, Dir::Nearest))
722 } else if n > 0 {
723 Q::PosSat
724 } else {
725 Q::NegSat
726 }
727 }
728
729 /// `x + y` for two representable rationals, saturating rather than clamping.
730 fn add_numbers(x: Rat, y: Rat) -> (r: Q)
731 requires
732 x.wf(),
733 y.wf(),
734 ensures
735 r.wf(),
736 !r.spec_is_nan(),
737 !r.spec_is_infinite(),
738 r == Q::spec_add_numbers(x, y),
739 {
740 let n: i128 = crate::q::add_n_exec(x, y);
741 let d: i128 = crate::q::prod_d_exec(x, y);
742 proof {
743 crate::q::lemma_op_widths(x, y);
744 crate::model::lemma_pow2_126();
745 }
746 if crate::q::magnitude_fits_exec(n, d) {
747 Q::Number(Rat::add(x, y))
748 } else if n > 0 {
749 Q::PosSat
750 } else {
751 Q::NegSat
752 }
753 }
754
755 /// Ghost mirror of [`Q::mul_numbers`]. See [`Q::spec_add_numbers`].
756 pub open spec fn spec_mul_numbers(x: Rat, y: Rat) -> Q {
757 let n = crate::q::mul_n(x, y);
758 let d = crate::q::prod_d(x, y);
759 if crate::model::magnitude_fits(n, d) {
760 Q::Number(crate::round::round_frac(n, d, Dir::Nearest))
761 } else if n > 0 {
762 Q::PosSat
763 } else {
764 Q::NegSat
765 }
766 }
767
768 /// `x * y` for two representable rationals, saturating rather than clamping.
769 fn mul_numbers(x: Rat, y: Rat) -> (r: Q)
770 requires
771 x.wf(),
772 y.wf(),
773 ensures
774 r.wf(),
775 !r.spec_is_nan(),
776 !r.spec_is_infinite(),
777 r == Q::spec_mul_numbers(x, y),
778 {
779 let n: i128 = crate::q::mul_n_exec(x, y);
780 let d: i128 = crate::q::prod_d_exec(x, y);
781 proof {
782 crate::q::lemma_op_widths(x, y);
783 crate::model::lemma_pow2_126();
784 }
785 if crate::q::magnitude_fits_exec(n, d) {
786 Q::Number(Rat::mul(x, y))
787 } else if n > 0 {
788 Q::PosSat
789 } else {
790 Q::NegSat
791 }
792 }
793
794 /// Ghost mirror of [`Q::number_plus_sat`]. See [`Q::spec_add_numbers`].
795 pub open spec fn spec_number_plus_sat(x: Rat, sat_pos: bool) -> Q {
796 if sat_pos {
797 if x.n() >= 0 {
798 Q::PosSat
799 } else {
800 Q::Nan
801 }
802 } else {
803 if x.n() <= 0 {
804 Q::NegSat
805 } else {
806 Q::Nan
807 }
808 }
809 }
810
811 /// `Number(x) + Sat`: sound for `x` on the saturation's side; for the other
812 /// sign the image reaches inside the budget and the result is `Nan`.
813 fn number_plus_sat(x: Rat, sat_pos: bool) -> (r: Q)
814 requires
815 x.wf(),
816 ensures
817 r.wf(),
818 !r.spec_is_infinite(),
819 !r.spec_is_number(),
820 r == Q::spec_number_plus_sat(x, sat_pos),
821 {
822 let s = x.signum();
823 if sat_pos {
824 if s >= 0 {
825 Q::PosSat
826 } else {
827 Q::Nan
828 }
829 } else {
830 if s <= 0 {
831 Q::NegSat
832 } else {
833 Q::Nan
834 }
835 }
836 }
837
838 /// `Number(x) * Sat`. Saturated for `|x| >= 1` (inclusive: `one() * PosSat`
839 /// must stay `PosSat`), `Nan` for `0 < |x| < 1` where the image reaches
840 /// below `MAX_MAG`, and exactly `Number(0)` at zero.
841 /// Ghost mirror of [`Q::number_times_sat`]. See [`Q::spec_add_numbers`].
842 pub open spec fn spec_number_times_sat(x: Rat, sat_pos: bool) -> Q {
843 if x.n() == 0 {
844 Q::Number(Rat::from_raw_spec(0, 1))
845 } else if !(x.n() >= x.d() || x.n() <= 0 - x.d()) {
846 Q::Nan
847 } else if (x.n() > 0) == sat_pos {
848 Q::PosSat
849 } else {
850 Q::NegSat
851 }
852 }
853
854 fn number_times_sat(x: Rat, sat_pos: bool) -> (r: Q)
855 requires
856 x.wf(),
857 ensures
858 r.wf(),
859 !r.spec_is_infinite(),
860 r == Q::spec_number_times_sat(x, sat_pos),
861 {
862 if x.is_zero() {
863 return Q::zero();
864 }
865 let n = x.numerator();
866 let d = x.denominator();
867 // `|x| >= 1` without division: `|n| >= d`, and `d > 0` by the invariant.
868 let at_least_one = n >= d || n <= 0 - d;
869 if !at_least_one {
870 return Q::Nan;
871 }
872 // Sign of the product of a sign-definite saturation and a nonzero `x`.
873 let positive = (n > 0) == sat_pos;
874 if positive {
875 Q::PosSat
876 } else {
877 Q::NegSat
878 }
879 }
880
881 /// Ghost mirror of [`Q::add`]'s match, cell for cell. This is *not* the
882 /// specification of `add`'s correctness — `denote.rs`'s
883 /// `add_sound`/`add_honest`/`add_nan_is_necessary` are, and none of them
884 /// call this function. Its only job is to make `add`'s result nameable in
885 /// ghost code (`r == Q::spec_add(a, b)`), which is the prerequisite the
886 /// commutativity/associativity/distributivity/monotonicity theorems in
887 /// `laws_q.rs` need: none of those properties are even statable against a
888 /// return value that only classification `ensures` clauses constrain.
889 pub open spec fn spec_add(a: Q, b: Q) -> Q {
890 match (a, b) {
891 (Q::Nan, _) => Q::Nan,
892 (_, Q::Nan) => Q::Nan,
893 (Q::Number(x), Q::Number(y)) => Q::spec_add_numbers(x, y),
894 (Q::Number(x), Q::PosSat) => Q::spec_number_plus_sat(x, true),
895 (Q::Number(x), Q::NegSat) => Q::spec_number_plus_sat(x, false),
896 (Q::PosSat, Q::Number(y)) => Q::spec_number_plus_sat(y, true),
897 (Q::NegSat, Q::Number(y)) => Q::spec_number_plus_sat(y, false),
898 (Q::PosSat, Q::PosSat) => Q::PosSat,
899 (Q::NegSat, Q::NegSat) => Q::NegSat,
900 (Q::PosSat, Q::NegSat) => Q::Nan,
901 (Q::NegSat, Q::PosSat) => Q::Nan,
902 (Q::PosInf, Q::NegInf) => Q::Nan,
903 (Q::NegInf, Q::PosInf) => Q::Nan,
904 (Q::PosInf, _) => Q::PosInf,
905 (Q::NegInf, _) => Q::NegInf,
906 (_, Q::PosInf) => Q::PosInf,
907 (_, Q::NegInf) => Q::NegInf,
908 }
909 }
910
911 /// `a + b`, total. Replaces the kernel `add`, which clamps silently.
912 ///
913 /// ```
914 /// use the_q::{Q, Rat, MAX_MAG};
915 ///
916 /// assert_eq!(Q::add(Q::one(), Q::one()), Q::number(Rat::new(2, 1).unwrap()));
917 /// // Two representable rationals overflowing the budget saturate, they do
918 /// // not become infinite.
919 /// let m = Q::number(Rat::new(MAX_MAG, 1).unwrap());
920 /// assert_eq!(Q::add(m, m), Q::PosSat);
921 /// assert!(Q::add(Q::Nan, Q::one()).is_nan());
922 /// ```
923 pub fn add(a: Q, b: Q) -> (r: Q)
924 requires
925 a.wf(),
926 b.wf(),
927 ensures
928 r.wf(),
929 r == Q::spec_add(a, b),
930 a.spec_is_nan() ==> r.spec_is_nan(),
931 b.spec_is_nan() ==> r.spec_is_nan(),
932 // Two representable rationals can only overflow, never become
933 // infinite and never lose all information.
934 (a.spec_is_number() && b.spec_is_number()) ==> (r.spec_is_number()
935 || r.spec_is_saturated()),
936 // An infinite result needs an infinite operand. Addition cannot
937 // create one. `is_infinite()` thus keeps the meaning "a division by
938 // zero occurred upstream".
939 r.spec_is_infinite() ==> (a.spec_is_infinite() || b.spec_is_infinite()),
940 // ...and conversely an infinite operand always survives, so no
941 // representable sum can come out of one.
942 (a.spec_is_infinite() || b.spec_is_infinite()) ==> !r.spec_is_number(),
943 {
944 match (a, b) {
945 (Q::Nan, _) => Q::Nan,
946 (_, Q::Nan) => Q::Nan,
947 (Q::Number(x), Q::Number(y)) => Q::add_numbers(x, y),
948 (Q::Number(x), Q::PosSat) => Q::number_plus_sat(x, true),
949 (Q::Number(x), Q::NegSat) => Q::number_plus_sat(x, false),
950 (Q::PosSat, Q::Number(y)) => Q::number_plus_sat(y, true),
951 (Q::NegSat, Q::Number(y)) => Q::number_plus_sat(y, false),
952 // Same-signed saturations reinforce; opposite-signed ones cancel to
953 // something entirely unknown.
954 (Q::PosSat, Q::PosSat) => Q::PosSat,
955 (Q::NegSat, Q::NegSat) => Q::NegSat,
956 (Q::PosSat, Q::NegSat) => Q::Nan,
957 (Q::NegSat, Q::PosSat) => Q::Nan,
958 // An infinity dominates anything finite, saturated or not.
959 (Q::PosInf, Q::NegInf) => Q::Nan,
960 (Q::NegInf, Q::PosInf) => Q::Nan,
961 (Q::PosInf, _) => Q::PosInf,
962 (Q::NegInf, _) => Q::NegInf,
963 (_, Q::PosInf) => Q::PosInf,
964 (_, Q::NegInf) => Q::NegInf,
965 }
966 }
967
968 /// `a - b`, total.
969 ///
970 /// The definition is `a + (-b)`, as §5 specifies. The two operations thus
971 /// agree for an overflowing difference.
972 ///
973 /// ```
974 /// use the_q::{Q, Rat};
975 ///
976 /// assert_eq!(Q::sub(Q::one(), Q::one()), Q::zero());
977 /// assert!(Q::sub(Q::Nan, Q::zero()).is_nan());
978 /// ```
979 pub fn sub(a: Q, b: Q) -> (r: Q)
980 requires
981 a.wf(),
982 b.wf(),
983 ensures
984 r.wf(),
985 r == Q::spec_add(a, Q::spec_neg(b)),
986 a.spec_is_nan() ==> r.spec_is_nan(),
987 b.spec_is_nan() ==> r.spec_is_nan(),
988 {
989 Q::add(a, b.neg())
990 }
991
992 /// Ghost mirror of [`Q::mul`]'s match. See [`Q::spec_add`].
993 pub open spec fn spec_mul(a: Q, b: Q) -> Q {
994 match (a, b) {
995 (Q::Nan, _) => Q::Nan,
996 (_, Q::Nan) => Q::Nan,
997 (Q::Number(x), Q::Number(y)) => Q::spec_mul_numbers(x, y),
998 (Q::Number(x), Q::PosSat) => Q::spec_number_times_sat(x, true),
999 (Q::Number(x), Q::NegSat) => Q::spec_number_times_sat(x, false),
1000 (Q::PosSat, Q::Number(y)) => Q::spec_number_times_sat(y, true),
1001 (Q::NegSat, Q::Number(y)) => Q::spec_number_times_sat(y, false),
1002 (Q::Number(x), Q::PosInf) => Q::spec_number_times_inf(x, true),
1003 (Q::Number(x), Q::NegInf) => Q::spec_number_times_inf(x, false),
1004 (Q::PosInf, Q::Number(y)) => Q::spec_number_times_inf(y, true),
1005 (Q::NegInf, Q::Number(y)) => Q::spec_number_times_inf(y, false),
1006 (Q::PosSat, Q::PosSat) => Q::PosSat,
1007 (Q::PosSat, Q::NegSat) => Q::NegSat,
1008 (Q::NegSat, Q::PosSat) => Q::NegSat,
1009 (Q::NegSat, Q::NegSat) => Q::PosSat,
1010 (Q::PosSat, Q::PosInf) => Q::PosInf,
1011 (Q::PosSat, Q::NegInf) => Q::NegInf,
1012 (Q::NegSat, Q::PosInf) => Q::NegInf,
1013 (Q::NegSat, Q::NegInf) => Q::PosInf,
1014 (Q::PosInf, Q::PosSat) => Q::PosInf,
1015 (Q::PosInf, Q::NegSat) => Q::NegInf,
1016 (Q::NegInf, Q::PosSat) => Q::NegInf,
1017 (Q::NegInf, Q::NegSat) => Q::PosInf,
1018 (Q::PosInf, Q::PosInf) => Q::PosInf,
1019 (Q::PosInf, Q::NegInf) => Q::NegInf,
1020 (Q::NegInf, Q::PosInf) => Q::NegInf,
1021 (Q::NegInf, Q::NegInf) => Q::PosInf,
1022 }
1023 }
1024
1025 /// `a * b`, total.
1026 ///
1027 /// ```
1028 /// use the_q::Q;
1029 ///
1030 /// // Saturation denotes a finite real, so zero times it is exactly zero...
1031 /// assert_eq!(Q::mul(Q::zero(), Q::PosSat), Q::zero());
1032 /// // ...but zero times a true infinity is the classic indeterminate.
1033 /// assert!(Q::mul(Q::zero(), Q::PosInf).is_nan());
1034 /// ```
1035 pub fn mul(a: Q, b: Q) -> (r: Q)
1036 requires
1037 a.wf(),
1038 b.wf(),
1039 ensures
1040 r.wf(),
1041 r == Q::spec_mul(a, b),
1042 a.spec_is_nan() ==> r.spec_is_nan(),
1043 b.spec_is_nan() ==> r.spec_is_nan(),
1044 (a.spec_is_number() && b.spec_is_number()) ==> (r.spec_is_number()
1045 || r.spec_is_saturated()),
1046 r.spec_is_infinite() ==> (a.spec_is_infinite() || b.spec_is_infinite()),
1047 // An infinite operand gives an infinity or `Nan`, and never a
1048 // representable product. Against zero the result is `Nan`.
1049 (a.spec_is_infinite() || b.spec_is_infinite()) ==> !r.spec_is_number(),
1050 {
1051 match (a, b) {
1052 (Q::Nan, _) => Q::Nan,
1053 (_, Q::Nan) => Q::Nan,
1054 (Q::Number(x), Q::Number(y)) => Q::mul_numbers(x, y),
1055 (Q::Number(x), Q::PosSat) => Q::number_times_sat(x, true),
1056 (Q::Number(x), Q::NegSat) => Q::number_times_sat(x, false),
1057 (Q::PosSat, Q::Number(y)) => Q::number_times_sat(y, true),
1058 (Q::NegSat, Q::Number(y)) => Q::number_times_sat(y, false),
1059 // `0 · ±∞` is indeterminate. `0 · Sat` is exactly zero.
1060 (Q::Number(x), Q::PosInf) => Q::number_times_inf(x, true),
1061 (Q::Number(x), Q::NegInf) => Q::number_times_inf(x, false),
1062 (Q::PosInf, Q::Number(y)) => Q::number_times_inf(y, true),
1063 (Q::NegInf, Q::Number(y)) => Q::number_times_inf(y, false),
1064 // Two saturations give a saturation with the product sign. A
1065 // product of two magnitudes above MAX_MAG is far above MAX_MAG.
1066 (Q::PosSat, Q::PosSat) => Q::PosSat,
1067 (Q::PosSat, Q::NegSat) => Q::NegSat,
1068 (Q::NegSat, Q::PosSat) => Q::NegSat,
1069 (Q::NegSat, Q::NegSat) => Q::PosSat,
1070 (Q::PosSat, Q::PosInf) => Q::PosInf,
1071 (Q::PosSat, Q::NegInf) => Q::NegInf,
1072 (Q::NegSat, Q::PosInf) => Q::NegInf,
1073 (Q::NegSat, Q::NegInf) => Q::PosInf,
1074 (Q::PosInf, Q::PosSat) => Q::PosInf,
1075 (Q::PosInf, Q::NegSat) => Q::NegInf,
1076 (Q::NegInf, Q::PosSat) => Q::NegInf,
1077 (Q::NegInf, Q::NegSat) => Q::PosInf,
1078 (Q::PosInf, Q::PosInf) => Q::PosInf,
1079 (Q::PosInf, Q::NegInf) => Q::NegInf,
1080 (Q::NegInf, Q::PosInf) => Q::NegInf,
1081 (Q::NegInf, Q::NegInf) => Q::PosInf,
1082 }
1083 }
1084
1085 /// `self^e`, total. `pow_u32(a, 0)` is `1` for every `a` including `Nan`, as
1086 /// IEEE `NaN^0`. A left fold of [`Q::mul`], associating like the kernel.
1087 ///
1088 /// ```
1089 /// use the_q::{Q, Rat};
1090 ///
1091 /// let two = Q::number(Rat::new(2, 1).unwrap());
1092 /// assert_eq!(two.pow_u32(3), Q::number(Rat::new(8, 1).unwrap()));
1093 /// assert!(Q::Nan.pow_u32(0).is_one());
1094 /// ```
1095 pub fn pow_u32(self, e: u32) -> (r: Q)
1096 requires
1097 self.wf(),
1098 ensures
1099 r.wf(),
1100 e == 0 ==> r.spec_is_one(),
1101 {
1102 let mut acc = Q::one();
1103 let mut i: u32 = 0;
1104 while i < e
1105 invariant
1106 acc.wf(),
1107 self.wf(),
1108 i <= e,
1109 i == 0 ==> acc.spec_is_one(),
1110 decreases e - i,
1111 {
1112 acc = Q::mul(acc, self);
1113 i = i + 1;
1114 }
1115 acc
1116 }
1117
1118 /// `a + b` as a `Rat`, `None` otherwise. A view over [`Q::add`]; cannot panic.
1119 ///
1120 /// ```
1121 /// use the_q::{Q, Rat};
1122 ///
1123 /// assert_eq!(Q::checked_add(Q::one(), Q::one()), Some(Rat::new(2, 1).unwrap()));
1124 /// assert_eq!(Q::checked_add(Q::PosInf, Q::one()), None);
1125 /// ```
1126 pub fn checked_add(a: Q, b: Q) -> (r: Option<Rat>)
1127 requires
1128 a.wf(),
1129 b.wf(),
1130 ensures
1131 r.is_some() ==> r.unwrap().wf(),
1132 (a.spec_is_nan() || b.spec_is_nan()) ==> r.is_none(),
1133 (a.spec_is_infinite() || b.spec_is_infinite()) ==> r.is_none(),
1134 {
1135 match Q::add(a, b) {
1136 Q::Number(f) => Some(f),
1137 _ => None,
1138 }
1139 }
1140
1141 /// `a - b` when the result is a representable rational. See
1142 /// [`Q::checked_add`].
1143 ///
1144 /// ```
1145 /// use the_q::{Q, Rat};
1146 ///
1147 /// assert_eq!(Q::checked_sub(Q::one(), Q::one()), Some(Rat::zero()));
1148 /// assert_eq!(Q::checked_sub(Q::Nan, Q::one()), None);
1149 /// ```
1150 pub fn checked_sub(a: Q, b: Q) -> (r: Option<Rat>)
1151 requires
1152 a.wf(),
1153 b.wf(),
1154 ensures
1155 r.is_some() ==> r.unwrap().wf(),
1156 (a.spec_is_nan() || b.spec_is_nan()) ==> r.is_none(),
1157 {
1158 match Q::sub(a, b) {
1159 Q::Number(f) => Some(f),
1160 _ => None,
1161 }
1162 }
1163
1164 /// `a * b` as a `Rat`, `None` otherwise. Can succeed on a saturated operand:
1165 /// `Number(0) * PosSat` is `0`.
1166 ///
1167 /// ```
1168 /// use the_q::{Q, Rat};
1169 ///
1170 /// assert_eq!(Q::checked_mul(Q::zero(), Q::PosSat), Some(Rat::zero()));
1171 /// assert_eq!(Q::checked_mul(Q::one(), Q::PosSat), None);
1172 /// ```
1173 pub fn checked_mul(a: Q, b: Q) -> (r: Option<Rat>)
1174 requires
1175 a.wf(),
1176 b.wf(),
1177 ensures
1178 r.is_some() ==> r.unwrap().wf(),
1179 (a.spec_is_nan() || b.spec_is_nan()) ==> r.is_none(),
1180 (a.spec_is_infinite() || b.spec_is_infinite()) ==> r.is_none(),
1181 {
1182 match Q::mul(a, b) {
1183 Q::Number(f) => Some(f),
1184 _ => None,
1185 }
1186 }
1187
1188 /// Ghost mirror of [`Q::number_times_inf`]. See [`Q::spec_add_numbers`].
1189 pub open spec fn spec_number_times_inf(x: Rat, inf_pos: bool) -> Q {
1190 if x.n() == 0 {
1191 Q::Nan
1192 } else if (x.n() > 0) == inf_pos {
1193 Q::PosInf
1194 } else {
1195 Q::NegInf
1196 }
1197 }
1198
1199 /// `Number(x) * ±∞`. Zero times an infinity is the classic indeterminate.
1200 fn number_times_inf(x: Rat, inf_pos: bool) -> (r: Q)
1201 requires
1202 x.wf(),
1203 ensures
1204 r.wf(),
1205 !r.spec_is_number(),
1206 !r.spec_is_saturated(),
1207 r == Q::spec_number_times_inf(x, inf_pos),
1208 {
1209 let s = x.signum();
1210 if s == 0 {
1211 Q::Nan
1212 } else if (s > 0) == inf_pos {
1213 Q::PosInf
1214 } else {
1215 Q::NegInf
1216 }
1217 }
1218}
1219
1220// ---------------------------------------------------------------------------
1221// Negation and absolute value (issue #26 §5)
1222//
1223// These two operations are the only entries in the design that are exact and
1224// total and have no precision cliff. Negation is a bijection on each state.
1225// Absolute value maps the two sign-definite pairs onto their positive halves.
1226// ---------------------------------------------------------------------------
1227
1228impl Q {
1229 /// Ghost mirror of [`Q::neg`]'s match. See [`Q::spec_add`]. The `Number`
1230 /// arm reconstructs the negated pair through `from_raw_spec` rather than
1231 /// calling the exec-only [`Rat::neg`], which cannot appear in a spec
1232 /// position.
1233 pub open spec fn spec_neg(a: Q) -> Q {
1234 match a {
1235 Q::Number(x) => Q::Number(Rat::from_raw_spec((0 - x.n()) as i64, x.d() as i64)),
1236 Q::PosSat => Q::NegSat,
1237 Q::NegSat => Q::PosSat,
1238 Q::PosInf => Q::NegInf,
1239 Q::NegInf => Q::PosInf,
1240 Q::Nan => Q::Nan,
1241 }
1242 }
1243
1244 /// `spec_neg` preserves `wf`, proven independently of the exec `neg`
1245 /// (which a `proof fn` cannot call): the `Number` arm's reconstructed
1246 /// pair carries the same `gcd` and budget as `x`'s, negation changing
1247 /// neither.
1248 pub proof fn lemma_spec_neg_wf(a: Q)
1249 requires
1250 a.wf(),
1251 ensures
1252 Q::spec_neg(a).wf(),
1253 {
1254 match a {
1255 Q::Number(x) => {
1256 let nx = Rat::from_raw_spec((0 - x.n()) as i64, x.d() as i64);
1257 Rat::lemma_from_raw_spec_components((0 - x.n()) as i64, x.d() as i64);
1258 assert(((0 - x.n()) as i64) as int == 0 - x.n());
1259 assert((x.d() as i64) as int == x.d());
1260 assert(crate::model::gcd_int(nx.n(), nx.d()) == crate::model::gcd_int(x.n(), x.d()));
1261 },
1262 _ => {},
1263 }
1264 }
1265
1266 /// `-self`, exact and total; saturations and infinities negate onto each other.
1267 ///
1268 /// ```
1269 /// use the_q::Q;
1270 ///
1271 /// assert_eq!(Q::one().neg(), Q::neg_one());
1272 /// assert_eq!(Q::PosSat.neg(), Q::NegSat);
1273 /// assert_eq!(Q::PosInf.neg(), Q::NegInf);
1274 /// assert!(Q::Nan.neg().is_nan());
1275 /// ```
1276 pub fn neg(self) -> (r: Q)
1277 requires
1278 self.wf(),
1279 ensures
1280 r.wf(),
1281 r == Q::spec_neg(self),
1282 // Negation permutes the classes rather than collapsing any of them.
1283 r.spec_is_number() == self.spec_is_number(),
1284 r.spec_is_saturated() == self.spec_is_saturated(),
1285 r.spec_is_infinite() == self.spec_is_infinite(),
1286 r.spec_is_nan() == self.spec_is_nan(),
1287 r.spec_is_zero() == self.spec_is_zero(),
1288 {
1289 match self {
1290 Q::Number(x) => {
1291 let nx = x.neg();
1292 proof {
1293 assert(((0 - x.n()) as i64) as int == 0 - x.n());
1294 assert((x.d() as i64) as int == x.d());
1295 Rat::lemma_from_raw_spec_components((0 - x.n()) as i64, x.d() as i64);
1296 Rat::lemma_extensional(nx, Rat::from_raw_spec((0 - x.n()) as i64, x.d() as i64));
1297 }
1298 Q::Number(nx)
1299 },
1300 Q::PosSat => Q::NegSat,
1301 Q::NegSat => Q::PosSat,
1302 Q::PosInf => Q::NegInf,
1303 Q::NegInf => Q::PosInf,
1304 Q::Nan => Q::Nan,
1305 }
1306 }
1307
1308 /// `|self|`, exact and total. Not injective on the specials, which is why
1309 /// `neg` carries class-preservation postconditions and this does not.
1310 ///
1311 /// ```
1312 /// use the_q::Q;
1313 ///
1314 /// assert_eq!(Q::neg_one().abs(), Q::one());
1315 /// assert_eq!(Q::NegSat.abs(), Q::PosSat);
1316 /// assert_eq!(Q::NegInf.abs(), Q::PosInf);
1317 /// ```
1318 pub fn abs(self) -> (r: Q)
1319 requires
1320 self.wf(),
1321 ensures
1322 r.wf(),
1323 r.spec_is_number() == self.spec_is_number(),
1324 r.spec_is_saturated() == self.spec_is_saturated(),
1325 r.spec_is_infinite() == self.spec_is_infinite(),
1326 r.spec_is_nan() == self.spec_is_nan(),
1327 // The result is never negative: a `Number` payload is `>= 0`, and
1328 // the only special that survives is the positive one of each pair.
1329 r.spec_is_number() ==> r->Number_0.n() >= 0,
1330 !r.spec_is_nan() ==> r != Q::NegSat && r != Q::NegInf,
1331 {
1332 match self {
1333 Q::Number(x) => Q::Number(x.abs()),
1334 Q::PosSat => Q::PosSat,
1335 Q::NegSat => Q::PosSat,
1336 Q::PosInf => Q::PosInf,
1337 Q::NegInf => Q::PosInf,
1338 Q::Nan => Q::Nan,
1339 }
1340 }
1341}
1342
1343// ---------------------------------------------------------------------------
1344// Selection: min, max, clamp (issue #26 §5)
1345//
1346// These operations propagate `Nan`. They are thus not the `Ord`-based selection
1347// that `slice.iter().min()` performs. A selection that comes from the order is
1348// a defect: `Ord`-based selection gives `min(Nan, Number(5)) == Number(5)`,
1349// which asserts that the true value is exactly 5 when the value can be
1350// anything.
1351//
1352// IEEE 754-2019 withdrew the NaN-ignoring `minNum`/`maxNum`; these follow the
1353// NaN-propagating `minimum`/`maximum`.
1354
1355impl Q {
1356 /// The smaller of `a` and `b`, propagating `Nan`; not `Ord`-based selection.
1357 ///
1358 /// ```
1359 /// use the_q::Q;
1360 ///
1361 /// assert_eq!(Q::min(Q::zero(), Q::one()), Q::zero());
1362 /// // Unlike `Ord::min`, a `Nan` operand makes the result `Nan`.
1363 /// assert!(Q::min(Q::Nan, Q::one()).is_nan());
1364 /// ```
1365 pub fn min(a: Q, b: Q) -> (r: Q)
1366 requires
1367 a.wf(),
1368 b.wf(),
1369 ensures
1370 r.wf(),
1371 (a.spec_is_nan() || b.spec_is_nan()) ==> r.spec_is_nan(),
1372 !(a.spec_is_nan() || b.spec_is_nan()) ==> {
1373 &&& (r == a || r == b)
1374 &&& Q::spec_le(r, a)
1375 &&& Q::spec_le(r, b)
1376 // ...and which argument. The contract thus names the result
1377 // and does not only constrain it.
1378 //
1379 // The three clauses above fix the answer uniquely, which is
1380 // `theorem_min_spec_categorical`. That uniqueness is a property
1381 // of the contract. These two clauses connect it to the output
1382 // of this function, thus the postcondition determines the
1383 // result for each input.
1384 &&& Q::spec_le(a, b) ==> r == a
1385 &&& !Q::spec_le(a, b) ==> r == b
1386 },
1387 {
1388 if a.is_nan() || b.is_nan() {
1389 Q::Nan
1390 } else if Q::le(a, b) {
1391 a
1392 } else {
1393 b
1394 }
1395 }
1396
1397 /// The larger of `a` and `b`, propagating `Nan`. See [`Q::min`].
1398 ///
1399 /// ```
1400 /// use the_q::Q;
1401 ///
1402 /// assert_eq!(Q::max(Q::zero(), Q::one()), Q::one());
1403 /// assert!(Q::max(Q::Nan, Q::one()).is_nan());
1404 /// ```
1405 pub fn max(a: Q, b: Q) -> (r: Q)
1406 requires
1407 a.wf(),
1408 b.wf(),
1409 ensures
1410 r.wf(),
1411 (a.spec_is_nan() || b.spec_is_nan()) ==> r.spec_is_nan(),
1412 !(a.spec_is_nan() || b.spec_is_nan()) ==> {
1413 &&& (r == a || r == b)
1414 &&& Q::spec_le(a, r)
1415 &&& Q::spec_le(b, r)
1416 // See `Q::min`: these name the result rather than bounding it.
1417 &&& Q::spec_le(a, b) ==> r == b
1418 &&& !Q::spec_le(a, b) ==> r == a
1419 },
1420 {
1421 if a.is_nan() || b.is_nan() {
1422 Q::Nan
1423 } else if Q::le(a, b) {
1424 b
1425 } else {
1426 a
1427 }
1428 }
1429
1430 /// `a` clamped into `[lo, hi]`; `Nan` in any argument, or an inverted range,
1431 /// gives `Nan` rather than an endpoint that asserts a false value.
1432 ///
1433 /// ```
1434 /// use the_q::{Q, Rat};
1435 ///
1436 /// let half = Q::number(Rat::new(1, 2).unwrap());
1437 /// assert_eq!(Q::clamp(half, Q::zero(), Q::one()), half);
1438 /// let two = Q::number(Rat::new(2, 1).unwrap());
1439 /// assert_eq!(Q::clamp(two, Q::zero(), Q::one()), Q::one());
1440 /// // An inverted range has no consistent answer.
1441 /// assert!(Q::clamp(half, Q::one(), Q::zero()).is_nan());
1442 /// ```
1443 pub fn clamp(a: Q, lo: Q, hi: Q) -> (r: Q)
1444 requires
1445 a.wf(),
1446 lo.wf(),
1447 hi.wf(),
1448 ensures
1449 r.wf(),
1450 (a.spec_is_nan() || lo.spec_is_nan() || hi.spec_is_nan()) ==> r.spec_is_nan(),
1451 (!a.spec_is_nan() && !lo.spec_is_nan() && !hi.spec_is_nan() && Q::spec_le(lo, hi)) ==> {
1452 &&& (r == a || r == lo || r == hi)
1453 &&& Q::spec_le(lo, r)
1454 &&& Q::spec_le(r, hi)
1455 // ...and which of the three values. Without these three
1456 // clauses a `clamp` that always returns `lo` satisfies the
1457 // contract above: for `lo < a < hi`, `r == lo` is one of the
1458 // three permitted values and lies in `[lo, hi]`.
1459 //
1460 // A postcondition that is wide enough to admit a wrong answer
1461 // is not a specification. A proof that the contract is
1462 // categorical finds this class of weakness.
1463 &&& (Q::spec_le(lo, a) && Q::spec_le(a, hi)) ==> r == a
1464 &&& !Q::spec_le(lo, a) ==> r == lo
1465 &&& !Q::spec_le(a, hi) ==> r == hi
1466 },
1467 {
1468 if a.is_nan() || lo.is_nan() || hi.is_nan() {
1469 Q::Nan
1470 } else if Q::lt(hi, lo) {
1471 // An inverted range has no consistent answer. `Nan` states that
1472 // condition. An endpoint result does not.
1473 Q::Nan
1474 } else if Q::lt(a, lo) {
1475 proof {
1476 // `a < lo <= hi` gives `a <= hi`, which makes the "returns
1477 // `hi`" clause vacuously true on this branch. Transitivity is
1478 // not free: the prover has `!spec_le(lo, a)` and needs totality
1479 // to turn it into `spec_le(a, lo)` first.
1480 theorem_order_total(lo, a);
1481 theorem_order_transitive(a, lo, hi);
1482 }
1483 lo
1484 } else if Q::lt(hi, a) {
1485 hi
1486 } else {
1487 a
1488 }
1489 }
1490}
1491
1492// ---------------------------------------------------------------------------
1493// Total division (issue #26 §10.2)
1494//
1495// This section closes the three defects that open #26. The kernel now fails
1496// loudly on each of them, and this layer answers with a value instead:
1497//
1498// * `Rat::zero().recip()` panics. It once returned `Rat { num: -1, den: 0 }`,
1499// which violates the type invariant and fails later, far from the cause.
1500// * `Rat::div(x, 0)` panics.
1501// * `Rat::checked_div(x, 0)` is `None`, as `std` and `num-traits` are for
1502// this case.
1503//
1504// Each cell below comes from the denotations in §2. The result is the smallest
1505// state whose denotation contains the true image
1506// `{ x/y : x ∈ ⟦a⟧, y ∈ ⟦b⟧ }`. `Nan` is always sound, because it denotes
1507// every value. Precision is thus the only property to argue.
1508// ---------------------------------------------------------------------------
1509
1510impl Q {
1511 /// Ghost mirror of [`Q::div_numbers`]. See [`Q::spec_add_numbers`].
1512 pub open spec fn spec_div_numbers(x: Rat, y: Rat) -> Q {
1513 let n = crate::q::div_n(x, y);
1514 let d = crate::q::div_d(x, y);
1515 if crate::model::magnitude_fits(n, d) {
1516 Q::Number(crate::round::round_frac(n, d, Dir::Nearest))
1517 } else if n > 0 {
1518 Q::PosSat
1519 } else {
1520 Q::NegSat
1521 }
1522 }
1523
1524 /// `x / y` for representable `x`, `y != 0`; saturates rather than clamps.
1525 fn div_numbers(x: Rat, y: Rat) -> (r: Q)
1526 requires
1527 x.wf(),
1528 y.wf(),
1529 y.n() != 0,
1530 ensures
1531 r.wf(),
1532 !r.spec_is_nan(),
1533 !r.spec_is_infinite(),
1534 r == Q::spec_div_numbers(x, y),
1535 {
1536 let n: i128 = crate::q::div_n_exec(x, y);
1537 let d: i128 = crate::q::div_d_exec(x, y);
1538 proof {
1539 crate::q::lemma_op_widths(x, y);
1540 crate::model::lemma_pow2_126();
1541 }
1542 if crate::q::magnitude_fits_exec(n, d) {
1543 Q::Number(Rat::div(x, y))
1544 } else if n > 0 {
1545 Q::PosSat
1546 } else {
1547 Q::NegSat
1548 }
1549 }
1550
1551 /// Ghost mirror of [`Q::sat_div_number`]. See [`Q::spec_add_numbers`].
1552 pub open spec fn spec_sat_div_number(pos: bool, y: Rat) -> Q {
1553 if y.n() == 0 {
1554 if pos {
1555 Q::PosInf
1556 } else {
1557 Q::NegInf
1558 }
1559 } else if y.n() > 0 {
1560 if y.n() <= y.d() {
1561 if pos {
1562 Q::PosSat
1563 } else {
1564 Q::NegSat
1565 }
1566 } else {
1567 Q::Nan
1568 }
1569 } else {
1570 if y.n() >= 0 - y.d() {
1571 if pos {
1572 Q::NegSat
1573 } else {
1574 Q::PosSat
1575 }
1576 } else {
1577 Q::Nan
1578 }
1579 }
1580 }
1581
1582 /// `Sat / y`. Sound while `|y| <= 1` (inclusive); above that the image
1583 /// contains representable values and the result is `Nan`.
1584 fn sat_div_number(pos: bool, y: Rat) -> (r: Q)
1585 requires
1586 y.wf(),
1587 ensures
1588 r.wf(),
1589 // A saturation is finite, so the quotient can only become infinite
1590 // by dividing by zero. `div` needs this to prove that an infinity in
1591 // its result always points at a zero divisor.
1592 r.spec_is_infinite() ==> y.n() == 0,
1593 // ...and conversely, a zero divisor always produces one, so no
1594 // representable quotient can survive it.
1595 y.n() == 0 ==> r.spec_is_infinite(),
1596 r == Q::spec_sat_div_number(pos, y),
1597 {
1598 let s = y.signum();
1599 if s == 0 {
1600 // A saturation has a definite sign and is nonzero, thus this case
1601 // is `x/0` with `x != 0`. The IEEE convention of §4 gives a signed
1602 // infinity.
1603 if pos {
1604 Q::PosInf
1605 } else {
1606 Q::NegInf
1607 }
1608 } else if s > 0 {
1609 if y.numerator() <= y.denominator() {
1610 if pos {
1611 Q::PosSat
1612 } else {
1613 Q::NegSat
1614 }
1615 } else {
1616 Q::Nan
1617 }
1618 } else {
1619 if y.numerator() >= 0 - y.denominator() {
1620 if pos {
1621 Q::NegSat
1622 } else {
1623 Q::PosSat
1624 }
1625 } else {
1626 Q::Nan
1627 }
1628 }
1629 }
1630
1631 /// Ghost mirror of [`Q::div`]'s match. See [`Q::spec_add`].
1632 pub open spec fn spec_div(a: Q, b: Q) -> Q {
1633 match (a, b) {
1634 (Q::Nan, _) => Q::Nan,
1635 (_, Q::Nan) => Q::Nan,
1636 (Q::Number(x), Q::Number(y)) => {
1637 if y.n() == 0 {
1638 if x.n() == 0 {
1639 Q::Nan
1640 } else if x.n() > 0 {
1641 Q::PosInf
1642 } else {
1643 Q::NegInf
1644 }
1645 } else {
1646 Q::spec_div_numbers(x, y)
1647 }
1648 },
1649 (Q::Number(x), Q::PosSat) => if x.n() == 0 {
1650 Q::Number(Rat::from_raw_spec(0, 1))
1651 } else {
1652 Q::Nan
1653 },
1654 (Q::Number(x), Q::NegSat) => if x.n() == 0 {
1655 Q::Number(Rat::from_raw_spec(0, 1))
1656 } else {
1657 Q::Nan
1658 },
1659 (Q::Number(_), Q::PosInf) => Q::Number(Rat::from_raw_spec(0, 1)),
1660 (Q::Number(_), Q::NegInf) => Q::Number(Rat::from_raw_spec(0, 1)),
1661 (Q::PosSat, Q::Number(y)) => Q::spec_sat_div_number(true, y),
1662 (Q::NegSat, Q::Number(y)) => Q::spec_sat_div_number(false, y),
1663 (Q::PosSat, Q::PosSat) => Q::Nan,
1664 (Q::PosSat, Q::NegSat) => Q::Nan,
1665 (Q::NegSat, Q::PosSat) => Q::Nan,
1666 (Q::NegSat, Q::NegSat) => Q::Nan,
1667 (Q::PosSat, Q::PosInf) => Q::Number(Rat::from_raw_spec(0, 1)),
1668 (Q::PosSat, Q::NegInf) => Q::Number(Rat::from_raw_spec(0, 1)),
1669 (Q::NegSat, Q::PosInf) => Q::Number(Rat::from_raw_spec(0, 1)),
1670 (Q::NegSat, Q::NegInf) => Q::Number(Rat::from_raw_spec(0, 1)),
1671 (Q::PosInf, Q::Number(y)) => if y.n() < 0 {
1672 Q::NegInf
1673 } else {
1674 Q::PosInf
1675 },
1676 (Q::NegInf, Q::Number(y)) => if y.n() < 0 {
1677 Q::PosInf
1678 } else {
1679 Q::NegInf
1680 },
1681 (Q::PosInf, Q::PosSat) => Q::PosInf,
1682 (Q::PosInf, Q::NegSat) => Q::NegInf,
1683 (Q::NegInf, Q::PosSat) => Q::NegInf,
1684 (Q::NegInf, Q::NegSat) => Q::PosInf,
1685 (Q::PosInf, Q::PosInf) => Q::Nan,
1686 (Q::PosInf, Q::NegInf) => Q::Nan,
1687 (Q::NegInf, Q::PosInf) => Q::Nan,
1688 (Q::NegInf, Q::NegInf) => Q::Nan,
1689 }
1690 }
1691
1692 /// `a / b`, total. Division by zero follows IEEE 754 uniformly, so that
1693 /// `recip(x) == div(one, x)` holds at zero. `Sat / Inf` is exactly `0` and
1694 /// `Inf / Sat` a signed infinity, because saturation denotes finite reals.
1695 ///
1696 /// ```
1697 /// use the_q::{Q, Rat};
1698 ///
1699 /// assert_eq!(Q::div(Q::one(), Q::one()), Q::one());
1700 /// // Division by zero follows IEEE 754: a signed infinity, and `0/0` is `Nan`.
1701 /// assert_eq!(Q::div(Q::one(), Q::zero()), Q::PosInf);
1702 /// assert!(Q::div(Q::zero(), Q::zero()).is_nan());
1703 /// ```
1704 pub fn div(a: Q, b: Q) -> (r: Q)
1705 requires
1706 a.wf(),
1707 b.wf(),
1708 ensures
1709 r.wf(),
1710 r == Q::spec_div(a, b),
1711 // `Nan` is absorbing on both sides — no information in, none out.
1712 a.spec_is_nan() ==> r.spec_is_nan(),
1713 b.spec_is_nan() ==> r.spec_is_nan(),
1714 // Dividing two representable rationals by a nonzero divisor yields a
1715 // rational or an overflow, never an infinity and never `Nan`. This
1716 // is what makes the `checked_div` sugar below meaningful.
1717 (a.spec_is_number() && b.spec_is_number() && !b.spec_is_zero()) ==> (r.spec_is_number()
1718 || r.spec_is_saturated()),
1719 // An infinite result means a zero divisor or an infinite
1720 // numerator, and never an overflow. This property keeps
1721 // `is_infinite()` usable as a diagnostic. It reports a division by
1722 // zero, and an overflow reports `is_saturated()` instead.
1723 r.spec_is_infinite() ==> (a.spec_is_infinite() || b.spec_is_zero()),
1724 // No representable quotient survives a zero divisor or an infinite
1725 // numerator. Together with the clauses above this is what makes
1726 // `checked_div` a faithful `Option` view of the discriminant.
1727 b.spec_is_zero() ==> !r.spec_is_number(),
1728 a.spec_is_infinite() ==> !r.spec_is_number(),
1729 {
1730 match (a, b) {
1731 (Q::Nan, _) => Q::Nan,
1732 (_, Q::Nan) => Q::Nan,
1733 // --- a representable numerator ---
1734 (Q::Number(x), Q::Number(y)) => {
1735 if y.is_zero() {
1736 if x.is_zero() {
1737 // 0/0 carries no information at all.
1738 Q::Nan
1739 } else if x.signum() > 0 {
1740 Q::PosInf
1741 } else {
1742 Q::NegInf
1743 }
1744 } else {
1745 Q::div_numbers(x, y)
1746 }
1747 },
1748 // `x / Sat`: the image is `(0, x/M)`, which contains representable
1749 // values. Only `x == 0` thus has a sound answer, and that answer is
1750 // exact, because `Sat` cannot be infinite.
1751 (Q::Number(x), Q::PosSat) => if x.is_zero() {
1752 Q::zero()
1753 } else {
1754 Q::Nan
1755 },
1756 (Q::Number(x), Q::NegSat) => if x.is_zero() {
1757 Q::zero()
1758 } else {
1759 Q::Nan
1760 },
1761 (Q::Number(_), Q::PosInf) => Q::zero(),
1762 (Q::Number(_), Q::NegInf) => Q::zero(),
1763 // --- a saturated numerator ---
1764 (Q::PosSat, Q::Number(y)) => Q::sat_div_number(true, y),
1765 (Q::NegSat, Q::Number(y)) => Q::sat_div_number(false, y),
1766 // `Sat / Sat` covers `(0, ∞)` or its mirror. The sign is known and
1767 // the magnitude is unknown. This lattice has no state for that.
1768 (Q::PosSat, Q::PosSat) => Q::Nan,
1769 (Q::PosSat, Q::NegSat) => Q::Nan,
1770 (Q::NegSat, Q::PosSat) => Q::Nan,
1771 (Q::NegSat, Q::NegSat) => Q::Nan,
1772 // `Sat / Inf` — exact, see the doc comment.
1773 (Q::PosSat, Q::PosInf) => Q::zero(),
1774 (Q::PosSat, Q::NegInf) => Q::zero(),
1775 (Q::NegSat, Q::PosInf) => Q::zero(),
1776 (Q::NegSat, Q::NegInf) => Q::zero(),
1777 // --- an infinite numerator ---
1778 // Including `y == 0`: §4 makes `±∞/0` sign-preserving, not `Nan`.
1779 (Q::PosInf, Q::Number(y)) => if y.signum() < 0 {
1780 Q::NegInf
1781 } else {
1782 Q::PosInf
1783 },
1784 (Q::NegInf, Q::Number(y)) => if y.signum() < 0 {
1785 Q::PosInf
1786 } else {
1787 Q::NegInf
1788 },
1789 (Q::PosInf, Q::PosSat) => Q::PosInf,
1790 (Q::PosInf, Q::NegSat) => Q::NegInf,
1791 (Q::NegInf, Q::PosSat) => Q::NegInf,
1792 (Q::NegInf, Q::NegSat) => Q::PosInf,
1793 // `∞/∞` is the classic indeterminate.
1794 (Q::PosInf, Q::PosInf) => Q::Nan,
1795 (Q::PosInf, Q::NegInf) => Q::Nan,
1796 (Q::NegInf, Q::PosInf) => Q::Nan,
1797 (Q::NegInf, Q::NegInf) => Q::Nan,
1798 }
1799 }
1800
1801 /// `1 / self`, total, defined as `div(one, self)` (`theorem_recip_is_div_one`).
1802 /// Exact on a nonzero `Number`: swapping a canonical pair cannot round.
1803 ///
1804 /// ```
1805 /// use the_q::{Q, Rat};
1806 ///
1807 /// let two = Q::number(Rat::new(2, 1).unwrap());
1808 /// assert_eq!(two.recip(), Q::number(Rat::new(1, 2).unwrap()));
1809 /// assert_eq!(Q::zero().recip(), Q::PosInf);
1810 /// ```
1811 pub fn recip(self) -> (r: Q)
1812 requires
1813 self.wf(),
1814 ensures
1815 r.wf(),
1816 self.spec_is_nan() ==> r.spec_is_nan(),
1817 // theorem_q_recip_is_div_one: true by definition, not derived —
1818 // recip's only body is this call.
1819 r == Q::spec_div(Q::Number(Rat::from_raw_spec(1, 1)), self),
1820 {
1821 // The cell-by-cell table is deliberately not restated in ghost form: a spec
1822 // shaped like the table verifies with a shared mistake.
1823 Q::div(Q::one(), self)
1824 }
1825
1826 /// `a / b` as a `Rat`, `None` otherwise, including a zero divisor.
1827 ///
1828 /// ```
1829 /// use the_q::{Q, Rat};
1830 ///
1831 /// assert_eq!(Q::checked_div(Q::one(), Q::one()), Some(Rat::one()));
1832 /// assert_eq!(Q::checked_div(Q::one(), Q::zero()), None);
1833 /// ```
1834 pub fn checked_div(a: Q, b: Q) -> (r: Option<Rat>)
1835 requires
1836 a.wf(),
1837 b.wf(),
1838 ensures
1839 r.is_some() ==> r.unwrap().wf(),
1840 // `None` for each case with no representable quotient: a zero
1841 // divisor, an operand with no information, or an infinite
1842 // numerator. The remaining `None` case is an overflow, which the
1843 // kernel `checked_div` also reports. This function is thus an
1844 // extension of the kernel function and not a change of meaning.
1845 (b.spec_is_zero() || a.spec_is_nan() || b.spec_is_nan() || a.spec_is_infinite())
1846 ==> r.is_none(),
1847 {
1848 match Q::div(a, b) {
1849 Q::Number(f) => Some(f),
1850 _ => None,
1851 }
1852 }
1853}
1854
1855// ---------------------------------------------------------------------------
1856// The classification really is a partition
1857//
1858// This property is stated and proven, and not assumed. The four predicates are
1859// the only supported case split on the type from outside the module. A caller
1860// that handles all four cases thus handles each state.
1861// ---------------------------------------------------------------------------
1862
1863/// The four classification predicates are mutually exclusive and jointly
1864/// exhaustive.
1865pub proof fn theorem_classification_partitions(q: Q)
1866 ensures
1867// exactly one holds
1868
1869 (if q.spec_is_number() { 1int } else { 0int }) + (if q.spec_is_saturated() {
1870 1int
1871 } else {
1872 0int
1873 }) + (if q.spec_is_infinite() { 1int } else { 0int }) + (if q.spec_is_nan() {
1874 1int
1875 } else {
1876 0int
1877 }) == 1,
1878{
1879 match q {
1880 Q::Number(_) => {},
1881 Q::PosSat => {},
1882 Q::NegSat => {},
1883 Q::PosInf => {},
1884 Q::NegInf => {},
1885 Q::Nan => {},
1886 }
1887}
1888
1889/// A saturation is never a number, an infinity or a `Nan` — the fact callers
1890/// most often want when reading `is_saturated` as an overflow diagnostic.
1891pub proof fn theorem_saturated_excludes_rest(q: Q)
1892 requires
1893 q.spec_is_saturated(),
1894 ensures
1895 !q.spec_is_number(),
1896 !q.spec_is_infinite(),
1897 !q.spec_is_nan(),
1898{
1899}
1900
1901// ---------------------------------------------------------------------------
1902// The total order (issue #26 §5)
1903//
1904// NegInf < NegSat < Number(...) < PosSat < PosInf < Nan
1905//
1906// This order applies to representations and not to denoted values. Outside
1907// `Number` that difference is visible: `PosSat == PosSat` compares `Equal`
1908// while the two true values can differ. Inside `Number` the order is the order
1909// on rationals.
1910//
1911// `NegSat < Number < PosSat` strictly, by I2; the position of `Nan` is a free
1912// choice.
1913
1914impl Q {
1915 /// The position of a variant in the order above.
1916 ///
1917 /// The order uses this rank and not the declaration order. A change to the
1918 /// variant order thus cannot change the order on values. For the same
1919 /// reason `Ord` is hand-written and not derived.
1920 pub open spec fn spec_rank(self) -> int {
1921 match self {
1922 Q::NegInf => 0,
1923 Q::NegSat => 1,
1924 Q::Number(_) => 2,
1925 Q::PosSat => 3,
1926 Q::PosInf => 4,
1927 Q::Nan => 5,
1928 }
1929 }
1930
1931 /// The ghost order: `a <= b`.
1932 pub open spec fn spec_le(a: Q, b: Q) -> bool {
1933 if a.spec_rank() != b.spec_rank() {
1934 a.spec_rank() < b.spec_rank()
1935 } else {
1936 match (a, b) {
1937 (Q::Number(x), Q::Number(y)) => crate::model::q_le(x, y),
1938 // Every other equal-rank pair is the same payload-free variant,
1939 // so the two representations are identical.
1940 _ => true,
1941 }
1942 }
1943 }
1944
1945 /// The ghost order: `a == b`, as representations.
1946 pub open spec fn spec_eq(a: Q, b: Q) -> bool {
1947 Q::spec_le(a, b) && Q::spec_le(b, a)
1948 }
1949}
1950
1951impl Q {
1952 /// Three-way comparison: negative, zero or positive as `a < b`, `a == b` or
1953 /// `a > b` in the order above.
1954 ///
1955 /// The comparison is total. There is no incomparable pair, because `Nan`
1956 /// has a definite position.
1957 ///
1958 /// ```
1959 /// use the_q::Q;
1960 ///
1961 /// assert_eq!(Q::compare(Q::zero(), Q::one()), -1);
1962 /// assert_eq!(Q::compare(Q::one(), Q::one()), 0);
1963 /// // `Nan` sorts last, above every other state.
1964 /// assert_eq!(Q::compare(Q::PosInf, Q::Nan), -1);
1965 /// ```
1966 pub fn compare(a: Q, b: Q) -> (r: i32)
1967 requires
1968 a.wf(),
1969 b.wf(),
1970 ensures
1971 r <= 0 <==> Q::spec_le(a, b),
1972 r >= 0 <==> Q::spec_le(b, a),
1973 r == 0 <==> Q::spec_eq(a, b),
1974 {
1975 match (a, b) {
1976 // This is the only case with a payload. It delegates to the
1977 // verified kernel comparison and does not cross-multiply again.
1978 (Q::Number(x), Q::Number(y)) => Rat::compare(x, y),
1979 _ => {
1980 let ra = Q::rank_exec(a);
1981 let rb = Q::rank_exec(b);
1982 if ra < rb {
1983 -1
1984 } else if ra > rb {
1985 1
1986 } else {
1987 0
1988 }
1989 },
1990 }
1991 }
1992
1993 /// Exec mirror of [`Q::spec_rank`].
1994 fn rank_exec(q: Q) -> (r: i32)
1995 ensures
1996 r == q.spec_rank(),
1997 {
1998 match q {
1999 Q::NegInf => 0,
2000 Q::NegSat => 1,
2001 Q::Number(_) => 2,
2002 Q::PosSat => 3,
2003 Q::PosInf => 4,
2004 Q::Nan => 5,
2005 }
2006 }
2007
2008 /// `a < b`.
2009 ///
2010 /// ```
2011 /// use the_q::Q;
2012 ///
2013 /// assert!(Q::lt(Q::zero(), Q::one()));
2014 /// assert!(!Q::lt(Q::one(), Q::one()));
2015 /// ```
2016 pub fn lt(a: Q, b: Q) -> (r: bool)
2017 requires
2018 a.wf(),
2019 b.wf(),
2020 ensures
2021 r <==> !Q::spec_le(b, a),
2022 {
2023 Q::compare(a, b) < 0
2024 }
2025
2026 /// `a <= b`.
2027 ///
2028 /// ```
2029 /// use the_q::Q;
2030 ///
2031 /// assert!(Q::le(Q::one(), Q::one()));
2032 /// assert!(!Q::le(Q::one(), Q::zero()));
2033 /// ```
2034 pub fn le(a: Q, b: Q) -> (r: bool)
2035 requires
2036 a.wf(),
2037 b.wf(),
2038 ensures
2039 r <==> Q::spec_le(a, b),
2040 {
2041 Q::compare(a, b) <= 0
2042 }
2043
2044 /// `a > b`.
2045 ///
2046 /// ```
2047 /// use the_q::Q;
2048 ///
2049 /// assert!(Q::gt(Q::one(), Q::zero()));
2050 /// assert!(!Q::gt(Q::zero(), Q::zero()));
2051 /// ```
2052 pub fn gt(a: Q, b: Q) -> (r: bool)
2053 requires
2054 a.wf(),
2055 b.wf(),
2056 ensures
2057 r <==> !Q::spec_le(a, b),
2058 {
2059 Q::compare(a, b) > 0
2060 }
2061
2062 /// `a >= b`.
2063 ///
2064 /// ```
2065 /// use the_q::Q;
2066 ///
2067 /// assert!(Q::ge(Q::one(), Q::one()));
2068 /// assert!(!Q::ge(Q::zero(), Q::one()));
2069 /// ```
2070 pub fn ge(a: Q, b: Q) -> (r: bool)
2071 requires
2072 a.wf(),
2073 b.wf(),
2074 ensures
2075 r <==> Q::spec_le(b, a),
2076 {
2077 Q::compare(a, b) >= 0
2078 }
2079}
2080
2081/// The order is total: every pair is comparable, `Nan` included.
2082pub proof fn theorem_order_total(a: Q, b: Q)
2083 requires
2084 a.wf(),
2085 b.wf(),
2086 ensures
2087 Q::spec_le(a, b) || Q::spec_le(b, a),
2088{
2089}
2090
2091/// Antisymmetry against *structural* equality: `a <= b <= a` gives `a == b`,
2092/// not merely `spec_eq`. This is what makes the derived `Eq`/`Hash` sound
2093/// beside the hand-written `Ord`; the `Number` case is kernel canonicality.
2094pub proof fn theorem_order_antisymmetric(a: Q, b: Q)
2095 requires
2096 a.wf(),
2097 b.wf(),
2098 Q::spec_le(a, b),
2099 Q::spec_le(b, a),
2100 ensures
2101 a == b,
2102{
2103 match (a, b) {
2104 (Q::Number(x), Q::Number(y)) => {
2105 crate::laws::lemma_canonical_eq(x, y);
2106 },
2107 _ => {},
2108 }
2109}
2110
2111/// `spec_eq` and the derived `PartialEq` are the same relation.
2112///
2113/// This theorem is the other direction of the theorem above. Together they
2114/// give the equivalence that `Ord`/`Eq` consistency needs.
2115pub proof fn theorem_spec_eq_is_structural_eq(a: Q, b: Q)
2116 requires
2117 a.wf(),
2118 b.wf(),
2119 ensures
2120 Q::spec_eq(a, b) <==> a == b,
2121{
2122 if Q::spec_eq(a, b) {
2123 theorem_order_antisymmetric(a, b);
2124 }
2125 match (a, b) {
2126 (Q::Number(x), Q::Number(y)) => {
2127 crate::laws::lemma_canonical_eq(x, y);
2128 },
2129 _ => {},
2130 }
2131}
2132
2133/// The order is transitive.
2134///
2135/// The cross-rank cases are integer transitivity on the rank. The all-`Number`
2136/// case uses the transitivity lemma of the kernel.
2137pub proof fn theorem_order_transitive(a: Q, b: Q, c: Q)
2138 requires
2139 a.wf(),
2140 b.wf(),
2141 c.wf(),
2142 Q::spec_le(a, b),
2143 Q::spec_le(b, c),
2144 ensures
2145 Q::spec_le(a, c),
2146{
2147 match (a, b, c) {
2148 (Q::Number(x), Q::Number(y), Q::Number(z)) => {
2149 crate::q::lemma_le_trans(x, y, z);
2150 },
2151 _ => {},
2152 }
2153}
2154
2155/// Saturation sits strictly outside the numbers, on the correct side.
2156///
2157/// This fact makes the placement sound. It states that the order on
2158/// representations agrees with the order on denoted values wherever both are
2159/// defined.
2160pub proof fn theorem_sat_separates_numbers(x: Rat)
2161 requires
2162 x.wf(),
2163 ensures
2164 Q::spec_le(Q::NegSat, Q::Number(x)),
2165 Q::spec_le(Q::Number(x), Q::PosSat),
2166 !Q::spec_le(Q::Number(x), Q::NegSat),
2167 !Q::spec_le(Q::PosSat, Q::Number(x)),
2168{
2169}
2170
2171/// `NegInf` is strictly below and `Nan` strictly above every other value, so
2172/// folds can start there and the extremes are unique.
2173pub proof fn theorem_order_extremes(q: Q)
2174 ensures
2175 Q::spec_le(Q::NegInf, q),
2176 Q::spec_le(q, Q::Nan),
2177 q != Q::NegInf ==> !Q::spec_le(q, Q::NegInf),
2178 q != Q::Nan ==> !Q::spec_le(Q::Nan, q),
2179{
2180}
2181
2182/// Trichotomy: exactly one of `a < b`, `a == b`, `b < a` holds.
2183pub proof fn theorem_order_trichotomy(a: Q, b: Q)
2184 requires
2185 a.wf(),
2186 b.wf(),
2187 ensures
2188 (if !Q::spec_le(b, a) {
2189 1int
2190 } else {
2191 0int
2192 }) + (if a == b {
2193 1int
2194 } else {
2195 0int
2196 }) + (if !Q::spec_le(a, b) {
2197 1int
2198 } else {
2199 0int
2200 }) == 1,
2201{
2202 theorem_order_total(a, b);
2203 if Q::spec_le(a, b) && Q::spec_le(b, a) {
2204 theorem_order_antisymmetric(a, b);
2205 }
2206}
2207
2208/// `min`'s contract admits exactly one answer; on a tie, canonicality makes the
2209/// two representations one.
2210pub proof fn theorem_min_spec_categorical(a: Q, b: Q, r1: Q, r2: Q)
2211 requires
2212 a.wf(),
2213 b.wf(),
2214 r1 == a || r1 == b,
2215 Q::spec_le(r1, a),
2216 Q::spec_le(r1, b),
2217 r2 == a || r2 == b,
2218 Q::spec_le(r2, a),
2219 Q::spec_le(r2, b),
2220 ensures
2221 r1 == r2,
2222{
2223 // r1 <= r2 because r2 is one of {a, b}, and symmetrically; antisymmetry
2224 // then forces structural equality.
2225 theorem_order_antisymmetric(r1, r2);
2226}
2227
2228/// **`max`'s postcondition pins its result uniquely.** This theorem is the dual
2229/// of [`theorem_min_spec_categorical`]. It is a separate theorem, because the
2230/// `max` contract is its own statement and not a rewriting of the `min`
2231/// contract.
2232pub proof fn theorem_max_spec_categorical(a: Q, b: Q, r1: Q, r2: Q)
2233 requires
2234 a.wf(),
2235 b.wf(),
2236 r1 == a || r1 == b,
2237 Q::spec_le(a, r1),
2238 Q::spec_le(b, r1),
2239 r2 == a || r2 == b,
2240 Q::spec_le(a, r2),
2241 Q::spec_le(b, r2),
2242 ensures
2243 r1 == r2,
2244{
2245 theorem_order_antisymmetric(r1, r2);
2246}
2247
2248/// `clamp`'s contract admits exactly one answer. Without its last three clauses
2249/// a `clamp` that always returned `lo` would verify.
2250pub proof fn theorem_clamp_spec_categorical(a: Q, lo: Q, hi: Q, r1: Q, r2: Q)
2251 requires
2252 a.wf(),
2253 lo.wf(),
2254 hi.wf(),
2255 Q::spec_le(lo, hi),
2256 (Q::spec_le(lo, a) && Q::spec_le(a, hi)) ==> r1 == a,
2257 !Q::spec_le(lo, a) ==> r1 == lo,
2258 !Q::spec_le(a, hi) ==> r1 == hi,
2259 (Q::spec_le(lo, a) && Q::spec_le(a, hi)) ==> r2 == a,
2260 !Q::spec_le(lo, a) ==> r2 == lo,
2261 !Q::spec_le(a, hi) ==> r2 == hi,
2262 ensures
2263 r1 == r2,
2264{
2265 // The three guards are exhaustive once `lo <= hi`: if `a` is below `lo` it
2266 // is below `hi` too, and symmetrically above. Totality supplies the
2267 // trichotomy that makes the case split complete.
2268 theorem_order_total(lo, a);
2269 theorem_order_total(a, hi);
2270}
2271
2272/// `(min(a, b), max(a, b))` is `(a, b)` or `(b, a)`: a two-element sort.
2273pub proof fn theorem_min_max_exchange(a: Q, b: Q, rmin: Q, rmax: Q)
2274 requires
2275 a.wf(),
2276 b.wf(),
2277 rmin == a || rmin == b,
2278 Q::spec_le(rmin, a),
2279 Q::spec_le(rmin, b),
2280 rmax == a || rmax == b,
2281 Q::spec_le(a, rmax),
2282 Q::spec_le(b, rmax),
2283 ensures
2284 (rmin == a && rmax == b) || (rmin == b && rmax == a),
2285{
2286 if rmin == a && rmax == a {
2287 // a <= b from min's bound, b <= a from max's: the arguments coincide.
2288 theorem_order_antisymmetric(a, b);
2289 }
2290 if rmin == b && rmax == b {
2291 theorem_order_antisymmetric(a, b);
2292 }
2293}
2294
2295/// `min` is the greatest lower bound: `q <= min(a, b)` iff `q <= a` and `q <= b`.
2296pub proof fn theorem_min_is_glb(a: Q, b: Q, r: Q, q: Q)
2297 requires
2298 a.wf(),
2299 b.wf(),
2300 q.wf(),
2301 r == a || r == b,
2302 Q::spec_le(r, a),
2303 Q::spec_le(r, b),
2304 ensures
2305 Q::spec_le(q, r) <==> (Q::spec_le(q, a) && Q::spec_le(q, b)),
2306{
2307 if Q::spec_le(q, r) {
2308 theorem_order_transitive(q, r, a);
2309 theorem_order_transitive(q, r, b);
2310 }
2311}
2312
2313/// **`max` computes the least upper bound** — the dual of
2314/// [`theorem_min_is_glb`].
2315pub proof fn theorem_max_is_lub(a: Q, b: Q, r: Q, q: Q)
2316 requires
2317 a.wf(),
2318 b.wf(),
2319 q.wf(),
2320 r == a || r == b,
2321 Q::spec_le(a, r),
2322 Q::spec_le(b, r),
2323 ensures
2324 Q::spec_le(r, q) <==> (Q::spec_le(a, q) && Q::spec_le(b, q)),
2325{
2326 if Q::spec_le(r, q) {
2327 theorem_order_transitive(a, r, q);
2328 theorem_order_transitive(b, r, q);
2329 }
2330}
2331
2332/// The component-wise `spec_in_unit_interval` agrees with the order-based
2333/// `0 <= q <= 1` on a `Number`; no special is in `[0, 1]`.
2334pub proof fn theorem_unit_interval_agrees_with_order(q: Q)
2335 requires
2336 q.wf(),
2337 ensures
2338 q.spec_in_unit_interval() <==> (q.spec_is_number() && Q::spec_le(
2339 Q::Number(Rat::from_raw_spec(0, 1)),
2340 q,
2341 ) && Q::spec_le(q, Q::Number(Rat::from_raw_spec(1, 1)))),
2342{
2343 Rat::lemma_from_raw_spec_components(0, 1);
2344 Rat::lemma_from_raw_spec_components(1, 1);
2345}
2346
2347/// `spec_is_zero` holds only of `Number(0/1)` and `spec_is_one` only of
2348/// `Number(1/1)`: the enum-level form of canonicality.
2349pub proof fn theorem_zero_one_unique_repr(q: Q)
2350 requires
2351 q.wf(),
2352 ensures
2353 q.spec_is_zero() <==> q == Q::Number(Rat::from_raw_spec(0, 1)),
2354 q.spec_is_one() <==> q == Q::Number(Rat::from_raw_spec(1, 1)),
2355{
2356 Rat::lemma_from_raw_spec_components(0, 1);
2357 Rat::lemma_from_raw_spec_components(1, 1);
2358 match q {
2359 Q::Number(x) => {
2360 if x.n() == 0 {
2361 assert(x.d() == 1);
2362 Rat::lemma_extensional(x, Rat::from_raw_spec(0, 1));
2363 }
2364 if x.n() == x.d() {
2365 // n == d > 0, so gcd_int(n, d) == gcd_nat(nn, nn) with nn > 0.
2366 let nn = x.n() as nat;
2367 assert(crate::model::abs_int(x.n()) == x.n());
2368 // One definitional unfold: gcd(nn, nn) == gcd(nn, nn % nn).
2369 assert(nn % nn == 0) by (nonlinear_arith)
2370 requires
2371 nn > 0,
2372 ;
2373 crate::gcd::lemma_gcd_zero(nn);
2374 assert(crate::model::gcd_nat(nn, nn) == nn);
2375 // wf says that gcd is 1, so n == d == 1.
2376 assert(x.n() == 1);
2377 assert(x.d() == 1);
2378 Rat::lemma_extensional(x, Rat::from_raw_spec(1, 1));
2379 }
2380 },
2381 _ => {},
2382 }
2383}
2384
2385} // verus!
2386
2387// ---------------------------------------------------------------------------
2388// Standard trait impls
2389//
2390// These implementations are total delegations to the verified functions above,
2391// in the pattern that `Rat` uses. Verus does not model the `core` comparison
2392// traits, thus these implementations are `external`. Verified code cannot call
2393// them and does not need them.
2394// ---------------------------------------------------------------------------
2395
2396#[cfg_attr(verus_keep_ghost, verifier::external)]
2397impl PartialOrd for Q {
2398 fn partial_cmp(&self, other: &Q) -> Option<core::cmp::Ordering> {
2399 Some(<Q as Ord>::cmp(self, other))
2400 }
2401}
2402
2403#[cfg_attr(verus_keep_ghost, verifier::external)]
2404impl Ord for Q {
2405 /// Delegates to the verified [`Q::compare`]. Do not use for selection:
2406 /// `Ord`-based `min(Nan, Number(5))` asserts the value 5.
2407 fn cmp(&self, other: &Q) -> core::cmp::Ordering {
2408 Q::compare(*self, *other).cmp(&0)
2409 }
2410}
2411
2412// ---------------------------------------------------------------------------
2413// Operator traits
2414//
2415// These implementations are total delegations, in the pattern that `Rat` uses.
2416// Verus does not model the `core::ops` traits, thus these implementations are
2417// `external` and add no assumption to a proof.
2418//
2419// This type implements `Div`, and `Rat` does not. Division on `Rat` has the
2420// precondition `!b.is_zero()`, which an operator cannot express, thus `a / b`
2421// on `Rat` can panic. `Q::div` is total, thus each input to `a / b` gives a
2422// value.
2423// ---------------------------------------------------------------------------
2424
2425#[cfg_attr(verus_keep_ghost, verifier::external)]
2426impl core::ops::Add for Q {
2427 type Output = Q;
2428
2429 fn add(self, rhs: Q) -> Q {
2430 Q::add(self, rhs)
2431 }
2432}
2433
2434#[cfg_attr(verus_keep_ghost, verifier::external)]
2435impl core::ops::Sub for Q {
2436 type Output = Q;
2437
2438 fn sub(self, rhs: Q) -> Q {
2439 Q::sub(self, rhs)
2440 }
2441}
2442
2443#[cfg_attr(verus_keep_ghost, verifier::external)]
2444impl core::ops::Mul for Q {
2445 type Output = Q;
2446
2447 fn mul(self, rhs: Q) -> Q {
2448 Q::mul(self, rhs)
2449 }
2450}
2451
2452/// Total division. The note above states why this operator exists here and not
2453/// on `Rat`.
2454#[cfg_attr(verus_keep_ghost, verifier::external)]
2455impl core::ops::Div for Q {
2456 type Output = Q;
2457
2458 fn div(self, rhs: Q) -> Q {
2459 Q::div(self, rhs)
2460 }
2461}
2462
2463#[cfg_attr(verus_keep_ghost, verifier::external)]
2464impl core::ops::Neg for Q {
2465 type Output = Q;
2466
2467 fn neg(self) -> Q {
2468 Q::neg(self)
2469 }
2470}
2471
2472/// `Q::zero()` — the additive identity, and the identity `Sum` folds from.
2473#[cfg_attr(verus_keep_ghost, verifier::external)]
2474impl Default for Q {
2475 fn default() -> Q {
2476 Q::zero()
2477 }
2478}