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}