Skip to main content

vstd/
bits.rs

1//! Properties of bitwise operators.
2use super::prelude::*;
3
4verus! {
5
6#[cfg(verus_keep_ghost)]
7use super::arithmetic::power::pow;
8#[cfg(verus_keep_ghost)]
9use super::arithmetic::power2::{
10    pow2,
11    lemma_pow2_unfold,
12    lemma_pow2_adds,
13    lemma_pow2_pos,
14    lemma2_to64,
15    lemma_pow2_strictly_increases,
16};
17#[cfg(verus_keep_ghost)]
18use super::arithmetic::div_mod::{
19    lemma_div_by_multiple,
20    lemma_div_denominator,
21    lemma_div_is_ordered,
22    lemma_mod_breakdown,
23    lemma_mod_multiples_vanish,
24    lemma_remainder_lower,
25};
26#[cfg(verus_keep_ghost)]
27use super::arithmetic::mul::{
28    lemma_mul_inequality,
29    lemma_mul_is_commutative,
30    lemma_mul_is_associative,
31};
32#[cfg(verus_keep_ghost)]
33use super::calc_macro::*;
34
35/// Proof that the max value of each unsigned integer type is 2^BITS - 1.
36pub proof fn lemma_unsigned_int_max_is_pow2_minus_1()
37    ensures
38        (u8::MAX as nat) == pow2(u8::BITS as nat) - 1,
39        (u16::MAX as nat) == pow2(u16::BITS as nat) - 1,
40        (u32::MAX as nat) == pow2(u32::BITS as nat) - 1,
41        (u64::MAX as nat) == pow2(u64::BITS as nat) - 1,
42        (u128::MAX as nat) == pow2(u128::BITS as nat) - 1,
43{
44    assert(0x100 == pow2(8)) by (compute);
45    assert(0x1_0000 == pow2(16)) by (compute);
46    assert(0x1_0000_0000 == pow2(32)) by (compute);
47    assert(0x1_0000_0000_0000_0000 == pow2(64)) by (compute);
48    assert(0x1_0000_0000_0000_0000_0000_0000_0000_0000 == pow2(128)) by (compute);
49}
50
51} // verus!
52// Proofs that shift right is equivalent to division by power of 2.
53macro_rules! lemma_shr_is_div {
54    ($name:ident, $uN:ty) => {
55        #[cfg(verus_keep_ghost)]
56        verus! {
57        #[doc = "Proof that for x and n of type "]
58        #[doc = stringify!($uN)]
59        #[doc = ", shifting x right by n is equivalent to division of x by 2^n."]
60        pub broadcast proof fn $name(x: $uN, shift: $uN)
61            requires
62                0 <= shift < <$uN>::BITS,
63            ensures
64                #[trigger] (x >> shift) == x as nat / pow2(shift as nat),
65            decreases shift,
66        {
67            // Step by 4 to reduce recursion depth (divisor 16 fits in all unsigned types).
68            reveal(pow);
69            if shift == 0 {
70                assert(x >> 0 == x) by (bit_vector);
71                assert(pow2(0) == 1) by (compute_only);
72            } else if shift == 1 {
73                assert(x >> 1 == x / 2) by (bit_vector);
74                assert(pow2(1) == 2) by (compute_only);
75            } else if shift == 2 {
76                assert(x >> 2 == x / 4) by (bit_vector);
77                assert(pow2(2) == 4) by (compute_only);
78            } else if shift == 3 {
79                assert(x >> 3 == x / 8) by (bit_vector);
80                assert(pow2(3) == 8) by (compute_only);
81            } else {
82                assert(x >> shift == (x >> (sub(shift, 4) as $uN)) / 16) by (bit_vector)
83                    requires
84                        4 <= shift < <$uN>::BITS,
85                ;
86                calc!{ (==)
87                    (x >> shift) as nat;
88                        {}
89                    ((x >> (sub(shift, 4) as $uN)) / 16) as nat;
90                        { $name(x, (shift - 4) as $uN); }
91                    (x as nat / pow2((shift - 4) as nat)) / 16;
92                        {
93                            lemma_pow2_pos((shift - 4) as nat);
94                            lemma2_to64();
95                            assert(pow2(4) == 16) by (compute_only);
96                            lemma_div_denominator(x as int, pow2((shift - 4) as nat) as int, 16);
97                        }
98                    x as nat / (pow2((shift - 4) as nat) * pow2(4));
99                        {
100                            lemma_pow2_adds((shift - 4) as nat, 4);
101                        }
102                    x as nat / pow2(shift as nat);
103                }
104            }
105        }
106        }
107    };
108}
109
110lemma_shr_is_div!(lemma_u128_shr_is_div, u128);
111lemma_shr_is_div!(lemma_u64_shr_is_div, u64);
112lemma_shr_is_div!(lemma_u32_shr_is_div, u32);
113lemma_shr_is_div!(lemma_u16_shr_is_div, u16);
114lemma_shr_is_div!(lemma_u8_shr_is_div, u8);
115lemma_shr_is_div!(lemma_usize_shr_is_div, usize);
116
117// Proofs of when a power of 2 fits in an unsigned type.
118macro_rules! lemma_pow2_no_overflow {
119    ($name:ident, $uN:ty) => {
120        #[cfg(verus_keep_ghost)]
121        verus! {
122        #[doc = "Proof that 2^n does not overflow "]
123        #[doc = stringify!($uN)]
124        #[doc = " for an exponent n."]
125        pub broadcast proof fn $name(n: nat)
126            requires
127                0 <= n < <$uN>::BITS,
128            ensures
129                0 < #[trigger] pow2(n) < <$uN>::MAX,
130        {
131            lemma_pow2_pos(n);
132            lemma_unsigned_int_max_is_pow2_minus_1();
133            lemma_pow2_unfold(<$uN>::BITS as nat);
134            if n < <$uN>::BITS - 1 {
135                lemma_pow2_strictly_increases(n, (<$uN>::BITS - 1) as nat);
136            }
137        }
138        }
139    };
140}
141
142lemma_pow2_no_overflow!(lemma_u128_pow2_no_overflow, u128);
143lemma_pow2_no_overflow!(lemma_u64_pow2_no_overflow, u64);
144lemma_pow2_no_overflow!(lemma_u32_pow2_no_overflow, u32);
145lemma_pow2_no_overflow!(lemma_u16_pow2_no_overflow, u16);
146lemma_pow2_no_overflow!(lemma_u8_pow2_no_overflow, u8);
147lemma_pow2_no_overflow!(lemma_usize_pow2_no_overflow, usize);
148
149// Proofs that shift left is equivalent to multiplication by power of 2.
150macro_rules! lemma_shl_is_mul {
151    ($name:ident, $no_overflow:ident, $uN:ty) => {
152        #[cfg(verus_keep_ghost)]
153        verus! {
154        #[doc = "Proof that for x and n of type "]
155        #[doc = stringify!($uN)]
156        #[doc = ", shifting x left by n is equivalent to multiplication of x by 2^n (provided no overflow)."]
157        pub broadcast proof fn $name(x: $uN, shift: $uN)
158            requires
159                0 <= shift < <$uN>::BITS,
160                x * pow2(shift as nat) <= <$uN>::MAX,
161            ensures
162                #[trigger] (x << shift) == x * pow2(shift as nat),
163            decreases shift,
164        {
165            // Step by 4 to reduce the cost of the bit_vector query, as in lemma_shr_is_div.
166            $no_overflow(shift as nat);
167            reveal(pow);
168            if shift == 0 {
169                assert(x << 0 == x) by (bit_vector);
170                assert(pow2(0) == 1) by (compute_only);
171                super::arithmetic::mul::lemma_mul_basics(x as int);
172            } else if shift == 1 {
173                assert(x << 1 == mul(x, 2)) by (bit_vector);
174                assert(pow2(1) == 2) by (compute_only);
175            } else if shift == 2 {
176                assert(x << 2 == mul(x, 4)) by (bit_vector);
177                assert(pow2(2) == 4) by (compute_only);
178            } else if shift == 3 {
179                assert(x << 3 == mul(x, 8)) by (bit_vector);
180                assert(pow2(3) == 8) by (compute_only);
181            } else {
182                assert(x << shift == mul(x << ((sub(shift, 4)) as $uN), 16)) by (bit_vector)
183                    requires
184                        4 <= shift < <$uN>::BITS,
185                ;
186                assert(pow2(4) == 16) by (compute_only);
187                assert((x << (sub(shift, 4) as $uN)) == x * pow2(sub(shift, 4) as nat)) by {
188                    lemma_pow2_strictly_increases((shift - 4) as nat, shift as nat);
189                    lemma_mul_inequality(
190                        pow2((shift - 4) as nat) as int,
191                        pow2(shift as nat) as int,
192                        x as int,
193                    );
194                    lemma_mul_is_commutative(x as int, pow2((shift - 4) as nat) as int);
195                    lemma_mul_is_commutative(x as int, pow2(shift as nat) as int);
196                    $name(x, (shift - 4) as $uN);
197                }
198                calc!{ (==)
199                    ((x << (sub(shift, 4) as $uN)) * 16);
200                        {}
201                    ((x * pow2(sub(shift, 4) as nat)) * 16);
202                        {
203                            lemma_mul_is_associative(x as int, pow2(sub(shift, 4) as nat) as int, 16);
204                        }
205                    x * ((pow2(sub(shift, 4) as nat)) * 16);
206                        {
207                            lemma_pow2_adds((shift - 4) as nat, 4);
208                        }
209                    x * pow2(shift as nat);
210                }
211            }
212        }
213        }
214    };
215}
216
217lemma_shl_is_mul!(lemma_u128_shl_is_mul, lemma_u128_pow2_no_overflow, u128);
218lemma_shl_is_mul!(lemma_u64_shl_is_mul, lemma_u64_pow2_no_overflow, u64);
219lemma_shl_is_mul!(lemma_u32_shl_is_mul, lemma_u32_pow2_no_overflow, u32);
220lemma_shl_is_mul!(lemma_u16_shl_is_mul, lemma_u16_pow2_no_overflow, u16);
221lemma_shl_is_mul!(lemma_u8_shl_is_mul, lemma_u8_pow2_no_overflow, u8);
222lemma_shl_is_mul!(lemma_usize_shl_is_mul, lemma_usize_pow2_no_overflow, usize);
223
224macro_rules! lemma_mul_pow2_le_max_iff_max_shr {
225    ($name:ident, $shr_is_div:ident, $uN:ty) => {
226        #[cfg(verus_keep_ghost)]
227        verus! {
228        #[doc = "Proof that for x, n and max of type "]
229        #[doc = stringify!($uN)]
230        #[doc = ", multiplication of x by 2^n is less than or equal to max if and only if x is less than or equal to shifting max right by n."]
231        pub proof fn $name(x: $uN, shift: $uN, max: $uN)
232        requires
233            0 <= shift < <$uN>::BITS,
234        ensures
235            x * pow2(shift as nat) <= max <==> x <= (max >> shift),
236    {
237        assert(max >> shift == max as nat / pow2(shift as nat)) by {
238            $shr_is_div(max, shift as $uN);
239        };
240
241        lemma_pow2_pos(shift as nat);
242
243        if x * pow2(shift as nat) <= max {
244            assert(x <= (max as nat) / pow2(shift as nat)) by {
245                lemma_div_is_ordered(x as int * pow2(shift as nat) as int, max as int, pow2(shift as nat) as int);
246                lemma_div_by_multiple(x as int, pow2(shift as nat) as int);
247            };
248        }
249        if x <= (max >> shift) {
250            assert(x * pow2(shift as nat) <= max as nat) by {
251                lemma_mul_inequality(x as int, max as int / pow2(shift as nat) as int,  pow2(shift as nat) as int);
252                lemma_remainder_lower(max as int, pow2(shift as nat) as int);
253                lemma_mul_is_commutative(max as int / pow2(shift as nat) as int,  pow2(shift as nat) as int);
254            };
255        }
256    }
257    }
258    };
259}
260
261lemma_mul_pow2_le_max_iff_max_shr!(
262    lemma_u128_mul_pow2_le_max_iff_max_shr,
263    lemma_u128_shr_is_div,
264    u128
265);
266lemma_mul_pow2_le_max_iff_max_shr!(
267    lemma_u64_mul_pow2_le_max_iff_max_shr,
268    lemma_u64_shr_is_div,
269    u64
270);
271lemma_mul_pow2_le_max_iff_max_shr!(
272    lemma_u32_mul_pow2_le_max_iff_max_shr,
273    lemma_u32_shr_is_div,
274    u32
275);
276lemma_mul_pow2_le_max_iff_max_shr!(
277    lemma_u16_mul_pow2_le_max_iff_max_shr,
278    lemma_u16_shr_is_div,
279    u16
280);
281lemma_mul_pow2_le_max_iff_max_shr!(lemma_u8_mul_pow2_le_max_iff_max_shr, lemma_u8_shr_is_div, u8);
282lemma_mul_pow2_le_max_iff_max_shr!(
283    lemma_usize_mul_pow2_le_max_iff_max_shr,
284    lemma_usize_shr_is_div,
285    usize
286);
287
288verus! {
289
290/// Mask with low n bits set.
291pub open spec fn low_bits_mask(n: nat) -> nat {
292    (pow2(n) - 1) as nat
293}
294
295/// Proof relating the n-bit mask to a function of the (n-1)-bit mask.
296pub broadcast proof fn lemma_low_bits_mask_unfold(n: nat)
297    requires
298        n > 0,
299    ensures
300        #[trigger] low_bits_mask(n) == 2 * low_bits_mask((n - 1) as nat) + 1,
301{
302    calc! {
303        (==)
304        low_bits_mask(n); {}
305        (pow2(n) - 1) as nat; {
306            lemma_pow2_unfold(n);
307        }
308        (2 * pow2((n - 1) as nat) - 1) as nat; {}
309        (2 * (pow2((n - 1) as nat) - 1) + 1) as nat; {
310            lemma_pow2_pos((n - 1) as nat);
311        }
312        (2 * low_bits_mask((n - 1) as nat) + 1) as nat;
313    }
314}
315
316/// Proof that low_bits_mask(n) is odd.
317pub broadcast proof fn lemma_low_bits_mask_is_odd(n: nat)
318    requires
319        n > 0,
320    ensures
321        #[trigger] (low_bits_mask(n) % 2) == 1,
322{
323    calc! {
324        (==)
325        low_bits_mask(n) % 2; {
326            lemma_low_bits_mask_unfold(n);
327        }
328        (2 * low_bits_mask((n - 1) as nat) + 1) % 2; {
329            lemma_mod_multiples_vanish(low_bits_mask((n - 1) as nat) as int, 1, 2);
330        }
331        1nat % 2;
332    }
333}
334
335/// Proof that dividing the low n bit mask by 2 gives the low n-1 bit mask.
336pub broadcast proof fn lemma_low_bits_mask_div2(n: nat)
337    requires
338        n > 0,
339    ensures
340        #[trigger] (low_bits_mask(n) / 2) == low_bits_mask((n - 1) as nat),
341{
342    lemma_low_bits_mask_unfold(n);
343}
344
345/// Proof establishing the concrete values of all masks of bit sizes from 0 to
346/// 32, and 64.
347pub proof fn lemma_low_bits_mask_values()
348    ensures
349        low_bits_mask(0) == 0x0,
350        low_bits_mask(1) == 0x1,
351        low_bits_mask(2) == 0x3,
352        low_bits_mask(3) == 0x7,
353        low_bits_mask(4) == 0xf,
354        low_bits_mask(5) == 0x1f,
355        low_bits_mask(6) == 0x3f,
356        low_bits_mask(7) == 0x7f,
357        low_bits_mask(8) == 0xff,
358        low_bits_mask(9) == 0x1ff,
359        low_bits_mask(10) == 0x3ff,
360        low_bits_mask(11) == 0x7ff,
361        low_bits_mask(12) == 0xfff,
362        low_bits_mask(13) == 0x1fff,
363        low_bits_mask(14) == 0x3fff,
364        low_bits_mask(15) == 0x7fff,
365        low_bits_mask(16) == 0xffff,
366        low_bits_mask(17) == 0x1ffff,
367        low_bits_mask(18) == 0x3ffff,
368        low_bits_mask(19) == 0x7ffff,
369        low_bits_mask(20) == 0xfffff,
370        low_bits_mask(21) == 0x1fffff,
371        low_bits_mask(22) == 0x3fffff,
372        low_bits_mask(23) == 0x7fffff,
373        low_bits_mask(24) == 0xffffff,
374        low_bits_mask(25) == 0x1ffffff,
375        low_bits_mask(26) == 0x3ffffff,
376        low_bits_mask(27) == 0x7ffffff,
377        low_bits_mask(28) == 0xfffffff,
378        low_bits_mask(29) == 0x1fffffff,
379        low_bits_mask(30) == 0x3fffffff,
380        low_bits_mask(31) == 0x7fffffff,
381        low_bits_mask(32) == 0xffffffff,
382        low_bits_mask(64) == 0xffffffffffffffff,
383{
384    #[verusfmt::skip]
385    assert(
386        low_bits_mask(0) == 0x0 &&
387        low_bits_mask(1) == 0x1 &&
388        low_bits_mask(2) == 0x3 &&
389        low_bits_mask(3) == 0x7 &&
390        low_bits_mask(4) == 0xf &&
391        low_bits_mask(5) == 0x1f &&
392        low_bits_mask(6) == 0x3f &&
393        low_bits_mask(7) == 0x7f &&
394        low_bits_mask(8) == 0xff &&
395        low_bits_mask(9) == 0x1ff &&
396        low_bits_mask(10) == 0x3ff &&
397        low_bits_mask(11) == 0x7ff &&
398        low_bits_mask(12) == 0xfff &&
399        low_bits_mask(13) == 0x1fff &&
400        low_bits_mask(14) == 0x3fff &&
401        low_bits_mask(15) == 0x7fff &&
402        low_bits_mask(16) == 0xffff &&
403        low_bits_mask(17) == 0x1ffff &&
404        low_bits_mask(18) == 0x3ffff &&
405        low_bits_mask(19) == 0x7ffff &&
406        low_bits_mask(20) == 0xfffff &&
407        low_bits_mask(21) == 0x1fffff &&
408        low_bits_mask(22) == 0x3fffff &&
409        low_bits_mask(23) == 0x7fffff &&
410        low_bits_mask(24) == 0xffffff &&
411        low_bits_mask(25) == 0x1ffffff &&
412        low_bits_mask(26) == 0x3ffffff &&
413        low_bits_mask(27) == 0x7ffffff &&
414        low_bits_mask(28) == 0xfffffff &&
415        low_bits_mask(29) == 0x1fffffff &&
416        low_bits_mask(30) == 0x3fffffff &&
417        low_bits_mask(31) == 0x7fffffff &&
418        low_bits_mask(32) == 0xffffffff &&
419        low_bits_mask(64) == 0xffffffffffffffff
420    ) by (compute_only);
421}
422
423} // verus!
424// Proofs that and with mask is equivalent to modulo with power of two.
425macro_rules! lemma_low_bits_mask_is_mod {
426    ($name:ident, $and_split_low_bit:ident, $no_overflow:ident, $uN:ty) => {
427        #[cfg(verus_keep_ghost)]
428        verus! {
429        #[doc = "Proof that for natural n and x of type "]
430        #[doc = stringify!($uN)]
431        #[doc = ", and with the low n-bit mask is equivalent to modulo 2^n."]
432        pub broadcast proof fn $name(x: $uN, n: nat)
433            requires
434                n < <$uN>::BITS,
435            ensures
436                #[trigger] (x & (low_bits_mask(n) as $uN)) == x % (pow2(n) as $uN),
437            decreases n,
438        {
439            // Bounds.
440            $no_overflow(n);
441            lemma_pow2_pos(n);
442
443            // Inductive proof.
444            if n == 0 {
445                assert(low_bits_mask(0) == 0) by (compute_only);
446                assert(x & 0 == 0) by (bit_vector);
447                assert(pow2(0) == 1) by (compute_only);
448                assert(x % 1 == 0);
449            } else {
450                lemma_pow2_unfold(n);
451                assert((x % 2) == ((x % 2) & 1)) by (bit_vector);
452                calc!{ (==)
453                    x % (pow2(n) as $uN);
454                        {}
455                    x % ((2 * pow2((n-1) as nat)) as $uN);
456                        {
457                            lemma_pow2_pos((n-1) as nat);
458                            lemma_mod_breakdown(x as int, 2, pow2((n-1) as nat) as int);
459                        }
460                    add(mul(2, (x / 2) % (pow2((n-1) as nat) as $uN)), x % 2);
461                        {
462                            $name(x/2, (n-1) as nat);
463                        }
464                    add(mul(2, (x / 2) & (low_bits_mask((n-1) as nat) as $uN)), x % 2);
465                        {
466                            lemma_low_bits_mask_div2(n);
467                        }
468                    add(mul(2, (x / 2) & (low_bits_mask(n) as $uN / 2)), x % 2);
469                        {
470                            lemma_low_bits_mask_is_odd(n);
471                        }
472                    add(mul(2, (x / 2) & (low_bits_mask(n) as $uN / 2)), (x % 2) & ((low_bits_mask(n) as $uN) % 2));
473                        {
474                            $and_split_low_bit(x as $uN, low_bits_mask(n) as $uN);
475                        }
476                    x & (low_bits_mask(n) as $uN);
477                }
478            }
479        }
480
481        // Helper lemma breaking a bitwise-and operation into the low bit and the rest.
482        proof fn $and_split_low_bit(x: $uN, m: $uN)
483            by (bit_vector)
484            ensures
485                x & m == add(mul(((x / 2) & (m / 2)), 2), (x % 2) & (m % 2)),
486        {
487        }
488        }
489    };
490}
491
492lemma_low_bits_mask_is_mod!(
493    lemma_u128_low_bits_mask_is_mod,
494    lemma_u128_and_split_low_bit,
495    lemma_u128_pow2_no_overflow,
496    u128
497);
498lemma_low_bits_mask_is_mod!(
499    lemma_u64_low_bits_mask_is_mod,
500    lemma_u64_and_split_low_bit,
501    lemma_u64_pow2_no_overflow,
502    u64
503);
504lemma_low_bits_mask_is_mod!(
505    lemma_u32_low_bits_mask_is_mod,
506    lemma_u32_and_split_low_bit,
507    lemma_u32_pow2_no_overflow,
508    u32
509);
510lemma_low_bits_mask_is_mod!(
511    lemma_u16_low_bits_mask_is_mod,
512    lemma_u16_and_split_low_bit,
513    lemma_u16_pow2_no_overflow,
514    u16
515);
516lemma_low_bits_mask_is_mod!(
517    lemma_u8_low_bits_mask_is_mod,
518    lemma_u8_and_split_low_bit,
519    lemma_u8_pow2_no_overflow,
520    u8
521);
522lemma_low_bits_mask_is_mod!(
523    lemma_usize_low_bits_mask_is_mod,
524    lemma_usize_and_split_low_bit,
525    lemma_usize_pow2_no_overflow,
526    usize
527);