1//! This file contains proofs related to multiplication. These are
2//! internal functions used within the math standard library.
3//!
4//! It's based on the following file from the Dafny math standard library:
5//! `Source/DafnyStandardLibraries/src/Std/Arithmetic/Internal/MulInternals.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#[allow(unused_imports)]
15use super::super::super::prelude::*;
1617#[cfg(verus_keep_ghost)]
18use super::super::super::arithmetic::internals::general_internals::{
19 is_le, lemma_induction_helper,
20};
21use super::super::super::arithmetic::internals::mul_internals_nonlinear as MulINL;
22#[cfg(verus_keep_ghost)]
23use super::super::super::math::{add as add1, sub as sub1};
2425verus! {
2627/// This function performs multiplication recursively. It's only valid
28/// when `x` is non-negative.
29#[verifier::opaque]
30pub open spec fn mul_pos(x: int, y: int) -> int
31 recommends
32 x >= 0,
33 decreases x,
34{
35if x <= 0 {
360
37} else {
38 y + mul_pos(x - 1, y)
39 }
40}
4142/// This function performs multiplication recursively.
43pub open spec fn mul_recursive(x: int, y: int) -> int {
44if x >= 0 {
45 mul_pos(x, y)
46 } else {
47 -1 * mul_pos(-1 * x, y)
48 }
49}
5051/// This utility function helps prove a mathematical property by
52/// induction. The caller supplies an integer predicate, proves the
53/// predicate holds in the base case of 0, and proves correctness of
54/// inductive steps both upward and downward from the base case. This
55/// lemma invokes induction to establish that the predicate holds for
56/// all integers.
57///
58/// To prove inductive steps upward from the base case, the caller
59/// must establish that, for any `i >= 0`, `f(i) ==> f(add1(i, 1))`.
60/// `add1(i, 1)` is just `i + 1`, but written in a functional style
61/// so that it can be used where functional triggers are required.
62///
63/// To prove inductive steps downward from the base case, the caller
64/// must establish that, for any `i <= 0`, `f(i) ==> f(sub1(i, 1))`.
65/// `sub1(i, 1)` is just `i - 1`, but written in a functional style
66/// so that it can be used where functional triggers are required.
67pub proof fn lemma_mul_induction(f: spec_fn(int) -> bool)
68 requires
69 f(0),
70 forall|i: int| i >= 0 && #[trigger] f(i) ==> #[trigger] f(add1(i, 1)),
71 forall|i: int| i <= 0 && #[trigger] f(i) ==> #[trigger] f(sub1(i, 1)),
72 ensures
73 forall|i: int| #[trigger] f(i),
74{
75 assert forall|i: int| #[trigger] f(i) by { lemma_induction_helper(1, f, i) };
76}
7778/// Proof that multiplication is always commutative
79pub broadcast proof fn lemma_mul_commutes(x: int, y: int)
80 ensures
81#[trigger] (x * y) == y * x,
82{
83}
8485/// Proof that multiplication distributes over addition by 1 and
86/// over subtraction by 1
87proof fn lemma_mul_successor()
88 ensures
89 forall|x: int, y: int| #[trigger] ((x + 1) * y) == x * y + y,
90 forall|x: int, y: int| #[trigger] ((x - 1) * y) == x * y - y,
91{
92 assert forall|x: int, y: int| #[trigger] ((x + 1) * y) == x * y + y by {
93 MulINL::lemma_mul_is_distributive_add(y, x, 1);
94 }
95 assert forall|x: int, y: int| #[trigger] ((x - 1) * y) == x * y - y by {
96 assert((x - 1) * y == y * (x - 1));
97 MulINL::lemma_mul_is_distributive_add(y, x, -1);
98 assert(y * (x - 1) == y * x + y * -1);
99 assert(-1 * y == -y);
100 assert(x * y + (-1 * y) == x * y - y);
101 }
102}
103104/// Proof that multiplication distributes over addition and over
105/// subtraction
106#[verifier::spinoff_prover]
107pub broadcast proof fn lemma_mul_distributes_plus(x: int, y: int, z: int)
108 ensures
109#[trigger] ((x + y) * z) == (x * z + y * z),
110{
111 lemma_mul_successor();
112 assert forall|x: int, y: int, z: int| #[trigger] ((x + y) * z) == (x * z + y * z) by {
113let f1 = |i: int| ((x + i) * z) == (x * z + i * z);
114 assert(f1(0));
115 assert forall|i: int| i >= 0 && #[trigger] f1(i) implies #[trigger] f1(add1(i, 1)) by {
116 assert((x + (i + 1)) * z == ((x + i) + 1) * z == (x + i) * z + z);
117 };
118 assert forall|i: int| i <= 0 && #[trigger] f1(i) implies #[trigger] f1(sub1(i, 1)) by {
119 assert((x + (i - 1)) * z == ((x + i) - 1) * z == (x + i) * z - z);
120 };
121 lemma_mul_induction(f1);
122 assert(f1(y));
123 }
124}
125126#[verifier::spinoff_prover]
127pub broadcast proof fn lemma_mul_distributes_minus(x: int, y: int, z: int)
128 ensures
129#[trigger] ((x - y) * z) == (x * z - y * z),
130{
131 lemma_mul_successor();
132 assert forall|x: int, y: int, z: int| #[trigger] ((x - y) * z) == (x * z - y * z) by {
133let f2 = |i: int| ((x - i) * z) == (x * z - i * z);
134 assert(f2(0));
135 assert forall|i: int| i >= 0 && #[trigger] f2(i) implies #[trigger] f2(add1(i, 1)) by {
136 assert((x - (i + 1)) * z == ((x - i) - 1) * z == (x - i) * z - z);
137 };
138 assert forall|i: int| i <= 0 && #[trigger] f2(i) implies #[trigger] f2(sub1(i, 1)) by {
139 assert((x - (i - 1)) * z == ((x - i) + 1) * z == (x - i) * z + z);
140 };
141 lemma_mul_induction(f2);
142 assert(f2(y));
143 }
144}
145146/// This function expresses that multiplication is commutative,
147/// distributes over addition, and distributes over subtraction
148pub open spec fn mul_auto() -> bool {
149 &&& forall|x: int, y: int| #[trigger] (x * y) == (y * x)
150 &&& forall|x: int, y: int, z: int| #[trigger] ((x + y) * z) == (x * z + y * z)
151 &&& forall|x: int, y: int, z: int| #[trigger] ((x - y) * z) == (x * z - y * z)
152}
153154pub broadcast group group_mul_properties_internal {
155 lemma_mul_commutes,
156 lemma_mul_distributes_plus,
157 lemma_mul_distributes_minus,
158}
159160// Check that the group_mul_properties_internal broadcast group group_provides the same properties as the _auto lemma it replaces
161proof fn lemma_mul_properties_internal_prove_mul_auto()
162 ensures
163 mul_auto(),
164{
165 broadcast use group_mul_properties_internal;
166167 assert(mul_auto());
168}
169170/// This utility function helps prove a mathematical property by
171/// induction. The caller supplies an integer predicate `f`, proves
172/// the predicate holds in the base case of 0, and proves correctness
173/// of inductive steps both upward and downward from the base case.
174/// This lemma invokes induction to establish that the predicate holds
175/// for the given integer `x`.
176///
177/// To prove inductive steps upward from the base case, the caller
178/// must establish that, for any `i`, `is_le(0, i)` implies `f(i) ==>
179/// f(i + 1)`.
180///
181/// To prove inductive steps downward from the base case, the caller
182/// must establish that, for any `i`, `is_le(i, 0)` implies `f(i) ==>
183/// f(i - 1)`.
184pub proof fn lemma_mul_induction_auto(x: int, f: spec_fn(int) -> bool)
185 requires
186 mul_auto() ==> {
187 &&& f(0)
188 &&& (forall|i| #[trigger] is_le(0, i) && f(i) ==> f(i + 1))
189 &&& (forall|i| #[trigger] is_le(i, 0) && f(i) ==> f(i - 1))
190 },
191 ensures
192 mul_auto(),
193 f(x),
194{
195 broadcast use group_mul_properties_internal;
196197 assert(forall|i| is_le(0, i) && #[trigger] f(i) ==> f(i + 1));
198 assert(forall|i| is_le(i, 0) && #[trigger] f(i) ==> f(i - 1));
199 lemma_mul_induction(f);
200}
201202/// This utility function helps prove a mathematical property by
203/// induction. The caller supplies an integer predicate `f`, proves
204/// the predicate holds in the base case of 0, and proves correctness
205/// of inductive steps both upward and downward from the base case.
206/// This lemma invokes induction to establish that the predicate holds
207/// for all integers.
208///
209/// To prove inductive steps upward from the base case, the caller
210/// must establish that, for any `i`, `is_le(0, i)` implies `f(i) ==>
211/// f(i + 1)`.
212///
213/// To prove inductive steps downward from the base case, the caller
214/// must establish that, for any `i`, `is_le(i, 0)` implies `f(i) ==>
215/// f(i - 1)`.
216pub proof fn lemma_mul_induction_auto_forall(f: spec_fn(int) -> bool)
217 requires
218 mul_auto() ==> {
219 &&& f(0)
220 &&& (forall|i| #[trigger] is_le(0, i) && f(i) ==> f(i + 1))
221 &&& (forall|i| #[trigger] is_le(i, 0) && f(i) ==> f(i - 1))
222 },
223 ensures
224 mul_auto(),
225 forall|i| #[trigger] f(i),
226{
227 assert(mul_auto()) by {
228 lemma_mul_induction_auto(0, f);
229 }
230 assert forall|i| #[trigger] f(i) by {
231 lemma_mul_induction_auto(i, f);
232 }
233}
234235} // verus!