# Lists

Require Export Perm.

Hint Resolve not_disj.

## Permutations on lists

Definition perm_list (l : list Name) p : list Name :=
map p l.

Notation "p *- l" := (perm_list l p) (at level 70).

Lemma perm_list_step A a p :
p *- (a::A) = (p a) :: (p *- A).
Proof.
reflexivity.
Qed.

Lemma perm_list_inv A p p' :
Inv p p' -> (p *- (p' *- A)) = A.
Proof.
intros H1. induction A as [|a A IH]; simpl; auto.
specialize (H1 a) as [_ H2]. rewrite H2, IH. reflexivity.
Qed.

(*
Identity and associativity are satisfied by the action.
List of Name is therefore a sym type
*)

Fact list_id A :
id *- A = A.
Proof.
induction A as [|a A IH]; simpl; auto; rewrite IH; auto.
Qed.

Fact list_assoc A p p':
(p ** p' *- A) = (p *- (p' *- A)).
Proof.
induction A; simpl; auto.
rewrite IHA. reflexivity.
Qed.

Lemma list_swap_perm A p p' :
(forall a, a el A -> p a = p' a) -> p *- A = (p' *- A).
Proof.
intros H1. induction A as [|a A IH]; simpl; auto. f_equal.
- apply H1. now left.
- apply IH. intros b H2. apply H1. now right.
Qed.

(*
With itself as the finite support, the list is a nominal type
*)

Lemma list_nominal A p :
(forall a, a el A -> p a = a) -> p *- A = A.
Proof.
induction A as [| a A IH]; intros H1; auto; simpl; rewrite (H1 a), IH; auto.
Qed.

Lemma perm_list_el' A a p :
a el A -> (p a) el (p *- A).
Proof.
induction A; auto. intros [<-|H1]; simpl; auto.
Qed.

Lemma perm_list_el'' A a p p' :
Inv p p' -> (p a) el (p *- A) -> a el A.
Proof.
intros HP. induction A; simpl; trivial.
simpl in *. intros [H1|H1]; auto.
left. now rewrite <-perm_inj' with (p := p)(p' := p').
Qed.

