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!