vstd/arithmetic/
mul.rs

1//! This file contains proofs related to integer multiplication (`*`).
2//! These are part of the math standard library.
3//!
4//! It's based on the following file from the Dafny math standard
5//! library:
6//! `Source/DafnyStandardLibraries/src/Std/Arithmetic/Mul.dfy`.
7//! That file has the following copyright notice:
8//! /*******************************************************************************
9//! * Original: Copyright (c) Microsoft Corporation *
10//! SPDX-License-Identifier: MIT * * Modifications and Extensions:
11//! Copyright by the contributors to the Dafny Project *
12//! SPDX-License-Identifier: MIT
13//! *******************************************************************************/
14#[allow(unused_imports)]
15use super::super::prelude::*;
16
17verus! {
18
19use super::super::arithmetic::internals::mul_internals_nonlinear as MulINL;
20use super::super::arithmetic::internals::mul_internals::*;
21
22/// Proof that multiplication using `*` is equivalent to
23/// multiplication using a recursive definition. Specifically,
24/// `x * y` is equivalent in that way.
25pub broadcast proof fn lemma_mul_is_mul_recursive(x: int, y: int)
26    ensures
27        #[trigger] (x * y) == mul_recursive(x, y),
28{
29    if (x >= 0) {
30        lemma_mul_is_mul_pos(x, y);
31        assert(x * y == mul_pos(x, y));
32        assert((x * y) == mul_recursive(x, y));
33    } else {
34        lemma_mul_is_mul_pos(-x, y);
35        assert(x * y == -1 * (-x * y)) by { lemma_mul_is_associative(-1, -x, y) };  // OBSERVE
36        assert((x * y) == mul_recursive(x, y));
37    }
38}
39
40/// Proof that multiplying two positive integers with `*` results in
41/// the same product as would be achieved by recursive addition.
42/// Specifically, `x * y == mul_pos(x, y)`.
43pub proof fn lemma_mul_is_mul_pos(x: int, y: int)
44    requires
45        x >= 0,
46    ensures
47        x * y == mul_pos(x, y),
48{
49    reveal(mul_pos);
50    lemma_mul_induction_auto(x, |u: int| u >= 0 ==> u * y == mul_pos(u, y));
51}
52
53pub proof fn lemma_mul_basics(x: int)
54    ensures
55        0 * x == 0,
56        x * 0 == 0,
57        x * 1 == x,
58        1 * x == x,
59{
60}
61
62/// Proof of basic properties of multiplication by `x`, specifically
63/// what happens when multiplying by 0 or 1.
64pub broadcast proof fn lemma_mul_basics_1(x: int)
65    ensures
66        #[trigger] (0 * x) == 0,
67{
68}
69
70/// Proof of basic properties of multiplication by `x`, specifically
71/// what happens when multiplying by 0 or 1.
72pub broadcast proof fn lemma_mul_basics_2(x: int)
73    ensures
74        #[trigger] (x * 0) == 0,
75{
76}
77
78/// Proof of basic properties of multiplication by `x`, specifically
79/// what happens when multiplying by 0 or 1.
80pub broadcast proof fn lemma_mul_basics_3(x: int)
81    ensures
82        #[trigger] (x * 1) == x,
83{
84}
85
86/// Proof of basic properties of multiplication by `x`, specifically
87/// what happens when multiplying by 0 or 1.
88pub broadcast proof fn lemma_mul_basics_4(x: int)
89    ensures
90        #[trigger] (1 * x) == x,
91{
92}
93
94pub broadcast group group_mul_basics {
95    lemma_mul_basics_1,
96    lemma_mul_basics_2,
97    lemma_mul_basics_3,
98    lemma_mul_basics_4,
99}
100
101/// Proof that `x * y` is nonzero if and only if both `x` and `y` are nonzero.
102pub broadcast proof fn lemma_mul_nonzero(x: int, y: int)
103    ensures
104        #[trigger] (x * y) != 0 <==> x != 0 && y != 0,
105{
106    MulINL::lemma_mul_nonzero(x, y);
107}
108
109/// Proof that any integer multiplied by 0 results in a product of 0.
110pub broadcast proof fn lemma_mul_by_zero_is_zero(x: int)
111    ensures
112        #![trigger x * 0]
113        #![trigger 0 * x]
114        x * 0 == 0 && 0 * x == 0,
115{
116    assert forall|x: int| #![trigger x * 0] #![trigger 0 * x] x * 0 == 0 && 0 * x == 0 by {
117        lemma_mul_basics(x);
118    }
119}
120
121/// Proof that multiplication is associative, specifically that
122/// `x * (y * z) == (x * y) * z`.
123pub broadcast proof fn lemma_mul_is_associative(x: int, y: int, z: int)
124    ensures
125        #![trigger x * (y * z)]
126        #![trigger (x * y) * z]
127        x * (y * z) == (x * y) * z,
128{
129    MulINL::lemma_mul_is_associative(x, y, z);
130}
131
132/// Proof that multiplication is commutative, specifically that
133/// `x * y == y * x`.
134pub broadcast proof fn lemma_mul_is_commutative(x: int, y: int)
135    ensures
136        #[trigger] (x * y) == y * x,
137{
138}
139
140/// Proof that, since the product of the two integers `x` and `y` is
141/// nonnegative, that product is greater than or equal to each of `x`
142/// and `y`
143pub broadcast proof fn lemma_mul_ordering(x: int, y: int)
144    requires
145        x != 0,
146        y != 0,
147        0 <= x * y,
148    ensures
149        #[trigger] (x * y) >= x && x * y >= y,
150{
151    MulINL::lemma_mul_ordering(x, y);
152}
153
154/*
155    We don't port LemmaMulEquality or LemmaMulEqualityAuto from the
156    Dafny standard library for arithmetic, since they're never useful.
157    They say that `x == y ==> x * z == y * z`, which is trivial. It
158    follows immediately from the basic SMT axiom that functions and
159    operators (including multiplication) have equal values when
160    applied to equal arguments.
161*/
162
163/// Proof that, since `x <= y` and `z >= 0`, `x * z <= y * z`.
164pub broadcast proof fn lemma_mul_inequality(x: int, y: int, z: int)
165    requires
166        x <= y,
167        z >= 0,
168    ensures
169        #[trigger] (x * z) <= #[trigger] (y * z),
170{
171    lemma_mul_induction_auto(z, |u: int| u >= 0 ==> x * u <= y * u);
172}
173
174/// Proof that since `x < y` and `z > 0`, `x * z < y * z`.
175pub broadcast proof fn lemma_mul_strict_inequality(x: int, y: int, z: int)
176    requires
177        x < y,
178        z > 0,
179    ensures
180        #[trigger] (x * z) < #[trigger] (y * z),
181{
182    MulINL::lemma_mul_strict_inequality(x, y, z);
183}
184
185/// Proof that since `x` is bounded above by `xbound` and `y` is
186/// bounded above by `ybound`, the product of `x` and `y` is bounded
187/// above by the product of the bounds.
188pub broadcast proof fn lemma_mul_upper_bound(x: int, xbound: int, y: int, ybound: int)
189    requires
190        x <= xbound,
191        y <= ybound,
192        0 <= x,
193        0 <= y,
194    ensures
195        #[trigger] (x * y) <= #[trigger] (xbound * ybound),
196{
197    lemma_mul_inequality(x, xbound, y);
198    lemma_mul_inequality(y, ybound, xbound);
199}
200
201/// Proof that when `x` has an exclusive upper bound `xbound` and `y`
202/// has an exclusive upper bound `ybound`, that the product of `x` and
203/// `y` is bounded above by the product of the predecessors of their
204/// upper bounds. In other words, `x * y <= (xbound - 1) * (ybound - 1)`.
205pub broadcast proof fn lemma_mul_strict_upper_bound(x: int, xbound: int, y: int, ybound: int)
206    requires
207        x < xbound,
208        y < ybound,
209        0 < x,
210        0 < y,
211    ensures
212        #[trigger] (x * y) <= #[trigger] ((xbound - 1) * (ybound - 1)),
213{
214    lemma_mul_inequality(x, xbound - 1, y);
215    lemma_mul_inequality(y, ybound - 1, xbound - 1);
216}
217
218/// Proof that multiplying the positive integer `x` by respectively
219/// `y` and `z` maintains the order of `y` and `z`. Specifically, `y
220/// <= z ==> x * y <= x * z` and `y < z ==> x * y < x * z`.
221pub broadcast proof fn lemma_mul_left_inequality(x: int, y: int, z: int)
222    requires
223        0 < x,
224    ensures
225        y <= z ==> #[trigger] (x * y) <= #[trigger] (x * z),
226        y < z ==> x * y < x * z,
227{
228    lemma_mul_induction_auto(x, |u: int| u > 0 ==> y <= z ==> u * y <= u * z);
229    lemma_mul_induction_auto(x, |u: int| u > 0 ==> y < z ==> u * y < u * z);
230}
231
232/// Proof that if `x` and `y` have equal results when multiplied by
233/// nonzero `m`, then they're equal.
234pub broadcast proof fn lemma_mul_equality_converse(m: int, x: int, y: int)
235    requires
236        m != 0,
237        #[trigger] (m * x) == #[trigger] (m * y),
238    ensures
239        x == y,
240{
241    lemma_mul_induction_auto(m, |u| x > y && 0 < u ==> x * u > y * u);
242    lemma_mul_induction_auto(m, |u: int| x < y && 0 < u ==> x * u < y * u);
243    lemma_mul_induction_auto(m, |u: int| x > y && 0 > u ==> x * u < y * u);
244    lemma_mul_induction_auto(m, |u: int| x < y && 0 > u ==> x * u > y * u);
245}
246
247/// Proof that since `x * z <= y * z` and `z > 0`, that `x <= y`.
248pub broadcast proof fn lemma_mul_inequality_converse(x: int, y: int, z: int)
249    requires
250        #[trigger] (x * z) <= #[trigger] (y * z),
251        z > 0,
252    ensures
253        x <= y,
254{
255    lemma_mul_induction_auto(z, |u: int| x * u <= y * u && u > 0 ==> x <= y);
256}
257
258/// Proof that since `x * z < y * z` and `z >= 0`, we know `x < y`.
259pub broadcast proof fn lemma_mul_strict_inequality_converse(x: int, y: int, z: int)
260    requires
261        #[trigger] (x * z) < #[trigger] (y * z),
262        z >= 0,
263    ensures
264        x < y,
265{
266    lemma_mul_induction_auto(z, |u: int| x * u < y * u && u >= 0 ==> x < y);
267}
268
269/// Proof that multiplication distributes over addition, specifically that
270/// `x * (y + z) == x * y + x * z`.
271pub broadcast proof fn lemma_mul_is_distributive_add(x: int, y: int, z: int)
272    ensures
273        #[trigger] (x * (y + z)) == x * y + x * z,
274{
275    MulINL::lemma_mul_is_distributive_add(x, y, z);
276}
277
278/// Proof that multiplication distributes over addition, specifically that
279/// `(y + z) * x == y * x + z * x`.
280pub broadcast proof fn lemma_mul_is_distributive_add_other_way(x: int, y: int, z: int)
281    ensures
282        #[trigger] ((y + z) * x) == y * x + z * x,
283{
284    broadcast use group_mul_properties_internal;
285
286}
287
288/// Proof that multiplication distributes over subtraction, specifically that
289/// `x * (y - z) == x * y - x * z`.
290pub broadcast proof fn lemma_mul_is_distributive_sub(x: int, y: int, z: int)
291    ensures
292        #[trigger] (x * (y - z)) == x * y - x * z,
293{
294    broadcast use group_mul_properties_internal;
295
296}
297
298/// Proof that multiplication distributes over subtraction when the
299/// subtraction happens in the multiplicand (i.e., in the left-hand
300/// argument to `*`). Specifically, `(y - z) * x == y * x - z * x`.
301pub broadcast proof fn lemma_mul_is_distributive_sub_other_way(x: int, y: int, z: int)
302    ensures
303        #[trigger] ((y - z) * x) == y * x - z * x,
304{
305    lemma_mul_is_distributive_sub(x, y, z);
306    lemma_mul_is_commutative(x, y - z);
307    lemma_mul_is_commutative(x, y);
308    lemma_mul_is_commutative(x, z);
309}
310
311pub broadcast group group_mul_is_distributive {
312    lemma_mul_is_distributive_add,
313    lemma_mul_is_distributive_add_other_way,
314    lemma_mul_is_distributive_sub,
315    lemma_mul_is_distributive_sub_other_way,
316}
317
318pub broadcast group group_mul_is_commutative_and_distributive {
319    lemma_mul_is_commutative,
320    group_mul_is_distributive,
321}
322
323/// Proof that multiplication is commutative, distributes over
324/// addition, and distributes over subtraction, in the specific cases
325/// where one of the arguments to the multiplication is `x` and the
326/// other arguments are `y` and `z`.
327proof fn lemma_mul_is_distributive(x: int, y: int, z: int)
328    ensures
329        x * (y + z) == x * y + x * z,
330        x * (y - z) == x * y - x * z,
331        (y + z) * x == y * x + z * x,
332        (y - z) * x == y * x - z * x,
333        x * (y + z) == (y + z) * x,
334        x * (y - z) == (y - z) * x,
335        x * y == y * x,
336        x * z == z * x,
337{
338    broadcast use group_mul_is_commutative_and_distributive;
339
340}
341
342/// Proof that multiplication distributes over addition and
343/// subtraction, whether the addition or subtraction happens in the
344/// first or the second argument to the multiplication.
345// pub broadcast proof fn lemma_mul_is_distributive_plus(x: int, y: int, z: int)
346//     ensures
347//         forall|x: int, y: int, z: int| #[trigger] (x * (y + z)) == x * y + x * z,
348//         forall|x: int, y: int, z: int| #[trigger] (x * (y - z)) == x * y - x * z,
349//         forall|x: int, y: int, z: int| #[trigger] ((y + z) * x) == y * x + z * x,
350//         forall|x: int, y: int, z: int| #[trigger] ((y - z) * x) == y * x - z * x,
351// {
352//     lemma_mul_is_distributive_add_auto();
353//     lemma_mul_is_distributive_sub_auto();
354//     broadcast use lemma_mul_is_commutative;
355// }
356/// Proof that if `x` and `y` are both positive, then their product is
357/// also positive.
358pub broadcast proof fn lemma_mul_strictly_positive(x: int, y: int)
359    ensures
360        (0 < x && 0 < y) ==> (0 < #[trigger] (x * y)),
361{
362    MulINL::lemma_mul_strictly_positive(x, y);
363}
364
365/// Proof that since `x > 1` and `y > 0`, `y < x * y`.
366pub broadcast proof fn lemma_mul_strictly_increases(x: int, y: int)
367    requires
368        1 < x,
369        0 < y,
370    ensures
371        y < #[trigger] (x * y),
372{
373    lemma_mul_induction_auto(x, |u: int| 1 < u ==> y < u * y);
374}
375
376/// Proof that since `x` and `y` are both positive, their product is
377/// greater than or equal to `y`.
378pub broadcast proof fn lemma_mul_increases(x: int, y: int)
379    requires
380        0 < x,
381        0 < y,
382    ensures
383        y <= #[trigger] (x * y),
384{
385    lemma_mul_induction_auto(x, |u: int| 0 < u ==> y <= u * y);
386}
387
388/// Proof that since `x` and `y` are non-negative, their product is
389/// non-negative.
390pub broadcast proof fn lemma_mul_nonnegative(x: int, y: int)
391    requires
392        0 <= x,
393        0 <= y,
394    ensures
395        0 <= #[trigger] (x * y),
396{
397    lemma_mul_induction_auto(x, |u: int| 0 <= u ==> 0 <= u * y);
398}
399
400/// Proof that negating `x` or `y` before multiplying them together
401/// produces the negation of the product of `x` and `y`.
402pub broadcast proof fn lemma_mul_unary_negation(x: int, y: int)
403    ensures
404        #![trigger (-x) * y]
405        #![trigger x * (-y)]
406        (-x) * y == -(x * y) == x * (-y),
407{
408    lemma_mul_induction_auto(x, |u: int| (-u) * y == -(u * y) == u * (-y));
409}
410
411/// Proof that multiplying `-x` and `-y` produces the same product as
412/// multiplying `x` and `y`.
413pub broadcast proof fn lemma_mul_cancels_negatives(x: int, y: int)
414    ensures
415        #[trigger] (x * y) == (-x) * (-y),
416{
417    lemma_mul_induction_auto(x, |u: int| (-u) * y == -(u * y) == u * (-y));
418}
419
420pub broadcast group group_mul_properties {
421    group_mul_basics,
422    lemma_mul_strict_inequality,
423    lemma_mul_inequality,
424    group_mul_is_commutative_and_distributive,
425    lemma_mul_is_associative,
426    lemma_mul_ordering,
427    lemma_mul_nonzero,
428    lemma_mul_nonnegative,
429    lemma_mul_strictly_increases,
430    lemma_mul_increases,
431}
432
433// Check that the group_mul_properties broadcast group group_provides the same properties as the _auto lemma it replaces
434proof fn lemma_mul_properties_prove_mul_properties_auto()
435    ensures
436        forall|x: int, y: int| #[trigger] (x * y) == y * x,
437        forall|x: int| #![trigger x * 1] #![trigger 1 * x] x * 1 == 1 * x == x,
438        forall|x: int, y: int, z: int| x < y && z > 0 ==> #[trigger] (x * z) < #[trigger] (y * z),
439        forall|x: int, y: int, z: int|
440            x <= y && z >= 0 ==> #[trigger] (x * z) <= #[trigger] (y * z),
441        forall|x: int, y: int, z: int| #[trigger] (x * (y + z)) == x * y + x * z,
442        forall|x: int, y: int, z: int| #[trigger] (x * (y - z)) == x * y - x * z,
443        forall|x: int, y: int, z: int| #[trigger] ((y + z) * x) == y * x + z * x,
444        forall|x: int, y: int, z: int| #[trigger] ((y - z) * x) == y * x - z * x,
445        forall|x: int, y: int, z: int|
446            #![trigger x * (y * z)]
447            #![trigger (x * y) * z]
448            x * (y * z) == (x * y) * z,
449        forall|x: int, y: int| #[trigger] (x * y) != 0 <==> x != 0 && y != 0,
450        forall|x: int, y: int| 0 <= x && 0 <= y ==> 0 <= #[trigger] (x * y),
451        forall|x: int, y: int|
452            0 < x && 0 < y && 0 <= x * y ==> x <= #[trigger] (x * y) && y <= (x * y),
453        forall|x: int, y: int| (1 < x && 0 < y) ==> (y < #[trigger] (x * y)),
454        forall|x: int, y: int| (0 < x && 0 < y) ==> (y <= #[trigger] (x * y)),
455        forall|x: int, y: int| (0 < x && 0 < y) ==> (0 < #[trigger] (x * y)),
456{
457    broadcast use group_mul_properties;
458
459}
460
461} // verus!