vstd_extra/temporal_logic/
rules.rs

1// Copyright 2022 VMware, Inc.
2// SPDX-License-Identifier: MIT
3#![allow(unused_imports)]
4use super::defs::*;
5use vstd::map_lib::*;
6use vstd::prelude::*;
7
8// Internal lemmas about executions and temporal predicates.
9verus! {
10
11broadcast proof fn lemma_execution_sufix_0<T>(ex: Execution<T>)
12    ensures
13        #[trigger] ex.suffix(0) == ex,
14{
15}
16
17broadcast proof fn lemma_execution_sufix_sufix<T>(ex: Execution<T>, i: nat, j: nat)
18    ensures
19        #[trigger] ex.suffix(i).suffix(j) == ex.suffix(i + j),
20{
21}
22
23broadcast group group_execution_suffix_lemmas {
24    lemma_execution_sufix_0,
25    lemma_execution_sufix_sufix,
26}
27
28} // verus!
29verus! {
30
31broadcast proof fn implies_apply<T>(ex: Execution<T>, p: TempPred<T>, q: TempPred<T>)
32    requires
33        #[trigger] p.implies(q).satisfied_by(ex),
34        p.satisfied_by(ex),
35    ensures
36        q.satisfied_by(ex),
37{
38}
39
40proof fn implies_contraposition_apply<T>(ex: Execution<T>, p: TempPred<T>, q: TempPred<T>)
41    requires
42        p.implies(q).satisfied_by(ex),
43        not(q).satisfied_by(ex),
44    ensures
45        not(p).satisfied_by(ex),
46{
47}
48
49pub proof fn temp_pred_equality<T>(p: TempPred<T>, q: TempPred<T>)
50    requires
51        p.entails(q),
52        q.entails(p),
53    ensures
54        p == q,
55{
56    assert forall|ex: Execution<T>| #[trigger] (p.pred)(ex) == (q.pred)(ex) by {
57        if (p.pred)(ex) {
58            implies_apply::<T>(ex, p, q);
59        } else {
60            implies_contraposition_apply::<T>(ex, q, p);
61        }
62    };
63}
64
65proof fn later_unfold<T>(ex: Execution<T>, p: TempPred<T>)
66    requires
67        later(p).satisfied_by(ex),
68    ensures
69        p.satisfied_by(ex.suffix(1)),
70{
71}
72
73broadcast proof fn always_unfold<T>(ex: Execution<T>, p: TempPred<T>)
74    requires
75        #[trigger] always(p).satisfied_by(ex),
76    ensures
77        forall|i: nat| p.satisfied_by(#[trigger] ex.suffix(i)),
78{
79}
80
81proof fn eventually_unfold<T>(ex: Execution<T>, p: TempPred<T>)
82    requires
83        eventually(p).satisfied_by(ex),
84    ensures
85        exists|i: nat| p.satisfied_by(#[trigger] ex.suffix(i)),
86{
87}
88
89proof fn not_eventually_unfold<T>(ex: Execution<T>, p: TempPred<T>)
90    requires
91        not(eventually(p)).satisfied_by(ex),
92    ensures
93        forall|i| !p.satisfied_by(#[trigger] ex.suffix(i)),
94{
95}
96
97proof fn stable_unfold<T>(ex: Execution<T>, p: TempPred<T>)
98    requires
99        stable(p).satisfied_by(ex),
100    ensures
101        p.satisfied_by(ex) ==> forall|i| p.satisfied_by(#[trigger] ex.suffix(i)),
102{
103}
104
105proof fn leads_to_unfold<T>(ex: Execution<T>, p: TempPred<T>, q: TempPred<T>)
106    requires
107        p.leads_to(q).satisfied_by(ex),
108    ensures
109        forall|i: nat| p.implies(eventually(q)).satisfied_by(#[trigger] ex.suffix(i)),
110{
111}
112
113proof fn weak_fairness_unfold<T>(ex: Execution<T>, p: ActionPred<T>)
114    requires
115        weak_fairness(p).satisfied_by(ex),
116    ensures
117        forall|i|
118            always(lift_state(enabled(p))).implies(eventually(lift_action(p))).satisfied_by(
119                #[trigger] ex.suffix(i),
120            ),
121{
122}
123
124proof fn always_lift_state_unfold<T>(ex: Execution<T>, p: StatePred<T>)
125    requires
126        always(lift_state(p)).satisfied_by(ex),
127    ensures
128        forall|i| p.apply(#[trigger] ex.suffix(i).head()),
129{
130    broadcast use always_unfold;
131
132}
133
134proof fn always_lift_action_unfold<T>(ex: Execution<T>, p: ActionPred<T>)
135    requires
136        always(lift_action(p)).satisfied_by(ex),
137    ensures
138        forall|i| p.apply(#[trigger] ex.suffix(i).head(), ex.suffix(i).head_next()),
139{
140    broadcast use always_unfold;
141
142}
143
144broadcast proof fn tla_forall_unfold<T, A>(
145    ex: Execution<T>,
146    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
147)
148    requires
149        #[trigger] tla_forall(a_to_temp_pred).satisfied_by(ex),
150    ensures
151        forall|a| #[trigger] a_to_temp_pred(a).satisfied_by(ex),
152{
153}
154
155broadcast proof fn tla_exists_unfold<T, A>(
156    ex: Execution<T>,
157    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
158)
159    requires
160        #[trigger] tla_exists(a_to_temp_pred).satisfied_by(ex),
161    ensures
162        exists|a| #[trigger] a_to_temp_pred(a).satisfied_by(ex),
163{
164}
165
166proof fn tla_exists_proved_by_witness<T, A>(
167    ex: Execution<T>,
168    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
169    witness_a: A,
170)
171    requires
172        a_to_temp_pred(witness_a).satisfied_by(ex),
173    ensures
174        tla_exists(a_to_temp_pred).satisfied_by(ex),
175{
176}
177
178spec fn tla_exists_choose_witness<T, A>(
179    ex: Execution<T>,
180    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
181) -> A
182    recommends
183        exists|a| #[trigger] a_to_temp_pred(a).satisfied_by(ex),
184{
185    let witness = choose|a| #[trigger] a_to_temp_pred(a).satisfied_by(ex);
186    witness
187}
188
189proof fn implies_apply_with_always<T>(ex: Execution<T>, p: TempPred<T>, q: TempPred<T>)
190    requires
191        always(p.implies(q)).satisfied_by(ex),
192        always(p).satisfied_by(ex),
193    ensures
194        always(q).satisfied_by(ex),
195{
196    broadcast use always_unfold;
197
198}
199
200proof fn entails_apply<T>(ex: Execution<T>, p: TempPred<T>, q: TempPred<T>)
201    requires
202        p.entails(q),
203        p.satisfied_by(ex),
204    ensures
205        q.satisfied_by(ex),
206{
207    implies_apply::<T>(ex, p, q);
208}
209
210proof fn not_proved_by_contraposition<T>(ex: Execution<T>, p: TempPred<T>, q: TempPred<T>)
211    requires
212        p.implies(q).satisfied_by(ex),
213        not(q).satisfied_by(ex),
214    ensures
215        not(p).satisfied_by(ex),
216{
217}
218
219proof fn not_eventually_by_always_not<T>(ex: Execution<T>, p: TempPred<T>)
220    requires
221        always(not(p)).satisfied_by(ex),
222    ensures
223        not(eventually(p)).satisfied_by(ex),
224{
225    broadcast use always_unfold;
226
227}
228
229broadcast proof fn always_propagate_forwards<T>(ex: Execution<T>, p: TempPred<T>, i: nat)
230    requires
231        always(p).satisfied_by(ex),
232    ensures
233        #[trigger] always(p).satisfied_by(ex.suffix(i)),
234{
235    broadcast use {always_unfold, group_execution_suffix_lemmas};
236
237}
238
239proof fn always_double<T>(ex: Execution<T>, p: TempPred<T>)
240    requires
241        always(p).satisfied_by(ex),
242    ensures
243        always(always(p)).satisfied_by(ex),
244{
245    broadcast use {always_unfold, always_propagate_forwards};
246
247}
248
249proof fn always_to_current<T>(ex: Execution<T>, p: TempPred<T>)
250    requires
251        always(p).satisfied_by(ex),
252    ensures
253        p.satisfied_by(ex),
254{
255    assert(ex == ex.suffix(0));
256}
257
258proof fn always_to_future<T>(ex: Execution<T>, p: TempPred<T>, i: nat)
259    requires
260        always(p).satisfied_by(ex),
261    ensures
262        p.satisfied_by(ex.suffix(i)),
263{
264    broadcast use {always_unfold, always_propagate_forwards};
265
266}
267
268proof fn eventually_propagate_backwards<T>(ex: Execution<T>, p: TempPred<T>, i: nat)
269    requires
270        eventually(p).satisfied_by(ex.suffix(i)),
271    ensures
272        eventually(p).satisfied_by(ex),
273{
274    broadcast use group_execution_suffix_lemmas;
275
276}
277
278proof fn eventually_proved_by_witness<T>(ex: Execution<T>, p: TempPred<T>, witness_idx: nat)
279    requires
280        p.satisfied_by(ex.suffix(witness_idx)),
281    ensures
282        eventually(p).satisfied_by(ex),
283{
284}
285
286spec fn eventually_choose_witness<T>(ex: Execution<T>, p: TempPred<T>) -> nat
287    recommends
288        exists|i| p.satisfied_by(#[trigger] ex.suffix(i)),
289{
290    let witness = choose|i| p.satisfied_by(#[trigger] ex.suffix(i));
291    witness
292}
293
294proof fn valid_p_implies_always_p<T>(p: TempPred<T>)
295    requires
296        valid(p),
297    ensures
298        valid(always(p)),
299{
300}
301
302proof fn always_distributed_by_and<T>(p: TempPred<T>, q: TempPred<T>)
303    ensures
304        valid(always(p.and(q)).implies(always(p).and(always(q)))),
305{
306    broadcast use always_unfold;
307
308}
309
310proof fn p_and_always_p_equals_always_p<T>(p: TempPred<T>)
311    ensures
312        p.and(always(p)) == always(p),
313{
314    assert forall|ex| #[trigger] always(p).satisfied_by(ex) implies p.and(always(p)).satisfied_by(
315        ex,
316    ) by {
317        always_to_current::<T>(ex, p);
318    };
319    temp_pred_equality::<T>(p.and(always(p)), always(p));
320}
321
322proof fn a_to_temp_pred_equality<T, A>(p: spec_fn(A) -> TempPred<T>, q: spec_fn(A) -> TempPred<T>)
323    requires
324        forall|a: A| #[trigger] p(a).entails(q(a)) && q(a).entails(p(a)),
325    ensures
326        p == q,
327{
328    assert forall|a: A| #[trigger] p(a) == q(a) by {
329        temp_pred_equality::<T>(p(a), q(a));
330    };
331}
332
333proof fn tla_forall_or_equality<T, A>(a_to_temp_pred: spec_fn(A) -> TempPred<T>, q: TempPred<T>)
334    ensures
335        tla_forall(|a: A| a_to_temp_pred(a).or(q)) == tla_forall(a_to_temp_pred).or(q),
336{
337    broadcast use tla_forall_unfold;
338
339    let a_to_temp_pred_or_q = |a: A| a_to_temp_pred(a).or(q);
340    assert forall|ex| #[trigger] tla_forall(a_to_temp_pred_or_q).satisfied_by(ex) implies (
341    tla_forall(a_to_temp_pred).or(q)).satisfied_by(ex) by {
342        if !q.satisfied_by(ex) {
343            assert forall|a| #[trigger] a_to_temp_pred(a).satisfied_by(ex) by {
344                assert(a_to_temp_pred_or_q(a).satisfied_by(ex));
345            };
346        }
347    };
348    temp_pred_equality::<T>(
349        tla_forall(|a: A| a_to_temp_pred(a).or(q)),
350        tla_forall(a_to_temp_pred).or(q),
351    );
352}
353
354proof fn tla_exists_and_equality<T, A>(a_to_temp_pred: spec_fn(A) -> TempPred<T>, q: TempPred<T>)
355    ensures
356        tla_exists(|a: A| a_to_temp_pred(a).and(q)) == tla_exists(a_to_temp_pred).and(q),
357{
358    let a_to_temp_pred_and_q = |a: A| a_to_temp_pred(a).and(q);
359    assert forall|ex| #[trigger]
360        (tla_exists(a_to_temp_pred).and(q)).satisfied_by(ex) implies tla_exists(
361        a_to_temp_pred_and_q,
362    ).satisfied_by(ex) by {
363        let witness_a = tla_exists_choose_witness::<T, A>(ex, a_to_temp_pred);
364        tla_exists_proved_by_witness::<T, A>(ex, a_to_temp_pred_and_q, witness_a);
365    };
366    temp_pred_equality::<T>(
367        tla_exists(|a: A| a_to_temp_pred(a).and(q)),
368        tla_exists(a_to_temp_pred).and(q),
369    );
370}
371
372proof fn tla_exists_or_equality<T, A>(a_to_temp_pred: spec_fn(A) -> TempPred<T>, q: TempPred<T>)
373    ensures
374        tla_exists(|a: A| a_to_temp_pred(a).or(q)) == tla_exists(a_to_temp_pred).or(q),
375{
376    let a_to_temp_pred_or_q = |a: A| a_to_temp_pred(a).or(q);
377    assert forall|ex| #[trigger]
378        (tla_exists(a_to_temp_pred).or(q)).satisfied_by(ex) implies tla_exists(
379        a_to_temp_pred_or_q,
380    ).satisfied_by(ex) by {
381        if !q.satisfied_by(ex) {
382            let witness_a = tla_exists_choose_witness::<T, A>(ex, a_to_temp_pred);
383            tla_exists_proved_by_witness::<T, A>(ex, a_to_temp_pred_or_q, witness_a);
384        } else {
385            assert(a_to_temp_pred_or_q(arbitrary()).satisfied_by(ex));
386        }
387    };
388    temp_pred_equality::<T>(
389        tla_exists(|a: A| a_to_temp_pred(a).or(q)),
390        tla_exists(a_to_temp_pred).or(q),
391    );
392}
393
394proof fn tla_forall_implies_equality1<T, A>(
395    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
396    q: TempPred<T>,
397)
398    ensures
399        tla_forall(|a: A| a_to_temp_pred(a).implies(q)) == tla_exists(a_to_temp_pred).implies(q),
400{
401    let a_to_not_p = |a: A| not(a_to_temp_pred(a));
402    a_to_temp_pred_equality::<T, A>(
403        |a: A| a_to_temp_pred(a).implies(q),
404        |a: A| a_to_not_p(a).or(q),
405    );
406    temp_pred_equality::<T>(
407        tla_forall(|a: A| a_to_temp_pred(a).implies(q)),
408        tla_forall(|a: A| a_to_not_p(a).or(q)),
409    );
410    tla_forall_or_equality::<T, A>(a_to_not_p, q);
411    tla_forall_not_equality::<T, A>(a_to_temp_pred);
412    temp_pred_equality::<T>(
413        not(tla_exists(a_to_temp_pred)).or(q),
414        tla_exists(a_to_temp_pred).implies(q),
415    );
416}
417
418proof fn tla_forall_implies_equality2<T, A>(
419    p: TempPred<T>,
420    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
421)
422    ensures
423        tla_forall(|a: A| p.implies(a_to_temp_pred(a))) == p.implies(tla_forall(a_to_temp_pred)),
424{
425    a_to_temp_pred_equality::<T, A>(
426        |a: A| p.implies(a_to_temp_pred(a)),
427        |a: A| a_to_temp_pred(a).or(not(p)),
428    );
429    temp_pred_equality::<T>(
430        tla_forall(|a: A| p.implies(a_to_temp_pred(a))),
431        tla_forall(|a: A| a_to_temp_pred(a).or(not(p))),
432    );
433    tla_forall_or_equality::<T, A>(a_to_temp_pred, not(p));
434    temp_pred_equality::<T>(
435        tla_forall(a_to_temp_pred).or(not(p)),
436        p.implies(tla_forall(a_to_temp_pred)),
437    );
438}
439
440proof fn tla_exists_implies_equality1<T, A>(
441    p: TempPred<T>,
442    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
443)
444    ensures
445        tla_exists(|a: A| p.implies(a_to_temp_pred(a))) == p.implies(tla_exists(a_to_temp_pred)),
446{
447    a_to_temp_pred_equality::<T, A>(
448        |a: A| p.implies(a_to_temp_pred(a)),
449        |a: A| a_to_temp_pred(a).or(not(p)),
450    );
451    temp_pred_equality::<T>(
452        tla_exists(|a: A| p.implies(a_to_temp_pred(a))),
453        tla_exists(|a: A| a_to_temp_pred(a).or(not(p))),
454    );
455    tla_exists_or_equality::<T, A>(a_to_temp_pred, not(p));
456    temp_pred_equality::<T>(
457        tla_exists(a_to_temp_pred).or(not(p)),
458        p.implies(tla_exists(a_to_temp_pred)),
459    );
460}
461
462proof fn tla_forall_leads_to_equality1<T, A>(
463    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
464    q: TempPred<T>,
465)
466    ensures
467        tla_forall(|a: A| a_to_temp_pred(a).leads_to(q)) == tla_exists(a_to_temp_pred).leads_to(q),
468{
469    tla_forall_always_equality_variant::<T, A>(
470        |a: A| a_to_temp_pred(a).leads_to(q),
471        |a: A| a_to_temp_pred(a).implies(eventually(q)),
472    );
473    tla_forall_implies_equality1::<T, A>(a_to_temp_pred, eventually(q));
474}
475
476proof fn tla_forall_always_implies_equality2<T, A>(
477    p: TempPred<T>,
478    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
479)
480    ensures
481        tla_forall(|a: A| always(p.implies(a_to_temp_pred(a)))) == always(
482            p.implies(tla_forall(a_to_temp_pred)),
483        ),
484{
485    tla_forall_always_equality_variant::<T, A>(
486        |a: A| always(p.implies(a_to_temp_pred(a))),
487        |a: A| p.implies(a_to_temp_pred(a)),
488    );
489    tla_forall_implies_equality2::<T, A>(p, a_to_temp_pred);
490}
491
492proof fn tla_forall_not_equality<T, A>(a_to_temp_pred: spec_fn(A) -> TempPred<T>)
493    ensures
494        tla_forall(|a: A| not(a_to_temp_pred(a))) == not(tla_exists(a_to_temp_pred)),
495{
496    let a_to_not_p = |a: A| not(a_to_temp_pred(a));
497    assert forall|ex| #[trigger] tla_forall(a_to_not_p).satisfied_by(ex) implies not(
498        tla_exists(a_to_temp_pred),
499    ).satisfied_by(ex) by {
500        assert forall|a| !#[trigger] a_to_temp_pred(a).satisfied_by(ex) by {
501            assert(a_to_not_p(a).satisfied_by(ex));
502        };
503    };
504    temp_pred_equality::<T>(
505        tla_forall(|a: A| not(a_to_temp_pred(a))),
506        not(tla_exists(a_to_temp_pred)),
507    );
508}
509
510proof fn tla_forall_and_equality<T, A>(a_to_temp_pred: spec_fn(A) -> TempPred<T>, q: TempPred<T>)
511    ensures
512        tla_forall(|a: A| a_to_temp_pred(a).and(q)) == tla_forall(a_to_temp_pred).and(q),
513{
514    broadcast use tla_forall_unfold;
515
516    let a_to_temp_pred_and_q = |a: A| a_to_temp_pred(a).and(q);
517    assert forall|ex| #[trigger] tla_forall(a_to_temp_pred_and_q).satisfied_by(ex) implies (
518    tla_forall(a_to_temp_pred).and(q)).satisfied_by(ex) by {
519        assert forall|a| #[trigger] a_to_temp_pred(a).satisfied_by(ex) by {
520            assert(a_to_temp_pred_and_q(a).satisfied_by(ex));
521        };
522        assert(a_to_temp_pred_and_q(arbitrary()).satisfied_by(ex));
523    };
524    temp_pred_equality::<T>(
525        tla_forall(|a: A| a_to_temp_pred(a).and(q)),
526        tla_forall(a_to_temp_pred).and(q),
527    );
528}
529
530proof fn init_invariant_rec<T>(
531    ex: Execution<T>,
532    init: StatePred<T>,
533    next: ActionPred<T>,
534    inv: StatePred<T>,
535    i: nat,
536)
537    requires
538        lift_state(init).satisfied_by(ex),
539        always(lift_action(next)).satisfied_by(ex),
540        lift_state(init).implies(lift_state(inv)).satisfied_by(ex),
541        forall|idx: nat|
542            inv.apply(#[trigger] ex.suffix(idx).head()) && next.apply(
543                ex.suffix(idx).head(),
544                ex.suffix(idx).head_next(),
545            ) ==> inv.apply(ex.suffix(idx).head_next()),
546    ensures
547        inv.apply(ex.suffix(i).head()),
548    decreases i,
549{
550    broadcast use {always_unfold, group_execution_suffix_lemmas};
551
552    if i != 0 {
553        init_invariant_rec::<T>(ex, init, next, inv, (i - 1) as nat);
554    }
555}
556
557proof fn implies_new_invariant_rec<T>(
558    ex: Execution<T>,
559    init: StatePred<T>,
560    next: ActionPred<T>,
561    inv: StatePred<T>,
562    proved_inv: StatePred<T>,
563    i: nat,
564)
565    requires
566        lift_state(init).satisfied_by(ex),
567        always(lift_action(next)).satisfied_by(ex),
568        always(lift_state(proved_inv)).satisfied_by(ex),
569        lift_state(init).implies(lift_state(inv)).satisfied_by(ex),
570        forall|idx: nat|
571            inv.apply(#[trigger] ex.suffix(idx).head()) && proved_inv.apply(
572                #[trigger] ex.suffix(idx).head(),
573            ) && next.apply(ex.suffix(idx).head(), ex.suffix(idx).head_next()) ==> inv.apply(
574                ex.suffix(idx).head_next(),
575            ),
576    ensures
577        inv.apply(ex.suffix(i).head()),
578    decreases i,
579{
580    broadcast use {always_unfold, group_execution_suffix_lemmas};
581
582    if i != 0 {
583        implies_new_invariant_rec(ex, init, next, inv, proved_inv, (i - 1) as nat);
584    }
585}
586
587proof fn always_p_or_eventually_q_rec<T>(
588    ex: Execution<T>,
589    next: TempPred<T>,
590    p: TempPred<T>,
591    q: TempPred<T>,
592    i: nat,
593)
594    requires
595        always(p.and(next).implies(later(p).or(later(q)))).satisfied_by(ex),
596        always(next).satisfied_by(ex),
597        always(not(q)).satisfied_by(ex),
598        p.satisfied_by(ex),
599    ensures
600        p.satisfied_by(ex.suffix(i)),
601    decreases i,
602{
603    broadcast use {always_unfold, group_execution_suffix_lemmas};
604
605    if i != 0 {
606        always_p_or_eventually_q_rec::<T>(ex, next, p, q, (i - 1) as nat);
607    }
608}
609
610proof fn always_p_or_eventually_q<T>(
611    ex: Execution<T>,
612    next: TempPred<T>,
613    p: TempPred<T>,
614    q: TempPred<T>,
615)
616    requires
617        always(p.and(next).implies(later(p).or(later(q)))).satisfied_by(ex),
618        always(next).satisfied_by(ex),
619    ensures
620        always(p.implies(always(p).or(eventually(q)))).satisfied_by(ex),
621{
622    broadcast use {always_unfold, implies_apply, group_execution_suffix_lemmas};
623
624    assert forall|i| p.satisfied_by(#[trigger] ex.suffix(i)) implies always(p).satisfied_by(
625        ex.suffix(i),
626    ) || eventually(q).satisfied_by(ex.suffix(i)) by {
627        if !eventually(q).satisfied_by(ex.suffix(i)) {
628            assert forall|j| p.satisfied_by(#[trigger] ex.suffix(i).suffix(j)) by {
629                always_p_or_eventually_q_rec::<T>(ex.suffix(i), next, p, q, j);
630            };
631        }
632    };
633}
634
635proof fn next_preserves_inv_rec<T>(ex: Execution<T>, next: TempPred<T>, inv: TempPred<T>, i: nat)
636    requires
637        inv.satisfied_by(ex),
638        always(next).satisfied_by(ex),
639        always(inv.and(next).implies(later(inv))).satisfied_by(ex),
640    ensures
641        inv.satisfied_by(ex.suffix(i)),
642    decreases i,
643{
644    broadcast use {always_unfold, group_execution_suffix_lemmas};
645
646    if i != 0 {
647        next_preserves_inv_rec::<T>(ex, next, inv, (i - 1) as nat);
648    }
649}
650
651proof fn instantiate_entailed_always<T>(ex: Execution<T>, i: nat, spec: TempPred<T>, p: TempPred<T>)
652    requires
653        spec.satisfied_by(ex),
654        spec.implies(always(p)).satisfied_by(ex),
655    ensures
656        p.satisfied_by(ex.suffix(i)),
657{
658    broadcast use {always_unfold, implies_apply};
659
660}
661
662proof fn instantiate_entailed_leads_to<T>(
663    ex: Execution<T>,
664    i: nat,
665    spec: TempPred<T>,
666    p: TempPred<T>,
667    q: TempPred<T>,
668)
669    requires
670        spec.satisfied_by(ex),
671        spec.implies(p.leads_to(q)).satisfied_by(ex),
672    ensures
673        p.implies(eventually(q)).satisfied_by(ex.suffix(i)),
674{
675    broadcast use implies_apply;
676
677}
678
679} // verus!
680// Public lemmas about temporal logic for developers to simplify liveness/safety proofs.
681verus! {
682
683// Basic Rules of Temporal Logic Operators
684/// Lift `StatePred::and` to Verus meta-level.
685pub broadcast proof fn state_pred_and_apply_equality<T>(p: StatePred<T>, q: StatePred<T>, s: T)
686    ensures
687        #[trigger] p.and(q).apply(s) == (p.apply(s) && q.apply(s)),
688{
689}
690
691/// Lift `StatePred::or` to Verus meta-level.
692pub broadcast proof fn state_pred_or_apply_equality<T>(p: StatePred<T>, q: StatePred<T>, s: T)
693    ensures
694        #[trigger] p.or(q).apply(s) == (p.apply(s) || q.apply(s)),
695{
696}
697
698/// Lift `StatePred::not` to Verus meta-level.
699pub broadcast proof fn state_pred_not_apply_equality<T>(p: StatePred<T>, s: T)
700    ensures
701        #[trigger] p.not().apply(s) == !p.apply(s),
702{
703}
704
705/// Lift `StatePred::implies` to Verus meta-level.
706pub broadcast proof fn state_pred_implies_apply_equality<T>(p: StatePred<T>, q: StatePred<T>, s: T)
707    ensures
708        #[trigger] p.implies(q).apply(s) == (p.apply(s) ==> q.apply(s)),
709{
710}
711
712/// `StatePred::and` distributes over `lift_state`.
713///
714/// ## Postconditions
715/// - `lift_state(p ∧ q) == lift_state(p) ∧ lift_state(q)`
716pub broadcast proof fn lift_state_and_equality<T>(p: StatePred<T>, q: StatePred<T>)
717    ensures
718        #![trigger lift_state(p.and(q))]
719        #![trigger lift_state(p).and(lift_state(q))]
720        lift_state(p.and(q)) == lift_state(p).and(lift_state(q)),
721{
722}
723
724/// `StatePred::or` distributes over `lift_state`.
725///
726/// ## Postconditions
727/// - `lift_state(p ∨ q) == lift_state(p) ∨ lift_state(q)`
728pub broadcast proof fn lift_state_or_equality<T>(p: StatePred<T>, q: StatePred<T>)
729    ensures
730        #![trigger lift_state(p.or(q))]
731        #![trigger lift_state(p).or(lift_state(q))]
732        lift_state(p.or(q)) == lift_state(p).or(lift_state(q)),
733{
734}
735
736/// `StatePred::not` distributes over `lift_state`.
737///
738/// ## Postconditions
739/// - `lift_state(!p) == !lift_state(p)`
740pub broadcast proof fn lift_state_not_equality<T>(p: StatePred<T>)
741    ensures
742        #![trigger lift_state(p.not())]
743        #![trigger not(lift_state(p))]
744        lift_state(p.not()) == not(lift_state(p)),
745{
746}
747
748/// `StatePred::implies` distributes over `lift_state`.
749///
750/// ## Postconditions
751/// - `lift_state(p ⇒ q) == lift_state(p) ⇒ lift_state(q)`
752pub broadcast proof fn lift_state_implies_equality<T>(p: StatePred<T>, q: StatePred<T>)
753    ensures
754        #[trigger] lift_state(p.implies(q)) == lift_state(p).implies(lift_state(q)),
755{
756}
757
758/// `StatePred::and` distributes over `□`.
759///
760/// ## Postconditions
761/// - `□(p ∧ q) == □p ∧ □q`
762pub broadcast proof fn always_and_equality<T>(p: TempPred<T>, q: TempPred<T>)
763    ensures
764        #[trigger] always(p.and(q)) == always(p).and(always(q)),
765{
766    broadcast use always_unfold;
767
768    temp_pred_equality::<T>(always(p.and(q)), always(p).and(always(q)));
769}
770
771/// Lift entails `TempPred::and` to Verus meta-level.
772///
773/// If `⊧ p` and `⊧ q`, then `⊧ p ∧ q`.
774///
775/// ## Preconditions
776/// - `spec ⊧ p`
777/// - `spec ⊧ q`
778/// ## Postconditions
779/// - `spec ⊧ p ∧ q`
780pub broadcast proof fn entails_and_temp<T>(spec: TempPred<T>, p: TempPred<T>, q: TempPred<T>)
781    requires
782        spec.entails(p),
783        spec.entails(q),
784    ensures
785        #[trigger] spec.entails(p.and(q)),
786{
787    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.and(q).satisfied_by(ex) by {
788        implies_apply::<T>(ex, spec, p);
789        implies_apply::<T>(ex, spec, q);
790    };
791}
792
793/// Lift entails `TempPred::and` to Verus meta-level (reversed direction).
794///
795/// If `⊧ p ∧ q`, then `⊧ p` and `⊧ q`.
796///
797/// ## Preconditions
798/// - `spec ⊧ p ∧ q`
799/// ## Postconditions
800/// - `spec ⊧ p`
801/// - `spec ⊧ q`
802pub broadcast proof fn entails_and_temp_reverse<T>(
803    spec: TempPred<T>,
804    p: TempPred<T>,
805    q: TempPred<T>,
806)
807    requires
808        #[trigger] spec.entails(p.and(q)),
809    ensures
810        spec.entails(p),
811        spec.entails(q),
812{
813    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.satisfied_by(ex) by {
814        implies_apply::<T>(ex, spec, p.and(q));
815    };
816    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies q.satisfied_by(ex) by {
817        implies_apply::<T>(ex, spec, p.and(q));
818    };
819}
820
821/// Lift entails `TempPred::or` to Verus meta-level.
822///
823/// If `⊧ p` or `⊧ q`, then `⊧ p ∨ q`.
824///
825/// ## Preconditions
826/// - `spec ⊧ p` or `spec ⊧ q`
827/// ## Postconditions
828/// - `spec ⊧ p ∨ q`
829///
830/// NOTE: The other direction does not hold in general.
831pub broadcast proof fn entails_or_temp<T>(spec: TempPred<T>, p: TempPred<T>, q: TempPred<T>)
832    requires
833        spec.entails(p) || spec.entails(q),
834    ensures
835        #[trigger] spec.entails(p.or(q)),
836{
837    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.or(q).satisfied_by(ex) by {
838        if spec.entails(p) {
839            implies_apply::<T>(ex, spec, p);
840        } else {
841            implies_apply::<T>(ex, spec, q);
842        }
843    };
844}
845
846/// Lift entails `TempPred::not` to Verus meta-level (reversed direction).
847///
848/// If `⊧ !p`, then `!(⊧ p)`.
849///
850/// ## Preconditions
851/// - `spec ⊧ !p`
852/// ## Postconditions
853/// - `!(spec ⊧ p)`
854///
855/// NOTE: The other direction does not hold in general.
856pub broadcast proof fn entails_not_temp_reverse<T>(spec: TempPred<T>, p: TempPred<T>)
857    requires
858        #[trigger] spec.entails(not(p)),
859        spec != false_pred::<T>(),
860    ensures
861        !spec.entails(p),
862{
863    if forall|ex| !#[trigger] spec.satisfied_by(ex) {
864        temp_pred_equality::<T>(spec, false_pred::<T>());
865    }
866    let ex = choose|ex| spec.satisfied_by(ex);
867    assert(spec.implies(not(p)).satisfied_by(ex));
868    assert(!spec.implies(p).satisfied_by(ex));
869}
870
871/// Lift entails `TempPred::implies` to Verus meta-level.
872///
873/// If `⊧ p ⇒ q`, then `⊧ p` implies `⊧ q`.
874///
875/// ## Preconditions
876/// - `spec ⊧ (p ⇒ q)`
877/// ## Postconditions
878/// - `(spec ⊧ p) ⇒ (spec ⊧ q)`
879///
880/// NOTE: The other direction does not hold in general.
881pub broadcast proof fn entails_implies_temp_reverse<T>(
882    spec: TempPred<T>,
883    p: TempPred<T>,
884    q: TempPred<T>,
885)
886    requires
887        #[trigger] spec.entails(p.implies(q)),
888    ensures
889        spec.entails(p) ==> spec.entails(q),
890{
891    if spec.entails(p) {
892        assert forall|ex| #[trigger] spec.satisfied_by(ex) implies q.satisfied_by(ex) by {
893            implies_apply::<T>(ex, spec, p.implies(q));
894            implies_apply::<T>(ex, spec, p);
895        };
896    }
897}
898
899broadcast group group_definition_basics {
900    state_pred_and_apply_equality,
901    state_pred_or_apply_equality,
902    state_pred_not_apply_equality,
903    state_pred_implies_apply_equality,
904    lift_state_and_equality,
905    lift_state_or_equality,
906    lift_state_not_equality,
907    lift_state_implies_equality,
908    always_and_equality,
909    entails_and_temp,
910    entails_and_temp_reverse,
911    entails_or_temp,
912    entails_not_temp_reverse,
913    entails_implies_temp_reverse,
914}
915
916/// Double `□` can be simplified.
917///
918/// ## Postconditions
919/// - `□□p == □p`
920pub broadcast proof fn always_double_equality<T>(p: TempPred<T>)
921    ensures
922        #[trigger] always(always(p)) == always(p),
923{
924    broadcast use {always_unfold, group_execution_suffix_lemmas};
925
926    assert forall|ex| #[trigger] always(always(p)).satisfied_by(ex) implies always(p).satisfied_by(
927        ex,
928    ) by {
929        assert(always(p).satisfied_by(ex.suffix(0)));
930    }
931    temp_pred_equality::<T>(always(always(p)), always(p));
932}
933
934/// `StatePred::and` distributes over `□` and `lift_state`.
935///
936/// ## Postconditions
937/// - `□(lift_state(p ∧ q)) == □lift_state(p) ∧ □lift_state(q)`
938pub broadcast proof fn always_lift_state_and_equality<T>(p: StatePred<T>, q: StatePred<T>)
939    ensures
940        #[trigger] always(lift_state(p.and(q))) == always(lift_state(p)).and(always(lift_state(q))),
941{
942    broadcast use group_definition_basics;
943
944}
945
946/// Obviously `p ⇝ p` is valid.
947///
948/// ## Postconditions
949/// - `⊧ p ⇝ p`
950pub broadcast proof fn leads_to_self_temp<T>(p: TempPred<T>)
951    ensures
952        #[trigger] valid(p.leads_to(p)),
953{
954    broadcast use group_definition_basics;
955
956    assert forall|ex| #[trigger] always(p.implies(eventually(p))).satisfied_by(ex) by {
957        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(p).satisfied_by(
958            ex.suffix(i),
959        ) by {
960            assert(ex.suffix(i) == ex.suffix(i).suffix(0));
961            eventually_proved_by_witness::<T>(ex.suffix(i), p, 0);
962        };
963    };
964}
965
966/// Entails is transitive.
967///
968/// ## Preconditions
969/// - `p ⊧ q`
970/// - `q ⊧ r`
971/// ## Postconditions
972/// - `p ⊧ r`
973pub proof fn entails_trans<T>(p: TempPred<T>, q: TempPred<T>, r: TempPred<T>)
974    requires
975        p.entails(q),
976        q.entails(r),
977    ensures
978        p.entails(r),
979{
980    assert forall|ex| p.satisfied_by(ex) implies r.satisfied_by(ex) by {
981        implies_apply::<T>(ex, p, q);
982        implies_apply::<T>(ex, q, r);
983    };
984}
985
986/// Introduce `□` to both sides of `⊧`.
987///
988/// ## Preconditions
989/// - `p ⊧ q`
990/// ## Postconditions
991/// - `□p ⊧ □q`
992pub broadcast proof fn entails_preserved_by_always<T>(p: TempPred<T>, q: TempPred<T>)
993    requires
994        p.entails(q),
995    ensures
996        #[trigger] always(p).entails(always(q)),
997{
998    broadcast use always_unfold;
999
1000    assert forall|ex| always(p).satisfied_by(ex) implies always(q).satisfied_by(ex) by {
1001        assert forall|i: nat| q.satisfied_by(#[trigger] ex.suffix(i)) by {
1002            implies_apply::<T>(ex.suffix(i), p, q);
1003        };
1004    };
1005}
1006
1007/// If `⊧ □p`, then `⊧ p`.
1008///
1009/// ## Preconditions
1010/// - `spec ⊧ □p`
1011/// ## Postconditions
1012/// - `spec ⊧ p`
1013pub proof fn eliminate_always<T>(spec: TempPred<T>, p: TempPred<T>)
1014    requires
1015        spec.entails(always(p)),
1016    ensures
1017        spec.entails(p),
1018{
1019    assert forall|ex| spec.satisfied_by(ex) implies #[trigger] p.satisfied_by(ex) by {
1020        implies_apply(ex, spec, always(p));
1021        assert(ex == ex.suffix(0));
1022    }
1023}
1024
1025/// If `⊧ □(p ⇒ q)`, then `⊧ p ⇝ q`.
1026///
1027/// ## Preconditions
1028/// - `spec ⊧ □(p ⇒ q)`
1029/// ## Postconditions
1030/// - `spec ⊧ p ⇝ q`
1031pub broadcast proof fn always_implies_to_leads_to<T>(
1032    spec: TempPred<T>,
1033    p: TempPred<T>,
1034    q: TempPred<T>,
1035)
1036    requires
1037        #[trigger] spec.entails(always(p.implies(q))),
1038    ensures
1039        spec.entails(p.leads_to(q)),
1040{
1041    broadcast use {always_unfold, implies_apply, group_definition_basics};
1042
1043    assert forall|ex| spec.satisfied_by(ex) implies #[trigger] p.leads_to(q).satisfied_by(ex) by {
1044        implies_apply(ex, spec, always(p.implies(q)));
1045        assert forall|i: nat| p.satisfied_by(#[trigger] ex.suffix(i)) implies eventually(
1046            q,
1047        ).satisfied_by(ex.suffix(i)) by {
1048            assert(ex.suffix(i) == ex.suffix(i).suffix(0));
1049        };
1050    };
1051}
1052
1053/// If entails `□p`, then entails `□(later(p))`.
1054///
1055/// ## Preconditions
1056/// - `spec ⊧ □p`
1057/// ## Postconditions
1058/// - `spec ⊧ □p'`
1059pub broadcast proof fn always_to_always_later<T>(spec: TempPred<T>, p: TempPred<T>)
1060    requires
1061        spec.entails(always(p)),
1062    ensures
1063        #[trigger] spec.entails(always(later(p))),
1064{
1065    broadcast use {always_unfold, group_execution_suffix_lemmas};
1066
1067    entails_trans(spec, always(p), always(later(p)));
1068}
1069
1070/// If `spec1 ⊧ p` and `spec2 ⊧ q`, then `spec1 ∧ spec2 ⊧ p ∧ q`.
1071///
1072/// ## Preconditions
1073/// - `spec1 ⊧ p`
1074/// - `spec2 ⊧ q`
1075/// ## Postconditions
1076/// - `spec1 ∧ spec2 ⊧ p ∧ q`
1077pub broadcast proof fn entails_and_different_temp<T>(
1078    spec1: TempPred<T>,
1079    spec2: TempPred<T>,
1080    p: TempPred<T>,
1081    q: TempPred<T>,
1082)
1083    requires
1084        spec1.entails(p),
1085        spec2.entails(q),
1086    ensures
1087        #[trigger] spec1.and(spec2).entails(p.and(q)),
1088{
1089    assert forall|ex| #[trigger] spec1.and(spec2).satisfied_by(ex) implies p.and(q).satisfied_by(
1090        ex,
1091    ) by {
1092        implies_apply::<T>(ex, spec1, p);
1093        implies_apply::<T>(ex, spec2, q);
1094    };
1095}
1096
1097/// If `p ⇒ q` for all executions, then `p ⇝ q`.
1098///
1099/// ## Preconditions
1100/// - `p ⊧ q`
1101/// ## Postconditions
1102/// - `spec ⊧ p ⇝ q`
1103///
1104/// NOTE: there is no constraint on `spec`, it can be true_pred().
1105pub proof fn entails_implies_leads_to<T>(spec: TempPred<T>, p: TempPred<T>, q: TempPred<T>)
1106    requires
1107        p.entails(q),
1108    ensures
1109        spec.entails(p.leads_to(q)),
1110{
1111    valid_p_implies_always_p(p.implies(q));
1112    always_implies_to_leads_to(spec, p, q);
1113}
1114
1115/// Weaken `□` by `⇒`.
1116///
1117/// ## Preconditions
1118/// - `⊧ p ⇒ q`
1119/// - `spec ⊧ □p`
1120/// ## Postconditions
1121/// - `spec ⊧ □q`
1122pub proof fn always_weaken<T>(spec: TempPred<T>, p: TempPred<T>, q: TempPred<T>)
1123    requires
1124        valid(p.implies(q)),
1125        spec.entails(always(p)),
1126    ensures
1127        spec.entails(always(q)),
1128{
1129    entails_preserved_by_always::<T>(p, q);
1130    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies always(q).satisfied_by(ex) by {
1131        implies_apply::<T>(ex, spec, always(p));
1132        implies_apply::<T>(ex, always(p), always(q));
1133    };
1134}
1135
1136/// If `⊧ p` and `⊧ p ⇝ q`, then `⊧ ◊q`.
1137///
1138/// ## Preconditions
1139/// - `spec ⊧ p`
1140/// - `spec ⊧ p ⇝ q`
1141/// ## Postconditions
1142/// - `spec ⊧ ◊q`
1143pub broadcast proof fn leads_to_apply<T>(spec: TempPred<T>, p: TempPred<T>, q: TempPred<T>)
1144    requires
1145        spec.entails(p),
1146        #[trigger] spec.entails(p.leads_to(q)),
1147    ensures
1148        spec.entails(eventually(q)),
1149{
1150    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies eventually(q).satisfied_by(ex) by {
1151        implies_apply::<T>(ex, spec, p);
1152        implies_apply::<T>(ex, spec, p.leads_to(q));
1153        leads_to_unfold::<T>(ex, p, q);
1154        assert(ex == ex.suffix(0));
1155        implies_apply::<T>(ex, p, eventually(q));
1156    };
1157}
1158
1159/// Leads to is transitive.
1160///
1161/// ## Preconditions
1162/// - `spec ⊧ p ⇝ q`
1163/// - `spec ⊧ q ⇝ r`
1164/// ## Postconditions
1165/// - `spec ⊧ p ⇝ r`
1166pub proof fn leads_to_trans<T>(spec: TempPred<T>, p: TempPred<T>, q: TempPred<T>, r: TempPred<T>)
1167    requires
1168        spec.entails(p.leads_to(q)),
1169        spec.entails(q.leads_to(r)),
1170    ensures
1171        spec.entails(p.leads_to(r)),
1172{
1173    broadcast use group_definition_basics;
1174
1175    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(r).satisfied_by(ex) by {
1176        assert forall|i: nat| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(
1177            r,
1178        ).satisfied_by(ex.suffix(i)) by {
1179            entails_apply(ex, spec, p.leads_to(q));
1180            always_unfold(ex, p.implies(eventually(q)));
1181            implies_apply(ex.suffix(i), p, eventually(q));
1182            eventually_unfold(ex.suffix(i), q);
1183            let q_witness_idx = eventually_choose_witness(ex.suffix(i), q);
1184
1185            assert(ex.suffix(i + q_witness_idx) == ex.suffix(i).suffix(q_witness_idx));
1186
1187            entails_apply(ex, spec, q.leads_to(r));
1188            always_unfold(ex, q.implies(eventually(r)));
1189            implies_apply(ex.suffix(i + q_witness_idx), q, eventually(r));
1190
1191            eventually_propagate_backwards(ex.suffix(i), r, q_witness_idx);
1192        }
1193    };
1194}
1195
1196/// Weaken `□` lifted state predicate by `⇒`.
1197///
1198/// ## Preconditions
1199/// - `∀ s. p(s) ⇒ q(s)`
1200/// - `spec ⊧ □lift_state(p)`
1201///
1202/// ## Postconditions
1203/// - `spec ⊧ □lift_state(q)`
1204pub proof fn always_lift_state_weaken<T>(spec: TempPred<T>, p: StatePred<T>, q: StatePred<T>)
1205    requires
1206        forall|s: T| #[trigger] p.apply(s) ==> q.apply(s),
1207        spec.entails(always(lift_state(p))),
1208    ensures
1209        spec.entails(always(lift_state(q))),
1210{
1211    always_weaken::<T>(spec, lift_state(p), lift_state(q));
1212}
1213
1214/// Introduce `□` to both sides of `⇒`.
1215///
1216/// ## Preconditions
1217/// - `spec ⊧ □(p ⇒ q)`
1218/// ## Postconditions
1219/// - `spec ⊧ □(□p ⇒ □q)`
1220pub proof fn always_implies_preserved_by_always<T>(
1221    spec: TempPred<T>,
1222    p: TempPred<T>,
1223    q: TempPred<T>,
1224)
1225    requires
1226        spec.entails(always(p.implies(q))),
1227    ensures
1228        spec.entails(always(always(p).implies(always(q)))),
1229{
1230    broadcast use group_tla_rules;
1231
1232    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies always(
1233        always(p).implies(always(q)),
1234    ).satisfied_by(ex) by {
1235        assert forall|i| #[trigger] always(p).satisfied_by(ex.suffix(i)) implies always(
1236            q,
1237        ).satisfied_by(ex.suffix(i)) by {
1238            assert forall|j| #[trigger] q.satisfied_by(ex.suffix(i).suffix(j)) by {
1239                implies_apply::<T>(ex, spec, always(p.implies(q)));
1240                assert(ex.suffix(i + j) == ex.suffix(i).suffix(j));
1241                implies_apply::<T>(ex.suffix(i).suffix(j), p, q);
1242            };
1243        };
1244    };
1245}
1246
1247/// Combine the premises of two `⇝` using `∨`.
1248///
1249/// ## Preconditions
1250///  - `spec ⊧ p ⇝ r`
1251///  - `spec ⊧ q ⇝ r`
1252/// ## Postconditions
1253///  - `spec ⊧ (p ∨ q) ⇝ r`
1254pub broadcast proof fn or_leads_to_combine<T>(
1255    spec: TempPred<T>,
1256    p: TempPred<T>,
1257    q: TempPred<T>,
1258    r: TempPred<T>,
1259)
1260    requires
1261        spec.entails(p.leads_to(r)),
1262        spec.entails(q.leads_to(r)),
1263    ensures
1264        #[trigger] spec.entails(p.or(q).leads_to(r)),
1265{
1266    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.or(q).leads_to(r).satisfied_by(
1267        ex,
1268    ) by {
1269        assert forall|i| #[trigger] p.or(q).satisfied_by(ex.suffix(i)) implies eventually(
1270            r,
1271        ).satisfied_by(ex.suffix(i)) by {
1272            implies_apply::<T>(ex, spec, p.leads_to(r));
1273            implies_apply::<T>(ex, spec, q.leads_to(r));
1274            leads_to_unfold::<T>(ex, p, r);
1275            leads_to_unfold::<T>(ex, q, r);
1276        };
1277    };
1278}
1279
1280/// Prove `p ⇝ r` by case analysis on `q`.
1281///
1282/// ## Preconditions
1283///  - `spec ⊧ (p ∧ q) ⇝ r`
1284///  - `spec ⊧ (p ∧ ~q) ⇝ r`
1285/// ## Postconditions
1286///  - `spec ⊧ p ⇝ r`
1287pub proof fn or_leads_to_case_analysis<T>(
1288    spec: TempPred<T>,
1289    p: TempPred<T>,
1290    q: TempPred<T>,
1291    r: TempPred<T>,
1292)
1293    requires
1294        spec.entails(p.and(q).leads_to(r)),
1295        spec.entails(p.and(not(q)).leads_to(r)),
1296    ensures
1297        spec.entails(p.leads_to(r)),
1298{
1299    broadcast use group_definition_basics;
1300
1301    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(r).satisfied_by(ex) by {
1302        if spec.satisfied_by(ex) {
1303            assert(spec.implies(p.and(q).leads_to(r)).satisfied_by(ex));
1304            assert(spec.implies(p.and(not(q)).leads_to(r)).satisfied_by(ex));
1305
1306            leads_to_unfold(ex, p.and(q), r);
1307            leads_to_unfold(ex, p.and(not(q)), r);
1308
1309            assert forall|i| p.implies(eventually(r)).satisfied_by(#[trigger] ex.suffix(i)) by {}
1310        }
1311    }
1312}
1313
1314/// Combine the conclusions of two `⇝` if the conclusions are stable.
1315///
1316/// ## Preconditions
1317///  - `spec ⊧ p ⇝ □q`
1318///  - `spec ⊧ p ⇝ □r`
1319/// ## Postconditions
1320/// - `spec ⊧ p ⇝ □(q ∧ r)`
1321pub broadcast proof fn leads_to_always_combine<T>(
1322    spec: TempPred<T>,
1323    p: TempPred<T>,
1324    q: TempPred<T>,
1325    r: TempPred<T>,
1326)
1327    requires
1328        spec.entails(p.leads_to(always(q))),
1329        spec.entails(p.leads_to(always(r))),
1330    ensures
1331        #![trigger spec.entails(p.leads_to(always(q).and(always(r))))]
1332        #![trigger spec.entails(p.leads_to(always(q.and(r))))]
1333        spec.entails(p.leads_to(always(q.and(r)))),
1334        spec.entails(p.leads_to(always(q).and(always(r)))),
1335{
1336    broadcast use group_definition_basics;
1337
1338    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(
1339        always(q.and(r)),
1340    ).satisfied_by(ex) by {
1341        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(
1342            always(q.and(r)),
1343        ).satisfied_by(ex.suffix(i)) by {
1344            implies_apply::<T>(ex, spec, p.leads_to(always(q)));
1345            implies_apply::<T>(ex, spec, p.leads_to(always(r)));
1346
1347            implies_apply::<T>(ex.suffix(i), p, eventually(always(q)));
1348            implies_apply::<T>(ex.suffix(i), p, eventually(always(r)));
1349
1350            let witness_q_idx = eventually_choose_witness::<T>(ex.suffix(i), always(q));
1351            let witness_r_idx = eventually_choose_witness::<T>(ex.suffix(i), always(r));
1352
1353            if witness_q_idx < witness_r_idx {
1354                always_propagate_forwards::<T>(
1355                    ex.suffix(i).suffix(witness_q_idx),
1356                    q,
1357                    (witness_r_idx - witness_q_idx) as nat,
1358                );
1359                assert(ex.suffix(i).suffix(witness_r_idx) == ex.suffix(i).suffix(
1360                    witness_q_idx,
1361                ).suffix((witness_r_idx - witness_q_idx) as nat));
1362                eventually_proved_by_witness(ex.suffix(i), always(q.and(r)), witness_r_idx);
1363            } else {
1364                always_propagate_forwards::<T>(
1365                    ex.suffix(i).suffix(witness_r_idx),
1366                    r,
1367                    (witness_q_idx - witness_r_idx) as nat,
1368                );
1369                assert(ex.suffix(i).suffix(witness_q_idx) == ex.suffix(i).suffix(
1370                    witness_r_idx,
1371                ).suffix((witness_q_idx - witness_r_idx) as nat));
1372                eventually_proved_by_witness(ex.suffix(i), always(q.and(r)), witness_q_idx);
1373            }
1374        };
1375    };
1376    always_and_equality(q, r);
1377}
1378
1379/// Prove `p ⇝ □q` by showing that `q` is preserved.
1380///
1381/// ## Preconditions
1382///  - `spec ⊧ □(q ∧ next ⇒ q')`
1383///  - `spec ⊧ □next`
1384///  - `spec ⊧ p ⇝ q`
1385/// ## Postconditions
1386///  - `spec ⊧ p ⇝ □q`
1387pub proof fn leads_to_stable<T>(
1388    spec: TempPred<T>,
1389    next: TempPred<T>,
1390    p: TempPred<T>,
1391    q: TempPred<T>,
1392)
1393    requires
1394        spec.entails(always(q.and(next).implies(later(q)))),
1395        spec.entails(always(next)),
1396        spec.entails(p.leads_to(q)),
1397    ensures
1398        spec.entails(p.leads_to(always(q))),
1399{
1400    broadcast use {always_unfold, always_propagate_forwards, group_definition_basics};
1401
1402    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(always(q)).satisfied_by(
1403        ex,
1404    ) by {
1405        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(
1406            always(q),
1407        ).satisfied_by(ex.suffix(i)) by {
1408            implies_apply::<T>(ex, spec, p.leads_to(q));
1409            leads_to_unfold::<T>(ex, p, q);
1410            implies_apply::<T>(ex.suffix(i), p, eventually(q));
1411            let witness_idx = eventually_choose_witness::<T>(ex.suffix(i), q);
1412
1413            implies_apply::<T>(ex, spec, always(q.and(next).implies(later(q))));
1414            always_propagate_forwards::<T>(
1415                ex.suffix(i),
1416                q.and(next).implies(later(q)),
1417                witness_idx,
1418            );
1419
1420            implies_apply::<T>(ex, spec, always(next));
1421            always_propagate_forwards::<T>(ex.suffix(i), next, witness_idx);
1422
1423            assert forall|j| #[trigger]
1424                q.satisfied_by(ex.suffix(i).suffix(witness_idx).suffix(j)) by {
1425                assert forall|idx|
1426                    q.satisfied_by(#[trigger] ex.suffix(i).suffix(witness_idx).suffix(idx))
1427                        && next.satisfied_by(
1428                        ex.suffix(i).suffix(witness_idx).suffix(idx),
1429                    ) implies q.satisfied_by(ex.suffix(i).suffix(witness_idx).suffix(idx + 1)) by {
1430                    implies_apply::<T>(
1431                        ex.suffix(i).suffix(witness_idx).suffix(idx),
1432                        q.and(next),
1433                        later(q),
1434                    );
1435                    assert(ex.suffix(i).suffix(witness_idx).suffix(idx + 1) == ex.suffix(i).suffix(
1436                        witness_idx,
1437                    ).suffix(idx).suffix(1));
1438                };
1439                next_preserves_inv_rec::<T>(ex.suffix(i).suffix(witness_idx), next, q, j);
1440            };
1441
1442            eventually_proved_by_witness::<T>(ex.suffix(i), always(q), witness_idx);
1443        };
1444    };
1445}
1446
1447/// Sandwich `⇝` with `∨ r`.
1448///
1449/// ## Preconditions
1450/// - `spec ⊧ p ⇝ q`
1451/// ## Postconditions
1452/// - `spec ⊧ p ∨ r ⇝ q ∨ r`
1453pub broadcast proof fn leads_to_framed_by_or<T>(
1454    spec: TempPred<T>,
1455    p: TempPred<T>,
1456    q: TempPred<T>,
1457    r: TempPred<T>,
1458)
1459    requires
1460        spec.entails(p.leads_to(q)),
1461    ensures
1462        #[trigger] spec.entails(p.or(r).leads_to(q.or(r))),
1463{
1464    broadcast use {implies_apply, group_execution_suffix_lemmas, group_definition_basics};
1465
1466    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.or(r).leads_to(
1467        q.or(r),
1468    ).satisfied_by(ex) by {
1469        assert forall|i| #[trigger] p.or(r).satisfied_by(ex.suffix(i)) implies eventually(
1470            q.or(r),
1471        ).satisfied_by(ex.suffix(i)) by {
1472            implies_apply(ex, spec, p.leads_to(q));
1473            leads_to_unfold(ex, p, q);
1474            if p.satisfied_by(ex.suffix(i)) {
1475                let witness_idx = eventually_choose_witness(ex.suffix(i), q);
1476                eventually_proved_by_witness(ex.suffix(i), q.or(r), witness_idx);
1477            } else {
1478                let witness_idx = 0;
1479                eventually_proved_by_witness(ex.suffix(i), q.or(r), witness_idx);
1480            }
1481        }
1482    }
1483}
1484
1485/// Combine two `⇝` with a shortcut.
1486///
1487/// ## Preconditions
1488/// - `spec ⊧ p ⇝ q ∨ s`
1489/// - `spec ⊧ q ⇝ r ∨ s`
1490/// ## Postconditions
1491/// - `spec ⊧ p ⇝ r ∨ s`
1492pub proof fn leads_to_shortcut_temp<T>(
1493    spec: TempPred<T>,
1494    p: TempPred<T>,
1495    q: TempPred<T>,
1496    r: TempPred<T>,
1497    s: TempPred<T>,
1498)
1499    requires
1500        spec.entails(p.leads_to(q.or(s))),
1501        spec.entails(q.leads_to(r.or(s))),
1502    ensures
1503        spec.entails(p.leads_to(r.or(s))),
1504{
1505    leads_to_framed_by_or(spec, q, r.or(s), s);
1506    temp_pred_equality(r.or(s).or(s), r.or(s));
1507    leads_to_trans(spec, p, q.or(s), r.or(s));
1508}
1509
1510/// If `⊧ □(lift_state(p))` and `⊧ □(lift_state(q))`, then `⊧ □(lift_state(p ∧ q))`.
1511///
1512/// ## Preconditions
1513///  - `spec ⊧ □lift_state(p)`
1514///  - `spec ⊧ □lift_state(q)`
1515/// ## Postconditions
1516///  - `spec ⊧ □lift_state(p ∧ q)`
1517pub broadcast proof fn entails_always_lift_state_and<T>(
1518    spec: TempPred<T>,
1519    p: StatePred<T>,
1520    q: StatePred<T>,
1521)
1522    requires
1523        spec.entails(always(lift_state(p))),
1524        spec.entails(always(lift_state(q))),
1525    ensures
1526        #[trigger] spec.entails(always(lift_state(p.and(q)))),
1527{
1528    broadcast use group_definition_basics;
1529
1530}
1531
1532/// Eliminate and split two state predicates under `□`.
1533///
1534/// ## Preconditions
1535/// - `spec ⊧ □lift_state(p ∧ q)`
1536/// ## Postconditions
1537/// - `spec ⊧ □lift_state(p)`
1538/// - `spec ⊧ □lift_state(q)`
1539pub broadcast proof fn entails_always_lift_state_and_elim<T>(
1540    spec: TempPred<T>,
1541    p: StatePred<T>,
1542    q: StatePred<T>,
1543)
1544    requires
1545        #[trigger] spec.entails(always(lift_state(p.and(q)))),
1546    ensures
1547        spec.entails(always(lift_state(p))),
1548        spec.entails(always(lift_state(q))),
1549{
1550    broadcast use group_definition_basics;
1551
1552}
1553
1554// Rules about quantifiers.
1555/// Lift the `exists` outside `lift_state`.
1556///
1557/// ## Postconditions
1558/// - `lift_state(∃ a: A. p(a)) == ∃ a: A. lift_state(p(a))`
1559pub broadcast proof fn lift_state_exists_equality<T, A>(a_to_state_pred: spec_fn(A) -> StatePred<T>)
1560    ensures
1561        #[trigger] lift_state_exists(a_to_state_pred) == tla_exists(
1562            |a| lift_state(a_to_state_pred(a)),
1563        ),
1564{
1565    let p = lift_state_exists(a_to_state_pred);
1566    let q = tla_exists(|a| lift_state(a_to_state_pred(a)));
1567    let partial_p = |t| exists|a| #[trigger] a_to_state_pred(a).apply(t);
1568    let partial_q = |a| lift_state(a_to_state_pred(a));
1569    assert forall|ex| p.satisfied_by(ex) implies q.satisfied_by(ex) by {
1570        assert(exists|a| #[trigger] a_to_state_pred(a).apply(ex.head()));
1571        let witness_a = choose|a| #[trigger] a_to_state_pred(a).apply(ex.head());
1572        assert(partial_q(witness_a).satisfied_by(ex));
1573    };
1574
1575    temp_pred_equality::<T>(p, q);
1576}
1577
1578/// Lift the `□` outside `tla_forall` if the function is previously wrapped by an `□`.
1579///
1580/// ## Postconditions
1581/// - `∀ a:A. □P(a) == □(∀ a:A. P(a)))`
1582///
1583/// NOTE: Verus may not able to infer that `(|a| func(a))(a)` equals `func(a)`.
1584///       Please turn to lemma [`tla_forall_always_equality_variant`] for troubleshooting.
1585pub proof fn tla_forall_always_equality<T, A>(a_to_temp_pred: spec_fn(A) -> TempPred<T>)
1586    ensures
1587        tla_forall(|a: A| always(a_to_temp_pred(a))) == always(tla_forall(a_to_temp_pred)),
1588{
1589    broadcast use {always_unfold, tla_forall_unfold};
1590
1591    let a_to_always_p = |a: A| always(a_to_temp_pred(a));
1592
1593    assert forall|ex| #[trigger] tla_forall(a_to_always_p).satisfied_by(ex) implies always(
1594        tla_forall(a_to_temp_pred),
1595    ).satisfied_by(ex) by {
1596        assert forall|i| #[trigger] tla_forall(a_to_temp_pred).satisfied_by(ex.suffix(i)) by {
1597            assert forall|a| #[trigger] a_to_temp_pred(a).satisfied_by(ex.suffix(i)) by {
1598                assert(a_to_always_p(a).satisfied_by(ex));
1599            };
1600        };
1601    };
1602
1603    temp_pred_equality::<T>(
1604        tla_forall(|a: A| always(a_to_temp_pred(a))),
1605        always(tla_forall(a_to_temp_pred)),
1606    );
1607}
1608
1609pub proof fn tla_forall_always_equality_variant<T, A>(
1610    a_to_always: spec_fn(A) -> TempPred<T>,
1611    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
1612)
1613    requires
1614        forall|a: A|
1615            #![trigger a_to_always(a)]
1616            a_to_always(a).entails((|a: A| always(a_to_temp_pred(a)))(a)) && ((|a: A|
1617                always(a_to_temp_pred(a)))(a)).entails(a_to_always(a)),
1618    ensures
1619        tla_forall(a_to_always) == always(tla_forall(a_to_temp_pred)),
1620{
1621    a_to_temp_pred_equality::<T, A>(a_to_always, |a: A| always(a_to_temp_pred(a)));
1622    temp_pred_equality::<T>(tla_forall(a_to_always), tla_forall(|a: A| always(a_to_temp_pred(a))));
1623    tla_forall_always_equality::<T, A>(a_to_temp_pred);
1624}
1625
1626/// If `forall a. P(a)` holds, then `P` holds for any particular `a`.
1627///
1628/// ## Postconditions
1629/// - `∀ a: A. P(a) ⊧ P(a)`
1630pub broadcast proof fn tla_forall_apply<T, A>(a_to_temp_pred: spec_fn(A) -> TempPred<T>, a: A)
1631    ensures
1632        #[trigger] tla_forall(a_to_temp_pred).entails(a_to_temp_pred(a)),
1633{
1634    broadcast use tla_forall_unfold;
1635
1636}
1637
1638// Used to be named as always_tla_forall_apply
1639/// If `⊧ □(∀ a. P(a))`, then `⊧ □(P(a))` for any particular `a`.
1640///
1641/// ## Preconditions
1642/// - `spec ⊧ □(∀ a: A. P(a))`
1643/// ## Postconditions
1644/// - `spec ⊧ □(P(a))`
1645pub proof fn use_always_tla_forall<T, A>(
1646    spec: TempPred<T>,
1647    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
1648    a: A,
1649)
1650    requires
1651        spec.entails(always(tla_forall(a_to_temp_pred))),
1652    ensures
1653        spec.entails(always(a_to_temp_pred(a))),
1654{
1655    entails_preserved_by_always(tla_forall(a_to_temp_pred), a_to_temp_pred(a));
1656    entails_trans(spec, always(tla_forall(a_to_temp_pred)), always(a_to_temp_pred(a)));
1657}
1658
1659/// If ⊧ `P(a)` for all `a`, then ⊧ `∀ a. P(a)`.
1660///
1661/// ## Preconditions
1662/// - `forall a: A. spec ⊧ P(a)`
1663///
1664/// ## Postconditions
1665/// - `spec ⊧ ∀ a: A. P(a)`
1666pub broadcast proof fn spec_entails_tla_forall<T, A>(
1667    spec: TempPred<T>,
1668    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
1669)
1670    requires
1671        forall|a: A| spec.entails(#[trigger] a_to_temp_pred(a)),
1672    ensures
1673        #[trigger] spec.entails(tla_forall(a_to_temp_pred)),
1674{
1675    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies tla_forall(
1676        a_to_temp_pred,
1677    ).satisfied_by(ex) by {
1678        assert forall|a| #[trigger] a_to_temp_pred(a).satisfied_by(ex) by {
1679            implies_apply::<T>(ex, spec, a_to_temp_pred(a));
1680        };
1681    };
1682}
1683
1684/// If ⊧ `□(P(a))` for all `a`, then ⊧ `□(∀ a. P(a))`.
1685///
1686/// ## Preconditions
1687/// - `forall a: A. spec ⊧ □P(a)`
1688/// ## Postconditions
1689/// - `spec ⊧ □(∀ a: A. P(a))`
1690pub proof fn spec_entails_always_tla_forall<T, A>(
1691    spec: TempPred<T>,
1692    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
1693)
1694    requires
1695        forall|a: A| spec.entails(always(#[trigger] a_to_temp_pred(a))),
1696    ensures
1697        spec.entails(always(tla_forall(a_to_temp_pred))),
1698{
1699    let a_to_always = |a: A| always(a_to_temp_pred(a));
1700    spec_entails_tla_forall(spec, a_to_always);
1701    tla_forall_always_equality_variant::<T, A>(a_to_always, a_to_temp_pred);
1702}
1703
1704/// If ⊧ `□(p ⇒ P(a))` for all `a`, then ⊧ `□(p ⇒ forall a. P(a))`.
1705///
1706/// ## Preconditions
1707///  - `forall a: A. spec ⊧ □(p ⇒ P(a))`
1708/// ## Postconditions
1709///  - `spec ⊧ □(p ⇒ (∀ a: A. P(a)))`
1710pub broadcast proof fn always_implies_forall_intro<T, A>(
1711    spec: TempPred<T>,
1712    p: TempPred<T>,
1713    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
1714)
1715    requires
1716        forall|a: A| #[trigger] spec.entails(always(p.implies(a_to_temp_pred(a)))),
1717    ensures
1718        #[trigger] spec.entails(always(p.implies(tla_forall(a_to_temp_pred)))),
1719{
1720    let a_to_always_p_implies_q = |a: A| always(p.implies(a_to_temp_pred(a)));
1721    spec_entails_tla_forall::<T, A>(spec, a_to_always_p_implies_q);
1722    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies always(
1723        p.implies(tla_forall(a_to_temp_pred)),
1724    ).satisfied_by(ex) by {
1725        implies_apply::<T>(ex, spec, tla_forall(a_to_always_p_implies_q));
1726        tla_forall_always_implies_equality2::<T, A>(p, a_to_temp_pred)
1727    };
1728}
1729
1730/// If ⊧ `P(a) ⇝ q` for all `a`, then ⊧ `exists a. P(a) leads_to q`.
1731///
1732/// ## Preconditions
1733///  - `forall a: A. spec ⊧ P(a) ⇝ q`
1734/// ## Postconditions
1735///  - `spec ⊧ (∃ a: A. P(a)) ⇝ q`
1736pub broadcast proof fn tla_exists_leads_to_intro<T, A>(
1737    spec: TempPred<T>,
1738    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
1739    q: TempPred<T>,
1740)
1741    requires
1742        forall|a: A| #[trigger] spec.entails(a_to_temp_pred(a).leads_to(q)),
1743    ensures
1744        #[trigger] spec.entails(tla_exists(a_to_temp_pred).leads_to(q)),
1745{
1746    let a_to_temp_pred_leads_to_q = |a: A| a_to_temp_pred(a).leads_to(q);
1747    spec_entails_tla_forall::<T, A>(spec, a_to_temp_pred_leads_to_q);
1748    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies tla_exists(a_to_temp_pred).leads_to(
1749        q,
1750    ).satisfied_by(ex) by {
1751        implies_apply::<T>(ex, spec, tla_forall(a_to_temp_pred_leads_to_q));
1752        tla_forall_leads_to_equality1::<T, A>(a_to_temp_pred, q);
1753    };
1754}
1755
1756/// If ⊧ `forall a. P(a)`, then ⊧ `P(a)` for any particular `a`.
1757///
1758/// ## Preconditions
1759///  - `spec ⊧ (∀ a: A. P(a))`
1760/// ## Postconditions
1761///  - `spec ⊧ P(a)`
1762pub proof fn use_tla_forall<T, A>(
1763    spec: TempPred<T>,
1764    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
1765    a: A,
1766)
1767    requires
1768        spec.entails(tla_forall(a_to_temp_pred)),
1769    ensures
1770        spec.entails(a_to_temp_pred(a)),
1771{
1772    assert forall|ex: Execution<T>| #[trigger] spec.satisfied_by(ex) implies (a_to_temp_pred(
1773        a,
1774    )).satisfied_by(ex) by {
1775        entails_apply(ex, spec, tla_forall(a_to_temp_pred));
1776        assert(spec.implies(tla_forall(a_to_temp_pred)).satisfied_by(ex));
1777    };
1778}
1779
1780pub broadcast proof fn entails_tla_exists_by_witness<T, A>(
1781    spec: TempPred<T>,
1782    p: spec_fn(A) -> TempPred<T>,
1783    a: A,
1784)
1785    requires
1786        #[trigger] spec.entails(p(a)),
1787    ensures
1788        spec.entails(tla_exists(p)),
1789{
1790    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies tla_exists(p).satisfied_by(ex) by {
1791        implies_apply::<T>(ex, spec, p(a));
1792        tla_exists_unfold::<T, A>(ex, p);
1793    };
1794}
1795
1796pub proof fn implies_tla_exists_by_witness<T, A>(
1797    spec: TempPred<T>,
1798    p: TempPred<T>,
1799    q: spec_fn(A) -> TempPred<T>,
1800    a: A,
1801)
1802    requires
1803        spec.entails(p.implies(q(a))),
1804    ensures
1805        spec.entails(p.implies(tla_exists(q))),
1806{
1807    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.implies(
1808        tla_exists(q),
1809    ).satisfied_by(ex) by {
1810        implies_apply(ex, spec, p.implies(q(a)));
1811    };
1812}
1813
1814pub broadcast proof fn implies_tla_exists_intro<T, A>(
1815    spec: TempPred<T>,
1816    p: TempPred<T>,
1817    q: spec_fn(A) -> TempPred<T>,
1818)
1819    requires
1820        exists|a: A| #[trigger] spec.entails(p.implies(q(a))),
1821    ensures
1822        #[trigger] spec.entails(p.implies(tla_exists(q))),
1823{
1824    let witness = choose|a: A| #[trigger] spec.entails(p.implies(q(a)));
1825    implies_tla_exists_by_witness(spec, p, q, witness);
1826}
1827
1828pub proof fn leads_to_tla_exists_by_witness<T, A>(
1829    spec: TempPred<T>,
1830    p: TempPred<T>,
1831    q: spec_fn(A) -> TempPred<T>,
1832    a: A,
1833)
1834    requires
1835        spec.entails(p.leads_to(q(a))),
1836    ensures
1837        spec.entails(p.leads_to(tla_exists(q))),
1838{
1839    assert forall|ex| #[trigger] always(q(a).implies(tla_exists(q))).satisfied_by(ex) by {
1840        assert forall|i: nat| q(a).implies(tla_exists(q)).satisfied_by(ex.suffix(i)) by {
1841            if q(a).satisfied_by(ex.suffix(i)) {
1842                tla_exists_proved_by_witness(ex.suffix(i), q, a);
1843            }
1844        };
1845    };
1846    leads_to_weaken(spec, p, q(a), p, tla_exists(q));
1847}
1848
1849pub broadcast proof fn leads_to_tla_exists_intro<T, A>(
1850    spec: TempPred<T>,
1851    p: TempPred<T>,
1852    q: spec_fn(A) -> TempPred<T>,
1853)
1854    requires
1855        exists|a: A| #[trigger] spec.entails(p.leads_to(q(a))),
1856    ensures
1857        #[trigger] spec.entails(p.leads_to(tla_exists(q))),
1858{
1859    let witness = choose|a: A| #[trigger] spec.entails(p.leads_to(q(a)));
1860    leads_to_tla_exists_by_witness(spec, p, q, witness);
1861}
1862
1863pub broadcast proof fn lift_state_exists_leads_to_intro<T, A>(
1864    spec: TempPred<T>,
1865    a_to_temp_pred: spec_fn(A) -> StatePred<T>,
1866    q: TempPred<T>,
1867)
1868    requires
1869        forall|a: A| #[trigger] spec.entails(lift_state(a_to_temp_pred(a)).leads_to(q)),
1870    ensures
1871        #[trigger] spec.entails(lift_state_exists(a_to_temp_pred).leads_to(q)),
1872{
1873    lift_state_exists_equality(a_to_temp_pred);
1874    let lifted_a_to_temp_pred = |a: A| lift_state(a_to_temp_pred(a));
1875    tla_exists_leads_to_intro(spec, lifted_a_to_temp_pred, q);
1876}
1877
1878pub broadcast proof fn state_exists_intro<T, A>(a_to_temp_pred: spec_fn(A) -> StatePred<T>, s: T)
1879    requires
1880        exists|a: A| #[trigger] a_to_temp_pred(a).apply(s),
1881    ensures
1882        #[trigger] StatePred::state_exists(a_to_temp_pred).apply(s),
1883{
1884}
1885
1886pub broadcast proof fn lift_state_exists_absorb_equality<T, A>(
1887    a_to_state_pred: spec_fn(A) -> StatePred<T>,
1888    state_pred: StatePred<T>,
1889)
1890    ensures
1891        #[trigger] lift_state_exists(a_to_state_pred).and(lift_state(state_pred))
1892            == lift_state_exists(StatePred::absorb(a_to_state_pred, state_pred)),
1893{
1894    broadcast use group_definition_basics;
1895
1896    let lhs = lift_state_exists(a_to_state_pred).and(lift_state(state_pred));
1897    let rhs = lift_state_exists(StatePred::absorb(a_to_state_pred, state_pred));
1898
1899    assert forall|ex| lhs.satisfied_by(ex) == rhs.satisfied_by(ex) by {
1900        let s = ex.head();
1901
1902        if lhs.satisfied_by(ex) {
1903            let a = choose|a| #[trigger] a_to_state_pred(a).apply(s);
1904            assert(StatePred::absorb(a_to_state_pred, state_pred)(a).apply(s));
1905        }
1906        if rhs.satisfied_by(ex) {
1907            let a = choose|a| #[trigger] StatePred::absorb(a_to_state_pred, state_pred)(a).apply(s);
1908        }
1909    }
1910    temp_pred_equality(lhs, rhs);
1911}
1912
1913pub broadcast proof fn lift_state_forall_absorb_equality<T, A>(
1914    a_to_state_pred: spec_fn(A) -> StatePred<T>,
1915    state_pred: StatePred<T>,
1916)
1917    requires
1918        Set::<A>::full() != Set::<A>::empty(),
1919    ensures
1920        #[trigger] lift_state_forall(a_to_state_pred).and(lift_state(state_pred))
1921            == lift_state_forall(StatePred::absorb(a_to_state_pred, state_pred)),
1922{
1923    broadcast use group_definition_basics;
1924
1925    let lhs = lift_state_forall(a_to_state_pred).and(lift_state(state_pred));
1926    let rhs = lift_state_forall(StatePred::absorb(a_to_state_pred, state_pred));
1927
1928    assert forall|ex| lhs.satisfied_by(ex) == rhs.satisfied_by(ex) by {
1929        let s = ex.head();
1930
1931        if lhs.satisfied_by(ex) {
1932            assert forall|a| #[trigger]
1933                StatePred::absorb(a_to_state_pred, state_pred)(a).apply(s) by {}
1934        }
1935        if rhs.satisfied_by(ex) {
1936            let a = choose|a| Set::<A>::full().contains(a);
1937            assert(StatePred::absorb(a_to_state_pred, state_pred)(a).apply(s));
1938            assert forall|a| #[trigger] a_to_state_pred(a).apply(s) by {
1939                assert(StatePred::absorb(a_to_state_pred, state_pred)(a).apply(s));
1940            }
1941        }
1942    }
1943    temp_pred_equality(lhs, rhs);
1944}
1945
1946/// Prove `lift_state_exists` leads to by case analysis on another `StatePred`.
1947///
1948/// ## Preconditions
1949/// - `spec ⊧ lift_state_exists(absorb(a_to_temp_pred, p)) ⇝ q`
1950/// - `spec ⊧ lift_state_exists(absorb(a_to_temp_pred, ~p)) ⇝ q`
1951/// ## Postconditions
1952/// - `spec ⊧ lift_state_exists(a_to_temp_pred) ⇝ q`
1953pub proof fn lift_state_exists_leads_to_case_analysis<T, A>(
1954    spec: TempPred<T>,
1955    a_to_temp_pred: spec_fn(A) -> StatePred<T>,
1956    p: StatePred<T>,
1957    q: TempPred<T>,
1958)
1959    requires
1960        spec.entails(lift_state_exists(StatePred::absorb(a_to_temp_pred, p)).leads_to(q)),
1961        spec.entails(lift_state_exists(StatePred::absorb(a_to_temp_pred, p.not())).leads_to(q)),
1962    ensures
1963        spec.entails(lift_state_exists(a_to_temp_pred).leads_to(q)),
1964{
1965    broadcast use {lift_state_exists_absorb_equality, group_definition_basics};
1966
1967    or_leads_to_case_analysis(spec, lift_state_exists(a_to_temp_pred), lift_state(p), q);
1968}
1969
1970/// Leads to `□tla_forall(a_to_temp_pred)` if forall `a`, it leads to `□a_to_temp_pred(a)`.
1971///
1972/// ## Preconditions
1973/// - `forall |a: A|, spec ⊧ p ⇝ □a_to_temp_pred(a)`
1974/// - `forall |a: A|, a \in domain`
1975/// - `domain.is_finite() && domain.len() > 0`
1976/// ## Postconditions
1977/// - `spec ⊧ □tla_forall(a_to_temp_pred)`
1978///
1979/// The domain set assist in showing type A contains finite elements.
1980//
1981// This lemma is actually similar to leads_to_always_combine_n when the n predicates are all a_to_temp_pred(a) for some a.
1982// This is because tla_forall(a_to_temp_pred) == a_to_temp_pred(a1).and(a_to_temp_pred(a2))....and(a_to_temp_pred(an)), We only consider the case when
1983// type A is finite here.
1984pub proof fn leads_to_always_tla_forall<T, A>(
1985    spec: TempPred<T>,
1986    p: TempPred<T>,
1987    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
1988    domain: Set<A>,
1989)
1990    requires
1991        forall|a: A| spec.entails(p.leads_to(always(#[trigger] a_to_temp_pred(a)))),
1992        domain.finite(),
1993        domain.len() > 0,
1994        forall|a: A| #[trigger] domain.contains(a),
1995    ensures
1996        spec.entails(p.leads_to(always(tla_forall(a_to_temp_pred)))),
1997{
1998    broadcast use {always_unfold, always_propagate_forwards, group_definition_basics};
1999
2000    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(
2001        always(tla_forall(a_to_temp_pred)),
2002    ).satisfied_by(ex) by {
2003        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(
2004            always(tla_forall(a_to_temp_pred)),
2005        ).satisfied_by(ex.suffix(i)) by {
2006            assert forall|a: A|
2007                p.leads_to(always(#[trigger] a_to_temp_pred(a))).satisfied_by(ex) by {
2008                implies_apply::<T>(ex, spec, p.leads_to(always(a_to_temp_pred(a))));
2009            }
2010            assert forall|a: A|
2011                eventually(always(#[trigger] a_to_temp_pred(a))).satisfied_by(ex.suffix(i)) by {
2012                implies_apply::<T>(ex.suffix(i), p, eventually(always(a_to_temp_pred(a))));
2013            }
2014
2015            let a_to_witness = Map::new(
2016                |a: A| domain.contains(a),
2017                |a: A|
2018                    {
2019                        let wit = eventually_choose_witness::<T>(
2020                            ex.suffix(i),
2021                            always(a_to_temp_pred(a)),
2022                        );
2023                        wit
2024                    },
2025            );
2026            assert(a_to_witness.dom() == domain);
2027            let r = |a1: nat, a2: nat| a1 <= a2;
2028            let values = a_to_witness.values();
2029            lemma_values_finite(a_to_witness);
2030            assert_by(
2031                values.len() > 0,
2032                {
2033                    let x = a_to_witness.dom().choose();
2034                    assert(values.contains(a_to_witness[x]));
2035                },
2036            );
2037            let max_witness = values.find_unique_maximal(r);
2038            values.find_unique_maximal_ensures(r);
2039            values.lemma_maximal_equivalent_greatest(r, max_witness);
2040
2041            assert forall|a: A|
2042                always(#[trigger] a_to_temp_pred(a)).satisfied_by(
2043                    ex.suffix(i).suffix(max_witness),
2044                ) by {
2045                let witness = a_to_witness[a];
2046                assert(r(witness, max_witness));
2047                assert(ex.suffix(i).suffix(max_witness) == ex.suffix(i).suffix(witness).suffix(
2048                    (max_witness - witness) as nat,
2049                ));
2050            }
2051            eventually_proved_by_witness(
2052                ex.suffix(i),
2053                always(tla_forall(a_to_temp_pred)),
2054                max_witness,
2055            );
2056        };
2057    };
2058}
2059
2060// Rules about stability.
2061/// An `□` predicate is `stable`.
2062///
2063/// ## Postconditions
2064/// - `⊧ stable(□p)`
2065pub broadcast proof fn always_p_is_stable<T>(p: TempPred<T>)
2066    ensures
2067        #[trigger] valid(stable(always(p))),
2068{
2069    broadcast use {always_unfold, always_propagate_forwards};
2070
2071}
2072
2073/// A `⇝` predicate is stable.
2074///
2075/// ## Postconditions
2076/// - `⊧ stable(p ⇝ q)`
2077pub proof fn p_leads_to_q_is_stable<T>(p: TempPred<T>, q: TempPred<T>)
2078    ensures
2079        valid(stable(p.leads_to(q))),
2080{
2081    always_p_is_stable(p.implies(eventually(q)));
2082}
2083
2084/// `p ∧ q` is stable if both `p` and `q` are stable.
2085///
2086/// ## Preconditions
2087/// - `⊧ stable(p)`
2088/// - `⊧ stable(q)`
2089/// ## Postconditions
2090/// - `⊧ stable(p ∧ q)`
2091pub broadcast proof fn stable_and_temp<T>(p: TempPred<T>, q: TempPred<T>)
2092    requires
2093        valid(stable(p)),
2094        valid(stable(q)),
2095    ensures
2096        #[trigger] valid(stable(p.and(q))),
2097{
2098    assert forall|ex| #[trigger] p.and(q).satisfied_by(ex) implies always(p.and(q)).satisfied_by(
2099        ex,
2100    ) by {
2101        stable_unfold::<T>(ex, p);
2102        stable_unfold::<T>(ex, q);
2103    }
2104}
2105
2106/// `forall a ⇝ p -> q(a)` is stable.
2107///
2108/// ## Preconditions
2109/// - `∀ a. ⊧ stable(p -> q(a))`
2110///
2111/// ## Postconditions
2112/// - `⊧ stable(∀ a. p -> q(a)))`
2113pub proof fn tla_forall_a_p_leads_to_q_a_is_stable<T, A>(
2114    p: TempPred<T>,
2115    a_to_temp_pred: spec_fn(A) -> TempPred<T>,
2116)
2117    requires
2118        forall|a: A| #[trigger] valid(stable(p.leads_to(a_to_temp_pred(a)))),
2119    ensures
2120        valid(stable(tla_forall(|a: A| p.leads_to(a_to_temp_pred(a))))),
2121{
2122    broadcast use group_definition_basics;
2123
2124    let target = tla_forall(|a: A| p.leads_to(a_to_temp_pred(a)));
2125    assert forall|ex|
2126        (forall|a: A| #[trigger]
2127            valid(stable(p.leads_to(a_to_temp_pred(a))))) implies #[trigger] stable(
2128        target,
2129    ).satisfied_by(ex) by {
2130        assert forall|i|
2131            (forall|a: A| #[trigger] valid(stable(p.leads_to(a_to_temp_pred(a)))))
2132                && target.satisfied_by(ex) implies #[trigger] target.satisfied_by(ex.suffix(i)) by {
2133            assert forall|a: A|
2134                p.leads_to(a_to_temp_pred(a)).satisfied_by(ex) implies #[trigger] p.leads_to(
2135                a_to_temp_pred(a),
2136            ).satisfied_by(ex.suffix(i)) by {
2137                assert(valid(stable(p.leads_to(a_to_temp_pred(a)))));
2138                assert(stable(p.leads_to(a_to_temp_pred(a))).satisfied_by(ex));
2139                if p.leads_to(a_to_temp_pred(a)).satisfied_by(ex) {
2140                }
2141            }
2142        }
2143    }
2144}
2145
2146/// Unpack the conditions from the left to the right side of `⊧`
2147///
2148/// ## Preconditions
2149/// - `⊧ stable(spec)`
2150/// - `spec ∧ c ⊧ p ⇝ q`
2151/// ## Postconditions
2152/// - `spec ⊧ p ∧ c ⇝ q`
2153pub proof fn unpack_conditions_from_spec<T>(
2154    spec: TempPred<T>,
2155    c: TempPred<T>,
2156    p: TempPred<T>,
2157    q: TempPred<T>,
2158)
2159    requires
2160        valid(stable(spec)),
2161        spec.and(c).entails(p.leads_to(q)),
2162    ensures
2163        spec.entails(p.and(c).leads_to(q)),
2164{
2165    broadcast use {always_unfold, implies_apply, group_definition_basics};
2166
2167    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.and(c).leads_to(q).satisfied_by(
2168        ex,
2169    ) by {
2170        assert forall|i| #[trigger] p.and(c).satisfied_by(ex.suffix(i)) implies eventually(
2171            q,
2172        ).satisfied_by(ex.suffix(i)) by {
2173            stable_unfold::<T>(ex, spec);
2174            implies_apply::<T>(ex.suffix(i), spec.and(c), p.leads_to(q));
2175            assert(ex.suffix(i) == ex.suffix(i).suffix(0));
2176            implies_apply::<T>(ex.suffix(i), p, eventually(q));
2177        };
2178    };
2179}
2180
2181/// Pack the conditions from the right to the left side of `⊧`.
2182///
2183/// ## Preconditions
2184/// - `spec ⊧ p ∧ c ⇝ q`
2185///
2186/// ## Postconditions
2187/// - `spec ∧ □c ⊧ p ⇝ q`
2188pub proof fn pack_conditions_to_spec<T>(
2189    spec: TempPred<T>,
2190    c: TempPred<T>,
2191    p: TempPred<T>,
2192    q: TempPred<T>,
2193)
2194    requires
2195        spec.entails(p.and(c).leads_to(q)),
2196    ensures
2197        spec.and(always(c)).entails(p.leads_to(q)),
2198{
2199    broadcast use {always_unfold, always_propagate_forwards};
2200
2201    assert forall|ex| #[trigger] spec.and(always(c)).satisfied_by(ex) implies p.leads_to(
2202        q,
2203    ).satisfied_by(ex) by {
2204        implies_apply(ex, spec, p.and(c).leads_to(q));
2205        leads_to_unfold(ex, p.and(c), q);
2206
2207    }
2208}
2209
2210/// Make the predicate as concise as possible.
2211///
2212/// Similar to the first-order logic where `p` equals `p ∧ q` when `p -> q` is satisfied,
2213/// we can reduce the size of predicate when some part of it implies the rest.
2214///
2215/// ## Preconditions
2216/// - `p ⊧ q`
2217/// ## Postconditions
2218/// - `p == p ∧ q`
2219// Used to be named as simplify_predicate.
2220pub proof fn entails_and_equality<T>(p: TempPred<T>, q: TempPred<T>)
2221    requires
2222        p.entails(q),
2223    ensures
2224        p == p.and(q),
2225{
2226    assert forall|ex| #[trigger] p.satisfied_by(ex) implies p.and(q).satisfied_by(ex) by {
2227        entails_and_temp::<T>(p, p, q);
2228        entails_apply::<T>(ex, p, p.and(q));
2229    };
2230    temp_pred_equality::<T>(p, p.and(q));
2231}
2232
2233/// Transitivity of `⊧` with simplification.
2234///
2235/// ## Preconditions
2236/// - `spec ⊧ p`
2237/// - `spec ∧ p ⊧ q`
2238/// ## Postconditions
2239/// - `spec ⊧ q`
2240pub proof fn entails_trans_by_simplify<T>(spec: TempPred<T>, p: TempPred<T>, q: TempPred<T>)
2241    requires
2242        spec.entails(p),
2243        spec.and(p).entails(q),
2244    ensures
2245        spec.entails(q),
2246{
2247    entails_and_equality(spec, p);
2248}
2249
2250// More advanced rules.
2251/// Proving `p ⇝ q` by borrowing the inv.
2252///
2253/// ## Preconditions
2254/// - `spec ⊧ p ∧ inv ⇝ q`
2255/// - `spec ⊧ □inv`
2256/// ## Postconditions
2257/// - `spec ⊧ p ⇝ q`
2258pub proof fn leads_to_by_borrowing_inv<T>(
2259    spec: TempPred<T>,
2260    p: TempPred<T>,
2261    q: TempPred<T>,
2262    inv: TempPred<T>,
2263)
2264    requires
2265        spec.entails(p.and(inv).leads_to(q)),
2266        spec.entails(always(inv)),
2267    ensures
2268        spec.entails(p.leads_to(q)),
2269{
2270    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(q).satisfied_by(ex) by {
2271        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(q).satisfied_by(
2272            ex.suffix(i),
2273        ) by {
2274            instantiate_entailed_always(ex, i, spec, inv);
2275            instantiate_entailed_leads_to(ex, i, spec, p.and(inv), q);
2276        }
2277    }
2278}
2279
2280/// Enhance the conclusion of leads-to □ using invariant.
2281///
2282/// ## Preconditions
2283/// - `spec ⊧ □inv`
2284/// - `spec ⊧ p ⇝ □q1`
2285/// - `q1 ∧ inv ⊧ q2`
2286/// ## Postconditions
2287/// - `spec ⊧ p ⇝ □q2`
2288pub proof fn leads_to_always_enhance<T>(
2289    spec: TempPred<T>,
2290    inv: TempPred<T>,
2291    p: TempPred<T>,
2292    q1: TempPred<T>,
2293    q2: TempPred<T>,
2294)
2295    requires
2296        spec.entails(always(inv)),
2297        spec.entails(p.leads_to(always(q1))),
2298        q1.and(inv).entails(q2),
2299    ensures
2300        spec.entails(p.leads_to(always(q2))),
2301{
2302    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(always(q2)).satisfied_by(
2303        ex,
2304    ) by {
2305        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(
2306            always(q2),
2307        ).satisfied_by(ex.suffix(i)) by {
2308            implies_apply(ex, spec, always(inv));
2309            implies_apply(ex, spec, p.leads_to(always(q1)));
2310            leads_to_unfold(ex, p, always(q1));
2311            implies_apply(ex.suffix(i), p, eventually(always(q1)));
2312            let witness = eventually_choose_witness(ex.suffix(i), always(q1));
2313            assert forall|j| #[trigger] q2.satisfied_by(ex.suffix(i).suffix(witness).suffix(j)) by {
2314                assert(ex.suffix(i).suffix(witness).suffix(j) == ex.suffix(i + witness + j));
2315                implies_apply(ex.suffix(i).suffix(witness).suffix(j), q1.and(inv), q2);
2316            }
2317            eventually_proved_by_witness(ex.suffix(i), always(q2), witness);
2318        }
2319    }
2320}
2321
2322/// Weaken `⇝` by `⇒`.
2323///
2324/// ## Preconditions
2325/// - `spec ⊧ □(p2 ⇒ p1)`
2326/// - `spec ⊧ □(q1 ⇒ q2)`
2327/// - `spec ⊧ p1 ⇝ q1`
2328/// ## Postconditions
2329/// - `spec ⊧ p2 ⇝ q2`
2330pub proof fn leads_to_weaken<T>(
2331    spec: TempPred<T>,
2332    p1: TempPred<T>,
2333    q1: TempPred<T>,
2334    p2: TempPred<T>,
2335    q2: TempPred<T>,
2336)
2337    requires
2338        spec.entails(always(p2.implies(p1))),
2339        spec.entails(always(q1.implies(q2))),
2340        spec.entails(p1.leads_to(q1)),
2341    ensures
2342        spec.entails(p2.leads_to(q2)),
2343{
2344    always_implies_to_leads_to::<T>(spec, p2, p1);
2345    always_implies_to_leads_to::<T>(spec, q1, q2);
2346    leads_to_trans::<T>(spec, p2, p1, q1);
2347    leads_to_trans::<T>(spec, p2, q1, q2);
2348}
2349
2350/// Proving `p ⇝ q` vacuously.
2351///
2352/// ## Preconditions
2353/// - `spec ⊧ □r`
2354/// - `p ∧ r == false`
2355/// ## Postconditions
2356/// - `spec ⊧ p ⇝ q`
2357pub proof fn vacuous_leads_to<T>(spec: TempPred<T>, p: TempPred<T>, q: TempPred<T>, r: TempPred<T>)
2358    requires
2359        spec.entails(always(r)),
2360        p.and(r) == false_pred::<T>(),
2361    ensures
2362        spec.entails(p.leads_to(q)),
2363{
2364    broadcast use {always_unfold, implies_apply, group_execution_suffix_lemmas};
2365
2366    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(q).satisfied_by(ex) by {
2367        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(q).satisfied_by(
2368            ex.suffix(i),
2369        ) by {
2370            assert_by(
2371                !p.satisfied_by(ex.suffix(i)),
2372                {
2373                    implies_apply::<T>(ex, spec, always(r));
2374                    if p.satisfied_by(ex.suffix(i)) {
2375                        assert(p.and(r).satisfied_by(ex.suffix(i)));
2376                    }
2377                },
2378            );
2379        }
2380    }
2381}
2382
2383/// Derive `p ⇝ q` from `□p ⇝ q` with the assumption that `p` is preserved unless `q` happens.
2384///
2385/// This lemma is useful if we want to show that given `□p ⇝ q`, `q` will eventually hold
2386/// even if `□p` doesn't hold, as long as `p` is preserved until `q` happens.
2387/// A concrete usage is to reason about a pair of concurrent components A and B, where
2388/// (1) A guarantees `□p ⇝ q`, and (2) B makes `p` hold at some point and keeps `p` until `q` holds.
2389/// Note that we formalize "p is preserved until q happens" using `□(p ∧ next ⇒ p' ∨ q')`:
2390/// if `p` holds now, then for any possible next state, either `p` or `q` holds.
2391///
2392/// ## Preconditions
2393/// - `spec ⊧ □p ⇝ q`
2394/// - `spec ⊧ □(p ∧ next ⇒ p' ∨ q')`
2395/// - `spec ⊧ □next`
2396/// ## Postconditions
2397///  - `spec ⊧ p ⇝ q`
2398pub proof fn strengthen_leads_to_with_until<T>(
2399    spec: TempPred<T>,
2400    next: TempPred<T>,
2401    p: TempPred<T>,
2402    q: TempPred<T>,
2403)
2404    requires
2405        spec.entails(always(p).leads_to(q)),
2406        spec.entails(always(p.and(next).implies(later(p).or(later(q))))),
2407        spec.entails(always(next)),
2408    ensures
2409        spec.entails(p.leads_to(q)),
2410{
2411    broadcast use {
2412        always_unfold,
2413        implies_apply,
2414        group_execution_suffix_lemmas,
2415        group_definition_basics,
2416    };
2417
2418    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(q).satisfied_by(ex) by {
2419        implies_apply(ex, spec, always(next));
2420        implies_apply(ex, spec, always(p.and(next).implies(later(p).or(later(q)))));
2421        always_p_or_eventually_q(ex, next, p, q);
2422        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(q).satisfied_by(
2423            ex.suffix(i),
2424        ) by {
2425            if always(p).satisfied_by(ex.suffix(i)) {
2426                implies_apply(ex, spec, always(p).leads_to(q));
2427            }
2428        }
2429    }
2430}
2431
2432/// Get a new `⇝` condition with an until condition.
2433///
2434/// This lemma can be used in compositional proof.
2435/// Suppose that a system consists of two concurrent components `A` and `B`,
2436/// and we want to prove that the entire system makes `P1 ⇝ P3`.
2437/// If we have already proved that
2438/// (1) `A` makes `P1 ⇝ P2` hold, and
2439/// (2) `P2` holds until `P3` holds, and
2440/// (3) if `P2` holds until `P3` holds, then `B` makes `P2 ⇝ P3`
2441/// (in other words, `B` requires that `P2` should hold until `B` makes `P3` hold),
2442/// then we can apply this lemma and prove that the entire system makes `P2 ⇝ P3`,
2443/// then by transitivity we have `P1 ⇝ P3`.
2444///
2445/// Such a case could happen between components with liveness dependencies:
2446/// `A` at some point needs to delegate the task to `B` and `A` needs to wait until `B` finish
2447/// so `A` can start the next task, meanwhile when `B` is working `A` should not disable `B`'s
2448/// progress (i.e., `A` should make sure `P2` holds until `P3` holds).
2449///
2450/// ## Preconditions
2451/// - `spec ⊧ p1 ⇝ q1`
2452/// - `spec ⊧ □(p2 ∧ next ⇒ p2' ∨ q2')`
2453/// - `spec ⊧ □next`
2454/// ## Postconditions
2455/// - `spec ⊧ p1 ∧ p2 ⇝ (q1 ∧ p2) ∨ q2`
2456pub proof fn transform_leads_to_with_until<T>(
2457    spec: TempPred<T>,
2458    next: TempPred<T>,
2459    p1: TempPred<T>,
2460    q1: TempPred<T>,
2461    p2: TempPred<T>,
2462    q2: TempPred<T>,
2463)
2464    requires
2465        spec.entails(p1.leads_to(q1)),
2466        spec.entails(always(p2.and(next).implies(later(p2).or(later(q2))))),
2467        spec.entails(always(next)),
2468    ensures
2469        spec.entails(p1.and(p2).leads_to((q1.and(p2)).or(q2))),
2470{
2471    broadcast use {
2472        always_unfold,
2473        implies_apply,
2474        group_execution_suffix_lemmas,
2475        group_definition_basics,
2476    };
2477
2478    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p1.and(p2).leads_to(
2479        (q1.and(p2)).or(q2),
2480    ).satisfied_by(ex) by {
2481        assert forall|i| #[trigger] p1.and(p2).satisfied_by(ex.suffix(i)) implies eventually(
2482            (q1.and(p2)).or(q2),
2483        ).satisfied_by(ex.suffix(i)) by {
2484            implies_apply::<T>(ex, spec, always(next));
2485            implies_apply::<T>(ex, spec, always(p2.and(next).implies(later(p2).or(later(q2)))));
2486
2487            always_p_or_eventually_q::<T>(ex, next, p2, q2);
2488
2489            implies_apply::<T>(ex, spec, p1.leads_to(q1));
2490
2491            if eventually(q2).satisfied_by(ex.suffix(i)) {
2492                let witness_idx = eventually_choose_witness::<T>(ex.suffix(i), q2);
2493                eventually_proved_by_witness::<T>(ex.suffix(i), (q1.and(p2)).or(q2), witness_idx);
2494            } else {
2495                let witness_idx = eventually_choose_witness::<T>(ex.suffix(i), q1);
2496                eventually_proved_by_witness::<T>(ex.suffix(i), (q1.and(p2)).or(q2), witness_idx);
2497            }
2498        }
2499    }
2500}
2501
2502/// Concluding `p(n) ⇝ p(0)` using ranking functions, with a step of one.
2503///
2504/// ## Preconditions
2505/// - `forall |n: nat|, n > 0 ==> spec ⊧ p(n) ⇝ p(n - 1)`
2506/// ## Postconditions
2507///  - `forall |n: nat|, spec ⊧ p(n) ⇝ p(0)`
2508pub proof fn leads_to_rank_step_one<T>(spec: TempPred<T>, p: spec_fn(nat) -> TempPred<T>)
2509    requires
2510        forall|n: nat| #![trigger p(n)] (n > 0 ==> spec.entails(p(n).leads_to(p((n - 1) as nat)))),
2511    ensures
2512        forall|n: nat| #[trigger] spec.entails(p(n).leads_to(p(0))),
2513{
2514    let pre = {
2515        forall|n: nat| #![trigger p(n)] (n > 0 ==> spec.entails(p(n).leads_to(p((n - 1) as nat))))
2516    };
2517    assert forall|n: nat| pre implies #[trigger] spec.entails(p(n).leads_to(p(0))) by {
2518        leads_to_rank_step_one_help(spec, p, n);
2519    }
2520}
2521
2522proof fn leads_to_rank_step_one_help<T>(spec: TempPred<T>, p: spec_fn(nat) -> TempPred<T>, n: nat)
2523    requires
2524        forall|n: nat| #![trigger p(n)] (n > 0 ==> spec.entails(p(n).leads_to(p((n - 1) as nat)))),
2525    ensures
2526        spec.entails(p(n).leads_to(p(0))),
2527    decreases n,
2528{
2529    if n > 0 {
2530        // p(n) ⇝ p(n - 1), p(n - 1) ⇝ p(0)
2531        // combine with leads-to transitivity
2532        leads_to_rank_step_one_help(spec, p, (n - 1) as nat);
2533        leads_to_trans_n!(spec, p(n), p((n - 1) as nat), p(0));
2534    } else {
2535        // p(0) ⇝ p(0) trivially
2536        leads_to_self_temp(p(0));
2537    }
2538}
2539
2540/// `usize` version of the proof rule [leads_to_rank_step_one].
2541pub proof fn leads_to_rank_step_one_usize<T>(spec: TempPred<T>, p: spec_fn(usize) -> TempPred<T>)
2542    requires
2543        forall|n: usize|
2544            #![trigger p(n)]
2545            (n > 0 ==> spec.entails(p(n).leads_to(p((n - 1) as usize)))),
2546    ensures
2547        forall|n: usize| #[trigger] spec.entails(p(n).leads_to(p(0))),
2548{
2549    let pre = {
2550        forall|n: usize|
2551            #![trigger p(n)]
2552            (n > 0 ==> spec.entails(p(n).leads_to(p((n - 1) as usize))))
2553    };
2554    assert forall|n: usize| pre implies #[trigger] spec.entails(p(n).leads_to(p(0))) by {
2555        leads_to_rank_step_one_usize_help(spec, p, n);
2556    }
2557}
2558
2559proof fn leads_to_rank_step_one_usize_help<T>(
2560    spec: TempPred<T>,
2561    p: spec_fn(usize) -> TempPred<T>,
2562    n: usize,
2563)
2564    requires
2565        forall|n: usize|
2566            #![trigger p(n)]
2567            (n > 0 ==> spec.entails(p(n).leads_to(p((n - 1) as usize)))),
2568    ensures
2569        spec.entails(p(n).leads_to(p(0))),
2570    decreases n,
2571{
2572    if n > 0 {
2573        // p(n) ⇝ p(n - 1), p(n - 1) ⇝ p(0)
2574        // combine with leads-to transitivity
2575        leads_to_rank_step_one_usize_help(spec, p, (n - 1) as usize);
2576        leads_to_trans_n!(spec, p(n), p((n - 1) as usize), p(0));
2577    } else {
2578        // p(0) ⇝ p(0) trivially
2579        leads_to_self_temp(p(0));
2580    }
2581}
2582
2583// Rules about state machine transitions.
2584/// Prove safety by induction.
2585///
2586/// ## Preconditions
2587/// - `⊧ init ⇒ inv``
2588/// - `⊧ inv ∧ next ⇒ inv'`
2589/// - `spec ⊧ init ∧ □next`
2590/// ## Postconditions
2591/// - `spec ⊧ □inv`
2592pub proof fn init_invariant<T>(
2593    spec: TempPred<T>,
2594    init: StatePred<T>,
2595    next: ActionPred<T>,
2596    inv: StatePred<T>,
2597)
2598    requires
2599        forall|s: T| #[trigger] init.apply(s) ==> inv.apply(s),
2600        forall|s, s_prime: T|
2601            inv.apply(s) && #[trigger] next.apply(s, s_prime) ==> inv.apply(s_prime),
2602        spec.entails(lift_state(init)),
2603        spec.entails(always(lift_action(next))),
2604    ensures
2605        spec.entails(always(lift_state(inv))),
2606{
2607    broadcast use group_tla_rules;
2608
2609    assert forall|ex: Execution<T>| spec.satisfied_by(ex) implies #[trigger] always(
2610        lift_state(inv),
2611    ).satisfied_by(ex) by {
2612        implies_apply(ex, spec, lift_state(init));
2613        implies_apply(ex, spec, always(lift_action(next)));
2614        assert forall|i: nat| inv.apply(#[trigger] ex.suffix(i).head()) by {
2615            init_invariant_rec(ex, init, next, inv, i);
2616        };
2617    };
2618}
2619
2620/// Implies new invariant from proved invariant by induction.
2621///
2622/// ## Preconditions
2623/// - `⊧ init ⇒ inv`
2624/// - `⊧ inv ∧ proved_inv ∧ next ⇒ inv'`
2625/// - `spec ⊧ init ∧ □next ∧ □proved_inv`
2626/// ## Postconditions
2627/// - `spec ⊧ □inv`
2628pub proof fn implies_new_invariant<T>(
2629    spec: TempPred<T>,
2630    init: StatePred<T>,
2631    next: ActionPred<T>,
2632    inv: StatePred<T>,
2633    proved_inv: StatePred<T>,
2634)
2635    requires
2636        forall|s: T| #[trigger] init.apply(s) ==> inv.apply(s),
2637        forall|s, s_prime: T|
2638            inv.apply(s) && proved_inv.apply(s) && #[trigger] next.apply(s, s_prime) ==> inv.apply(
2639                s_prime,
2640            ),
2641        spec.entails(lift_state(init)),
2642        spec.entails(always(lift_action(next))),
2643        spec.entails(always(lift_state(proved_inv))),
2644    ensures
2645        spec.entails(always(lift_state(inv))),
2646{
2647    broadcast use group_tla_rules;
2648
2649    assert forall|ex: Execution<T>| spec.satisfied_by(ex) implies #[trigger] always(
2650        lift_state(inv),
2651    ).satisfied_by(ex) by {
2652        implies_apply(ex, spec, lift_state(init));
2653        implies_apply(ex, spec, always(lift_action(next)));
2654        implies_apply(ex, spec, always(lift_state(proved_inv)));
2655        assert forall|i: nat| inv.apply(#[trigger] ex.suffix(i).head()) by {
2656            implies_new_invariant_rec(ex, init, next, inv, proved_inv, i);
2657        };
2658    };
2659}
2660
2661/// Get the initial `⇝`.
2662///
2663/// ## Preconditions
2664/// - `spec ⊧ □(p ∧ next ⇒ p' ∨ q')`
2665/// - `spec ⊧ □(p ∧ next ∧ forward ⇒ q')`
2666/// - `spec ⊧ □next`
2667/// - `spec ⊧ □p ⇝ forward`
2668/// ## Postconditions
2669/// - `spec ⊧ p ⇝ q`
2670pub proof fn wf1_variant_temp<T>(
2671    spec: TempPred<T>,
2672    next: TempPred<T>,
2673    forward: TempPred<T>,
2674    p: TempPred<T>,
2675    q: TempPred<T>,
2676)
2677    requires
2678        spec.entails(always(p.and(next).implies(later(p).or(later(q))))),
2679        spec.entails(always(p.and(next).and(forward).implies(later(q)))),
2680        spec.entails(always(next)),
2681        spec.entails(always(p).leads_to(forward)),
2682    ensures
2683        spec.entails(p.leads_to(q)),
2684{
2685    broadcast use group_tla_rules;
2686
2687    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies p.leads_to(q).satisfied_by(ex) by {
2688        assert forall|i| #[trigger] p.satisfied_by(ex.suffix(i)) implies eventually(q).satisfied_by(
2689            ex.suffix(i),
2690        ) by {
2691            implies_apply::<T>(ex, spec, always(next));
2692            implies_apply::<T>(ex, spec, always(p.and(next).implies(later(p).or(later(q)))));
2693            implies_apply::<T>(ex, spec, always(p.and(next).and(forward).implies(later(q))));
2694
2695            always_p_or_eventually_q::<T>(ex, next, p, q);
2696            implies_apply::<T>(ex.suffix(i), p, always(p).or(eventually(q)));
2697            if always(p).satisfied_by(ex.suffix(i)) {
2698                implies_apply::<T>(ex, spec, always(p).leads_to(forward));
2699                leads_to_unfold::<T>(ex, always(p), forward);
2700                implies_apply::<T>(ex.suffix(i), always(p), eventually(forward));
2701                let witness_idx = eventually_choose_witness::<T>(ex.suffix(i), forward);
2702
2703                always_propagate_forwards::<T>(ex, next, i);
2704                always_propagate_forwards::<T>(ex, p.and(next).and(forward).implies(later(q)), i);
2705                implies_apply::<T>(
2706                    ex.suffix(i).suffix(witness_idx),
2707                    p.and(next).and(forward),
2708                    later(q),
2709                );
2710                assert(ex.suffix(i).suffix(witness_idx).suffix(1) == ex.suffix(i).suffix(
2711                    witness_idx + 1,
2712                ));
2713                eventually_proved_by_witness::<T>(ex.suffix(i), q, witness_idx + 1);
2714            }
2715        }
2716    }
2717}
2718
2719/// Get the initial `⇝` with a stronger wf assumption than [`wf1_variant_temp`].
2720///
2721/// ## Preconditions
2722/// - `⊧ p ∧ next ⇒ p' ∨ q'`
2723/// - `⊧ p ∧ next ∧ forward ⇒ q'`
2724/// - `⊧ p ⇒ enabled(forward)`
2725/// - `spec ⊧ □lift_action(next)`
2726/// - `spec ⊧ wf(forward)`
2727/// ## Postconditions
2728/// - `spec ⊧ p ⇝ q`
2729pub proof fn wf1<T>(
2730    spec: TempPred<T>,
2731    next: ActionPred<T>,
2732    forward: ActionPred<T>,
2733    p: StatePred<T>,
2734    q: StatePred<T>,
2735)
2736    requires
2737        forall|s, s_prime: T|
2738            p.apply(s) && #[trigger] next.apply(s, s_prime) ==> p.apply(s_prime) || q.apply(
2739                s_prime,
2740            ),
2741        forall|s, s_prime: T|
2742            p.apply(s) && #[trigger] next.apply(s, s_prime) && forward.apply(s, s_prime)
2743                ==> q.apply(s_prime),
2744        forall|s: T| #[trigger] p.apply(s) ==> enabled(forward).apply(s),
2745        spec.entails(always(lift_action(next))),
2746        spec.entails(weak_fairness(forward)),
2747    ensures
2748        spec.entails(lift_state(p).leads_to(lift_state(q))),
2749{
2750    broadcast use group_tla_rules;
2751
2752    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies always(lift_state(p)).leads_to(
2753        lift_action(forward),
2754    ).satisfied_by(ex) by {
2755        assert forall|i| #[trigger]
2756            always(lift_state(p)).satisfied_by(ex.suffix(i)) implies eventually(
2757            lift_action(forward),
2758        ).satisfied_by(ex.suffix(i)) by {
2759            implies_apply_with_always::<T>(
2760                ex.suffix(i),
2761                lift_state(p),
2762                lift_state(enabled(forward)),
2763            );
2764            assert(ex.suffix(i).suffix(0) == ex.suffix(i));
2765
2766            implies_apply::<T>(ex, spec, weak_fairness(forward));
2767            weak_fairness_unfold::<T>(ex, forward);
2768            implies_apply::<T>(
2769                ex.suffix(i),
2770                lift_state(enabled(forward)),
2771                eventually(lift_action(forward)),
2772            );
2773        };
2774    };
2775    wf1_variant_temp::<T>(
2776        spec,
2777        lift_action(next),
2778        lift_action(forward),
2779        lift_state(p),
2780        lift_state(q),
2781    );
2782}
2783
2784/// Get the initial `⇝` with a stronger wf assumption by borrowing existing inv.
2785///
2786/// ## Preconditions
2787/// - `⊧ p ∧ inv ∧ next ⇒ p' ∨ q'`
2788/// - `⊧ p ∧ inv ∧ next ∧ forward ⇒ q'`
2789/// - `⊧ p ∧ inv ⇒ enabled(forward)`
2790/// - `spec ⊧ □lift_action(next)`
2791/// - `spec ⊧ □lift_state(inv)`
2792/// - `spec ⊧ wf(forward)`
2793/// ## Postconditions
2794/// - `spec ⊧ p ⇝ q`
2795pub proof fn wf1_by_borrowing_inv<T>(
2796    spec: TempPred<T>,
2797    next: ActionPred<T>,
2798    forward: ActionPred<T>,
2799    p: StatePred<T>,
2800    q: StatePred<T>,
2801    inv: StatePred<T>,
2802)
2803    requires
2804        forall|s, s_prime: T|
2805            p.apply(s) && inv.apply(s) && #[trigger] next.apply(s, s_prime) ==> p.apply(s_prime)
2806                || q.apply(s_prime),
2807        forall|s, s_prime: T|
2808            p.apply(s) && inv.apply(s) && #[trigger] next.apply(s, s_prime) && forward.apply(
2809                s,
2810                s_prime,
2811            ) ==> q.apply(s_prime),
2812        forall|s: T| #[trigger] p.apply(s) && inv.apply(s) ==> enabled(forward).apply(s),
2813        spec.entails(always(lift_action(next))),
2814        spec.entails(always(lift_state(inv))),
2815        spec.entails(weak_fairness(forward)),
2816    ensures
2817        spec.entails(lift_state(p).leads_to(lift_state(q))),
2818{
2819    broadcast use group_tla_rules;
2820
2821    let next_and_inv = lift_action(next).and(lift_state(inv));
2822
2823    assert forall|ex| #[trigger] spec.satisfied_by(ex) implies always(
2824        lift_state(p).and(lift_state(inv)),
2825    ).leads_to(lift_action(forward)).satisfied_by(ex) by {
2826        assert forall|i| #[trigger]
2827            always(lift_state(p).and(lift_state(inv))).satisfied_by(
2828                ex.suffix(i),
2829            ) implies eventually(lift_action(forward)).satisfied_by(ex.suffix(i)) by {
2830            implies_apply_with_always::<T>(
2831                ex.suffix(i),
2832                lift_state(p).and(lift_state(inv)),
2833                lift_state(enabled(forward)),
2834            );
2835            assert(ex.suffix(i).suffix(0) == ex.suffix(i));
2836            implies_apply::<T>(ex, spec, weak_fairness(forward));
2837            weak_fairness_unfold::<T>(ex, forward);
2838            implies_apply::<T>(
2839                ex.suffix(i),
2840                lift_state(enabled(forward)),
2841                eventually(lift_action(forward)),
2842            );
2843        };
2844    };
2845    entails_always_and_n!(spec, lift_action(next), lift_state(inv));
2846    always_and_equality(lift_state(p), lift_state(inv));
2847    always_double_equality(lift_state(inv));
2848    leads_to_by_borrowing_inv(
2849        spec,
2850        always(lift_state(p)),
2851        lift_action(forward),
2852        always(lift_state(inv)),
2853    );
2854    wf1_variant_temp::<T>(spec, next_and_inv, lift_action(forward), lift_state(p), lift_state(q));
2855}
2856
2857/// Strengthen next with inv.
2858///
2859/// ## Preconditions
2860/// - `spec ⊧ □next`
2861/// - `spec ⊧ □inv`
2862/// - `⊧ next ∧ inv <=> next_and_inv`
2863/// ## Postconditions
2864/// - `spec ⊧ □next_and_inv`
2865pub proof fn strengthen_next<T>(
2866    spec: TempPred<T>,
2867    next: ActionPred<T>,
2868    inv: StatePred<T>,
2869    next_and_inv: ActionPred<T>,
2870)
2871    requires
2872        spec.entails(always(lift_action(next))),
2873        spec.entails(always(lift_state(inv))),
2874        lift_action(next_and_inv).entails(lift_action(next).and(lift_state(inv))),
2875        lift_action(next).and(lift_state(inv)).entails(lift_action(next_and_inv)),
2876    ensures
2877        spec.entails(always(lift_action(next_and_inv))),
2878{
2879    entails_and_temp::<T>(spec, always(lift_action(next)), always(lift_state(inv)));
2880    always_and_equality::<T>(lift_action(next), lift_state(inv));
2881    temp_pred_equality::<T>(lift_action(next_and_inv), lift_action(next).and(lift_state(inv)));
2882}
2883
2884// Macros for multiple-predicate variants of the above rules.
2885#[macro_export]
2886macro_rules! leads_to_trans_n {
2887    [$($tail:tt)*] => {
2888        ::vstd::prelude::verus_proof_macro_exprs!($crate::temporal_logic::rules::leads_to_trans_n_internal!($($tail)*));
2889    };
2890}
2891
2892#[macro_export]
2893macro_rules! leads_to_trans_n_internal {
2894    ($spec:expr, $p1:expr, $p2:expr, $p3:expr $(,)?) => {
2895        leads_to_trans($spec, $p1, $p2, $p3);
2896    };
2897    ($spec:expr, $p1:expr, $p2:expr, $p3:expr, $($tail:tt)*) => {
2898        leads_to_trans($spec, $p1, $p2, $p3);
2899        leads_to_trans_n_internal!($spec, $p1, $p3, $($tail)*);
2900    };
2901}
2902
2903pub use leads_to_trans_n;
2904pub use leads_to_trans_n_internal;
2905
2906// Combine multiple StatePred using `.and`.
2907// Usage: state_pred_and!(p1) | p1
2908//        state_pred_and!(p1, p2) | p1.and(p2)
2909//        state_pred_and!(p1, p2, p3, ...) | p1.and(p2).and(p3)...
2910#[macro_export]
2911macro_rules! state_pred_and {
2912    ($p1:expr $(,)?) => {
2913        $p1
2914    };
2915    ($p1:expr, $p2:expr $(,)?) => {
2916        $p1.and($p2)
2917    };
2918    ($p1:expr, $p2:expr, $($tail:expr),+ $(,)?) => {
2919        {
2920            let tmp = $p1.and($p2);
2921            $( let tmp = tmp.and($tail); )*
2922            tmp
2923        }
2924    };
2925}
2926
2927pub use state_pred_and;
2928
2929// Combine multiple TempPred using `.and`.
2930// Usage: temp_pred_and!(p1) | p1
2931//        temp_pred_and!(p1, p2) | p1.and(p2)
2932//        temp_pred_and!(p1, p2, p3, ...) | p1.and(p2).and(p3)...
2933#[macro_export]
2934macro_rules! temp_pred_and {
2935    [$($tail:tt)*] => {
2936        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::temp_pred_and_internal!($($tail)*))
2937    }
2938}
2939
2940#[macro_export]
2941macro_rules! temp_pred_and_internal {
2942    ($next:expr) => {
2943        $next
2944    };
2945    ($next:expr, $($expr:expr),* $(,)?) => {
2946        $next $(
2947            .and($expr)
2948        )*
2949    };
2950}
2951
2952pub use temp_pred_and;
2953pub use temp_pred_and_internal;
2954
2955// Entails the conjunction of all the p if entails each of them.
2956// ## Preconditions
2957//     spec ⊧ p1
2958//     spec ⊧ p2
2959//        ...
2960//     spec ⊧ pn
2961// ## Postconditions
2962//     spec ⊧ p1 ∧ p2 ∧ ... ∧ pn
2963//
2964// Usage: entails_and_n!(spec, p1, p2, p3, p4)
2965#[macro_export]
2966macro_rules! entails_and_n {
2967    [$($tail:tt)*] => {
2968        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::entails_and_n_internal!($($tail)*));
2969    };
2970}
2971
2972#[macro_export]
2973macro_rules! entails_and_n_internal {
2974    ($spec:expr, $p1:expr, $p2:expr $(,)?) => {
2975        entails_and_temp($spec, $p1, $p2);
2976    };
2977    ($spec:expr, $p1:expr, $p2:expr, $($tail:tt)*) => {
2978        entails_and_temp($spec, $p1, $p2);
2979        entails_and_n_internal!($spec, $p1.and($p2), $($tail)*);
2980    };
2981}
2982
2983pub use entails_and_n;
2984pub use entails_and_n_internal;
2985
2986// Entails □ the conjunction of all the p if entails each always p.
2987// ## Preconditions
2988//     spec ⊧ □p1
2989//     spec ⊧ □p2
2990//        ...
2991//     spec ⊧ □pn
2992// ## Postconditions
2993//     spec ⊧ □(p1 ∧ p2 ∧ ... ∧ pn)
2994//
2995// Usage: entails_always_and_n!(spec, p1, p2, p3, p4)
2996#[macro_export]
2997macro_rules! entails_always_and_n {
2998    [$($tail:tt)*] => {
2999        ::vstd::prelude::verus_proof_macro_exprs!($crate::temporal_logic::rules::entails_always_and_n_internal!($($tail)*));
3000    };
3001}
3002
3003#[macro_export]
3004macro_rules! entails_always_and_n_internal {
3005    ($spec:expr, $p1:expr $(,)?) => {};
3006    ($spec:expr, $p1:expr, $p2:expr $(,)?) => {
3007        entails_and_temp($spec, always($p1), always($p2));
3008        always_and_equality($p1, $p2);
3009    };
3010    ($spec:expr, $p1:expr, $p2:expr, $($tail:tt)*) => {
3011        entails_and_temp($spec, always($p1), always($p2));
3012        always_and_equality($p1, $p2);
3013        entails_always_and_n_internal!($spec, $p1.and($p2), $($tail)*);
3014    };
3015}
3016
3017pub use entails_always_and_n;
3018pub use entails_always_and_n_internal;
3019
3020#[macro_export]
3021macro_rules! always_and_equality_n {
3022    [$($tail:tt)*] => {
3023        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::always_and_equality_n_internal!($($tail)*));
3024    };
3025}
3026
3027#[macro_export]
3028macro_rules! always_and_equality_n_internal {
3029    ($p1:expr, $p2:expr $(,)?) => {
3030        always_and_equality($p1, $p2);
3031    };
3032    ($p1:expr, $p2:expr, $($tail:tt)*) => {
3033        always_and_equality($p1, $p2);
3034        always_and_equality_n_internal!($p1.and($p2), $($tail)*);
3035    };
3036}
3037
3038pub use always_and_equality_n;
3039pub use always_and_equality_n_internal;
3040
3041// □(lift_state(state_pred_and!(p1, p2, ..., pn))) == □(lift_state(p1)).and(□(lift_state(p2))).and(...).and(□(lift_state(pn)))
3042#[macro_export]
3043macro_rules! always_lift_state_and_equality_n {
3044    [$($tail:tt)*] => {
3045        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::always_lift_state_and_equality_n_internal!($($tail)*));
3046    };
3047}
3048
3049#[macro_export]
3050macro_rules! always_lift_state_and_equality_n_internal {
3051    ($p1:expr, $p2:expr $(,)?) => {
3052        always_lift_state_and_equality($p1, $p2);
3053    };
3054    ($p1:expr, $p2:expr, $($tail:tt)*) => {
3055        always_lift_state_and_equality($p1, $p2);
3056        always_lift_state_and_equality_n_internal!(state_pred_and!($p1, $p2), $($tail)*);
3057    };
3058}
3059
3060pub use always_lift_state_and_equality_n;
3061pub use always_lift_state_and_equality_n_internal;
3062
3063// Combine the premises of all the leads_to using or.
3064// ## Preconditions
3065//     spec ⊧ p1 ⇝ q
3066//     spec ⊧ p2 ⇝ q
3067//         ...
3068//     spec ⊧ pn ⇝ q
3069// ## Postconditions
3070//     spec ⊧ (p1 ∨ p2 ∨ ... ∨ pn) ⇝ q
3071//
3072// Usage: or_leads_to_combine_n!(spec, p1, p2, p3, p4; q)
3073#[macro_export]
3074macro_rules! or_leads_to_combine_n {
3075    [$($tail:tt)*] => {
3076        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::or_leads_to_combine_n_internal!($($tail)*));
3077    };
3078}
3079
3080#[macro_export]
3081macro_rules! or_leads_to_combine_n_internal {
3082    ($spec:expr, $p1:expr, $p2:expr; $q:expr) => {
3083        or_leads_to_combine($spec, $p1, $p2, $q);
3084    };
3085    ($spec:expr, $p1:expr, $p2:expr, $($rest:expr),+; $q:expr) => {
3086        or_leads_to_combine($spec, $p1, $p2, $q);
3087        or_leads_to_combine_n_internal!($spec, $p1.or($p2), $($rest),+; $q);
3088    };
3089}
3090
3091pub use or_leads_to_combine_n;
3092pub use or_leads_to_combine_n_internal;
3093
3094// Combine or_leads_to_combine and temp_pred_equality.
3095// The 'result' is the equivalent temporal predicate of joining all following predicates with ∨.
3096#[macro_export]
3097macro_rules! or_leads_to_combine_and_equality2 {
3098    ($spec:expr, $result:expr, $p1:expr, $($rest:expr),+; $q:expr) => {
3099        temp_pred_equality(
3100            $result,
3101            $p1$(.or($rest))+
3102        );
3103        or_leads_to_combine_n!($spec, $p1, $($rest),+; $q);
3104    }
3105}
3106
3107pub use or_leads_to_combine_and_equality2;
3108
3109// Leads to the conjunction of all the □q if leads to each of them.
3110// ## Preconditions
3111//     spec ⊧ p ⇝ □q1
3112//     spec ⊧ p ⇝ □q2
3113//        ...
3114//     spec ⊧ p ⇝ □qn
3115// ## Postconditions
3116//     spec ⊧ p ⇝ □(q1 ∧ q2 ∧ ... ∧ qn)
3117//
3118// Usage: leads_to_always_combine_n!(spec, p, q1, q2, q3, q4)
3119#[macro_export]
3120macro_rules! leads_to_always_combine_n {
3121    [$($tail:tt)*] => {
3122        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::leads_to_always_combine_n_internal!($($tail)*));
3123    };
3124}
3125
3126#[macro_export]
3127macro_rules! leads_to_always_combine_n_internal {
3128    ($spec:expr, $p:expr, $q1:expr, $q2:expr $(,)?) => {
3129        leads_to_always_combine($spec, $p, $q1, $q2);
3130    };
3131    ($spec:expr, $p:expr, $q1:expr, $q2:expr, $($tail:tt)*) => {
3132        leads_to_always_combine($spec, $p, $q1, $q2);
3133        always_and_equality($q1, $q2);
3134        leads_to_always_combine_n_internal!($spec, $p, $q1.and($q2), $($tail)*);
3135    };
3136}
3137
3138pub use leads_to_always_combine_n;
3139pub use leads_to_always_combine_n_internal;
3140
3141#[macro_export]
3142macro_rules! leads_to_always_combine_n_with_equality {
3143    [$($tail:tt)*] => {
3144        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::leads_to_always_combine_n_with_equality_internal!($($tail)*));
3145    };
3146}
3147
3148#[macro_export]
3149macro_rules! leads_to_always_combine_n_with_equality_internal {
3150    ($spec:expr, $p:expr, $dst:expr, $($tail:tt)*) => {
3151        temp_pred_equality($dst, combine_with_next!($($tail)*));
3152        leads_to_always_combine_n!($spec, $p, $($tail)*);
3153    };
3154}
3155
3156pub use leads_to_always_combine_n_with_equality;
3157pub use leads_to_always_combine_n_with_equality_internal;
3158
3159// Strengthen next with arbitrary number of predicates.
3160// ## Preconditions
3161//     spec ⊧ □p1
3162//     spec ⊧ □p2
3163//        ...
3164//     spec ⊧ □pn
3165//     p1 ∧ p2 ∧ ... ∧ pn ==> partial_spec
3166// ## Postconditions
3167//     spec ⊧ □all
3168//
3169// Usage: combine_spec_entails_always_n!(spec, partial_spec, p1, p2, p3, p4)
3170#[macro_export]
3171macro_rules! combine_spec_entails_always_n {
3172    [$($tail:tt)*] => {
3173        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::combine_spec_entails_always_n_internal!($($tail)*))
3174    }
3175}
3176
3177#[macro_export]
3178macro_rules! combine_spec_entails_always_n_internal {
3179    ($spec:expr, $partial_spec:expr, $($tail:tt)*) => {
3180        assert_by(
3181            valid($spec.implies(always(temp_pred_and!($($tail)*)))),
3182            {
3183                entails_always_and_n!($spec, $($tail)*);
3184            }
3185        );
3186        entails_preserved_by_always(temp_pred_and!($($tail)*), $partial_spec);
3187        entails_trans($spec, always(temp_pred_and!($($tail)*)), always($partial_spec));
3188    };
3189}
3190
3191pub use combine_spec_entails_always_n;
3192pub use combine_spec_entails_always_n_internal;
3193
3194// Combine multiple □ lift_state predicates using AND.
3195// ## Preconditions
3196//     spec ⊧ □lift_state(p1)
3197//     spec ⊧ □lift_state(p2)
3198//     ...
3199//     spec ⊧ □lift_state(pn)
3200// ## Postconditions
3201// spec ⊧ □lift_state(combine_state_pred!(p1, p2, ..., pn))  // spec ⊧ □lift_state(|s| p1(s) && p2(s) && ... && pn(s))
3202//
3203// Usage: entails_always_lift_state_and_n!(spec, p1, p2, p3, p4)
3204#[macro_export]
3205macro_rules! entails_always_lift_state_and_n {
3206    [$($tail:tt)*] => {
3207        ::vstd::prelude::verus_proof_macro_exprs!($crate::temporal_logic::rules::entails_always_lift_state_and_n_internal!($($tail)*))
3208    };
3209}
3210
3211#[macro_export]
3212macro_rules! entails_always_lift_state_and_n_internal {
3213    ($spec:expr, $p1:expr) => {
3214        // Single predicate case: nothing to do
3215    };
3216    ($spec:expr, $p1:expr, $p2:expr $(,)?) => {
3217        // Two predicate case: use entails_always_lift_state_and
3218        entails_always_lift_state_and($spec, $p1, $p2);
3219    };
3220    ($spec:expr, $p1:expr, $p2:expr, $p3:expr $(,)?) => {
3221        // Three predicate case: build step by step
3222        entails_always_lift_state_and($spec, $p1, $p2);
3223        let combined_p1_p2 = state_pred_and!($p1, $p2);
3224        entails_always_lift_state_and($spec, combined_p1_p2, $p3);
3225    };
3226    ($spec:expr, $p1:expr, $p2:expr, $($tail:expr),+ $(,)?) => {
3227        // Multi predicate case: recursively combine predicates
3228        entails_always_lift_state_and($spec, $p1, $p2);
3229        let combined_p1_p2 = state_pred_and!($p1, $p2);
3230        entails_always_lift_state_and_n_internal!($spec, combined_p1_p2, $($tail),+);
3231    };
3232}
3233
3234pub use entails_always_lift_state_and_n;
3235pub use entails_always_lift_state_and_n_internal;
3236
3237// Show that an spec entails the invariant by a group of action/state predicates which are also invariants entailed by spec.
3238// ## Preconditions
3239//     partial_spec ⊧ inv
3240//     spec ⊧ □p1
3241//     spec ⊧ □p2
3242//         ...
3243//     spec ⊧ □pn
3244//     p1 ∧ p2 ∧ ... ∧ pn ==> partial_spec
3245// ## Postconditions
3246//     spec ⊧ □inv
3247//
3248// Usage: invariant_n!(spec, partial_spec, inv, p1, p2, ..., pn)
3249#[macro_export]
3250macro_rules! invariant_n {
3251    [$($tail:tt)*] => {
3252        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::invariant_n_internal!($($tail)*))
3253    }
3254}
3255
3256#[macro_export]
3257macro_rules! invariant_n_internal {
3258    ($spec:expr, $partial_spec:expr, $inv:expr, $($tail:tt)*) => {
3259        assert_by(
3260            valid($spec.implies(always(temp_pred_and!($($tail)*)))),
3261            {
3262                entails_always_and_n!($spec, $($tail)*);
3263            }
3264        );
3265        entails_preserved_by_always(temp_pred_and!($($tail)*), $partial_spec);
3266        entails_trans($spec, always(temp_pred_and!($($tail)*)), always($partial_spec));
3267        entails_preserved_by_always($partial_spec, $inv);
3268        entails_trans($spec, always($partial_spec), always($inv));
3269    };
3270}
3271
3272pub use invariant_n;
3273pub use invariant_n_internal;
3274
3275// The conjunction of all the p is stable if each p is stable.
3276// ## Preconditions
3277//     ⊧ stable(p1)
3278//     ⊧ stable(p2)
3279//      ...
3280//     ⊧ stable(pn)
3281// ## Postconditions
3282//     ⊧ stable(p1 ∧ p2 ∧ ... ∧ pn)
3283//
3284// Usage: stable_and_n!(p1, p2, p3, p4)
3285#[macro_export]
3286macro_rules! stable_and_n {
3287    [$($tail:tt)*] => {
3288        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::stable_and_n_internal!($($tail)*));
3289    };
3290}
3291
3292#[macro_export]
3293macro_rules! stable_and_n_internal {
3294    ($p1:expr, $p2:expr $(,)?) => {
3295        stable_and_temp($p1, $p2);
3296    };
3297    ($p1:expr, $p2:expr, $($tail:tt)*) => {
3298        stable_and_temp($p1, $p2);
3299        stable_and_n_internal!($p1.and($p2), $($tail)*);
3300    };
3301}
3302
3303pub use stable_and_n;
3304pub use stable_and_n_internal;
3305
3306// The conjunction of all the p is stable if each p is stable.
3307// ## Postconditions
3308//     ⊧ stable(□p1 ∧ □p2 ∧ ... ∧ □pn)
3309//
3310// Usage: stable_and_always_n!(p1, p2, p3, p4)
3311#[macro_export]
3312macro_rules! stable_and_always_n {
3313    [$($tail:tt)*] => {
3314        ::verus_builtin_macros::verus_proof_macro_exprs!($crate::temporal_logic::rules::stable_and_always_n_internal!($($tail)*));
3315    };
3316}
3317
3318#[macro_export]
3319macro_rules! stable_and_always_n_internal {
3320    ($p1:expr, $($tail:expr),* $(,)?) => {
3321        always_p_is_stable($p1);
3322        $(always_p_is_stable($tail);)*
3323        stable_and_n!(always($p1), $(always($tail)),*);
3324    };
3325}
3326
3327pub use stable_and_always_n;
3328pub use stable_and_always_n_internal;
3329
3330// Implies new invariant from multiple proved invariants.
3331// ## Preconditions
3332//     ⊧ init ⇒ inv
3333//     ⊧ inv ∧ p1 ∧ p2 ∧ ... ∧ pn ∧ next ⇒ inv'
3334//     spec ⊧ lift_state(init)
3335//     spec ⊧ □(lift_action(next))
3336//     spec ⊧ □lift_state(p1)
3337//     spec ⊧ □lift_state(p2)
3338//     ...
3339//     spec ⊧ □lift_state(pn)
3340// ## Postconditions
3341//     spec ⊧ □lift_state(inv)
3342//
3343// Usage: implies_new_invariant_n!(spec, init, next, inv, p1, p2, p3, ...)
3344#[macro_export]
3345macro_rules! implies_new_invariant_n {
3346    [$($tail:tt)*] => {
3347        ::vstd::prelude::verus_proof_macro_exprs!($crate::temporal_logic::rules::implies_new_invariant_n_internal!($($tail)*))
3348    };
3349}
3350
3351#[macro_export]
3352macro_rules! implies_new_invariant_n_internal {
3353    ($spec:expr, $init:expr, $next:expr, $inv:expr, $p1:expr) => {
3354        implies_new_invariant($spec, $init, $next, $inv, $p1);
3355    };
3356    ($spec:expr, $init:expr, $next:expr, $inv:expr, $p1:expr,) => {
3357        implies_new_invariant_n_internal!($spec, $init, $next, $inv, $p1);
3358    };
3359    ($spec:expr, $init:expr, $next:expr, $inv:expr, $($preds:expr),+) => {
3360        {
3361            let combined_pred = state_pred_and!($($preds),+);
3362            entails_always_lift_state_and_n!($spec, $($preds),+);
3363            implies_new_invariant($spec, $init, $next, $inv, combined_pred);
3364        }
3365    };
3366    ($spec:expr, $init:expr, $next:expr, $inv:expr, $($preds:expr),+ ,) => {
3367        implies_new_invariant_n_internal!($spec, $init, $next, $inv, $($preds),+);
3368    };
3369}
3370
3371pub use implies_new_invariant_n;
3372pub use implies_new_invariant_n_internal;
3373
3374// Strengthen wf1_by_borrowing_inv with multiple invariants.
3375// ## Preconditions
3376//     spec ⊧ □next
3377//     spec ⊧ □inv1
3378//     spec ⊧ □inv2
3379//        ...
3380//     spec ⊧ □invn
3381//     ⊧ p ∧ inv1 ∧ inv2 ∧ ... ∧ invn ∧ next ⇒ p' ∨ q'
3382//     ⊧ p ∧ inv1 ∧ inv2 ∧ ... ∧ invn ∧ next ∧ forward ⇒ q'
3383//     ⊧ p ∧ inv1 ∧ inv2 ∧ ... ∧ invn ⇒ enabled(forward)
3384//     spec ⊧ wf(forward)
3385// ## Postconditions
3386//     spec ⊧ p ⇝ q
3387// Usage: wf1_by_borrowing_inv_n!(spec, next, forward, p, q, inv1, inv2, inv3, ...)
3388#[macro_export]
3389macro_rules! wf1_by_borrowing_inv_n {
3390    ($spec:expr, $next:expr, $forward:expr, $p:expr, $q:expr, $($inv:expr),+ $(,)?) => {{
3391        entails_always_lift_state_and_n!($spec, $($inv),+);
3392        // Explicitly call the base case to ensure we have the needed condition
3393        let combined_inv = state_pred_and!($($inv),+);
3394        // After entails_always_lift_state_and_n!, we should have spec.entails(always(lift_state(combined_inv)))
3395        wf1_by_borrowing_inv($spec, $next, $forward, $p, $q, combined_inv)
3396    }};
3397}
3398
3399pub use wf1_by_borrowing_inv_n;
3400
3401// Apply always_lift_state_weaken with multiple predicates.
3402// ## Preconditions
3403//     forall |s| p1(s) && p2(s) && ... && pn(s) ==> h(s)
3404//     spec ⊧ □lift_state(p1)
3405//     spec ⊧ □lift_state(p2)
3406//     ...
3407//     spec ⊧ □lift_state(pn)
3408// ## Postconditions
3409//     spec ⊧ □lift_state(h)
3410//
3411// Usage: always_lift_state_weaken_n!(spec, p1, p2, p3, ..., h)
3412#[macro_export]
3413macro_rules! always_lift_state_weaken_n {
3414    [$($tail:tt)*] => {
3415        ::vstd::prelude::verus_proof_macro_exprs!($crate::temporal_logic::rules::always_lift_state_weaken_n_internal!($($tail)*))
3416    };
3417}
3418
3419#[macro_export]
3420macro_rules! always_lift_state_weaken_n_internal {
3421    ($spec:expr, $p1:expr, $h:expr) => {
3422        always_lift_state_weaken($spec, $p1, $h);
3423    };
3424    ($spec:expr, $p1:expr, $p2:expr, $h:expr) => {
3425        entails_always_lift_state_and_n!($spec, $p1, $p2);
3426        always_lift_state_weaken($spec, state_pred_and!($p1, $p2), $h);
3427    };
3428    ($spec:expr, $p1:expr, $p2:expr, $h:expr,) => {
3429        always_lift_state_weaken_n_internal!($spec, $p1, $p2, $h);
3430    };
3431    ($spec:expr, $p1:expr, $p2:expr, $($tail:expr),+ , $h:expr) => {
3432        entails_always_lift_state_and_n!($spec, $p1, $p2, $($tail),+);
3433        always_lift_state_weaken($spec, state_pred_and!($p1, $p2, $($tail),+), $h);
3434    };
3435    ($spec:expr, $p1:expr, $p2:expr, $($tail:expr),+ , $h:expr,) => {
3436        always_lift_state_weaken_n_internal!($spec, $p1, $p2, $($tail),+ , $h);
3437    };
3438}
3439
3440pub use always_lift_state_weaken_n;
3441pub use always_lift_state_weaken_n_internal;
3442
3443pub broadcast group group_tla_rules {
3444    state_pred_and_apply_equality,
3445    state_pred_or_apply_equality,
3446    state_pred_implies_apply_equality,
3447    state_pred_not_apply_equality,
3448    lift_state_and_equality,
3449    lift_state_or_equality,
3450    lift_state_not_equality,
3451    lift_state_implies_equality,
3452    entails_and_temp,
3453    entails_and_temp_reverse,
3454    entails_or_temp,
3455    entails_not_temp_reverse,
3456    entails_implies_temp_reverse,
3457    always_lift_state_and_equality,
3458    always_implies_to_leads_to,
3459    always_to_always_later,
3460    always_double_equality,
3461    always_and_equality,
3462    tla_forall_apply,
3463    spec_entails_tla_forall,  // may slow down proofs
3464    always_implies_forall_intro,  // may slow down proofs
3465    tla_exists_leads_to_intro,  // may slow down proofs
3466    leads_to_self_temp,
3467    entails_and_different_temp,
3468    always_p_is_stable,
3469    stable_and_temp,
3470    entails_preserved_by_always,
3471    leads_to_apply,  // may slow down proofs
3472    or_leads_to_combine,
3473    leads_to_always_combine,
3474    leads_to_framed_by_or,
3475    lift_state_exists_equality,
3476    implies_tla_exists_intro,
3477    leads_to_tla_exists_intro,
3478    state_exists_intro,  // may be unnecessary
3479    entails_always_lift_state_and,  // may slow down proofs
3480    entails_always_lift_state_and_elim,
3481    entails_tla_exists_by_witness,  // may slow down proofs
3482    lift_state_exists_leads_to_intro,
3483    lift_state_exists_absorb_equality,
3484    lift_state_forall_absorb_equality,
3485    lift_state_not_equality,
3486}
3487
3488} // verus!