Skip to main content

the_q/
q.rs

1//! The public `Rat` API: constructors, arithmetic, comparison, predicates.
2//!
3//! Each operation computes its exact result in `i128` and passes it to
4//! [`crate::round::round_frac_exec`]. Widest intermediates under the budget:
5//! `mul`/`div`/`cmp` products below `2^124`, `add`/`sub` sums below `2^125`,
6//! leaving two bits under `i128::MAX`; a `2^63` budget would overflow the
7//! `add` row. Division by zero is a precondition; [`require_condition`] turns
8//! it into a panic for unverified callers.
9
10use verus_builtin_macros::verus;
11
12#[allow(unused_imports)]
13use verus_builtin::*;
14#[allow(unused_imports)]
15use vstd::prelude::*;
16
17#[allow(unused_imports)]
18use crate::gcd::*;
19#[allow(unused_imports)]
20use crate::model::*;
21#[allow(unused_imports)]
22use crate::round::*;
23use crate::types::{Dir, MAX_DEC_PLACES, MAX_MAG, Rat};
24
25verus! {
26
27// ---------------------------------------------------------------------------
28// The runtime half of the division preconditions
29// ---------------------------------------------------------------------------
30
31/// Panic with `msg` unless `condition` holds. Verified callers discharge the
32/// precondition and never reach it; for unverified callers it is the
33/// division-by-zero panic. `external_body` for its message only (`TRUSTED.md`).
34///
35/// Public only because Verus's visibility rules require it; not part of the
36/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
37///
38/// ```
39/// the_q::q::require_condition(true, "unreachable");
40/// ```
41///
42/// ```should_panic
43/// the_q::q::require_condition(false, "the-q: condition failed");
44/// ```
45#[verifier::external_body]
46pub fn require_condition(condition: bool, msg: &str)
47    requires
48        condition,
49{
50    assert!(condition, "{}", msg);
51}
52
53// ---------------------------------------------------------------------------
54// Exact numerators and denominators of the four operations, in ghost form
55// ---------------------------------------------------------------------------
56
57/// The exact numerator of `a + b`.
58pub open spec fn add_n(a: Rat, b: Rat) -> int {
59    a.n() * b.d() + b.n() * a.d()
60}
61
62/// The exact numerator of `a - b`.
63pub open spec fn sub_n(a: Rat, b: Rat) -> int {
64    a.n() * b.d() - b.n() * a.d()
65}
66
67/// The exact numerator of `a * b`.
68pub open spec fn mul_n(a: Rat, b: Rat) -> int {
69    a.n() * b.n()
70}
71
72/// The common denominator of `a + b`, `a - b` and `a * b`.
73pub open spec fn prod_d(a: Rat, b: Rat) -> int {
74    a.d() * b.d()
75}
76
77/// The exact numerator of `a / b`, sign-normalised so the denominator is
78/// positive.
79pub open spec fn div_n(a: Rat, b: Rat) -> int {
80    if b.n() > 0 {
81        a.n() * b.d()
82    } else {
83        -(a.n() * b.d())
84    }
85}
86
87/// The exact denominator of `a / b`, always positive.
88pub open spec fn div_d(a: Rat, b: Rat) -> int {
89    if b.n() > 0 {
90        a.d() * b.n()
91    } else {
92        -(a.d() * b.n())
93    }
94}
95
96/// `num` with a negative `den`'s sign folded onto it, so `-3 / -4` rounds as
97/// `3 / 4` and not as `-3 / 4`.
98pub open spec fn signed_den_num(num: int, den: int) -> int {
99    if den < 0 {
100        -num
101    } else {
102        num
103    }
104}
105
106// ---------------------------------------------------------------------------
107// Constructors
108// ---------------------------------------------------------------------------
109
110impl Rat {
111    /// `0`.
112    ///
113    /// ```
114    /// use the_q::Rat;
115    ///
116    /// let z = Rat::zero();
117    /// assert_eq!(z.numerator(), 0);
118    /// assert_eq!(z.denominator(), 1);
119    /// assert!(z.is_zero());
120    /// ```
121    pub fn zero() -> (r: Rat)
122        ensures
123            r.wf(),
124            r == Rat::from_raw_spec(0, 1),
125            r.n() == 0,
126            r.d() == 1,
127    {
128        proof {
129            crate::round::lemma_gcd_one();
130            lemma_max_mag_pow2();
131        }
132        Rat::from_raw_parts(0, 1)
133    }
134
135    /// `1`.
136    ///
137    /// ```
138    /// use the_q::Rat;
139    ///
140    /// let o = Rat::one();
141    /// assert_eq!(o.numerator(), 1);
142    /// assert_eq!(o.denominator(), 1);
143    /// assert!(o.is_one());
144    /// ```
145    pub fn one() -> (r: Rat)
146        ensures
147            r.wf(),
148            r == Rat::from_raw_spec(1, 1),
149            r.n() == 1,
150            r.d() == 1,
151    {
152        proof {
153            crate::round::lemma_gcd_one();
154            lemma_max_mag_pow2();
155        }
156        Rat::from_raw_parts(1, 1)
157    }
158
159    /// `-1`.
160    ///
161    /// ```
162    /// use the_q::Rat;
163    ///
164    /// let n = Rat::neg_one();
165    /// assert_eq!(n.numerator(), -1);
166    /// assert_eq!(n.denominator(), 1);
167    /// assert_eq!(n, Rat::one().neg());
168    /// ```
169    pub fn neg_one() -> (r: Rat)
170        ensures
171            r.wf(),
172            r.n() == -1,
173            r.d() == 1,
174    {
175        Rat::one().neg()
176    }
177
178    /// The integer `i` as a rational.
179    ///
180    /// `None` when `|i| > MAX_MAG` — in particular for `i64::MIN`, whose
181    /// absolute value is not an `i64` at all.
182    ///
183    /// ```
184    /// use the_q::{Rat, MAX_MAG};
185    ///
186    /// let three = Rat::from_int(3).unwrap();
187    /// assert_eq!(three.numerator(), 3);
188    /// assert_eq!(three.denominator(), 1);
189    ///
190    /// assert_eq!(Rat::from_int(MAX_MAG + 1), None);
191    /// assert_eq!(Rat::from_int(i64::MIN), None);
192    /// ```
193    pub fn from_int(i: i64) -> (r: Option<Rat>)
194        ensures
195            r.is_some() ==> {
196                &&& r.unwrap().wf()
197                &&& r.unwrap().n() == i as int
198                &&& r.unwrap().d() == 1
199            },
200            r.is_none() ==> abs_int(i as int) > crate::model::max_mag(),
201    {
202        if i > MAX_MAG || i < -MAX_MAG {
203            None
204        } else {
205            proof {
206                crate::round::lemma_gcd_one();
207                lemma_max_mag_pow2();
208            }
209            Some(Rat::from_raw_parts(i, 1))
210        }
211    }
212
213    /// The exact rational `num / den`, canonicalised. `None` when `den == 0` or
214    /// the reduced form exceeds the budget (`Rat::new(i64::MAX, 1)`);
215    /// [`Rat::new_rounded`] rounds instead.
216    ///
217    /// ```
218    /// use the_q::Rat;
219    ///
220    /// let half = Rat::new(2, 4).unwrap();
221    /// assert_eq!(half.numerator(), 1);
222    /// assert_eq!(half.denominator(), 2);
223    ///
224    /// assert_eq!(Rat::new(1, 0), None);
225    /// assert_eq!(Rat::new(i64::MAX, 1), None);
226    /// ```
227    pub fn new(num: i64, den: i64) -> (r: Option<Rat>)
228        ensures
229            den == 0 ==> r.is_none(),
230            r.is_some() ==> {
231                &&& r.unwrap().wf()
232                &&& q_is(r.unwrap(), num as int, den as int)
233            },
234            // Completeness. Without this clause an implementation that returns
235            // `None` for each nonzero denominator satisfies the contract.
236            // `q_is` states the answer when an answer exists, and no other
237            // clause states that an answer exists.
238            //
239            // The condition uses the unreduced pair. The tight form, "`None`
240            // exactly when the reduced form does not fit", needs the caller to
241            // compute the gcd. A caller can check both components against the
242            // budget at the call site, and reduction only makes them smaller.
243            (den != 0 && abs_int(num as int) <= max_mag() && abs_int(den as int) <= max_mag())
244                ==> r.is_some(),
245    {
246        if den == 0 {
247            return None;
248        }
249        let mut n: i128 = num as i128;
250        let mut d: i128 = den as i128;
251        let neg: bool = d < 0;
252        if neg {
253            n = 0 - n;
254            d = 0 - d;
255        }
256        let g: i128 = gcd_abs_i128(n, d);
257        let rn: i128 = n / g;
258        let rd: i128 = d / g;
259        let arn: i128 = if rn < 0 {
260            0 - rn
261        } else {
262            rn
263        };
264        if arn <= MAX_MAG as i128 && rd <= MAX_MAG as i128 {
265            proof {
266                lemma_max_mag_pow2();
267                crate::round::lemma_reduce_exact(n as int, d as int);
268                crate::gcd::lemma_gcd_reduce_coprime(abs_int(n as int) as nat, d as nat);
269                crate::round::lemma_reduce_abs(n as int, d as int);
270                // I1's zero clause: `gcd(0, d) == d`, so a zero numerator
271                // reduces the denominator all the way to one.
272                if num == 0 {
273                    crate::gcd::lemma_gcd_zero(d as nat);
274                    vstd::arithmetic::div_mod::lemma_fundamental_div_mod_converse(
275                        d as int,
276                        d as int,
277                        1,
278                        0,
279                    );
280                }
281                // n == rn·g and d == rd·g, and (n, d) is (num, den) up to a
282                // shared sign flip, so rn/rd is num/den as a value either way.
283                lemma_new_value(
284                    num as int,
285                    den as int,
286                    n as int,
287                    d as int,
288                    rn as int,
289                    rd as int,
290                    g as int,
291                    neg,
292                );
293                // I1's zero clause: `rn == 0` forces `rd == 1`, via `lemma_gcd_zero`.
294                if rn == 0 {
295                    assert(n == 0) by (nonlinear_arith)
296                        requires
297                            n as int == (rn as int) * (g as int),
298                            rn == 0,
299                    ;
300                    assert(num == 0);
301                    assert(rd == 1);
302                }
303            }
304            Some(Rat::from_raw_parts(rn as i64, rd as i64))
305        } else {
306            proof {
307                // Completeness, discharged contrapositively on this path only.
308                // `g == gcd_int(n, d)`, thus `rn` and `rd` are `red_num` and
309                // `red_den`, and reduction never enlarges either component.
310                // This branch thus means that the pair of the caller is outside
311                // the budget.
312                //
313                // These two facts also prove the clause above the `if`, but
314                // that position destabilises the `rn == 0 ==> rd == 1` step in
315                // the other branch. The `saturation` module header describes
316                // that effect.
317                lemma_max_mag_pow2();
318                crate::round::lemma_reduce_shrinks(n as int, d as int);
319            }
320            None
321        }
322    }
323
324    /// `num / den`, rounded to the budget if it does not fit; `None` iff
325    /// `den == 0`. The sign is normalised onto the numerator before rounding.
326    ///
327    /// ```
328    /// use the_q::{Dir, Rat};
329    ///
330    /// assert_eq!(Rat::new_rounded(6, 4, Dir::Nearest), Rat::new(3, 2));
331    /// // Sign normalisation: a negative denominator folds onto the numerator.
332    /// assert_eq!(Rat::new_rounded(-3, -4, Dir::Down), Rat::new(3, 4));
333    /// assert_eq!(Rat::new_rounded(1, 0, Dir::Nearest), None);
334    /// ```
335    pub fn new_rounded(num: i64, den: i64, dir: Dir) -> (r: Option<Rat>)
336        ensures
337            r.is_none() <==> den == 0,
338            r.is_some() ==> r.unwrap().wf(),
339            // The value pin. This clause fixes the result completely and does
340            // not only describe properties of it. The R2 and R3 clauses below
341            // follow from this clause. They are stated so that a caller does
342            // not derive them.
343            r.is_some() ==> r.unwrap() == round_frac(
344                signed_den_num(num as int, den as int),
345                abs_int(den as int),
346                dir,
347            ),
348            // R2 and R3, both guarded on `!saturated`. That guard is the scope
349            // of the rounding contract of the crate. `new_rounded` accepts each
350            // `i64` pair, and `(i64::MAX, 1)` is above the magnitude ceiling,
351            // thus an unguarded R3 is false here. `from_decimal` discharges the
352            // guard for its callers, because its inputs cannot saturate.
353            (r.is_some() && !saturated(signed_den_num(num as int, den as int), abs_int(
354                den as int,
355            ))) ==> {
356                // R2: each directed mode lands on its own side of the exact
357                // value. R3: the per-operation error bound of the crate,
358                // against the exact input rational.
359                &&& dir == Dir::Down ==> q_le_frac(
360                    r.unwrap(),
361                    signed_den_num(num as int, den as int),
362                    abs_int(den as int),
363                )
364                &&& dir == Dir::Up ==> q_ge_frac(
365                    r.unwrap(),
366                    signed_den_num(num as int, den as int),
367                    abs_int(den as int),
368                )
369                &&& within_error_bound(
370                    r.unwrap(),
371                    signed_den_num(num as int, den as int),
372                    abs_int(den as int),
373                )
374            },
375    {
376        if den == 0 {
377            return None;
378        }
379        let mut n: i128 = num as i128;
380        let mut d: i128 = den as i128;
381        if d < 0 {
382            n = 0 - n;
383            d = 0 - d;
384        }
385        proof {
386            lemma_pow2_124();
387            lemma_pow2_126();
388            if !saturated(n as int, d as int) {
389                crate::round::lemma_r2_r3_directed(n as int, d as int, dir);
390            }
391        }
392        Some(round_frac_exec(n, d, dir))
393    }
394
395    /// The exact decimal `mantissa · 10^-dec_places`; `(85, 2)` is `0.85`. `None`
396    /// when `dec_places > 18` or `|mantissa| > MAX_MAG`.
397    ///
398    /// ```
399    /// use the_q::{Rat, MAX_DEC_PLACES};
400    ///
401    /// assert_eq!(Rat::from_decimal(85, 2), Rat::new(17, 20));
402    /// assert_eq!(Rat::from_decimal(1, MAX_DEC_PLACES + 1), None);
403    /// ```
404    pub fn from_decimal(mantissa: i64, dec_places: u8) -> (r: Option<Rat>)
405        ensures
406            r.is_some() ==> r.unwrap().wf(),
407            dec_places > MAX_DEC_PLACES ==> r.is_none(),
408            // The value of the result, and not only its well-formedness. The
409            // README calls this constructor the primary ingestion path, and the
410            // doc comment states that the conversion is exact. This clause puts
411            // both claims in the verified contract and pins the value of
412            // `from_decimal(85, 2)`.
413            r.is_some() ==> q_is(r.unwrap(), mantissa as int, pow10(dec_places as nat)),
414            // The existence condition. A caller can check both guards, and
415            // together they are the failure set. Inside them `Rat::new` cannot
416            // fail, by its completeness clause.
417            r.is_some() <==> (dec_places <= MAX_DEC_PLACES && abs_int(mantissa as int)
418                <= max_mag()),
419    {
420        if dec_places > MAX_DEC_PLACES {
421            return None;
422        }
423        if mantissa > MAX_MAG || mantissa < -MAX_MAG {
424            return None;
425        }
426        let scale: i64 = pow10_i64(dec_places);
427        proof {
428            // `scale` is in `[1, 10^18]` and `max_mag()` is `2^62 - 1`, about
429            // `4.6 · 10^18`, so the scale factor is always inside the budget
430            // and `Rat::new`'s completeness precondition is met.
431            lemma_max_mag_pow2();
432        }
433        Rat::new(mantissa, scale)
434    }
435}
436
437/// `10^n` for `n <= 18`, as a literal table (a loop needs an invariant and an
438/// overflow lemma for the same result).
439///
440/// Public only because Verus's visibility rules require it; not part of the
441/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
442///
443/// ```
444/// assert_eq!(the_q::q::pow10_i64(0), 1);
445/// assert_eq!(the_q::q::pow10_i64(2), 100);
446/// assert_eq!(the_q::q::pow10_i64(18), 1_000_000_000_000_000_000);
447/// ```
448pub fn pow10_i64(n: u8) -> (r: i64)
449    requires
450        n <= MAX_DEC_PLACES,
451    ensures
452        1 <= r <= 1000000000000000000,
453        // The table is `10^n`, and this clause states that property. Without
454        // it `from_decimal` can describe its result only as well-formed. The
455        // proof is nineteen unfoldings of a linear recursion, which is cheap.
456        // The literal pins of `pow2` need lemmas, because nonlinear goals
457        // consume them. These pins appear in linear goals only.
458        r == pow10(n as nat),
459{
460    reveal_with_fuel(pow10, 20);
461    match n {
462        0 => 1,
463        1 => 10,
464        2 => 100,
465        3 => 1000,
466        4 => 10000,
467        5 => 100000,
468        6 => 1000000,
469        7 => 10000000,
470        8 => 100000000,
471        9 => 1000000000,
472        10 => 10000000000,
473        11 => 100000000000,
474        12 => 1000000000000,
475        13 => 10000000000000,
476        14 => 100000000000000,
477        15 => 1000000000000000,
478        16 => 10000000000000000,
479        17 => 100000000000000000,
480        _ => 1000000000000000000,
481    }
482}
483
484// ---------------------------------------------------------------------------
485// Arithmetic
486// ---------------------------------------------------------------------------
487
488impl Rat {
489    /// `a + b`, rounded in direction `dir`.
490    ///
491    /// ```
492    /// use the_q::{Dir, Rat};
493    ///
494    /// let a = Rat::new(1, 3).unwrap();
495    /// let b = Rat::new(1, 6).unwrap();
496    /// assert_eq!(Rat::add_dir(a, b, Dir::Down), Rat::new(1, 2).unwrap());
497    ///
498    /// // `Down` and `Up` bracket the exact value; they agree on the exact path.
499    /// let a = Rat::new(1, 3_037_000_493).unwrap();
500    /// let b = Rat::new(1, 3_037_000_499).unwrap();
501    /// let lo = Rat::add_dir(a, b, Dir::Down);
502    /// let hi = Rat::add_dir(a, b, Dir::Up);
503    /// assert!(Rat::lt(lo, hi));
504    /// ```
505    pub fn add_dir(a: Rat, b: Rat, dir: Dir) -> (r: Rat)
506        requires
507            a.wf(),
508            b.wf(),
509        ensures
510            r.wf(),
511            r == round_frac(add_n(a, b), prod_d(a, b), dir),
512            exact_path(add_n(a, b), prod_d(a, b)) ==> q_is(r, add_n(a, b), prod_d(a, b)),
513            !saturated(add_n(a, b), prod_d(a, b)) ==> within_error_bound(
514                r,
515                add_n(a, b),
516                prod_d(a, b),
517            ),
518    {
519        proof {
520            lemma_op_widths(a, b);
521        }
522        let n: i128 = (a.numerator() as i128) * (b.denominator() as i128)
523            + (b.numerator() as i128) * (a.denominator() as i128);
524        let d: i128 = (a.denominator() as i128) * (b.denominator() as i128);
525        let r = round_frac_exec(n, d, dir);
526        proof {
527            if exact_path(add_n(a, b), prod_d(a, b)) {
528                crate::round::lemma_r1_identity(add_n(a, b), prod_d(a, b), dir);
529            }
530            if !saturated(add_n(a, b), prod_d(a, b)) {
531                crate::round::lemma_r3_error(add_n(a, b), prod_d(a, b), dir);
532            }
533        }
534        r
535    }
536
537    /// `a - b`, rounded in direction `dir`.
538    ///
539    /// ```
540    /// use the_q::{Dir, Rat};
541    ///
542    /// let a = Rat::new(3, 4).unwrap();
543    /// let b = Rat::new(1, 4).unwrap();
544    /// assert_eq!(Rat::sub_dir(a, b, Dir::Up), Rat::new(1, 2).unwrap());
545    ///
546    /// // `Down` and `Up` bracket the exact value.
547    /// let lo = Rat::sub_dir(a, b, Dir::Down);
548    /// let hi = Rat::sub_dir(a, b, Dir::Up);
549    /// assert!(Rat::le(lo, hi));
550    /// ```
551    pub fn sub_dir(a: Rat, b: Rat, dir: Dir) -> (r: Rat)
552        requires
553            a.wf(),
554            b.wf(),
555        ensures
556            r.wf(),
557            r == round_frac(sub_n(a, b), prod_d(a, b), dir),
558            exact_path(sub_n(a, b), prod_d(a, b)) ==> q_is(r, sub_n(a, b), prod_d(a, b)),
559            !saturated(sub_n(a, b), prod_d(a, b)) ==> within_error_bound(
560                r,
561                sub_n(a, b),
562                prod_d(a, b),
563            ),
564    {
565        proof {
566            lemma_op_widths(a, b);
567        }
568        let n: i128 = (a.numerator() as i128) * (b.denominator() as i128)
569            - (b.numerator() as i128) * (a.denominator() as i128);
570        let d: i128 = (a.denominator() as i128) * (b.denominator() as i128);
571        let r = round_frac_exec(n, d, dir);
572        proof {
573            if exact_path(sub_n(a, b), prod_d(a, b)) {
574                crate::round::lemma_r1_identity(sub_n(a, b), prod_d(a, b), dir);
575            }
576            if !saturated(sub_n(a, b), prod_d(a, b)) {
577                crate::round::lemma_r3_error(sub_n(a, b), prod_d(a, b), dir);
578            }
579        }
580        r
581    }
582
583    /// `a * b`, rounded in direction `dir`.
584    ///
585    /// ```
586    /// use the_q::{Dir, Rat};
587    ///
588    /// let a = Rat::new(2, 3).unwrap();
589    /// let b = Rat::new(3, 4).unwrap();
590    /// assert_eq!(Rat::mul_dir(a, b, Dir::Down), Rat::new(1, 2).unwrap());
591    ///
592    /// // `Down` and `Up` bracket the exact value.
593    /// let lo = Rat::mul_dir(a, b, Dir::Down);
594    /// let hi = Rat::mul_dir(a, b, Dir::Up);
595    /// assert!(Rat::le(lo, hi));
596    /// ```
597    pub fn mul_dir(a: Rat, b: Rat, dir: Dir) -> (r: Rat)
598        requires
599            a.wf(),
600            b.wf(),
601        ensures
602            r.wf(),
603            r == round_frac(mul_n(a, b), prod_d(a, b), dir),
604            exact_path(mul_n(a, b), prod_d(a, b)) ==> q_is(r, mul_n(a, b), prod_d(a, b)),
605            !saturated(mul_n(a, b), prod_d(a, b)) ==> within_error_bound(
606                r,
607                mul_n(a, b),
608                prod_d(a, b),
609            ),
610    {
611        proof {
612            lemma_op_widths(a, b);
613        }
614        let n: i128 = (a.numerator() as i128) * (b.numerator() as i128);
615        let d: i128 = (a.denominator() as i128) * (b.denominator() as i128);
616        let r = round_frac_exec(n, d, dir);
617        proof {
618            if exact_path(mul_n(a, b), prod_d(a, b)) {
619                crate::round::lemma_r1_identity(mul_n(a, b), prod_d(a, b), dir);
620            }
621            if !saturated(mul_n(a, b), prod_d(a, b)) {
622                crate::round::lemma_r3_error(mul_n(a, b), prod_d(a, b), dir);
623            }
624        }
625        r
626    }
627
628    /// `a / b` in direction `dir`. `b != 0` is a precondition; an unverified caller
629    /// with a zero divisor panics here. Use [`Rat::checked_div`] or [`crate::Q::div`].
630    ///
631    /// ```
632    /// use the_q::{Dir, Rat};
633    ///
634    /// let a = Rat::new(1, 2).unwrap();
635    /// let b = Rat::new(1, 3).unwrap();
636    /// assert_eq!(Rat::div_dir(a, b, Dir::Down), Rat::new(3, 2).unwrap());
637    /// ```
638    ///
639    /// ```should_panic
640    /// use the_q::{Dir, Rat};
641    ///
642    /// let a = Rat::new(1, 2).unwrap();
643    /// let _ = Rat::div_dir(a, Rat::zero(), Dir::Down);
644    /// ```
645    pub fn div_dir(a: Rat, b: Rat, dir: Dir) -> (r: Rat)
646        requires
647            a.wf(),
648            b.wf(),
649            b.n() != 0,
650        ensures
651            r.wf(),
652            r == round_frac(div_n(a, b), div_d(a, b), dir),
653            exact_path(div_n(a, b), div_d(a, b)) ==> q_is(r, div_n(a, b), div_d(a, b)),
654            !saturated(div_n(a, b), div_d(a, b)) ==> within_error_bound(
655                r,
656                div_n(a, b),
657                div_d(a, b),
658            ),
659    {
660        // The runtime half of the precondition. Verified code proves this and
661        // pays nothing. Unverified code that passes a zero divisor panics here
662        // instead of reaching the division inside `round_frac_exec`.
663        require_condition(
664            b.numerator() != 0,
665            "the-q: Rat::div by zero. Rat::div requires a nonzero divisor. Use \
666             Rat::checked_div, which returns None, or Q::div, which returns an \
667             infinity or Nan.",
668        );
669        proof {
670            lemma_op_widths(a, b);
671            crate::model::lemma_pow2_124();
672            crate::model::lemma_pow2_126();
673            // The sign of the divisor is the sign of its numerator, because
674            // `a.d() > 0`. The flip below thus gives `div_d(a, b) > 0`.
675            assert(b.n() > 0 ==> a.d() * b.n() > 0) by (nonlinear_arith)
676                requires
677                    a.d() > 0,
678            ;
679            assert(b.n() < 0 ==> a.d() * b.n() < 0) by (nonlinear_arith)
680                requires
681                    a.d() > 0,
682            ;
683        }
684        let mut n: i128 = (a.numerator() as i128) * (b.denominator() as i128);
685        let mut d: i128 = (a.denominator() as i128) * (b.numerator() as i128);
686        if d < 0 {
687            n = 0 - n;
688            d = 0 - d;
689        }
690        let r = round_frac_exec(n, d, dir);
691        proof {
692            if exact_path(div_n(a, b), div_d(a, b)) {
693                crate::round::lemma_r1_identity(div_n(a, b), div_d(a, b), dir);
694            }
695            if !saturated(div_n(a, b), div_d(a, b)) {
696                crate::round::lemma_r3_error(div_n(a, b), div_d(a, b), dir);
697            }
698        }
699        r
700    }
701
702    /// `a + b`, round to nearest (ties to even).
703    ///
704    /// The error is a half grid step and not a whole one, thus this operation
705    /// achieves `B = 62`. That is one bit better than the `B = 61` of the
706    /// directed modes. See `round::lemma_r3_error_nearest`.
707    ///
708    /// ```
709    /// use the_q::Rat;
710    ///
711    /// let a = Rat::new(1, 3).unwrap();
712    /// let b = Rat::new(1, 6).unwrap();
713    /// assert_eq!(Rat::add(a, b), Rat::new(1, 2).unwrap());
714    /// assert_eq!(Rat::add(a, Rat::zero()), a);
715    /// ```
716    pub fn add(a: Rat, b: Rat) -> (r: Rat)
717        requires
718            a.wf(),
719            b.wf(),
720        ensures
721            r.wf(),
722            r == round_frac(add_n(a, b), prod_d(a, b), Dir::Nearest),
723            exact_path(add_n(a, b), prod_d(a, b)) ==> q_is(r, add_n(a, b), prod_d(a, b)),
724            !saturated(add_n(a, b), prod_d(a, b)) ==> crate::model::within_error_bound_nearest(
725                r,
726                add_n(a, b),
727                prod_d(a, b),
728            ),
729    {
730        let r = Rat::add_dir(a, b, Dir::Nearest);
731        proof {
732            lemma_op_widths(a, b);
733            if !saturated(add_n(a, b), prod_d(a, b)) {
734                crate::round::lemma_r3_error_nearest(add_n(a, b), prod_d(a, b));
735            }
736        }
737        r
738    }
739
740    /// `a - b`, round to nearest (ties to even). Achieves `B = 62`, as `add`
741    /// does.
742    ///
743    /// ```
744    /// use the_q::Rat;
745    ///
746    /// let a = Rat::new(3, 4).unwrap();
747    /// let b = Rat::new(1, 4).unwrap();
748    /// assert_eq!(Rat::sub(a, b), Rat::new(1, 2).unwrap());
749    /// assert_eq!(Rat::sub(a, a), Rat::zero());
750    /// ```
751    pub fn sub(a: Rat, b: Rat) -> (r: Rat)
752        requires
753            a.wf(),
754            b.wf(),
755        ensures
756            r.wf(),
757            r == round_frac(sub_n(a, b), prod_d(a, b), Dir::Nearest),
758            exact_path(sub_n(a, b), prod_d(a, b)) ==> q_is(r, sub_n(a, b), prod_d(a, b)),
759            !saturated(sub_n(a, b), prod_d(a, b)) ==> crate::model::within_error_bound_nearest(
760                r,
761                sub_n(a, b),
762                prod_d(a, b),
763            ),
764    {
765        let r = Rat::sub_dir(a, b, Dir::Nearest);
766        proof {
767            lemma_op_widths(a, b);
768            if !saturated(sub_n(a, b), prod_d(a, b)) {
769                crate::round::lemma_r3_error_nearest(sub_n(a, b), prod_d(a, b));
770            }
771        }
772        r
773    }
774
775    /// `a * b`, round to nearest (ties to even). Achieves `B = 62`, as `add`
776    /// does.
777    ///
778    /// ```
779    /// use the_q::Rat;
780    ///
781    /// let a = Rat::new(2, 3).unwrap();
782    /// let b = Rat::new(3, 4).unwrap();
783    /// assert_eq!(Rat::mul(a, b), Rat::new(1, 2).unwrap());
784    /// assert_eq!(Rat::mul(a, Rat::one()), a);
785    /// ```
786    pub fn mul(a: Rat, b: Rat) -> (r: Rat)
787        requires
788            a.wf(),
789            b.wf(),
790        ensures
791            r.wf(),
792            r == round_frac(mul_n(a, b), prod_d(a, b), Dir::Nearest),
793            exact_path(mul_n(a, b), prod_d(a, b)) ==> q_is(r, mul_n(a, b), prod_d(a, b)),
794            !saturated(mul_n(a, b), prod_d(a, b)) ==> crate::model::within_error_bound_nearest(
795                r,
796                mul_n(a, b),
797                prod_d(a, b),
798            ),
799    {
800        let r = Rat::mul_dir(a, b, Dir::Nearest);
801        proof {
802            lemma_op_widths(a, b);
803            if !saturated(mul_n(a, b), prod_d(a, b)) {
804                crate::round::lemma_r3_error_nearest(mul_n(a, b), prod_d(a, b));
805            }
806        }
807        r
808    }
809
810    /// `a / b`, round to nearest (ties to even). Requires `!b.is_zero()`.
811    /// Achieves `B = 62`, as `add` does.
812    ///
813    /// ```
814    /// use the_q::Rat;
815    ///
816    /// let a = Rat::new(1, 2).unwrap();
817    /// let b = Rat::new(1, 3).unwrap();
818    /// assert_eq!(Rat::div(a, b), Rat::new(3, 2).unwrap());
819    /// ```
820    ///
821    /// ```should_panic
822    /// use the_q::Rat;
823    ///
824    /// let a = Rat::new(1, 2).unwrap();
825    /// let _ = Rat::div(a, Rat::zero());
826    /// ```
827    pub fn div(a: Rat, b: Rat) -> (r: Rat)
828        requires
829            a.wf(),
830            b.wf(),
831            b.n() != 0,
832        ensures
833            r.wf(),
834            r == round_frac(div_n(a, b), div_d(a, b), Dir::Nearest),
835            exact_path(div_n(a, b), div_d(a, b)) ==> q_is(r, div_n(a, b), div_d(a, b)),
836            !saturated(div_n(a, b), div_d(a, b)) ==> crate::model::within_error_bound_nearest(
837                r,
838                div_n(a, b),
839                div_d(a, b),
840            ),
841    {
842        let r = Rat::div_dir(a, b, Dir::Nearest);
843        proof {
844            lemma_op_widths(a, b);
845            assert(b.n() > 0 ==> a.d() * b.n() > 0) by (nonlinear_arith)
846                requires
847                    a.d() > 0,
848            ;
849            assert(b.n() < 0 ==> a.d() * b.n() < 0) by (nonlinear_arith)
850                requires
851                    a.d() > 0,
852            ;
853            if !saturated(div_n(a, b), div_d(a, b)) {
854                crate::round::lemma_r3_error_nearest(div_n(a, b), div_d(a, b));
855            }
856        }
857        r
858    }
859
860    /// `a + b`, or `None` if the exact sum is too large in magnitude to be
861    /// represented at all (`|a + b| > MAX_MAG`).
862    ///
863    /// ```
864    /// use the_q::{Rat, MAX_MAG};
865    ///
866    /// let a = Rat::new(1, 3).unwrap();
867    /// let b = Rat::new(1, 6).unwrap();
868    /// assert_eq!(Rat::checked_add(a, b), Some(Rat::new(1, 2).unwrap()));
869    ///
870    /// // `MAX_MAG + 1` is past the budget, not merely rounded.
871    /// let max = Rat::from_int(MAX_MAG).unwrap();
872    /// assert_eq!(Rat::checked_add(max, Rat::one()), None);
873    /// ```
874    pub fn checked_add(a: Rat, b: Rat) -> (r: Option<Rat>)
875        requires
876            a.wf(),
877            b.wf(),
878        ensures
879            r.is_none() <==> saturated(add_n(a, b), prod_d(a, b)),
880            r.is_some() ==> r.unwrap() == round_frac(add_n(a, b), prod_d(a, b), Dir::Nearest),
881            r.is_some() ==> r.unwrap().wf(),
882    {
883        proof {
884            lemma_op_widths(a, b);
885            crate::model::lemma_pow2_126();
886        }
887        if magnitude_fits_exec(add_n_exec(a, b), prod_d_exec(a, b)) {
888            Some(Rat::add(a, b))
889        } else {
890            None
891        }
892    }
893
894    /// `a * b`, or `None` if the exact product is too large in magnitude.
895    ///
896    /// ```
897    /// use the_q::{Rat, MAX_MAG};
898    ///
899    /// let a = Rat::new(2, 3).unwrap();
900    /// let b = Rat::new(3, 4).unwrap();
901    /// assert_eq!(Rat::checked_mul(a, b), Some(Rat::new(1, 2).unwrap()));
902    ///
903    /// let max = Rat::from_int(MAX_MAG).unwrap();
904    /// let two = Rat::from_int(2).unwrap();
905    /// assert_eq!(Rat::checked_mul(max, two), None);
906    /// ```
907    pub fn checked_mul(a: Rat, b: Rat) -> (r: Option<Rat>)
908        requires
909            a.wf(),
910            b.wf(),
911        ensures
912            r.is_none() <==> saturated(mul_n(a, b), prod_d(a, b)),
913            r.is_some() ==> r.unwrap() == round_frac(mul_n(a, b), prod_d(a, b), Dir::Nearest),
914            r.is_some() ==> r.unwrap().wf(),
915    {
916        proof {
917            lemma_op_widths(a, b);
918            crate::model::lemma_pow2_126();
919        }
920        if magnitude_fits_exec(mul_n_exec(a, b), prod_d_exec(a, b)) {
921            Some(Rat::mul(a, b))
922        } else {
923            None
924        }
925    }
926
927    /// `a - b`, or `None` if the exact difference is too large in magnitude.
928    ///
929    /// ```
930    /// use the_q::{Rat, MAX_MAG};
931    ///
932    /// let a = Rat::new(3, 4).unwrap();
933    /// let b = Rat::new(1, 4).unwrap();
934    /// assert_eq!(Rat::checked_sub(a, b), Some(Rat::new(1, 2).unwrap()));
935    ///
936    /// let neg_max = Rat::from_int(-MAX_MAG).unwrap();
937    /// assert_eq!(Rat::checked_sub(neg_max, Rat::one()), None);
938    /// ```
939    pub fn checked_sub(a: Rat, b: Rat) -> (r: Option<Rat>)
940        requires
941            a.wf(),
942            b.wf(),
943        ensures
944            r.is_none() <==> saturated(sub_n(a, b), prod_d(a, b)),
945            r.is_some() ==> r.unwrap() == round_frac(sub_n(a, b), prod_d(a, b), Dir::Nearest),
946            r.is_some() ==> r.unwrap().wf(),
947    {
948        proof {
949            lemma_op_widths(a, b);
950            crate::model::lemma_pow2_126();
951        }
952        if magnitude_fits_exec(sub_n_exec(a, b), prod_d_exec(a, b)) {
953            Some(Rat::sub(a, b))
954        } else {
955            None
956        }
957    }
958
959    /// `a - b`, rounded in `dir`, or `None` if the exact difference is too
960    /// large in magnitude. Unlike [`Rat::checked_sub`], this preserves the
961    /// requested rounding direction when the result is not exact.
962    ///
963    /// ```
964    /// use the_q::{Dir, Rat, MAX_MAG};
965    ///
966    /// let a = Rat::new(3, 4).unwrap();
967    /// let b = Rat::new(1, 4).unwrap();
968    /// assert_eq!(Rat::checked_sub_dir(a, b, Dir::Down), Some(Rat::new(1, 2).unwrap()));
969    ///
970    /// let neg_max = Rat::from_int(-MAX_MAG).unwrap();
971    /// assert_eq!(Rat::checked_sub_dir(neg_max, Rat::one(), Dir::Down), None);
972    /// ```
973    pub fn checked_sub_dir(a: Rat, b: Rat, dir: Dir) -> (r: Option<Rat>)
974        requires
975            a.wf(),
976            b.wf(),
977        ensures
978            r.is_none() <==> saturated(sub_n(a, b), prod_d(a, b)),
979            r.is_some() ==> r.unwrap() == round_frac(sub_n(a, b), prod_d(a, b), dir),
980            r.is_some() ==> r.unwrap().wf(),
981            r.is_some() && dir == Dir::Down ==> q_le_frac(
982                r.unwrap(), sub_n(a, b), prod_d(a, b),
983            ),
984            r.is_some() && dir == Dir::Up ==> q_ge_frac(
985                r.unwrap(), sub_n(a, b), prod_d(a, b),
986            ),
987            r.is_some() && exact_path(sub_n(a, b), prod_d(a, b)) ==> q_is(
988                r.unwrap(), sub_n(a, b), prod_d(a, b),
989            ),
990            r.is_some() ==> within_error_bound(
991                r.unwrap(), sub_n(a, b), prod_d(a, b),
992            ),
993    {
994        proof {
995            lemma_op_widths(a, b);
996            crate::model::lemma_pow2_126();
997        }
998        if magnitude_fits_exec(sub_n_exec(a, b), prod_d_exec(a, b)) {
999            let value = Rat::sub_dir(a, b, dir);
1000            proof {
1001                crate::round::lemma_r2_directed(sub_n(a, b), prod_d(a, b));
1002            }
1003            Some(value)
1004        } else {
1005            None
1006        }
1007    }
1008
1009    /// `a / b`, or `None` on a zero divisor or a quotient past the budget.
1010    ///
1011    /// ```
1012    /// use the_q::{Rat, MAX_MAG};
1013    ///
1014    /// let a = Rat::new(1, 2).unwrap();
1015    /// let b = Rat::new(1, 3).unwrap();
1016    /// assert_eq!(Rat::checked_div(a, b), Some(Rat::new(3, 2).unwrap()));
1017    ///
1018    /// assert_eq!(Rat::checked_div(a, Rat::zero()), None);
1019    ///
1020    /// let max = Rat::from_int(MAX_MAG).unwrap();
1021    /// let half = Rat::new(1, 2).unwrap();
1022    /// assert_eq!(Rat::checked_div(max, half), None);
1023    /// ```
1024    pub fn checked_div(a: Rat, b: Rat) -> (r: Option<Rat>)
1025        requires
1026            a.wf(),
1027            b.wf(),
1028        ensures
1029            b.n() == 0 ==> r.is_none(),
1030            b.n() != 0 ==> (r.is_none() <==> saturated(div_n(a, b), div_d(a, b))),
1031            r.is_some() ==> b.n() != 0,
1032            r.is_some() ==> r.unwrap() == round_frac(div_n(a, b), div_d(a, b), Dir::Nearest),
1033            r.is_some() ==> r.unwrap().wf(),
1034    {
1035        if b.numerator() == 0 {
1036            return None;
1037        }
1038        proof {
1039            lemma_op_widths(a, b);
1040            crate::model::lemma_pow2_126();
1041        }
1042        if magnitude_fits_exec(div_n_exec(a, b), div_d_exec(a, b)) {
1043            Some(Rat::div(a, b))
1044        } else {
1045            None
1046        }
1047    }
1048
1049    /// `-a`. Always exact: the budget is symmetric in sign.
1050    ///
1051    /// ```
1052    /// use the_q::Rat;
1053    ///
1054    /// let a = Rat::new(3, 4).unwrap();
1055    /// assert_eq!(a.neg(), Rat::new(-3, 4).unwrap());
1056    /// assert_eq!(a.neg().neg(), a);
1057    /// ```
1058    pub fn neg(self) -> (r: Rat)
1059        requires
1060            self.wf(),
1061        ensures
1062            r.wf(),
1063            r.n() == -self.n(),
1064            r.d() == self.d(),
1065    {
1066        proof {
1067            lemma_max_mag_pow2();
1068            assert(gcd_int(-self.n(), self.d()) == gcd_int(self.n(), self.d()));
1069        }
1070        Rat::from_raw_parts(0 - self.numerator(), self.denominator())
1071    }
1072
1073    /// `|a|`. Always exact.
1074    ///
1075    /// ```
1076    /// use the_q::Rat;
1077    ///
1078    /// let a = Rat::new(-3, 4).unwrap();
1079    /// assert_eq!(a.abs(), Rat::new(3, 4).unwrap());
1080    /// assert_eq!(a.abs().abs(), a.abs());
1081    /// ```
1082    pub fn abs(self) -> (r: Rat)
1083        requires
1084            self.wf(),
1085        ensures
1086            r.wf(),
1087            r.n() == abs_int(self.n()),
1088            r.d() == self.d(),
1089    {
1090        if self.numerator() < 0 {
1091            self.neg()
1092        } else {
1093            self
1094        }
1095    }
1096
1097    /// `1 / a`, exact (a swap of a canonical pair). Zero is a precondition and
1098    /// panics for an unverified caller; [`crate::Q::recip`] gives `PosInf`.
1099    ///
1100    /// ```
1101    /// use the_q::Rat;
1102    ///
1103    /// let a = Rat::new(-2, 3).unwrap();
1104    /// assert_eq!(a.recip(), Rat::new(-3, 2).unwrap());
1105    /// assert_eq!(Rat::mul(a, a.recip()), Rat::one());
1106    /// ```
1107    ///
1108    /// ```should_panic
1109    /// use the_q::Rat;
1110    ///
1111    /// let _ = Rat::zero().recip();
1112    /// ```
1113    pub fn recip(self) -> (r: Rat)
1114        requires
1115            self.wf(),
1116            self.n() != 0,
1117        ensures
1118            r.wf(),
1119            r.n() * self.n() > 0,
1120            q_is_recip(r, self),
1121    {
1122        // Without this check a zero receiver returns `Rat { num: -1, den: 0 }`,
1123        // which violates I1 and fails in a later operation, far from the cause.
1124        require_condition(
1125            self.numerator() != 0,
1126            "the-q: Rat::recip of zero. The reciprocal of zero is not a \
1127             rational and no Rat denotes it. Use Q::recip, which returns \
1128             PosInf.",
1129        );
1130        proof {
1131            lemma_max_mag_pow2();
1132        }
1133        if self.numerator() > 0 {
1134            proof {
1135                assert(gcd_int(self.d(), self.n()) == gcd_int(self.n(), self.d())) by {
1136                    lemma_gcd_sym(abs_int(self.n()) as nat, self.d() as nat);
1137                }
1138                assert(self.d() * self.n() > 0) by (nonlinear_arith)
1139                    requires
1140                        self.d() > 0,
1141                        self.n() > 0,
1142                ;
1143            }
1144            Rat::from_raw_parts(self.denominator(), self.numerator())
1145        } else {
1146            proof {
1147                assert(gcd_int(-self.d(), -self.n()) == gcd_int(self.n(), self.d())) by {
1148                    lemma_gcd_sym(abs_int(self.n()) as nat, self.d() as nat);
1149                }
1150                assert((-self.d()) * self.n() > 0) by (nonlinear_arith)
1151                    requires
1152                        self.d() > 0,
1153                        self.n() < 0,
1154                ;
1155                assert((-self.d()) * self.n() == self.d() * (-self.n())) by (nonlinear_arith);
1156            }
1157            Rat::from_raw_parts(0 - self.denominator(), 0 - self.numerator())
1158        }
1159    }
1160
1161    /// `a^e` by repeated multiplication, as a left fold. This module has no
1162    /// rational-exponent power.
1163    ///
1164    /// ```
1165    /// use the_q::Rat;
1166    ///
1167    /// let a = Rat::new(2, 3).unwrap();
1168    /// assert_eq!(a.pow_u32(3), Rat::new(8, 27).unwrap());
1169    /// assert_eq!(a.pow_u32(0), Rat::one());
1170    /// ```
1171    pub fn pow_u32(self, e: u32) -> (r: Rat)
1172        requires
1173            self.wf(),
1174        ensures
1175            r.wf(),
1176    {
1177        let mut acc = Rat::one();
1178        let mut i: u32 = 0;
1179        while i < e
1180            invariant
1181                acc.wf(),
1182                self.wf(),
1183                i <= e,
1184            decreases e - i,
1185        {
1186            acc = Rat::mul(acc, self);
1187            i = i + 1;
1188        }
1189        acc
1190    }
1191}
1192
1193/// `r == 1 / q`, division-free.
1194pub open spec fn q_is_recip(r: Rat, q: Rat) -> bool {
1195    r.n() * q.n() == q.d() * r.d()
1196}
1197
1198/// `gcd` is symmetric.
1199pub proof fn lemma_gcd_sym(a: nat, b: nat)
1200    ensures
1201        crate::model::gcd_nat(a, b) == crate::model::gcd_nat(b, a),
1202{
1203    crate::gcd::lemma_gcd_divides(a, b);
1204    crate::gcd::lemma_gcd_divides(b, a);
1205    crate::gcd::lemma_gcd_greatest(a, b, crate::model::gcd_nat(b, a) as int);
1206    crate::gcd::lemma_gcd_greatest(b, a, crate::model::gcd_nat(a, b) as int);
1207    if a > 0 || b > 0 {
1208        crate::gcd::lemma_gcd_pos(a, b);
1209        crate::gcd::lemma_gcd_pos(b, a);
1210        crate::model::lemma_divides_le(
1211            crate::model::gcd_nat(a, b) as int,
1212            crate::model::gcd_nat(b, a) as int,
1213        );
1214        crate::model::lemma_divides_le(
1215            crate::model::gcd_nat(b, a) as int,
1216            crate::model::gcd_nat(a, b) as int,
1217        );
1218    }
1219}
1220
1221/// All four operations' intermediates are inside the `i128` range, and inside
1222/// the input bounds `round_frac_exec` requires.
1223pub proof fn lemma_op_widths(a: Rat, b: Rat)
1224    requires
1225        a.wf(),
1226        b.wf(),
1227    ensures
1228        // Literal forms. These discharge the `i128` overflow checks at the
1229        // call sites. `pow2(124)` is an opaque recursive term for the solver,
1230        // thus a bound in that form proves nothing about an `i128`.
1231        abs_int(a.n() * b.d()) < 21267647932558653966460912964485513216,
1232        abs_int(b.n() * a.d()) < 21267647932558653966460912964485513216,
1233        // The two cross terms, in the order that `div_dir` uses. Both orders
1234        // are necessary. `abs_int` applies to the product, thus the solver
1235        // must otherwise commute the factors inside an opaque application.
1236        abs_int(a.d() * b.n()) < 21267647932558653966460912964485513216,
1237        abs_int(a.d() * b.n()) <= pow2(124),
1238        abs_int(a.n() * b.d()) <= pow2(124),
1239        abs_int(a.n() * b.n()) < 21267647932558653966460912964485513216,
1240        abs_int(a.d() * b.d()) < 21267647932558653966460912964485513216,
1241        abs_int(add_n(a, b)) < 42535295865117307932921825928971026432,
1242        abs_int(sub_n(a, b)) < 42535295865117307932921825928971026432,
1243        abs_int(mul_n(a, b)) < 42535295865117307932921825928971026432,
1244        a.d() * b.d() > 0,
1245        a.d() > 0,
1246        b.d() > 0,
1247        // `pow2` forms, for `round_frac_exec`'s preconditions.
1248        abs_int(a.d() * b.d()) <= pow2(124),
1249        abs_int(add_n(a, b)) < pow2(126),
1250        abs_int(sub_n(a, b)) < pow2(126),
1251        abs_int(mul_n(a, b)) < pow2(126),
1252{
1253    lemma_mul_in_i128(a.n(), b.d());
1254    lemma_mul_in_i128(b.n(), a.d());
1255    lemma_mul_in_i128(a.d(), b.n());
1256    lemma_mul_in_i128(a.n(), b.n());
1257    lemma_mul_in_i128(a.d(), b.d());
1258    lemma_pow2_124();
1259    lemma_pow2_126();
1260    assert(a.d() * b.d() > 0) by (nonlinear_arith)
1261        requires
1262            a.d() > 0,
1263            b.d() > 0,
1264    ;
1265}
1266
1267/// The reduced pair denotes the original fraction, whichever way the sign was
1268/// normalised.
1269pub proof fn lemma_new_value(
1270    num: int,
1271    den: int,
1272    n: int,
1273    d: int,
1274    rn: int,
1275    rd: int,
1276    g: int,
1277    neg: bool,
1278)
1279    requires
1280        den != 0,
1281        g > 0,
1282        n == rn * g,
1283        d == rd * g,
1284        neg ==> (n == -num && d == -den),
1285        !neg ==> (n == num && d == den),
1286    ensures
1287        rn * den == num * rd,
1288{
1289    if neg {
1290        assert(den == -(rd * g) && num == -(rn * g));
1291        assert(rn * den == -((rn * rd) * g)) by (nonlinear_arith)
1292            requires
1293                den == -(rd * g),
1294        ;
1295        assert(num * rd == -((rn * rd) * g)) by (nonlinear_arith)
1296            requires
1297                num == -(rn * g),
1298        ;
1299    } else {
1300        assert(den == rd * g && num == rn * g);
1301        assert(rn * den == (rn * rd) * g) by (nonlinear_arith)
1302            requires
1303                den == rd * g,
1304        ;
1305        assert(num * rd == (rn * rd) * g) by (nonlinear_arith)
1306            requires
1307                num == rn * g,
1308        ;
1309    }
1310}
1311
1312/// Exec mirrors of the ghost numerators, used by the `checked_*` variants.
1313///
1314/// Public only because Verus's visibility rules require it; not part of the
1315/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
1316///
1317/// ```
1318/// use the_q::Rat;
1319///
1320/// let a = Rat::new(1, 2).unwrap();
1321/// let b = Rat::new(1, 3).unwrap();
1322/// assert_eq!(the_q::q::add_n_exec(a, b), 5);
1323/// ```
1324pub fn add_n_exec(a: Rat, b: Rat) -> (r: i128)
1325    requires
1326        a.wf(),
1327        b.wf(),
1328    ensures
1329        r as int == add_n(a, b),
1330{
1331    proof {
1332        lemma_op_widths(a, b);
1333        lemma_pow2_126();
1334    }
1335    (a.numerator() as i128) * (b.denominator() as i128)
1336        + (b.numerator() as i128) * (a.denominator() as i128)
1337}
1338
1339/// Exec mirror of `sub_n`.
1340///
1341/// Public only because Verus's visibility rules require it; not part of the
1342/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
1343///
1344/// ```
1345/// use the_q::Rat;
1346///
1347/// let a = Rat::new(1, 2).unwrap();
1348/// let b = Rat::new(1, 3).unwrap();
1349/// assert_eq!(the_q::q::sub_n_exec(a, b), 1);
1350/// ```
1351pub fn sub_n_exec(a: Rat, b: Rat) -> (r: i128)
1352    requires
1353        a.wf(),
1354        b.wf(),
1355    ensures
1356        r as int == sub_n(a, b),
1357{
1358    proof {
1359        lemma_op_widths(a, b);
1360        lemma_pow2_126();
1361    }
1362    (a.numerator() as i128) * (b.denominator() as i128)
1363        - (b.numerator() as i128) * (a.denominator() as i128)
1364}
1365
1366/// Exec mirror of `mul_n`.
1367///
1368/// Public only because Verus's visibility rules require it; not part of the
1369/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
1370///
1371/// ```
1372/// use the_q::Rat;
1373///
1374/// let a = Rat::new(1, 2).unwrap();
1375/// let b = Rat::new(1, 3).unwrap();
1376/// assert_eq!(the_q::q::mul_n_exec(a, b), 1);
1377/// ```
1378pub fn mul_n_exec(a: Rat, b: Rat) -> (r: i128)
1379    requires
1380        a.wf(),
1381        b.wf(),
1382    ensures
1383        r as int == mul_n(a, b),
1384{
1385    proof {
1386        lemma_op_widths(a, b);
1387        lemma_pow2_126();
1388    }
1389    (a.numerator() as i128) * (b.numerator() as i128)
1390}
1391
1392/// Exec mirror of `prod_d`.
1393///
1394/// Public only because Verus's visibility rules require it; not part of the
1395/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
1396///
1397/// ```
1398/// use the_q::Rat;
1399///
1400/// let a = Rat::new(1, 2).unwrap();
1401/// let b = Rat::new(1, 3).unwrap();
1402/// assert_eq!(the_q::q::prod_d_exec(a, b), 6);
1403/// ```
1404pub fn prod_d_exec(a: Rat, b: Rat) -> (r: i128)
1405    requires
1406        a.wf(),
1407        b.wf(),
1408    ensures
1409        r as int == prod_d(a, b),
1410        r > 0,
1411{
1412    proof {
1413        lemma_op_widths(a, b);
1414        lemma_pow2_126();
1415    }
1416    (a.denominator() as i128) * (b.denominator() as i128)
1417}
1418
1419/// Exec mirror of `div_n`: sign-normalised the same way [`Rat::div_dir`]
1420/// computes it, so it pairs with [`div_d_exec`].
1421///
1422/// Public only because Verus's visibility rules require it; not part of the
1423/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
1424///
1425/// ```
1426/// use the_q::Rat;
1427///
1428/// let a = Rat::new(1, 2).unwrap();
1429/// let b = Rat::new(-1, 3).unwrap();
1430/// assert_eq!(the_q::q::div_n_exec(a, b), -3);
1431/// assert_eq!(the_q::q::div_d_exec(a, b), 2);
1432/// ```
1433pub fn div_n_exec(a: Rat, b: Rat) -> (r: i128)
1434    requires
1435        a.wf(),
1436        b.wf(),
1437        b.n() != 0,
1438    ensures
1439        r as int == div_n(a, b),
1440{
1441    proof {
1442        lemma_op_widths(a, b);
1443        lemma_pow2_126();
1444        assert(b.n() > 0 ==> a.d() * b.n() > 0) by (nonlinear_arith)
1445            requires
1446                a.d() > 0,
1447        ;
1448        assert(b.n() < 0 ==> a.d() * b.n() < 0) by (nonlinear_arith)
1449            requires
1450                a.d() > 0,
1451        ;
1452    }
1453    let mut n: i128 = (a.numerator() as i128) * (b.denominator() as i128);
1454    let d: i128 = (a.denominator() as i128) * (b.numerator() as i128);
1455    if d < 0 {
1456        n = 0 - n;
1457    }
1458    n
1459}
1460
1461/// Exec mirror of `div_d`: always positive, the denominator [`div_n_exec`]
1462/// pairs with.
1463///
1464/// Public only because Verus's visibility rules require it; not part of the
1465/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
1466///
1467/// ```
1468/// use the_q::Rat;
1469///
1470/// let a = Rat::new(1, 2).unwrap();
1471/// let b = Rat::new(-1, 3).unwrap();
1472/// assert_eq!(the_q::q::div_d_exec(a, b), 2);
1473/// ```
1474pub fn div_d_exec(a: Rat, b: Rat) -> (r: i128)
1475    requires
1476        a.wf(),
1477        b.wf(),
1478        b.n() != 0,
1479    ensures
1480        r as int == div_d(a, b),
1481        r > 0,
1482{
1483    proof {
1484        lemma_op_widths(a, b);
1485        lemma_pow2_126();
1486        assert(b.n() > 0 ==> a.d() * b.n() > 0) by (nonlinear_arith)
1487            requires
1488                a.d() > 0,
1489        ;
1490        assert(b.n() < 0 ==> a.d() * b.n() < 0) by (nonlinear_arith)
1491            requires
1492                a.d() > 0,
1493        ;
1494    }
1495    let mut d: i128 = (a.denominator() as i128) * (b.numerator() as i128);
1496    if d < 0 {
1497        d = 0 - d;
1498    }
1499    d
1500}
1501
1502/// The magnitude test, without ever forming `MAX_MAG · d`.
1503///
1504/// Public only because Verus's visibility rules require it; not part of the
1505/// crate's semver-stable API (see the "API stability" note in `CLAUDE.md`).
1506///
1507/// ```
1508/// use the_q::MAX_MAG;
1509///
1510/// assert!(the_q::q::magnitude_fits_exec(MAX_MAG as i128, 1));
1511/// assert!(!the_q::q::magnitude_fits_exec(MAX_MAG as i128 + 1, 1));
1512/// assert!(the_q::q::magnitude_fits_exec(1, 2));
1513/// ```
1514pub fn magnitude_fits_exec(n: i128, d: i128) -> (r: bool)
1515    requires
1516        d > 0,
1517        // Negating `n` below is only safe away from `i128::MIN`. Every caller
1518        // passes a numerator bounded by `lemma_op_widths`, well inside this.
1519        abs_int(n as int) < crate::round::num_input_bound(),
1520    ensures
1521        r <==> magnitude_fits(n as int, d as int),
1522{
1523    proof {
1524        crate::model::lemma_pow2_126();
1525    }
1526    let m: i128 = if n < 0 {
1527        0 - n
1528    } else {
1529        n
1530    };
1531    let ip: i128 = m / d;
1532    let fr: i128 = m % d;
1533    proof {
1534        crate::round::lemma_magnitude_test(m as int, d as int, ip as int, fr as int);
1535    }
1536    ip < MAX_MAG as i128 || (ip == MAX_MAG as i128 && fr == 0)
1537}
1538
1539// ---------------------------------------------------------------------------
1540// Comparison and predicates — all exact, no epsilon, total
1541// ---------------------------------------------------------------------------
1542
1543impl Rat {
1544    /// Three-way comparison, `-1`/`0`/`1`, exact by `i128` cross-multiplication.
1545    ///
1546    /// ```
1547    /// use the_q::Rat;
1548    ///
1549    /// let a = Rat::new(1, 3).unwrap();
1550    /// let b = Rat::new(1, 2).unwrap();
1551    /// assert_eq!(Rat::compare(a, b), -1);
1552    /// assert_eq!(Rat::compare(b, a), 1);
1553    /// assert_eq!(Rat::compare(a, a), 0);
1554    /// ```
1555    pub fn compare(a: Rat, b: Rat) -> (r: i32)
1556        requires
1557            a.wf(),
1558            b.wf(),
1559        ensures
1560            r == 0 <==> q_eq(a, b),
1561            r < 0 <==> q_lt(a, b),
1562            r > 0 <==> q_lt(b, a),
1563    {
1564        proof {
1565            lemma_op_widths(a, b);
1566        }
1567        let l: i128 = (a.numerator() as i128) * (b.denominator() as i128);
1568        let r: i128 = (b.numerator() as i128) * (a.denominator() as i128);
1569        if l < r {
1570            -1
1571        } else if l > r {
1572            1
1573        } else {
1574            0
1575        }
1576    }
1577
1578    /// `a == b`. Because `Rat` is canonical this is also structural equality.
1579    ///
1580    /// ```
1581    /// use the_q::Rat;
1582    ///
1583    /// let a = Rat::new(1, 2).unwrap();
1584    /// let b = Rat::new(2, 4).unwrap();
1585    /// assert!(Rat::eq_q(a, b));
1586    /// assert!(!Rat::eq_q(a, Rat::one()));
1587    /// ```
1588    pub fn eq_q(a: Rat, b: Rat) -> (r: bool)
1589        requires
1590            a.wf(),
1591            b.wf(),
1592        ensures
1593            r <==> q_eq(a, b),
1594    {
1595        Rat::compare(a, b) == 0
1596    }
1597
1598    /// `a < b`.
1599    ///
1600    /// ```
1601    /// use the_q::Rat;
1602    ///
1603    /// let a = Rat::new(1, 3).unwrap();
1604    /// let b = Rat::new(1, 2).unwrap();
1605    /// assert!(Rat::lt(a, b));
1606    /// assert!(!Rat::lt(a, a));
1607    /// ```
1608    pub fn lt(a: Rat, b: Rat) -> (r: bool)
1609        requires
1610            a.wf(),
1611            b.wf(),
1612        ensures
1613            r <==> q_lt(a, b),
1614    {
1615        Rat::compare(a, b) < 0
1616    }
1617
1618    /// `a <= b`.
1619    ///
1620    /// ```
1621    /// use the_q::Rat;
1622    ///
1623    /// let a = Rat::new(1, 3).unwrap();
1624    /// let b = Rat::new(1, 2).unwrap();
1625    /// assert!(Rat::le(a, b));
1626    /// assert!(Rat::le(a, a));
1627    /// assert!(!Rat::le(b, a));
1628    /// ```
1629    pub fn le(a: Rat, b: Rat) -> (r: bool)
1630        requires
1631            a.wf(),
1632            b.wf(),
1633        ensures
1634            r <==> q_le(a, b),
1635    {
1636        Rat::compare(a, b) <= 0
1637    }
1638
1639    /// `a > b`.
1640    ///
1641    /// ```
1642    /// use the_q::Rat;
1643    ///
1644    /// let a = Rat::new(1, 2).unwrap();
1645    /// let b = Rat::new(1, 3).unwrap();
1646    /// assert!(Rat::gt(a, b));
1647    /// assert!(!Rat::gt(a, a));
1648    /// ```
1649    pub fn gt(a: Rat, b: Rat) -> (r: bool)
1650        requires
1651            a.wf(),
1652            b.wf(),
1653        ensures
1654            r <==> q_lt(b, a),
1655    {
1656        Rat::compare(a, b) > 0
1657    }
1658
1659    /// `a >= b`.
1660    ///
1661    /// ```
1662    /// use the_q::Rat;
1663    ///
1664    /// let a = Rat::new(1, 2).unwrap();
1665    /// let b = Rat::new(1, 3).unwrap();
1666    /// assert!(Rat::ge(a, b));
1667    /// assert!(Rat::ge(a, a));
1668    /// assert!(!Rat::ge(b, a));
1669    /// ```
1670    pub fn ge(a: Rat, b: Rat) -> (r: bool)
1671        requires
1672            a.wf(),
1673            b.wf(),
1674        ensures
1675            r <==> q_le(b, a),
1676    {
1677        Rat::compare(a, b) >= 0
1678    }
1679
1680    /// `a == 0`.
1681    ///
1682    /// ```
1683    /// use the_q::Rat;
1684    ///
1685    /// assert!(Rat::zero().is_zero());
1686    /// assert!(!Rat::one().is_zero());
1687    /// ```
1688    pub fn is_zero(&self) -> (r: bool)
1689        requires
1690            self.wf(),
1691        ensures
1692            r <==> self.n() == 0,
1693    {
1694        self.numerator() == 0
1695    }
1696
1697    /// `a == 1`.
1698    ///
1699    /// ```
1700    /// use the_q::Rat;
1701    ///
1702    /// assert!(Rat::one().is_one());
1703    /// assert!(!Rat::zero().is_one());
1704    /// ```
1705    pub fn is_one(&self) -> (r: bool)
1706        requires
1707            self.wf(),
1708        ensures
1709            r <==> self.n() == self.d(),
1710    {
1711        proof {
1712            // `n == d` makes `n` a common divisor of `|n|` and `d`, so it
1713            // divides their gcd, which `wf` pins at 1 — forcing `n == 1`.
1714            if self.n() == self.d() {
1715                crate::model::lemma_divides_basic(self.n());
1716                crate::gcd::lemma_gcd_greatest(
1717                    abs_int(self.n()) as nat,
1718                    self.d() as nat,
1719                    self.n(),
1720                );
1721                crate::model::lemma_divides_le(self.n(), 1);
1722            }
1723        }
1724        self.numerator() == 1 && self.denominator() == 1
1725    }
1726
1727    /// `-1`, `0` or `1` according to the sign.
1728    ///
1729    /// ```
1730    /// use the_q::Rat;
1731    ///
1732    /// assert_eq!(Rat::new(-3, 4).unwrap().signum(), -1);
1733    /// assert_eq!(Rat::zero().signum(), 0);
1734    /// assert_eq!(Rat::new(3, 4).unwrap().signum(), 1);
1735    /// ```
1736    pub fn signum(&self) -> (r: i32)
1737        requires
1738            self.wf(),
1739        ensures
1740            r == 0 <==> self.n() == 0,
1741            r < 0 <==> self.n() < 0,
1742            r > 0 <==> self.n() > 0,
1743    {
1744        if self.numerator() < 0 {
1745            -1
1746        } else if self.numerator() > 0 {
1747            1
1748        } else {
1749            0
1750        }
1751    }
1752
1753    /// `0 <= a <= 1`.
1754    ///
1755    /// The consuming engine checks this constantly on beliefs, disbeliefs and
1756    /// uncertainties, so it is a first-class predicate rather than two
1757    /// comparisons.
1758    ///
1759    /// ```
1760    /// use the_q::Rat;
1761    ///
1762    /// assert!(Rat::new(1, 2).unwrap().in_unit_interval());
1763    /// assert!(Rat::one().in_unit_interval());
1764    /// assert!(!Rat::new(-1, 2).unwrap().in_unit_interval());
1765    /// assert!(!Rat::new(3, 2).unwrap().in_unit_interval());
1766    /// ```
1767    pub fn in_unit_interval(&self) -> (r: bool)
1768        requires
1769            self.wf(),
1770        ensures
1771            r <==> (self.n() >= 0 && self.n() <= self.d()),
1772    {
1773        self.numerator() >= 0 && self.numerator() <= self.denominator()
1774    }
1775
1776    /// The smaller of `a` and `b`. Exact.
1777    ///
1778    /// ```
1779    /// use the_q::Rat;
1780    ///
1781    /// let a = Rat::new(1, 3).unwrap();
1782    /// let b = Rat::new(1, 2).unwrap();
1783    /// assert_eq!(Rat::min(a, b), a);
1784    /// assert_eq!(Rat::min(b, a), a);
1785    /// ```
1786    pub fn min(a: Rat, b: Rat) -> (r: Rat)
1787        requires
1788            a.wf(),
1789            b.wf(),
1790        ensures
1791            r.wf(),
1792            r == a || r == b,
1793            q_le(r, a),
1794            q_le(r, b),
1795            // ...and *which* argument. Without these the contract constrains
1796            // the result without naming it, leaving the connection between
1797            // "the contract pins a unique answer" and "this function returns
1798            // it" to a reader rather than to the prover.
1799            q_le(a, b) ==> r == a,
1800            !q_le(a, b) ==> r == b,
1801    {
1802        if Rat::le(a, b) {
1803            a
1804        } else {
1805            b
1806        }
1807    }
1808
1809    /// The larger of `a` and `b`. Exact.
1810    ///
1811    /// ```
1812    /// use the_q::Rat;
1813    ///
1814    /// let a = Rat::new(1, 3).unwrap();
1815    /// let b = Rat::new(1, 2).unwrap();
1816    /// assert_eq!(Rat::max(a, b), b);
1817    /// assert_eq!(Rat::max(b, a), b);
1818    /// ```
1819    pub fn max(a: Rat, b: Rat) -> (r: Rat)
1820        requires
1821            a.wf(),
1822            b.wf(),
1823        ensures
1824            r.wf(),
1825            r == a || r == b,
1826            q_le(a, r),
1827            q_le(b, r),
1828            // See `Rat::min`: these name the result rather than bounding it.
1829            q_le(a, b) ==> r == b,
1830            !q_le(a, b) ==> r == a,
1831    {
1832        if Rat::le(a, b) {
1833            b
1834        } else {
1835            a
1836        }
1837    }
1838
1839    /// `a` clamped into `[lo, hi]`. Exact. Requires `lo <= hi`.
1840    ///
1841    /// ```
1842    /// use the_q::Rat;
1843    ///
1844    /// let lo = Rat::zero();
1845    /// let hi = Rat::one();
1846    /// let mid = Rat::new(1, 2).unwrap();
1847    /// assert_eq!(Rat::clamp(mid, lo, hi), mid);
1848    /// assert_eq!(Rat::clamp(Rat::new(-1, 2).unwrap(), lo, hi), lo);
1849    /// assert_eq!(Rat::clamp(Rat::new(3, 2).unwrap(), lo, hi), hi);
1850    /// ```
1851    pub fn clamp(a: Rat, lo: Rat, hi: Rat) -> (r: Rat)
1852        requires
1853            a.wf(),
1854            lo.wf(),
1855            hi.wf(),
1856            q_le(lo, hi),
1857        ensures
1858            r.wf(),
1859            r == a || r == lo || r == hi,
1860            q_le(lo, r),
1861            q_le(r, hi),
1862            // ...and *which* of the three.
1863            //
1864            // Without this clause a `clamp` that always returned `lo` would verify.
1865            (q_le(lo, a) && q_le(a, hi)) ==> r == a,
1866            !q_le(lo, a) ==> r == lo,
1867            !q_le(a, hi) ==> r == hi,
1868    {
1869        proof {
1870            lemma_le_trans(lo, hi, a);
1871            // `a < lo <= hi` gives `a <= hi`, which makes the "returns `hi`"
1872            // clause vacuous on the `lo` branch.
1873            lemma_le_trans(a, lo, hi);
1874        }
1875        if Rat::lt(a, lo) {
1876            lo
1877        } else if Rat::lt(hi, a) {
1878            hi
1879        } else {
1880            a
1881        }
1882    }
1883}
1884
1885/// `<=` is transitive on well-formed `Rat`.
1886pub proof fn lemma_le_trans(a: Rat, b: Rat, c: Rat)
1887    requires
1888        a.wf(),
1889        b.wf(),
1890        c.wf(),
1891    ensures
1892        (q_le(a, b) && q_le(b, c)) ==> q_le(a, c),
1893{
1894    if q_le(a, b) && q_le(b, c) {
1895        assert(a.n() * c.d() <= c.n() * a.d()) by (nonlinear_arith)
1896            requires
1897                a.d() > 0,
1898                b.d() > 0,
1899                c.d() > 0,
1900                a.n() * b.d() <= b.n() * a.d(),
1901                b.n() * c.d() <= c.n() * b.d(),
1902        ;
1903    }
1904}
1905
1906} // verus!
1907
1908// ---------------------------------------------------------------------------
1909// Standard trait impls
1910//
1911// These are thin, total delegations to the verified functions above. Verus does
1912// not model the `core` comparison traits, so they are marked `external`: they
1913// cannot be called from verified code, and verified code never needs them.
1914// They are enumerated in TRUSTED.md.
1915// ---------------------------------------------------------------------------
1916
1917#[cfg_attr(verus_keep_ghost, verifier::external)]
1918impl PartialOrd for Rat {
1919    fn partial_cmp(&self, other: &Rat) -> Option<core::cmp::Ordering> {
1920        Some(<Rat as Ord>::cmp(self, other))
1921    }
1922}
1923
1924#[cfg_attr(verus_keep_ghost, verifier::external)]
1925impl Ord for Rat {
1926    fn cmp(&self, other: &Rat) -> core::cmp::Ordering {
1927        // Delegate to the verified `Rat::compare` (proven against the ghost
1928        // order in `verus!` above) rather than reimplementing the
1929        // cross-multiplication here. This impl only maps its `-1`/`0`/`1`
1930        // onto `Ordering`.
1931        Rat::compare(*self, *other).cmp(&0)
1932    }
1933}
1934
1935#[cfg_attr(verus_keep_ghost, verifier::external)]
1936impl core::ops::Add for Rat {
1937    type Output = Rat;
1938
1939    fn add(self, rhs: Rat) -> Rat {
1940        Rat::add(self, rhs)
1941    }
1942}
1943
1944#[cfg_attr(verus_keep_ghost, verifier::external)]
1945impl core::ops::Sub for Rat {
1946    type Output = Rat;
1947
1948    fn sub(self, rhs: Rat) -> Rat {
1949        Rat::sub(self, rhs)
1950    }
1951}
1952
1953#[cfg_attr(verus_keep_ghost, verifier::external)]
1954impl core::ops::Mul for Rat {
1955    type Output = Rat;
1956
1957    fn mul(self, rhs: Rat) -> Rat {
1958        Rat::mul(self, rhs)
1959    }
1960}
1961
1962#[cfg_attr(verus_keep_ghost, verifier::external)]
1963impl core::ops::Neg for Rat {
1964    type Output = Rat;
1965
1966    fn neg(self) -> Rat {
1967        Rat::neg(self)
1968    }
1969}