vstd/arithmetic/internals/general_internals.rs
1//! This file contains general internal functions used within the math
2//! standard library.
3//!
4//! It's based on the following file from the Dafny math standard library:
5//! `Source/DafnyStandardLibraries/src/Std/Arithmetic/Internal/GeneralInternals.dfy`.
6//! That file has the following copyright notice:
7//! /*******************************************************************************
8//! * Original: Copyright (c) Microsoft Corporation
9//! * SPDX-License-Identifier: MIT
10//! *
11//! * Modifications and Extensions: Copyright by the contributors to the Dafny Project
12//! * SPDX-License-Identifier: MIT
13//! *******************************************************************************/
14//! Declares helper lemmas and predicates for non-linear arithmetic
15#[cfg(verus_keep_ghost)]
16use super::super::super::math::{add as add1, sub as sub1};
17use super::super::super::prelude::*;
18
19verus! {
20
21/// Computes the boolean `x <= y`. This is useful where we want to
22/// trigger on a `<=` operator but we need a functional rather than a
23/// mathematical trigger. (A trigger must be fully functional or fully
24/// mathematical.)
25pub open spec fn is_le(x: int, y: int) -> bool {
26 x <= y
27}
28
29/// This proof, local to this module, aids in the process of proving
30/// [`lemma_induction_helper`] by covering only the case of nonnegative
31/// values of `x`.
32proof fn lemma_induction_helper_pos(n: int, f: spec_fn(int) -> bool, x: int)
33 requires
34 x >= 0,
35 n > 0,
36 forall|i: int| 0 <= i < n ==> #[trigger] f(i),
37 forall|i: int| i >= 0 && #[trigger] f(i) ==> #[trigger] f(add1(i, n)),
38 forall|i: int| i < n && #[trigger] f(i) ==> #[trigger] f(sub1(i, n)),
39 ensures
40 f(x),
41 decreases x,
42{
43 if (x >= n) {
44 assert(x - n < x);
45 lemma_induction_helper_pos(n, f, x - n);
46 assert(f(add1(x - n, n)));
47 assert(f((x - n) + n));
48 }
49}
50
51/// This proof, local to this module, aids in the process of proving
52/// [`lemma_induction_helper`] by covering only the case of negative
53/// values of `x`.
54proof fn lemma_induction_helper_neg(n: int, f: spec_fn(int) -> bool, x: int)
55 requires
56 x < 0,
57 n > 0,
58 forall|i: int| 0 <= i < n ==> #[trigger] f(i),
59 forall|i: int| i >= 0 && #[trigger] f(i) ==> #[trigger] f(add1(i, n)),
60 forall|i: int| i < n && #[trigger] f(i) ==> #[trigger] f(sub1(i, n)),
61 ensures
62 f(x),
63 decreases -x,
64{
65 if (-x <= n) {
66 lemma_induction_helper_pos(n, f, x + n);
67 assert(f(sub1(x + n, n)));
68 assert(f((x + n) - n));
69 } else {
70 lemma_induction_helper_neg(n, f, x + n);
71 assert(f(sub1(x + n, n)));
72 assert(f((x + n) - n));
73 }
74}
75
76/// This utility function helps prove a mathematical property by
77/// induction. The caller supplies an integer predicate, proves the
78/// predicate holds in certain base cases, and proves correctness of
79/// inductive steps both upward and downward from the base cases. This
80/// lemma invokes induction to establish that the predicate holds for
81/// the given arbitrary input `x`.
82///
83/// `f`: The integer predicate
84///
85/// `n`: Upper bound on the base cases. Specifically, the caller
86/// establishes `f(i)` for every value `i` satisfying `0 <= i < n`.
87///
88/// `x`: The desired case established by this lemma. Its postcondition
89/// is thus simply `f(x)`.
90///
91/// To prove inductive steps upward from the base cases, the caller
92/// must establish that, for any `i >= 0`, `f(i) ==> f(add1(i, n))`.
93/// `add1(i, n)` is just `i + n`, but written in a functional style
94/// so that it can be used where functional triggers are required.
95///
96/// To prove inductive steps downward from the base cases, the caller
97/// must establish that, for any `i < n`, `f(i) ==> f(sub1(i, n))`.
98/// `sub1(i, n)` is just `i - n`, but written in a functional style
99/// so that it can be used where functional triggers are required.
100pub proof fn lemma_induction_helper(n: int, f: spec_fn(int) -> bool, x: int)
101 requires
102 n > 0,
103 forall|i: int| 0 <= i < n ==> #[trigger] f(i),
104 forall|i: int| i >= 0 && #[trigger] f(i) ==> #[trigger] f(add1(i, n)),
105 forall|i: int| i < n && #[trigger] f(i) ==> #[trigger] f(sub1(i, n)),
106 ensures
107 f(x),
108{
109 if (x >= 0) {
110 lemma_induction_helper_pos(n, f, x);
111 } else {
112 lemma_induction_helper_neg(n, f, x);
113 }
114}
115
116} // verus!