UniAna.util.ListExtra

ListExtra
  • includes some useful facts about lists
Require Import Program.Equality Lia.
Require Export List.
Require Export Take.
Require Export Util UtilTac.

Notations for lists used as sets

Infix "∈" := In (at level 50).
Notation "a '∉' l" := (~ a l) (at level 50).
Infix "⊆" := incl (at level 50).

Definition set_eq {A : Type} (a b : list A) := a b /\ b a.

Infix "='" := (set_eq) (at level 50).

Facts about lists used as sets

Section Facts.

  Variables (A B : Type).

  Lemma list_emp_in : forall l, (forall (a: A), ~ List.In a l) -> l = nil.
  Proof.
    intros.
    induction l.
    - reflexivity.
    - cut (forall a, ~ List.In a l).
      + intros.
        apply IHl in H0.
        subst. specialize (H a).
        exfalso. simpl in H. auto.
      + intros. specialize (H a0).
        simpl in H. auto.
  Qed.

  Lemma in_fst a (l : list (A * B)) :
    In a (map fst l)
    -> exists b, In (a,b) l.
  Proof.
    intros.
    induction l;cbn in *;[contradiction|].
    destruct H.
    - exists (snd a0). left. rewrite <-H. eapply surjective_pairing.
    - eapply IHl in H. destruct H. exists x; right;eauto.
  Qed.

  Lemma in_snd
    : forall (b : B) (l : list (A * B)), b map snd l -> exists a : A, (a, b) l.
  Proof.
    intros.
    induction l.
    - contradiction.
    - destruct a. cbn in H. destruct H.
      + subst. eexists. left. eauto.
      + exploit IHl. destructH. eexists. right. eassumption.
  Qed.

  Fixpoint list_is_set (l : list A) : Prop :=
    match l with
    | x :: xs => list_is_set xs /\ ~ In x xs
    | nil => True
    end.

  Lemma incl_cons (l l' : list A) (a : A) :
    incl (a :: l) l' -> incl l l'.
  Proof.
    unfold incl. cbn. firstorder.
  Qed.

  Lemma incl_app (c : A)
        (π ϕ : list A)
        (Hincl : π ϕ)
    : (c :: π) (c :: ϕ).
  Proof.
    unfold incl in *. firstorder.
  Qed.

  Lemma eq_incl (l l':list A) :
    l = l' -> incl l l'.
  Proof.
    intros Heql; rewrite Heql;unfold incl; tauto.
  Qed.

  Lemma tl_incl (l : list A)
    : tl l l.
  Proof.
    induction l;cbn;firstorder.
  Qed.

  Lemma hd_tl_len_eq (l l' : list A) (a : A)
        (Hheq : hd a l = hd a l')
        (Hteq : tl l = tl l')
        (Hleq : | l | = | l' |)
    : l = l'.
  Proof.
    clear - Hheq Hteq Hleq.
    revert dependent l'. induction l; intros; destruct l';cbn in *;eauto. 1,2: congruence.
    f_equal;eauto.
  Qed.

End Facts.

iter

Fixpoint iter {X : Type} (f : X -> X) (l : X) (n : nat) : X
  := match n with
     | O => l
     | S n => iter f (f l) n
     end.

rcons

Notation "l ':r:' a" := (l ++ [a]) (at level 50).

Section Rcons.

  Variables (A B : Type).

  Lemma rev_cons (a : A) l : rev (a :: l) = (rev l) :r: a.
    induction l; cbn; eauto.
  Qed.

  Lemma cons_rcons_assoc (a b : A) l : (a :: l) :r: b = a :: (l :r: b).
    induction l; cbn; eauto.
  Qed.

  Lemma rev_rcons (a : A) l : rev (l :r: a) = a :: (rev l).
    induction l; cbn; eauto. rewrite IHl; eauto using cons_rcons_assoc.
  Qed.

  Lemma tl_rcons (a : A) l : length l > 0 -> tl (l :r: a) = tl l :r: a.
    induction l; intros; cbn in *; eauto. lia.
  Qed.

  Lemma hd_rcons (a x y : A) l : length l > 0 -> hd x (l :r: a) = hd y l.
    induction l; intros; cbn in *; eauto. lia.
  Qed.

  Lemma hd_rcons' (a : A) (l : list A)
    : hd a (l ++ [a]) = hd a l.
  Proof.
    induction l;cbn;eauto.
  Qed.

  Lemma In_rcons (a b : A) l :
    In a (l :r: b) <-> a = b \/ In a l.
  Proof.
    split.
    - induction l; cbn; firstorder.
    - intros. destruct H; induction l; cbn; firstorder.
  Qed.

  Lemma cons_rcons' (a : A) l :
    (a :: l) = (rev (tl (rev (a :: l))) :r: hd a (rev (a :: l))).
  Proof.
    revert a; induction l; intros b; [ cbn in *; eauto|].
    rewrite rev_cons. rewrite tl_rcons.
    - rewrite rev_rcons. erewrite hd_rcons.
      + rewrite cons_rcons_assoc. f_equal. apply IHl.
      + rewrite rev_length; cbn; lia.
    - rewrite rev_length; cbn; lia.
  Qed.

  Lemma cons_rcons (a : A) l : exists a' l', (a :: l) = (l' :r: a').
  Proof.
    exists (hd a (rev (a :: l))). exists (rev (tl (rev (a :: l)))).
    apply cons_rcons'.
  Qed.

  (*  Lemma rcons_cons (a : A) l : exists a' l', (l :r: a) = (a' :: l').
    remember (@cons_rcons A) as H.*)


  Ltac rem_cons_rcons a l H :=
    let a' := fresh a in
    let l' := fresh l in
    specialize (cons_rcons a l) as [a' [l' H]].

  Lemma rev_nil (l : list A) : rev l = nil <-> l = nil.
  Proof.
    split; induction l; cbn in *; eauto; intros.
    - exfalso. induction (rev l); cbn in *; congruence.
    - congruence.
  Qed.

  (*  Lemma rev_inj (l l': list A) : rev l = rev l' -> l = l'.*)

  Lemma rcons_destruct (l : list A) : l = nil \/ exists a l', l = l' :r: a.
  Proof.
    destruct l.
    - left. reflexivity.
    - right. apply cons_rcons.
  Qed.

  Lemma rcons_not_nil (l : list A) a : l :r: a <> nil.
  Proof.
    intro N. induction l; cbn in *; congruence.
  Qed.

  Lemma length_rcons l (a : A) : length (l :r: a) = S (length l).
  Proof.
    induction l; cbn; eauto.
  Qed.

  Lemma rcons_length n (l l' : list A) a :
    S n = length l -> l = l' :r: a -> n = length l'.
  Proof.
    revert l l'.
    induction n; intros; cbn in *; eauto; subst l; rewrite length_rcons in H; lia.
  Qed.

  Lemma rcons_ind
    : forall (P : list A -> Prop),
      P nil -> (forall (a : A) (l : list A), P l -> P (l :r: a)) -> forall l : list A, P l.
  Proof.
    intros.
    remember (length l) as n.
    revert l n Heqn.
    refine ((fix F (l : list A) (n : nat) {struct n} : n = length l -> P l :=
               match rcons_destruct l,n with
               | or_introl lnil, O => (fun (Hnl : O = length l) => _)
               | or_intror (ex_intro _ a (ex_intro _ _ _)), S n => fun (Hnl : (S n) = length l) => _
               | _,_ => _
               end)).
    - subst l. eauto.
    - clear F. intros. subst l. eauto.
    - clear F. intros. destruct l; eauto using rcons_not_nil. cbn in H1. congruence.
    - rewrite e1. apply H0. apply (F l0 n).
      eapply rcons_length; eauto.
  Qed.

  Lemma app_cons_assoc (a : A) l l'
  : l ++ a :: l' = l :r: a ++ l'.
  Proof.
    induction l; cbn; eauto.
    f_equal. rewrite IHl. reflexivity.
  Qed.

  Lemma incl_cons_hd (a : A) l l'
        (Hincl : (a :: l) l')
    : a l'.
  Proof.
    induction l;cbn in *;unfold incl in Hincl;eauto;firstorder.
  Qed.

map facts

  Lemma map_rcons (f : A -> B) :
    forall a l, map f (l :r: a) = map f l :r: f a.
  Proof.
    intros. induction l;cbn;eauto. rewrite IHl. reflexivity.
  Qed.

  Lemma map_inj_in (f : A -> B) (Hinj : injective f) (l : list A) (a : A)
    : (f a) map f l -> a l.
  Proof.
    induction l;intros;cbn in *.
    - contradiction.
    - destruct H.
      + eapply Hinj in H. subst. left;auto.
      + right;eauto.
  Qed.

  Lemma map_inj_incl (f : A -> B) (Hinj : injective f) (l l' : list A)
    : map f l map f l' -> l l'.
  Proof.
    revert l'.
    induction l;intros;cbn in *.
    - eauto.
    - intros b Hb. destruct Hb.
      + subst. eapply map_inj_in;eauto.
      + eapply IHl;eauto. intros c Hc. eapply H. right. auto.
  Qed.

End Rcons.

Ltac congruence' :=
  lazymatch goal with
  | [ H : ?l ++ (?a :: ?l') = nil |- _ ] => destruct l; cbn in H; congruence
  | [ H : (?l ++ ?l') :: ?a :: ?l'' = nil |- _ ] => destruct l; cbn in H; congruence
  | [ H : ?l :r: ?a = nil |- _ ] => eapply rcons_not_nil in H; contradiction
  | [ H : nil = ?l ++ (?a :: ?l') |- _ ] => destruct l; cbn in H; congruence
  | [ H : nil = (?l ++ ?l') :: ?a :: ?l'' |- _ ] => destruct l; cbn in H; congruence
  | [ H : nil = ?l :r: ?a |- _ ] => symmetry in H; eapply rcons_not_nil in H; contradiction
  end.

Ltac fold_rcons H :=
  match type of H with
  | context C [?a :: ?b :: nil] => fold (app (a :: nil) (b :: nil)) in H;
                                  fold ((a :: nil) :r: b) in H;
                                  repeat rewrite <-cons_rcons_assoc in H
  | context C [?l ++ ?a :: nil] => fold (l :r: a) in H
  end.

Section Rcons.

  Lemma rcons_eq1 {A : Type} (l l' : list A) (a a' : A)
    : l :r: a = l' :r: a' -> l = l'.
  Proof.
    revert l'; induction l; intros; destruct l'; cbn in *; inversion H; eauto; try congruence'.
    f_equal. eapply IHl. repeat fold_rcons H2. auto.
  Qed.

  Lemma rcons_eq2 {A : Type} (l l' : list A) (a a' : A)
    : l :r: a = l' :r: a' -> a = a'.
  Proof.
    revert l'; induction l; intros; destruct l'; cbn in *; inversion H; eauto; congruence'.
  Qed.

  Lemma rev_injective {A : Type} (l l' : list A)
    : rev l = rev l' -> l = l'.
  Proof.
    revert l. induction l'; intros; cbn in *.
    - destruct l;[reflexivity|cbn in *;congruence'].
    - destruct l;cbn in *;[congruence'|].
      repeat fold_rcons H.
      f_equal;[eapply rcons_eq2;eauto|apply IHl';eapply rcons_eq1;eauto].
  Qed.

  Lemma rev_rev_eq (A : Type) (l l' : list A)
    : l = l' <-> rev l = rev l'.
  Proof.
    revert l l'.
    enough (forall (l l' : list A), l = l' -> rev l = rev l').
    { split;eauto. intros. rewrite <-rev_involutive. rewrite <-rev_involutive at 1. eauto. }
    intros ? ? Hll.
    subst. reflexivity.
  Qed.

  Lemma NoDup_rcons (A : Type) (x : A) (l : list A)
    : x l -> NoDup l -> NoDup (l :r: x).
  Proof.
    intros Hnin Hnd. revert x Hnin.
    induction Hnd; intros y Hnin; cbn.
    - econstructor; eauto. econstructor.
    - econstructor.
      + rewrite in_app_iff. contradict Hnin. destruct Hnin; [contradiction|firstorder].
      + eapply IHHnd. contradict Hnin. right; assumption.
  Qed.

  Lemma NoDup_nin_rcons (A : Type) (x : A) (l : list A)
    : NoDup (l :r: x) -> x l.
  Proof.
    intros Hnd Hin.
    dependent induction l.
    - destruct Hin.
    - destruct Hin; rewrite cons_rcons_assoc in Hnd; inversion Hnd.
      + subst a. eapply H2. apply In_rcons;firstorder.
      + eapply IHl; eauto.
  Qed.

  Lemma NoDup_app (A : Type) (l l' : list A)
    : NoDup (l ++ l') -> forall a, a l -> a l'.
  Proof.
    induction l; intros; cbn; [contradiction|].
    inversion H;subst.
    destruct H0; subst.
    - contradict H3. eapply in_app_iff. firstorder.
    - eapply IHl;auto.
  Qed.

  Lemma NoDup_iff_dupfree (A : Type) (l : list A)
    : NoDup l <-> dupfree l.
  Proof.
    split;intro H; induction H;try econstructor;eauto.
  Qed.

End Rcons.

Lemma set_eq_NoDup_len (A : Type) (l l' : list A)
      (Heq : l =' l')
      (Hnd : NoDup l)
      (Hnd' : NoDup l')
  : | l | = | l' |.
Proof.
  destruct Heq.
  eapply Nat.le_antisymm;eapply NoDup_incl_length;eauto.
Qed.

take_r
  • 'take_r' is the dual variant to 'take'

Section Take_r.

  Variable (A : Type).

  Definition take_r (A : Type) (n : nat) (l : list A) := rev (take n (rev l)).

  Lemma take_tl (n : nat) (l : list A)
        (Hn : S n = |l|)
    : take n l = rev (tl (rev l)).
  Proof.
    revert dependent n.
    induction l;intros;cbn in *.
    - congruence.
    - destruct n;cbn.
      + inversion Hn;subst. symmetry in H0. rewrite length_zero_iff_nil in H0. subst l. cbn. reflexivity.
      + inversion Hn;subst. exploit IHl. rewrite IHl.
        fold (rev l :r: a). rewrite tl_rcons.
        * rewrite rev_rcons. reflexivity.
        * rewrite rev_length. lia.
  Qed.

  Lemma take_r_tl (n : nat) (l : list A)
        (Hn : S n = |l|)
    : take_r n l = tl l.
  Proof.
    unfold take_r. rewrite take_tl; [|rewrite rev_length;eauto].
    do 2 rewrite rev_involutive. reflexivity.
  Qed.

End Take_r.

Lemma rcons_cons'
  : forall (A : Type) (a : A) (l : list A),
    l ++ [a] = hd a l :: (tl (l ++ [a])).
Proof.
  intros.
  induction l;cbn;eauto.
Qed.

Lemma rcons_cons
  : forall (A : Type) (a' : A) (l' : list A), exists (a : A) (l : list A), l' :r: a' = a :: l.
Proof.
  intros. eexists. eexists. eapply rcons_cons'.
Qed.

Lemma take_rcons_drop (A : Type) (l : list A) n a
      (Hle : n <= | l |)
  : take n (l ++[a]) = take n l.
Proof.
  revert l Hle.
  induction n;[cbn;eauto|];intros.
  rewrite rcons_cons'.
  cbn.
  destruct l;cbn.
  - cbn in Hle. lia.
  - cbn in Hle.
    f_equal. eapply IHn. lia.
Qed.

Lemma take_r_cons_drop (A : Type) (l : list A) n a
      (Hle : n <= | l |)
  : take_r n (a :: l) = take_r n l.
Proof.
  unfold take_r.
  rewrite rev_cons. rewrite take_rcons_drop.
  - reflexivity.
  - rewrite rev_length. auto.
Qed.

Lemma take_r_cons_replace (A : Type) (a b : A) l n
      (Hlen : n <= |l|)
  : take_r n (a :: l) = take_r n (b :: l).
Proof.
  rewrite take_r_cons_drop;eauto.
  rewrite take_r_cons_drop;eauto.
Qed.

Lemma tl_len (A : Type) (l : list A)
  : |tl l| = |l| - 1.
Proof.
  induction l;cbn;eauto. lia.
Qed.

Lemma take_self (A : Type) (l : list A)
  : take (|l|) l = l.
Proof.
  induction l;cbn;eauto.
  rewrite IHl;eauto.
Qed.

Lemma take_r_self (A : Type) (l : list A)
  : take_r (|l|) l = l.
Proof.
  unfold take_r. eapply rev_rev_eq. rewrite rev_involutive.
  rewrite <-rev_length. eapply take_self.
Qed.

Lemma take_r_tl_eq (A : Type) (l : list A)
  : tl l = take_r (|l| - 1) l.
Proof.
  rewrite <- tl_len.
  destruct l;eauto. unfold tl in *.
  rewrite take_r_cons_drop;[|eauto].
  rewrite take_r_self. reflexivity.
Qed.

Lemma take_r_geq (A : Type) n (l : list A)
      (Hgeq : n >= | l |)
  : take_r n l = l.
Proof.
  unfold take_r. rewrite take_eq_ge.
  - eapply rev_involutive.
  - rewrite rev_length. auto.
Qed.

Lemma take_take_le:
  forall [X : Type] (L : list X) [n m : nat], n <= m -> take n L = take n (take m L).
Proof.
  intros. revert dependent L; revert dependent m; induction n;intros; destruct L, m; simpl; eauto.
  - lia.
  - erewrite IHn; eauto. lia.
Qed.

Lemma take_r_take_r_leq (A : Type) (l : list A) n m
      (Hle : n <= m)
  : take_r n (take_r m l) = take_r n l.
Proof.
  unfold take_r.
  rewrite rev_involutive.
  erewrite <-take_take_le;eauto.
Qed.

Lemma take_r_length_le (A : Type) n (l : list A)
      (Hle : n <= |l|)
  : | take_r n l | = n.
Proof.
  unfold take_r.
  rewrite rev_length.
  rewrite take_length_le;auto.
  rewrite rev_length. auto.
Qed.

Lemma take_r_length_ge (A : Type) n (l : list A)
      (Hle : n >= |l|)
  : | take_r n l | = | l |.
Proof.
  unfold take_r.
  rewrite rev_length.
  rewrite take_length_ge;rewrite rev_length;auto.
Qed.

Lemma take_take_r (A : Type) (l : list A) (n m : nat)
      (Hsum : n + m = | l |)
  : l = take n l ++ take_r m l.
Proof.
  revert n m Hsum.
  induction l;intros;cbn.
  - cbn. cbn in Hsum. destruct n;[|lia];destruct m;[|lia]. cbn. reflexivity.
  - destruct n.
    + cbn. rewrite take_r_geq;eauto. lia.
    + cbn. f_equal. rewrite take_r_cons_drop.
      * eapply IHl. cbn in Hsum. lia.
      * cbn in Hsum. lia.
Qed.

Ltac destr_r' l :=
  let H := fresh "Hl" in
  let x := fresh "x" in
  let l' := fresh "l" in
  let Hlx := fresh "Hx" in
  specialize (rcons_destruct l) as H;
  destruct H as [H|[x [l' Hlx]]].

Lemma nin_tl_iff (A : Type) `{EqDec A eq} (a : A) (l : list A)
  : a tl (rev l) -> a l \/ Some a = hd_error (rev l).
Proof.
  intros.
  destr_r' l;subst.
  - left. cbn. exact id.
  - rewrite rev_rcons in H0. cbn in H0.
    rewrite rev_rcons. cbn.
    destruct (x == a).
    + right. rewrite e. reflexivity.
    + left. contradict H0. rewrite <-in_rev. eapply In_rcons in H0. destruct H0. subst.
      * exfalso;apply c; reflexivity.
      * auto.
Qed.

Definition r_tl (A : Type) (l : list A) := rev (tl (rev l)).

Lemma r_tl_rcons (A : Type) (l : list A) (a : A)
  : r_tl (l ++[a]) = l.
Proof.
  unfold r_tl.
  rewrite rev_unit. simpl. rewrite rev_involutive. reflexivity.
Qed.

Definition inner (A : Type) (l : list A) := tl (rev (tl (rev l))).

Lemma inner_cons_rcons (A : Type) (l : list A) (a b : A)
  : inner (a :: l ++ [b]) = l.
Proof.
  unfold inner.
  destruct l.
  - cbn. reflexivity.
  - rewrite app_comm_cons.
    do 3 (rewrite rev_unit; simpl).
    rewrite rev_involutive.
    reflexivity.
Qed.

Lemma inner_rcons (A : Type) (l : list A) (a : A)
  : inner (l ++ [a]) = tl l.
Proof.
  unfold inner.
  rewrite rev_unit. simpl. rewrite rev_involutive. reflexivity.
Qed.

Lemma hd_map_fst_snd (A B : Type) (a : A) (b : B) (l : list (A * B))
  : hd (a,b) l = (hd a (map fst l), hd b (map snd l)).
Proof.
  destruct l;cbn;eauto using surjective_pairing.
Qed.

Lemma cons_neq (A : Type) (l : list A) (a : A)
  : l = a :: l -> False.
Proof.
  induction l;intros;[congruence|].
  inversion H. subst. eauto.
Qed.

Lemma tl_neq_cons (A : Type) (a : A) (l : list A)
  : tl l = a :: l -> False.
Proof.
  revert a. induction l;intros;[cbn in *;congruence|].
  eapply IHl. cbn in H. rewrite H at 1. cbn. reflexivity.
Qed.

Lemma filter_nil (A : Type) (f : decPred A) l
      (Hnil : filter f l = [])
  : forall x, x l -> ~ f x.
Proof.
  induction l;intros;cbn.
  - contradiction.
  - destruct H;subst.
    + cbn in Hnil. decide (f x);[congruence|auto].
    + eapply IHl;eauto. cbn in Hnil. decide (f a);[congruence|auto].
Qed.

Definition inner' := fun (A : Type) (l : list A) => rev (tl (rev (tl l))).

Lemma inner_inner' (A : Type) (l : list A)
  : inner l = inner' l.
Proof.
  unfold inner, inner'.
  destruct l;cbn;eauto.
  destr_r' l;subst l;cbn;eauto.
  rewrite rev_rcons. cbn.
  rewrite rev_involutive.
  rewrite rev_rcons. cbn.
  rewrite rev_involutive.
  reflexivity.
Qed.

Lemma inner_rtl (A : Type) (l : list A) (a : A)
  : inner (a :: l) = r_tl l.
Proof.
  rewrite inner_inner'.
  unfold inner',r_tl in *.
  cbn.
  reflexivity.
Qed.

Lemma inner_tl (A : Type) (l : list A) (a : A)
  : inner (l ++ [a]) = tl l.
Proof.
  unfold inner.
  rewrite rev_rcons.
  cbn.
  rewrite rev_involutive.
  reflexivity.
Qed.

Lemma inner_eval_lr (A : Type) (l : list A) (a b : A)
  : inner (a :: l ++ [b]) = l.
Proof.
  rewrite <-cons_rcons_assoc.
  rewrite inner_tl.
  cbn.
  reflexivity.
Qed.

Lemma inner_empty_iff (A : Type) (l : list A) (a b : A)
  : inner (a :: b :: l) = [] <-> l = nil.
Proof.
  destruct l;cbn.
  - firstorder.
  - split;intro H;[|congruence].
    specialize (cons_rcons a0 l) as Hspec. destructH. rewrite Hspec in H.
    rewrite inner_rtl in H.
    rewrite <-cons_rcons_assoc in H.
    rewrite r_tl_rcons in H.
    congruence.
Qed.

Lemma list_cycle2 (A : Type) (a b : A) (l : list A)
  : a :: b :: l <> l.
Proof.
  revert a b.
  induction l;intros;cbn.
  - congruence.
  - intro N. inv N. eapply IHl;eauto.
Qed.

Lemma len_tl_neq_cons (A : Type) (l l' : list A)
  : |l| = |l'| -> tl l = tl l' -> l <> l' -> exists a a' l'', l = a :: l'' /\ l' = a' :: l''.
Proof.
  intros.
  destruct l,l';cbn in *;try congruence.
  subst l'.
  exists a, a0, l.
  split;eauto.
Qed.

Lemma map_rcons' {A B : Type} (a : A) (r : list A) (f : A -> B)
  : map f (r :r: a) = (map f r) ++ (map f [a]).
Proof.
  induction r.
  - reflexivity.
  - simpl. rewrite IHr. f_equal.
Qed.

Lemma app_cons_rcons {A : Type} (a b : list A) x
  : a ++ x :: b = (a :r: x) ++ b.
Proof.
  revert a b.
  induction a; intros.
  - reflexivity.
  - simpl. rewrite IHa. reflexivity.
Qed.

Lemma hd_rev_idempotent {A : Type} (l : list A) a b d1 d2
  : hd d1 (rev (b :: l)) = hd d2 (rev (a :: b :: l)).
Proof.
  revert a b.
  induction l as [| x l].
  - reflexivity.
  - intros a b. rewrite rev_cons.
    erewrite hd_rcons.
    2: { rewrite rev_length. simpl. eauto with zarith. }
    rewrite (IHl b x).
    repeat rewrite rev_cons.
    repeat rewrite <- app_assoc.
    remember (rev l) as rl.
    destruct rl as [| y rl]; reflexivity.
Qed.

Lemma hd_rev_app_eq {A : Type} (p l : list A) a d
  : hd d (rev (p ++ a :: l)) = hd d (rev (a :: l)).
Proof.
  revert a p.
  induction l; intros.
  - rewrite rev_unit. reflexivity.
  - rewrite app_cons_rcons. rewrite IHl. eauto using hd_rev_idempotent.
Qed.

Lemma hd_rev_cons_eq {A : Type} (l : list A) a b d
  : hd d (rev (a :: b :: l)) = hd d (rev (b :: l)).
Proof.
  revert a b.
  induction l; intros.
  - reflexivity.
  - simpl. rewrite <- 3 app_cons_rcons. remember (rev l) as l'. destruct l'; simpl; eauto.
Qed.

Lemma NoDup_app_drop (A : Type) (l l' : list A)
      (Hnd : NoDup (l ++ l'))
  : NoDup l.
Proof.
  induction l;inversion Hnd;cbn in *;eauto.
  - econstructor.
  - econstructor.
  - subst. econstructor;eauto.
Qed.

Lemma take_r_app_eq (A : Type) n (i j : list A)
      (Hle : n = | j |)
  : take_r n (i ++ j) = j.
Proof.
  revert i j Hle.
  induction n;intros;destr_r' j;subst;inversion Hle.
  - cbn. reflexivity.
  - rewrite app_length in Hle. cbn in Hle. lia.
  - rewrite app_length in Hle. cbn in Hle.
    unfold take_r. rewrite app_assoc. rewrite rev_rcons. cbn. f_equal.
    eapply IHn. lia.
Qed.