Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 338093d

Browse files
committedJan 24, 2023
Add some automation to All_Forall for proving via nth_error
The existing strategy of combining `Forall`s in the context and then trying to prove the property universally is lossy when there are goals like `Forall (Forall2 P l1) l2` lying around, because it doesn't guarantee that all relevant hypotheses are found. Instead, we can convert all hypotheses to `nth_error` and try to carefully specialize hypotheses so that `nth_error`s line up.
1 parent da09038 commit 338093d

File tree

3 files changed

+1221
-157
lines changed

3 files changed

+1221
-157
lines changed
 

‎utils/theories/All_Forall.v

+1,027-157
Large diffs are not rendered by default.

‎utils/theories/MCList.v

+173
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,15 @@ Proof.
774774
by case: i.
775775
Qed.
776776

777+
Lemma nth_error_firstn {A} n (l : list A) i : nth_error (firstn n l) i = if (i <? n) then nth_error l i else None.
778+
Proof.
779+
induction l in n, i |- *; destruct n; simpl; auto.
780+
all: rewrite ?nth_error_nil => //.
781+
all: try by case: Nat.ltb.
782+
move: (IHl n (pred i)).
783+
by case: i.
784+
Qed.
785+
777786
Lemma skipn_skipn {A} n m (l : list A) : skipn n (skipn m l) = skipn (m + n) l.
778787
Proof.
779788
induction m in n, l |- *. auto.
@@ -813,6 +822,12 @@ Proof.
813822
simpl. rewrite IHl; auto with arith.
814823
Qed.
815824

