Module Utils
From Stdlib Require Import Lists.List.From Stdlib Require Import Arith.Compare_dec.
From Stdlib Require Import PeanoNat.
From Stdlib Require Import Lia.
From Stdlib Require Import Classes.RelationClasses.
Import Nat.
Import ListNotations.
Module imported from coq-tricks.
Provides a mechanism to store learnt hypotheses to avoid re-deriving
the same facts repeatedly during proof automation.
Module Learn. A wrapper type used to record that a proposition `P` has been learnt.
Inductive Learnt {P:Prop} :=| AlreadyLearnt (H:P).
Internal tactic: marks a hypothesis as learnt if it hasn't been recorded yet.
Local Ltac learn_fact H :=let P := type of H in
lazymatch goal with
| [ Hlearnt: @Learnt P |- _ ] =>
fail 0 "already knew" P "through" Hlearnt
| _ => pose proof H; pose proof (AlreadyLearnt H)
end.
Public interface for `learn_fact`.
Usage: `learn H` records the fact `H` as already known.
Tactic Notation "learn" constr(H) := learn_fact H.End Learn.
Utility module providing:
- Custom Ltac tactics for common proof patterns.
- Helper lemmas for reasoning about lists and natural numbers.
- Definitions of indexed mapping functions and option-based ordering.
Module Utils.Import Learn.
## Custom tactics
`inv H`: performs inversion on hypothesis `H`, clears it, and substitutes
resulting equalities into the context.
Ltac inv H := inversion H; clear H; subst. `antisym`: applies antisymmetry on a pair of `<=` relations in the context
and records the resulting equality using `learn`.
Ltac antisym :=match goal with
| [ H: ?x <= ?y, F: ?y <= ?x |- _ ] =>
learn (le_antisymm _ _ H F)
end.
`splitHyp`: destructs a conjunction in the context.
Ltac splitHyp :=match goal with
| [ H: _ /\ _ |- _ ] => destruct H
end.
`existsHyp`: destructs an existential hypothesis in the context.
Ltac existsHyp :=match goal with
| [ H: ex _ |- _ ] => destruct H
end.
Lemma and tactic to help build conjunctions incrementally.
Lemma and_with_impl : forall P Q, P -> (P -> Q) -> P /\ Q.Proof.
split; intros; auto.
Qed.
Qed.
`splitAnd`: prepares the goal for proving a conjunction by introducing
the left-hand side immediately and the right-hand side later.
Ltac splitAnd :=match goal with
| [ |- _ /\ _ ] => apply and_with_impl; [idtac|intros]
end.
## Reasoning on `max`
Decomposes the `max` function into explicit cases, preserving order relations.
Lemma max_dec_bis: forall a b, (max a b = a /\ b <= a) \/ (max a b = b /\ a <= b).Proof.
Auxiliary tactic used by `splitMax` to destruct results of `max_dec_bis`.
Ltac _useMaxDecBis :=match goal with
| [ H: (_ = _ /\ _) \/ (_ = _ /\ _) |- _ ] =>
destruct H as [[?Heq ?H]|[?Heq ?H]]; rewrite Heq in *; clear Heq
end.
`splitMax`: applies `max_dec_bis` to all occurrences of `max x y` in either
the goal or context, and simplifies the resulting branches.
Ltac splitMax :=match goal with
| [ H: context[max ?x ?y] |- _ ] => learn (max_dec_bis x y); _useMaxDecBis
| [ |- context[max ?x ?y] ] => learn (max_dec_bis x y); _useMaxDecBis
end.
## List reasoning
Lemma stating that if two lists have the same length, then for every element
in one list accessible via `nth_error`, there exists a corresponding element
at the same index in the other list.
Lemma nth_error_lists : forall A B (l: list A) (l': list B) n x,nth_error l n = Some x -> length l = length l' -> exists y, nth_error l' n = Some y.
Proof.
induction l; intros.
- destruct n; discriminate H.
- destruct l'. discriminate H0. inv H0. destruct n.
+ eexists. reflexivity.
+ firstorder.
Qed.
- destruct n; discriminate H.
- destruct l'. discriminate H0. inv H0. destruct n.
+ eexists. reflexivity.
+ firstorder.
Qed.
`gen_nth`: given two lists of equal length and an index known to exist in one,
generates the corresponding element in the other list.
Ltac gen_nth :=match goal with
| [ Hlen: length ?l1 = length ?l2, Hnth: nth_error ?l1 ?n = Some _ |- _ ] =>
destruct (nth_error_lists _ _ _ _ _ _ Hnth Hlen); clear Hlen
| [ Hlen: length ?l2 = length ?l1, Hnth: nth_error ?l1 ?n = Some _ |- _ ] =>
let HnewTh := fresh in assert(HnewTh: exists y, nth_error l2 n = Some y) by
(apply (nth_error_lists _ _ _ _ _ _ Hnth); symmetry; apply Hlen);
destruct HnewTh; clear Hlen
end.
## Indexed mapping function
`mapI'` applies a function `f` to each element of a list `l`,
passing its index (starting from `i`) as an additional argument.
Fixpoint mapI'{X Y: Type} f i (l: list X) : list Y :=match l with
| [] => []
| hd :: tl => f i hd :: mapI' f (i + 1) tl
end.
`mapI` is a specialization of `mapI'` starting from index 0.
Definition mapI {X Y: Type} (f: nat -> X -> Y) := mapI' f 0. Characterization of elements in `mapI'`:
an element `y` appears in `mapI' f i l` iff there exists an index `n`
and element `x` such that `nth_error l n = Some x` and `y = f (i + n) x`.
Lemma mapI'_values : forall (X Y: Type) (f: nat -> X -> Y) (l: list X) y i,In y (mapI' f i l) <-> exists n x, nth_error l n = Some x /\ y = f (i + n) x.
Proof.
induction l.
- split; intros. contradiction. destruct H as [[|n] [x [H1 H2]]]; discriminate H1.
- split; intros.
+ destruct H as [H1 | H2].
* exists 0. exists a. intuition. assert (HNat: i + 0 = i). lia. rewrite HNat. auto.
* rewrite IHl in H2. destruct H2 as [n [x [H1 H2]]].
exists (1 + n). exists x. intuition. assert (HNat: i + (1 + n) = i + 1 + n). lia.
rewrite HNat. apply H2.
+ destruct H as [n [x [H1 H2]]]. destruct n.
* inversion H1. subst. left. assert (HNat: i + 0 = i). lia. rewrite HNat.
reflexivity.
* right. apply IHl. exists n. exists x. intuition. assert (HNat: i + 1 + n = i + S n). lia.
rewrite HNat. apply H2.
Qed.
- split; intros. contradiction. destruct H as [[|n] [x [H1 H2]]]; discriminate H1.
- split; intros.
+ destruct H as [H1 | H2].
* exists 0. exists a. intuition. assert (HNat: i + 0 = i). lia. rewrite HNat. auto.
* rewrite IHl in H2. destruct H2 as [n [x [H1 H2]]].
exists (1 + n). exists x. intuition. assert (HNat: i + (1 + n) = i + 1 + n). lia.
rewrite HNat. apply H2.
+ destruct H as [n [x [H1 H2]]]. destruct n.
* inversion H1. subst. left. assert (HNat: i + 0 = i). lia. rewrite HNat.
reflexivity.
* right. apply IHl. exists n. exists x. intuition. assert (HNat: i + 1 + n = i + S n). lia.
rewrite HNat. apply H2.
Qed.
Simplified version of `mapI'_values` for the common case where indexing starts at 0.
Lemma mapI_values : forall (X Y: Type) (f: nat -> X -> Y) (l: list X) y,In y (mapI f l) <-> exists n x, nth_error l n = Some x /\ y = f n x.
Proof.
## Custom helper functions
Computes the sum of a list of natural numbers.
Fixpoint sum (l: list nat) :=match l with
| [] => 0
| hd :: tl => hd + sum tl
end.
Defines an ordering relation on optional natural numbers,
treating `None` as the smallest element.
Definition le_option_nat (x y : option nat) : Prop :=match x, y with
| None, _ => True
| Some _, None => False
| Some a, Some b => a <= b
end.
`le_option_nat` is reflexive.
Instance le_option_nat_Reflexive : Reflexive le_option_nat.Proof.
intros [n|]; simpl; auto.
Qed.
Qed.