Lemma perm_list_el A a p :
Perm p -> (p a) el (p *- A) <-> a el A.
Proof.
intros HP. split.
- destruct HP as [ _ [p' HP]]. apply (perm_list_el'' HP).
- apply perm_list_el'.
Qed.

Lemma perm_list_shift A a p p' :
Inv p p' -> a el (p' *- A) <-> (p a) el A.
Proof.
intros HP. split; induction A as [ | b A IH]; auto; simpl in *; intros [H1|H2]; subst; auto.
Qed.

Lemma transp_list_el A a b c :
c el A -> c <> a -> c <> b -> c el (a\$b *- A).
Proof.
intros. rewrite perm_list_shift with (p:=a\$b); auto. solve_dec.
Qed.

Lemma transp_list_indep A a b :
~ a el A -> ~ b el A -> (a\$b) *- A = A.
Proof.
intros. induction A; auto.
simpl. f_equal; auto. simpl in *. solve_dec; intuition.
Qed.

## Concat, nth, and map

Lemma concat_el (X: Type) (x : X) (ll : list (list X)) :
x el concat ll <-> exists (l : list X), x el l /\ l el ll.
Proof.
split.
- induction ll as [| A ll IH]; simpl; auto. intros [H1|H1] % in_app_or.
+ exists A. auto.
+ specialize (IH H1) as [c [H2 H3]]. exists c. auto.
- intros [l [H1 H2]]. induction ll as [| A ll IH]; simpl; auto.
apply in_or_app. destruct H2 as [->|H2]; auto.
Qed.

Lemma concat_el_sub (X : Type) (x : X) (A B: list (list X)) :
A <<= B -> x el concat A -> x el concat B.
Proof.
intros H1 [E [H2 H3]] % concat_el. apply concat_el; eauto.
Qed.

Lemma concat_nil' (X : Type) (ll : list (list X)) :
concat ll = [] <-> (forall l, l el ll -> l = []).
Proof.
split; induction ll as [|A ll IH]; simpl; auto.
- intros H1 % app_eq_nil l [H2|H2].
+ now subst.
+ now apply IH.
- intros H1. rewrite IH, (H1 A); auto.
Qed.

Lemma concat_map_el (X Y : Type) (A : list X) (f : X -> list Y) (y : Y) :
y el concat (map f A) <-> exists x, x el A /\ y el f x.
Proof.
split.
- intros [l [H1 [x [H2 H3]] % in_map_iff]] % concat_el. subst. now exists x.
- intros [x [H1 H2]]. apply concat_el. exists (f x). split; trivial.
apply in_map_iff. exists x. split; trivial.
Qed.

Lemma incl_cons (X: Type) (x : X) (A B : list X) :
A <<= B -> A <<= (x :: B).
Proof.
intros H1 a H2. right. apply H1, H2.
Qed.

Lemma concat_incl_list (X Y : Type) l (f g : X -> list Y) :
(forall x, x el l -> f x <<= g x) -> concat (map f l) <<= concat (map g l).
Proof.
induction l as [|t l IH]; intros H1; auto.
simpl. apply incl_app.
- apply incl_appl, H1. auto.
- apply incl_appr, IH. auto.
Qed.

Lemma map_nth_indep (X : Type) (f : X->X) (l : list X) (d : X) (k: nat):
k < |l| -> nth k (map f l) d = f (nth k l d).
Proof.
intros. rewrite nth_indep with (d':=f d); try now rewrite map_length. now rewrite map_nth.
Qed.

Lemma nth_el_indx (X:Type) (x:X) (A:list X) :
x el A <-> exists k, k < |A| /\ (nth k A x) = x.
Proof.
split.
- intros H1. induction A as [|b A]; simpl in *; auto. destruct H1 as [H1|H1].
+ exists 0. split; try assumption. omega.
+ specialize (IHA H1). destruct IHA as [k [H2 H3]]. exists (S k).
split; try assumption. omega.
- intros [k [H1 <-]]. apply nth_In, H1.
Qed.

Lemma nth_el_indx_map (X Y :Type) (x:X)(y:Y) (A:list X) (f:X->Y) :
y el (map f A) <-> exists k, k < |A| /\ f (nth k A x) = y.
Proof.
split.
- intros [a [H1 [k [H2 H3]] % nth_el_indx ]] % in_map_iff.
exists k. split; try assumption.
now rewrite (nth_indep A x a H2), H3.
- intros [k [H1 H2]]. apply in_map_iff.
exists (nth k A x). split; auto using nth_In.
Qed.

(*
If every element x in a list l has a corresponding y, such that P x y holds,
then there is a list l' that has all those elements y in the right order
*)

Lemma list_prop_rel (X Y : Type) (l : list X) (P : X -> Y -> Prop) (xd : X) (yd : Y) :
(forall x, x el l -> exists y, P x y) -> exists l', |l| = |l'| /\ forall k, k < |l| -> (P (nth k l xd) (nth k l' yd)).
Proof.
induction l as [| a l IHl]; intros H.
- exists []. split; trivial. simpl. intros. omega.
- assert(H1 : a el a :: l) by auto.
assert(H2 : forall x, x el l -> exists y, P x y) by auto.
destruct (H a H1) as [y H3]. destruct (IHl H2) as [l' [H4 H5]].
exists (y::l'). split; simpl; try congruence.
intros. destruct k; auto. apply H5. omega.
Qed.

## Advanced list functions

(*
The next are more advanced implementations on lists used in decision procedures.
We implement map2, a pointwise map, a big 'and', and a big 'or'.
We also introduce an inductive predicate for two lists of the same length.
*)

(*
Like map2 in ocaml
*)

Fixpoint map2 (X Y Z :Type) (f : X -> Y -> Z) (l: list X) (l' : list Y) : list Z :=
match l, l' with
| a :: xs, b :: ys => (f a b) :: (map2 f xs ys)
| _, _ => []
end.

(*
A big boolean 'and' on the list
*)

Fixpoint all_true (l : list bool) : bool :=
match l with
| [] => true
| a :: xs => if a then all_true xs else false
end.

Lemma all_true_true l :
all_true l = true <-> forall x, x el l -> x = true.
Proof.
split; induction l; simpl in *; auto; intros; try destruct H0; destruct a; auto.
Qed.

(*
Induction principle for two lists of same length
*)

Lemma dl_induction (X Y : Type) (p : list X -> list Y -> Prop) :
(p [] []) ->
(forall x y l l', p l l' -> p (x::l) (y::l')) ->
forall (l : list X) (l' : list Y), |l| = |l'| -> p l l'.
Proof.
induction l, l'; intros; simpl in *; auto; omega.
Qed.

(*
Inductive predicate for two lists of same length.
plist for "parallel lists"
*)

Inductive plist (X Y : Type) : list X -> list Y -> Prop :=
| pListN : plist [] []
| pListC l l' x y : plist l l' -> plist (x::l) (y::l').

Lemma plist_len (X Y : Type) (l: list X) (l': list Y) :
|l| = |l'| -> plist l l'.
Proof.
apply dl_induction; now constructor.
Qed.

Lemma len_plist (X Y : Type) (l: list X) (l': list Y) :
plist l l' -> |l| = |l'|.
Proof.
induction 1; simpl; auto.
Qed.

Lemma plist_eq (X: Type) l l' (x:X) :
|l| = |l'| -> (forall k, k < |l| -> nth k l x = nth k l' x) -> l = l'.
Proof.
intros H1 H2. induction (plist_len H1); simpl in *; auto.
rewrite (H2 0); try omega. rewrite IHp; auto. intros k H3. apply (H2 (S k)). omega.
Qed.

Lemma map_eq (X Y : Type) (f: X -> Y) (l l' : list X) (x:X) :
|l| = |l'| -> (forall k, k < |l| -> f (nth k l x) = f (nth k l' x)) -> map f l = map f l'.
Proof.
intros H1 H2. apply plist_eq with (x:=f x); try now rewrite map_length, map_length.
intros k H3. rewrite map_length in H3. rewrite map_nth, map_nth. now apply H2.
Qed.

Lemma app_equi (X : Type) (x y l l' : list X) :
x === y -> l === l' -> x++l === y++l'.
Proof.
intros H1 H2. split; intros a [H3|H3] % in_app_iff; apply in_app_iff.
- left. now apply H1.
- right. now apply H2.
- left. now apply H1.
- right. now apply H2.
Qed.

Lemma concat_equi (X : Type) (l l' : list (list X)) (x : list X) :
|l| = |l'| -> (forall k, k < |l| -> (nth k l x) === (nth k l' x)) -> concat (l) === concat (l').
Proof.
intros H1. induction (plist_len H1); auto.
intros H2. simpl. apply app_equi.
- specialize (H2 0). simpl in H2. apply H2. omega.
- apply IHp; auto. intros. specialize (H2 (S k)). simpl in H2. apply H2. omega.
Qed.

Lemma concat_map_equi (X Y : Type) (l l' : list X) (f : X -> list Y) (x : X) :
|l| = |l'| -> (forall k, k < |l| -> f (nth k l x) === f (nth k l' x)) -> concat (map f l) === concat (map f l').
Proof.
intros H1 H2. apply concat_equi with (x:=f x).
- now repeat rewrite map_length.
- intros k. rewrite map_length. intros H3. repeat rewrite map_nth. now apply H2.
Qed.

Lemma in_map2_iff (X Y Z : Type) (f : X -> Y -> Z) (l : list X) (l' : list Y) (z : Z) (x : X) (y : Y) :
|l| = |l'| -> (z el map2 f l l' <-> (exists k, k < |l| /\ f (nth k l x) (nth k l' y) = z)).
Proof.
intros HL. split.
- intros H1. induction (plist_len HL); simpl in *; auto. destruct H1 as [H1|H1].
+ exists 0. split; trivial. omega.
+ inversion HL as [H2]. specialize (IHp H2 H1) as [k [IH1 IH2]]. exists (S k). split; trivial. omega.
- intros [k [H1 H2]]. revert H1 H2. revert k.
induction (plist_len HL), k; intros; simpl in *; try omega; auto.
right. apply (IHp) with (k := k); auto. omega.
Qed.

Lemma map2_all_true (X Y : Type) (f: X -> Y -> bool) (l : list X) (l' : list Y) (x : X) (y : Y) :
|l| = |l'| -> all_true (map2 f l l') = true <-> forall k, k < |l| -> f (nth k l x) (nth k l' y) = true.
Proof.
intros HL. split.
- rewrite all_true_true. intros H1 k H2. apply H1. rewrite (in_map2_iff); eauto.
- intros H1. rewrite all_true_true. intros z H2.
rewrite in_map2_iff with (x:=x)(y:=y) in H2; try assumption.
destruct H2 as [k [H2 H3]]. rewrite <-H3. apply H1, H2.
Qed.

(*
A big 'or' on the list.
*)

Fixpoint some_true (l : list bool) : bool :=
match l with
| [] => false
| a :: xs => if a then true else some_true xs
end.

Lemma some_true_true l :
some_true l = true <-> true el l.
Proof.
split; induction l; simpl; auto; intros H1; destruct a eqn:E; auto. intuition.
Qed.

(*
A pointwise map
*)

Fixpoint map_pw (X Y : Type) (FL : list (X -> Y)) (PL : list X) : list Y :=
match FL, PL with
| f :: FL, x :: PL => (f x) :: (map_pw FL PL)
| _, _ => []
end.

(*
Combining map_pw and map is equivalent to map2.
It is used instead of map2 in some decision procedures,
because coq does not find the decreasing argument when using map2
*)

Lemma map_pw_map (X Y : Type) (f : X -> Y -> bool) l l' z x y :
|l| = |l'| -> z el (map_pw (map f l) l') -> exists k, k < |l| /\ f (nth k l x) (nth k l' y) = z.
Proof.
intros H3 H4. induction (plist_len H3); simpl in *; auto. destruct H4 as [H4|H4].
- exists 0. split; try assumption. omega.
- inversion H3 as [H5]. specialize (IHp H5 H4) as [k [H6 H7]]. exists (S k). split; try assumption. omega.
Qed.

Lemma map_pw_map_eq (X Y : Type) (l:list X) (l':list Y) f x y :
|l| = |l'| -> (forall k, k < |l| -> f (nth k l x) (nth k l' y) = nth k l x) -> map_pw (map f l) l' = l.
Proof.
intros H1. induction (plist_len H1); auto.
intros H2. simpl. f_equal.
- specialize (H2 0). simpl in H2. apply H2. omega.
- apply IHp; auto. intros. specialize (H2 (S k)). simpl in H2. apply H2. omega.
Qed.

(*
all_true on map_pw is like a big 'and' on the applications on functions that return a bool.
*)

Lemma all_true_map_pw (X : Type) (l : list (X -> bool)) (l' : list X) (f : X -> bool) (x : X) :
|l| = |l'| -> all_true (map_pw l l') = true <-> forall k, k < |l| -> (nth k l f) (nth k l' x) = true.
Proof.
intros H1. split.
- induction (plist_len H1) as [| l l' a b H2 H3]; simpl in *; intros H4 k; try omega.
inversion H1 as [HL]. destruct k, (a b); auto. intros H5. apply (H3 HL H4). omega.
- induction (plist_len H1) as [|l l' a b H2 H3]; simpl in *; intros H4; try reflexivity.
destruct(a b) eqn:E.
+ apply H3; intros; auto. assert(H5: (S k) < (S (|l|))) by omega. specialize (H4 (S k) H5). now simpl in H4.
+ assert(H5: 0 < S(|l|)) by omega. specialize(H4 0 H5). simpl in H4. now rewrite <-E, H4.
Qed.

Lemma all_true_map_pw_map (X Y : Type) (l : list X) (l' : list Y) (f : X -> Y -> bool) (x : X) (y : Y) :
|l| = |l'| -> all_true (map_pw (map f l) l') = true <-> forall k, k < |l| -> f (nth k l x) (nth k l' y) = true.
Proof.
intros H1.
split; rewrite all_true_map_pw with (f:= f x)(x:=y); rewrite map_length;
try assumption; intros H2 k H3; specialize (H2 k H3).
- now rewrite map_nth in H2.
- now rewrite map_nth.
Qed.

## Lists of natural numbers

Section NatNumList.

Implicit Type x y z : nat.
Implicit Type l : list nat.

Fixpoint num_max x y: nat :=
match x,y with
| 0, 0 => 0
| 0, b => b
| a, 0 => a
| S a, S b => S (num_max a b)
end.

Fixpoint list_max' l (acc : nat) : nat :=
match l with
| [] => acc
| x :: xs => list_max' xs (num_max x acc)
end.

Definition list_max l : nat :=
list_max' l 0.

Lemma num_max_zero x :
num_max x 0 = x.
Proof.
induction x; simpl; auto.
Qed.

Lemma num_max_or x y :
num_max x y = x \/ num_max x y = y.
Proof.
revert x. induction y; simpl; intros x; induction x; simpl; auto.
destruct (IHy x); f_equal; auto.
Qed.

Lemma num_max_comm x y :
num_max x y = num_max y x.
Proof.
revert y. induction x; intros y; induction y; auto using num_max_zero.
simpl. f_equal. exact (IHx y).
Qed.

Lemma num_max_le x y :
num_max x y = x -> y <= x.
Proof.
revert x. induction y; intros x; induction x; intros H1; simpl in *; try omega.
assert(num_max x y = x) by omega; enough (y <= x) by omega. auto.
Qed.

Lemma list_max_dep' l x y :
x <= y -> list_max' l x <= list_max' l y.
Proof.
revert x y. induction l as [|z l IH]; intros x y H1; simpl; auto.
destruct (num_max_or z x) as [E1|E1], (num_max_or z y) as [E2|E2]; rewrite E1, E2; trivial; apply IH.
- apply num_max_le in E1. rewrite num_max_comm in E2. apply num_max_le in E2. omega.
- apply num_max_le in E2. rewrite num_max_comm in E1. apply num_max_le in E1. omega.
- rewrite num_max_comm in E1. rewrite num_max_comm in E2. apply num_max_le in E1. apply num_max_le in E2. omega.
Qed.

Lemma list_max_cons' l x :
list_max l <= list_max (x::l).
Proof.
revert x. unfold list_max; simpl.
induction l as [|y l IH]; intros x; simpl; auto; try omega.
rewrite num_max_zero, num_max_zero. destruct (num_max_or y x) as [->|E]; trivial.
rewrite E. rewrite num_max_comm in E. apply num_max_le in E. now apply list_max_dep'.
Qed.

Lemma list_max_cons'' l x :
x <= list_max (x::l).
Proof.
unfold list_max. simpl. rewrite num_max_zero. revert x.
induction l as [|y l IH]; auto. simpl. intros x. destruct (num_max_or y x) as [E|E].
- rewrite E. apply num_max_le in E. specialize (IH y). omega.
- rewrite E. rewrite num_max_comm in E. apply num_max_le in E. specialize (IH x). omega.
Qed.

Lemma list_max_max l x :
x el l -> x <= list_max l.
Proof.
induction l as [|y l IH]; simpl; auto.
intros [->|H1].
- exact (list_max_cons'' l x).
- assert(list_max l <= list_max (y::l)) by apply list_max_cons'.
specialize (IH H1). omega.
Qed.

Lemma gt_list_max l x :
x > list_max l -> ~x el l.
Proof.
intros H1 H2 % list_max_max. omega.
Qed.

Fixpoint list_sum l :=
match l with
| [] => 0
| x :: xs => x + (list_sum xs)
end.

Fact list_sum_zero l :
list_sum l = 0 <-> forall x, x el l -> x = 0.
Proof.
assert(Z: forall m n, m + n = 0 <-> m = 0 /\ n = 0) by (intros; omega).
split.
- intros H1. induction l; intros x H2; auto. simpl in H1.
rewrite Z in H1. destruct H1 as [H11 H12]. specialize (IHl H12). destruct H2 as [<-|H2]; auto.
- intros H1. induction l; simpl in *; trivial.
rewrite Z. split; auto.
Qed.

End NatNumList.

## Construction of Fresh Names Relative to Blocked Names

Section Freshness.

Definition name_list_max (l : list Name) :=
list_max (map fst l).

Definition fresh' l k : Name :=
(S (name_list_max l), k).

Lemma name_list_max_max l x :
x el l -> fst x <= name_list_max l.
Proof.
intros. apply list_max_max, in_map_iff; eauto.
Qed.

Lemma gt_name_list_max l x :
x > name_list_max l -> forall y, ~(x,y) el l.
Proof.
intros H1 y H2 % name_list_max_max. simpl in H2. omega.
Qed.

Fact freshness' l k :
~(fresh' l k) el l.
Proof.
unfold fresh'. intros H1 % gt_name_list_max; auto.
Qed.

End Freshness.

## Replacement

(*
Replace kth element in l with x.
*)

Fixpoint rep_nth (X : Type) (k : nat) (l : list X) (x : X) : list X :=
match k, l with
| _, [] => []
| S k, (x' :: r) => x' :: rep_nth k r x
| O, (_ :: r) => x :: r
end.

Lemma rep_nth_nil (X : Type) (k : nat) (x : X) :
rep_nth k [] x = [].
Proof.
destruct k; reflexivity.
Qed.

Hint Resolve rep_nth_nil.

Lemma rep_nth_len (X : Type) (k : nat) (l : list X) (x : X) :
|rep_nth k l x| = |l|.
Proof.
revert k. induction l; simpl; intros; induction k; simpl; auto.
Qed.

Lemma rep_nth_len' (X Y : Type) (k : nat) (l : list X) (l' : list Y) (x : X) :
|l| = |l'| -> |rep_nth k l x| = |l'|.
Proof.
intros H1. rewrite <-H1. apply rep_nth_len.
Qed.

Lemma rep_nth_exact (X : Type) (k : nat) (l : list X) (x xd : X) :
k < |l| -> nth k (rep_nth k l x) xd = x.
Proof.
revert k. induction l; simpl; intros; try omega.
induction k; simpl; auto. apply IHl; omega.
Qed.

Lemma rep_nth_el (X : Type) (k : nat) (l : list X) (x : X) :
k < |l| -> x el rep_nth k l x.
Proof.
revert k. induction l; simpl; intros; try omega.
induction k; simpl; auto. right. apply IHl. omega.
Qed.

Lemma rep_nth_el_or (X : Type) (k : nat) (l : list X) (x xr : X) :
x el rep_nth k l xr -> x = xr \/ x el l.
Proof.
revert k. induction l; simpl; intros.
- rewrite rep_nth_nil in H. auto.
- induction k; simpl in*; destruct H; auto. specialize (IHl k H). intuition.
Qed.

Lemma rep_nth_overwrite (X : Type) (k : nat) (l : list X) (x x' : X) :
rep_nth k (rep_nth k l x) x' = rep_nth k l x'.
Proof.
revert k. induction l; simpl; intros.
- now repeat rewrite rep_nth_nil.
- induction k; simpl; auto. f_equal. apply IHl.
Qed.

Lemma rep_nth_indep (X : Type) (k : nat) (l : list X) (x xd : X) :
forall k', k <> k' -> nth k' (rep_nth k l x) xd = nth k' l xd.
Proof.
revert k. induction l; simpl; intros.
- rewrite rep_nth_nil. simpl. now destruct k'.
- destruct k, k'; simpl; auto.
Qed.

Lemma rep_nth_el' (X : Type) (k k' : nat) (l : list X) (x xd : X) :
k < |l| -> k' < |l| -> k <> k' -> nth k' l xd el rep_nth k l x.
Proof.
intros H1 H2 H3.
rewrite <-rep_nth_indep with (k:=k)(x:=x); trivial. apply nth_In.
now rewrite rep_nth_len.
Qed.

Lemma rep_nth_id (X : Type) (k : nat) (l : list X) (xd : X) :
rep_nth k l (nth k l xd) = l.
Proof.
revert k. induction l; simpl; intros.
- now rewrite rep_nth_nil.
- destruct k; simpl; auto. f_equal. apply IHl.
Qed.