825+
Lemma nth_error_app_full {A} (l l' : list A) (v : nat) :
826+
nth_error (l ++ l') v = if v <? #|l| then nth_error l v else nth_error l' (v - #|l|).
827+
Proof.
828+
case: Nat.ltb_spec0 => ?; first [ apply nth_error_app1 | apply nth_error_app2 ]; lia.
829+
Qed.
830+
816831
Lemma nth_error_app_inv X (x : X) n l1 l2 :
817832
nth_error (l1 ++ l2) n = Some x -> (n < #|l1| /\ nth_error l1 n = Some x) \/ (n >= #|l1| /\ nth_error l2 (n - List.length l1) = Some x).
818833
Proof.
@@ -856,6 +871,13 @@ Proof.
856871
now rewrite Nat.sub_diag.
857872
Qed.
858873

874+
Lemma nth_error_last {A l a} : l <> [] -> nth_error l (Nat.pred #|l|) = Some (@last A l a).
875+
Proof.
876+
induction l using rev_ind => // ?.
877+
rewrite app_length -Nat.add_pred_r //= Nat.add_0_r nth_error_snoc //= last_last //.
878+
Qed.
879+
880+
859881
Lemma map_inj :
860882
forall A B (f : A -> B) l l',
861883
(forall x y, f x = f y -> x = y) ->
@@ -1468,3 +1490,154 @@ Proof.
14681490
{ move => [->|[n H]]; [ exists 0 | exists (S n) ];
14691491
rewrite ?skipn_0 ?skipn_S => //=. }
14701492
Qed.
1493+
1494+
Lemma cons_eq_iff A x xs y ys
1495+
: x :: xs = y :: ys <-> ((x = y :> A) /\ xs = ys).
1496+
Proof. split; intuition congruence. Qed.
1497+
1498+
Lemma nth_error_Some_ext_iff A (l l' : list A)
1499+
: (#|l| = #|l'| /\ (forall n d d', nth_error l n = Some d -> nth_error l' n = Some d' -> d = d')) <-> l = l'.
1500+
Proof.
1501+
move: l'; induction l as [|?? IH], l' as [|? l'];
1502+
try specialize (IH l'); cbn; try (rewrite cons_eq_iff -IH; clear IH).
1503+
all: repeat first [ done
1504+
| match goal with
1505+
| [ H : _ /\ _ |- _ ] => destruct H
1506+
| [ H : forall n d d', nth_error (_ :: _) n = Some d -> _ |- _ ]
1507+
=> pose proof (H 0); specialize (fun n => H (S n))
1508+
| [ H : nth_error (_ :: _) ?n = _ |- _ ] => is_var n; destruct n
1509+
end
1510+
| exactly_once constructor
1511+
| progress rewrite -> ?nth_error_nil in *
1512+
| progress intros
1513+
| progress cbn in *
1514+
| now eauto ].
1515+
Qed.
1516+
1517+
Lemma nth_error_ext_iff A (l l' : list A)
1518+
: (forall n, nth_error l n = nth_error l' n) <-> l = l'.
1519+
Proof.
1520+
move: l'; induction l as [|?? IH], l' as [|? l'];
1521+
try specialize (IH l'); cbn; try (rewrite cons_eq_iff -IH; clear IH).
1522+
all: repeat first [ done
1523+
| progress cbn in *
1524+
| congruence
1525+
| progress subst
1526+
| match goal with
1527+
| [ H : _ /\ _ |- _ ] => destruct H
1528+
| [ H : forall n, nth_error (_ :: _) n = _ |- _ ]
1529+
=> pose proof (H 0); specialize (fun n => H (S n))
1530+
| [ H : forall n, _ = nth_error (_ :: _) n |- _ ]
1531+
=> pose proof (H 0); specialize (fun n => H (S n))
1532+
| [ H : forall n, nth_error nil n = _ |- _ ]
1533+
=> setoid_rewrite nth_error_nil in H
1534+
| [ |- context[nth_error (_ :: _) ?n] ] => is_var n; destruct n
1535+
end
1536+
| exactly_once constructor
1537+
| progress rewrite -> ?nth_error_nil in *
1538+
| progress intros ].
1539+
Qed.
1540+
1541+
Lemma In_iff_nth_error A x l : @In A x l <-> exists n, nth_error l n = Some x.
1542+
Proof.
1543+
split; try case; intros *; first [ apply nth_error_In | apply In_nth_error ].
1544+
Qed.
1545+
1546+
Lemma List_rev_alt {A ls} : List.rev ls = @rev A ls.
1547+
Proof. rewrite rev_alt; reflexivity. Qed.
1548+
1549+
Lemma nth_error_rev_full {A ls i}
1550+
: nth_error (@rev A ls) i = if i <? #|ls| then nth_error ls (#|ls| - S i) else None.
1551+
Proof.
1552+
case: Nat.ltb_spec0 => ?.
1553+
{ rewrite -List_rev_alt nth_error_rev_inv //. }
1554+
{ rewrite nth_error_None rev_length; lia. }
1555+
Qed.
1556+
1557+
Lemma nth_error_List_rev_full {A ls i}
1558+
: nth_error (@List.rev A ls) i = if i <? #|ls| then nth_error ls (#|ls| - S i) else None.
1559+
Proof.
1560+
case: Nat.ltb_spec0 => ?.
1561+
{ rewrite nth_error_rev_inv //. }
1562+
{ rewrite nth_error_None List.rev_length; lia. }
1563+
Qed.
1564+
1565+
Lemma nth_error_rev_map {A B f ls i}
1566+
: nth_error (@rev_map A B f ls) i = if i <? #|ls| then option_map f (nth_error ls (#|ls| - S i)) else None.
1567+
Proof.
1568+
rewrite rev_map_spec nth_error_List_rev_full map_length nth_error_map //.
1569+
Qed.
1570+
1571+
Lemma nth_error_rev_Some {A ls i v}
1572+
: nth_error (@rev A ls) i = Some v -> nth_error ls (#|ls| - S i) = Some v.
1573+
Proof.
1574+
rewrite nth_error_rev_full; case: nth_error; case: Nat.ltb_spec0 => //=.
1575+
Qed.
1576+
1577+
Lemma nth_error_rev_Some_inv {A ls i v}
1578+
: nth_error ls i = Some v -> nth_error (@rev A ls) (#|ls| - S i) = Some v.
1579+
Proof.
1580+
rewrite nth_error_rev_full; case: Nat.ltb_spec0;
1581+
try now case: ls => //=; rewrite ?nth_error_nil.
1582+
intros H0 H1.
1583+
assert (H : i < #|ls|) by now eapply nth_error_Some_length.
1584+
now replace (#|ls| - S (#|ls| - S i)) with i by lia.
1585+
Qed.
1586+
1587+
Lemma nth_error_List_rev_Some {A ls i v}
1588+
: nth_error (@List.rev A ls) i = Some v -> nth_error ls (#|ls| - S i) = Some v.
1589+
Proof.
1590+
rewrite nth_error_List_rev_full; case: nth_error; case: Nat.ltb_spec0 => //=.
1591+
Qed.
1592+
1593+
Lemma nth_error_List_rev_Some_inv {A ls i v}
1594+
: nth_error ls i = Some v -> nth_error (@List.rev A ls) (#|ls| - S i) = Some v.
1595+
Proof.
1596+
rewrite nth_error_List_rev_full; case: Nat.ltb_spec0;
1597+
try now case: ls => //=; rewrite ?nth_error_nil.
1598+
intros H0 H1.
1599+
assert (H : i < #|ls|) by now eapply nth_error_Some_length.
1600+
now replace (#|ls| - S (#|ls| - S i)) with i by lia.
1601+
Qed.
1602+
1603+
Lemma nth_error_rev_map_Some {A B f ls i v}
1604+
: nth_error (@rev_map A B f ls) i = Some v -> { v' : _ | nth_error ls (#|ls| - S i) = Some v' /\ f v' = v }.
1605+
Proof.
1606+
rewrite nth_error_rev_map; case: nth_error; case: Nat.ltb_spec0 => //=.
1607+
intros ? *; inversion 1; subst; eauto.
1608+
Qed.
1609+
1610+
Lemma nth_error_rev_map_Some_inv {A B f ls i v}
1611+
: nth_error ls i = Some v -> nth_error (@rev_map A B f ls) (#|ls| - S i) = Some (f v).
1612+
Proof.
1613+
rewrite nth_error_rev_map; case: Nat.ltb_spec0;
1614+
try now case: ls => //=; rewrite ?nth_error_nil.
1615+
intros H0 H1.
1616+
assert (H : i < #|ls|) by now eapply nth_error_Some_length.
1617+
replace (#|ls| - S (#|ls| - S i)) with i by lia.
1618+
now rewrite H1.
1619+
Qed.
1620+
1621+
Lemma mapi_rec_nth_error {A B f n l d k}
1622+
: nth_error l n = Some d -> nth_error (@mapi_rec A B f l k) n = Some (f (n + k) d).
1623+
Proof.
1624+
move: l n k.
1625+
elim => [|? l IH]; case => [|?] k.
1626+
all: rewrite ?nth_error_nil => //=; try congruence.
1627+
move => ?; rewrite IH => //.
1628+
do 2 f_equal; lia.
1629+
Qed.
1630+
1631+
Lemma mapi_nth_error {A B f n l d}
1632+
: nth_error l n = Some d -> nth_error (@mapi A B f l) n = Some (f n d).
1633+
Proof.
1634+
rewrite /mapi => ?; erewrite mapi_rec_nth_error; tea; do 2 f_equal; lia.
1635+
Qed.
1636+
1637+
Lemma nth_error_cons_S {A x xs n}
1638+
: @nth_error A (x :: xs) (S n) = nth_error xs n.
1639+
Proof. reflexivity. Qed.
1640+
1641+
Lemma nth_error_cons_O {A x xs}
1642+
: @nth_error A (x :: xs) 0 = Some x.
1643+
Proof. reflexivity. Qed.

‎utils/theories/MCOption.v

+21
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,27 @@ Fixpoint map_option_out {A} (l : list (option A)) : option (list A) :=
7070
end
7171
end.
7272

73+
Lemma map_option_out_Some_spec {A}
74+
: forall l l', @map_option_out A l = Some l' <-> l = map Some l'.
75+
Proof.
76+
induction l as [|[?|]? IH], l' as [|? l'] => //=; try specialize (IH l').
77+
all: destruct map_option_out => //=.
78+
all: split; inversion 1; subst.
79+
all: intuition (congruence || subst).
80+
Qed.
81+
82+
Lemma map_option_out_None_spec {A}
83+
: forall l, @map_option_out A l = None <-> (exists n, nth_error l n = Some None).
84+
Proof.
85+
induction l as [|[?|]? IH] => //=.
86+
all: try destruct map_option_out => //=.
87+
all: split; [ inversion 1 | case; case; intros * => //= ]; subst.
88+
all: try now (exists 0).
89+
all: destruct IH as [IH0 IH1]; specialize (fun n pf => IH1 (ex_intro _ n pf)); cbn in *.
90+
all: firstorder (congruence || eauto).
91+
eexists (S _); cbn; tea.
92+
Qed.
93+
7394
Lemma map_option_out_map_option_map {A} (l : list (option A)) (f : A -> A) :
7495
map_option_out (map (option_map f) l) =
7596
option_map (map f) (map_option_out l).

0 commit comments

Comments
 (0)
Please sign in to comment.