1use vstd::arithmetic::div_mod::*;
2use vstd::arithmetic::mul::*;
3use vstd::{pervasive::trigger, prelude::*};
4
5verus! {
6
7pub open spec fn nat_align_down(x: nat, align: nat) -> nat
8 recommends
9 align > 0,
10{
11 (x - x % align) as nat
12}
13
14pub open spec fn nat_align_up(x: nat, align: nat) -> nat
15 recommends
16 align > 0,
17{
18 if x % align == 0 {
19 x
20 } else {
21 nat_align_down(x, align) + align
22 }
23}
24
25pub broadcast proof fn lemma_nat_align_up_sound(x: nat, align: nat)
26 requires
27 align > 0,
28 ensures
29 #[trigger] nat_align_up(x, align) >= x,
30 nat_align_up(x, align) % align == 0,
31 forall|n: nat| n >= x && #[trigger] (n % align) == 0 ==> n >= nat_align_up(x, align),
32 nat_align_up(x, align) - x < align,
33{
34 if x % align == 0 {
35 } else {
36 let down = nat_align_down(x, align);
37 lemma_fundamental_div_mod(x as int, align as int);
38 lemma_mul_is_commutative(align as int, x as int / align as int);
39 lemma_mod_multiples_basic(x as int / align as int, align as int);
40 lemma_mod_add_multiples_vanish(down as int, align as int);
41 }
42
43 assert forall|n: nat| n >= x && (#[trigger] (n % align)) == 0 implies n >= nat_align_up(
44 x,
45 align,
46 ) by {
47 if x % align == 0 {
48 } else {
49 lemma_mul_is_commutative(align as int, x as int / align as int);
50
51 lemma_fundamental_div_mod(n as int, align as int);
52 if n < nat_align_up(x, align) {
53 let q_n = n as int / align as int;
54 let q_x = x as int / align as int;
55 lemma_mul_is_distributive_add(align as int, q_x, 1);
56 if q_n >= q_x + 1 {
57 lemma_mul_inequality(q_x + 1, q_n, align as int);
58 lemma_mul_is_commutative(align as int, q_n);
59 }
60 lemma_mul_inequality(q_n, q_x, align as int);
61 lemma_mul_is_commutative(align as int, q_n);
62 }
63 }
64 }
65}
66
67pub broadcast proof fn lemma_nat_align_down_sound(x: nat, align: nat)
68 requires
69 align > 0,
70 ensures
71 #[trigger] nat_align_down(x, align) <= x,
72 nat_align_down(x, align) % align == 0,
73 forall|n: nat| n <= x && #[trigger] (n % align) == 0 ==> n <= nat_align_down(x, align),
74 x - nat_align_down(x, align) < align,
75{
76 lemma_fundamental_div_mod(x as int, align as int);
77 let q_x = x as int / align as int;
78 lemma_mod_multiples_basic(q_x, align as int);
79 lemma_mul_is_commutative(align as int, q_x);
80
81 assert forall|n: nat| n <= x && #[trigger] (n % align) == 0 implies n <= nat_align_down(
82 x,
83 align,
84 ) by {
85 if n > nat_align_down(x, align) && n % align == 0 {
86 lemma_fundamental_div_mod(n as int, align as int);
87 let q_n = n as int / align as int;
88 if q_n <= q_x {
89 lemma_mul_inequality(q_n, q_x, align as int);
90 lemma_mul_is_commutative(align as int, q_n);
91 } else {
92 lemma_mul_inequality(q_x + 1, q_n, align as int);
93 lemma_mul_is_commutative(align as int, q_n);
94 lemma_mul_is_distributive_add(align as int, q_x, 1);
95 }
96 }
97 }
98}
99
100pub proof fn lemma_nat_align_down_monotone(x: nat, k1: nat, k2: nat)
104 requires
105 k1 > 0,
106 k2 > 0,
107 k2 % k1 == 0,
108 ensures
109 nat_align_down(x, k2) <= nat_align_down(x, k1),
110 nat_align_down(x, k2) % k1 == 0,
111{
112 lemma_nat_align_down_sound(x, k1);
113 lemma_nat_align_down_sound(x, k2);
114 let a = nat_align_down(x, k2) as int;
117 let q = a / k2 as int;
118 let r = k2 as int / k1 as int;
119 lemma_fundamental_div_mod(a, k2 as int);
120 assert(a == q * k2 as int);
121 lemma_fundamental_div_mod(k2 as int, k1 as int);
122 assert(k2 as int == r * k1 as int);
123 assert(a == q * (r * k1 as int));
125 lemma_mul_is_associative(q, r, k1 as int);
127 assert(a == (q * r) * k1 as int);
128 lemma_mod_multiples_basic(q * r, k1 as int);
130 assert(nat_align_down(x, k2) % k1 == 0);
131}
132
133pub proof fn lemma_nat_align_down_within_block(x: nat, k1: nat, k2: nat)
137 requires
138 k1 > 0,
139 k2 > 0,
140 k2 % k1 == 0,
141 ensures
142 nat_align_down(x, k1) + k1 <= nat_align_down(x, k2) + k2,
143{
144 lemma_nat_align_down_sound(x, k1);
145 lemma_nat_align_down_sound(x, k2);
146 lemma_nat_align_down_monotone(x, k1, k2);
147 let a1 = nat_align_down(x, k1) as int;
154 let a2 = nat_align_down(x, k2) as int;
155 let diff = a1 - a2;
156 assert(diff >= 0);
157 assert(diff < k2 as int);
158 let q1 = a1 / k1 as int;
160 let q2 = a2 / k1 as int;
161 lemma_fundamental_div_mod(a1, k1 as int);
162 lemma_fundamental_div_mod(a2, k1 as int);
163 lemma_nat_align_down_monotone(x, k1, k2);
165 assert(a1 == q1 * k1 as int);
167 assert(a2 == q2 * k1 as int);
168 lemma_mul_is_distributive_sub(k1 as int, q1, q2);
169 lemma_mul_is_commutative(k1 as int, q1);
170 lemma_mul_is_commutative(k1 as int, q2);
171 lemma_mul_is_commutative(k1 as int, q1 - q2);
172 assert(diff == (q1 - q2) * k1 as int);
173 let qk = k2 as int / k1 as int;
175 lemma_fundamental_div_mod(k2 as int, k1 as int);
176 assert(k2 as int == qk * k1 as int);
177 lemma_mul_is_commutative(k1 as int, qk);
178 if q1 - q2 >= qk {
182 lemma_mul_inequality(qk, q1 - q2, k1 as int);
184 lemma_mul_is_commutative(k1 as int, q1 - q2);
185 } else {
186 lemma_mul_inequality(q1 - q2 + 1, qk, k1 as int);
189 lemma_mul_is_commutative(k1 as int, q1 - q2 + 1);
190 lemma_mul_is_distributive_add(k1 as int, q1 - q2, 1 as int);
191 lemma_mul_is_commutative(k1 as int, q1 - q2);
192 }
193}
194
195broadcast group group_arithmetic_lemmas {
196 lemma_nat_align_up_sound,
197 lemma_nat_align_down_sound,
198}
199
200}