From stdpp Require Import prelude fin_maps.

List Utility Results


Ltac splits := repeat split.
Tactic Notation "intros*" := repeat intro.

Ltac forward H :=
  match type of H with
  | ?x -> _ =>
      let name := fresh in assert x as name; [| specialize (H name); clear name]
  end.
Tactic Notation "forward" ident(H) :=
  forward H.

Tactic Notation "is_eq" ident(a) ident(b) :=
  destruct (decide (a = b)) as [<- | ?].

Tactic Notation "simp" := simplify_map_eq.

Section ListLemmas.
  Context {X Y: Type}.

  Lemma map_eq: forall (l: list X) (f g: X -> Y),
      (forall x, f x = g x) ->
      map f l = map g l.
  Proof.
    induction l; intros; auto.
    simpl.
    rewrite (H a).
    erewrite (IHl); eauto.
  Qed.

  Lemma map_split {Z W: Type}: forall (l: list (Z*X)) (l2: list (X*Y*Z*W)),
      map fst l = map (fun '(_, _, z, _) => z) l2 ->
      map snd l = map (fun '(x, _, _, _) => x) l2 ->
      l = map (fun '(x, _, z,_) => (z, x)) l2.
  Proof.
    induction l; intros.
    - inv H.
      symmetry in H2.
      apply map_eq_nil in H2.
      now subst.
    - destruct a.
      destruct l2.
      + inv H.
      + destruct p as (((?, ?), ?), ?).
        inv H.
        rewrite (IHl l2); auto.
  Qed.

  Lemma map_xs {Z W: Type}: forall (T_x_t_y_list: list (X*Y*Z*W)),
      map (fun pat_ : X * Y => let (_, x_) := pat_ in x_) (map (fun '(T_, x_, _, _) => (T_, x_)) T_x_t_y_list) =
        map (fun '(_, x, _, _) => x) T_x_t_y_list.
  Proof.
    induction T_x_t_y_list.
    - easy.
    - destruct a as (((?, ?), ?), ?).
      simpl.
      now rewrite IHT_x_t_y_list.
  Qed.

  Lemma map_split' {Z Z' W: Type}: forall (l: list (Z'*X)) (l2: list (X*Y*Z*W)) (f: Z -> Z'),
      map fst l = map (fun '(_, _, z, _) => f z) l2 ->
      map snd l = map (fun '(x, _, _, _) => x) l2 ->
      l = map (fun '(x, _, z,_) => (f z, x)) l2.
  Proof.
    induction l; intros.
    - inv H.
      symmetry in H2.
      apply map_eq_nil in H2.
      now subst.
    - destruct a.
      destruct l2.
      + inv H.
      + destruct p as (((?, ?), ?), ?).
        inv H.
        rewrite (IHl l2 f); auto.
  Qed.

  Lemma combine_fst: forall (l1: list X) (l2: list Y),
      length l1 = length l2 ->
      map (fun pat_: (X*Y) => let (e_, _) := pat_ in e_) (combine l1 l2) = l1.
  Proof.
    induction l1;intros.
    - easy.
    - destruct l2.
      + inv H.
      + inv H.
        simpl.
        rewrite (IHl1 l2 H1).
        reflexivity.
  Qed.

  Lemma combine_nil: forall (l1: list X),
      @combine X Y l1 [] = [].
  Proof. destruct l1; easy. Qed.

  Lemma combine_snd: forall (l1: list X) (l2: list Y),
      length l1 = length l2 ->
      map (fun pat_: (X*Y) => let (_, e_) := pat_ in e_) (combine l1 l2) = l2.
  Proof.
    intros.
    generalize dependent l1.
    induction l2; intros.
    - rewrite combine_nil.
      auto.
    - destruct l1.
      + inv H.
      + inv H.
        simpl.
        rewrite (IHl2 l1 H1).
        reflexivity.
  Qed.

  Lemma in_combine: forall (l1: list X) (l2: list Y) x y,
      In (x,y) (combine l1 l2) -> In x l1 /\ In y l2.
  Proof.
    induction l1;intros.
    - inv H.
    - destruct l2.
      + inv H.
      + destruct H.
        * inv H.
          split; left; auto.
        * destruct (IHl1 l2 x y H).
          split; right; auto.
  Qed.

  Lemma combine_app: forall (l1 l2: list X) (l1' l2': list Y),
      length l1 = length l1' ->
      length l2 = length l2' ->
      combine (l1 ++ l2) (l1' ++ l2') = combine l1 l1' ++ combine l2 l2'.
  Proof.
    induction l1; intros.
    - inv H.
      symmetry in H2.
      apply length_zero_iff_nil in H2.
      rewrite H2.
      easy.
    - destruct l1'.
      + inv H.
      + inv H.
        simpl.
        rewrite IHl1; easy.
  Qed.

  Lemma in_split: forall (l1 l2: list X) x,
      x (l1 ++ [x] ++ l2).
  Proof.
    induction l1; intros.
    - left; auto.
    - right.
      apply IHl1.
  Qed.

  Lemma in_combine_split: forall (l: list (X*Y)) l1 l1' l2 l2' x y,
      length l1 = length l2 ->
      l = combine (l1 ++ [x] ++ l1') (l2 ++ [y] ++ l2') ->
      In (x, y) l.
  Proof.
    induction l; intros; subst.
    - destruct l1, l2; easy.
    - destruct a.
      destruct l1,l2.
      + inv H0.
        left;auto.
      + inv H.
      + inv H.
      + inv H.
        right.
        inv H0.
        eapply IHl.
        apply H2.
        reflexivity.
  Qed.

  Lemma in_fst_iff: forall (l : list (X*Y)) x,
      (exists y, In (x, y) l) <-> In x (map (fun pat_ : X * Y => let (e_, _) := pat_ in e_) l).
  Proof.
    split; intros.
    - induction l.
      + destruct H.
        inv H.
      + destruct H as (?y & H).
        inv H.
        * left; auto.
        * right.
          apply IHl.
          exists y; auto.
    - induction l.
      + inv H.
      + destruct a as (?x & ?y).
        inv H.
        * exists y.
          left;auto.
        * destruct (IHl H0) as (?y & ?).
          exists y0.
          right.
          apply H.
  Qed.

  Lemma pat2_id: forall l: list (X*Y),
      map (fun pat_ : X*Y => let (x_, y_) := pat_ in (x_,y_)) l = l.
  Proof.
    induction l;auto.
    destruct a; cbn.
    rewrite IHl.
    reflexivity.
  Qed.

  Lemma fold_map {A Z W: Type}: forall (f: W -> Z -> A -> A) (l: list (X*Y*Z*W)) a0,
      foldr (fun '(_, _, z, w) a => f w z a) a0 l =
        foldr (fun (xt : W * Z) a => f (fst xt) (snd xt) a) a0
          (map (fun pat_ : X*Y*Z*W => let (p, y_) := pat_ in let (p0, t_) := p in let (_, _) := p0 in (y_, t_)) l).
  Proof.
    induction l; intros; eauto.
    destruct a as [[[? ?] ?] ?].
    simpl.
    now rewrite IHl.
  Qed.

  Lemma fold_map1 {A Z W: Type}: forall (f: W -> X -> A -> A) (l: list (X*Y*Z*W)) a0,
      foldr (fun '(z, _, _, w) a => f w z a) a0 l =
        foldr (fun '(z, w) a => f z w a) a0
          (map (fun '(z, _, _, w) => (w, z)) l).
  Proof.
    induction l; intros; eauto.
    destruct a as [[[? ?] ?] ?].
    simpl.
    now rewrite IHl.
  Qed.

  Lemma fold_map2 {A Z W: Type}: forall (f: Y -> X -> A -> A) (l: list (X*Y*Z*W)) a0,
      foldr (fun '(z, w, _, _) a => f w z a) a0 l =
        foldr (fun '(z, w) a => f z w a) a0
          (map (fun '(z, w, _, _) => (w, z)) l).
  Proof.
    induction l; intros; eauto.
    destruct a as [[[? ?] ?] ?].
    simpl.
    now rewrite IHl.
  Qed.

  Lemma fold_map3 {A Z W: Type}: forall (f: W -> Y -> A -> A) (l: list (X*Y*Z*W)) a0,
      foldr (fun '(_, z, _, w) a => f w z a) a0 l =
        foldr (fun '(z, w) a => f z w a) a0
          (map (fun '(_, z, _, w) => (w, z)) l).
  Proof.
    induction l; intros; eauto.
    destruct a as [[[? ?] ?] ?].
    simpl.
    now rewrite IHl.
  Qed.

  Lemma fold_map4 {A Z W: Type}: forall (f : _ -> _ -> A -> A) (l: list (X*Y*Z*W)) a0,
      foldr (fun '(z, w, _, _) a => f z w a) a0 l =
        foldr (fun '(z, w) a => f z w a) a0
          (map (fun '(z, w, _, _) => (z, w)) l).
  Proof.
    induction l; intros; eauto.
    destruct a as [[[? ?] ?] ?].
    simpl.
    now rewrite IHl.
  Qed.

  Lemma fold_map5 {A Z W: Type}: forall (f : _ -> _ -> A -> A) (l: list (X*Y*Z*W)) a0,
      foldr (fun '(z, _, _, w) a => f z w a) a0 l =
        foldr (fun '(z, w) a => f z w a) a0
          (map (fun '(z, _, _, w) => (z, w)) l).
  Proof.
    induction l; intros; eauto.
    destruct a as [[[? ?] ?] ?].
    simpl.
    now rewrite IHl.
  Qed.

  Lemma combine_map: forall (l: list (X*Y)),
      combine (map fst l) (map snd l) = l.
  Proof.
    induction l; auto.
    destruct a; simpl.
    rewrite IHl.
    reflexivity.
  Qed.

  Lemma split_corresponding_list: forall (l: list (X*Y)) left_list mid right_list,
      map (fun '(x, _) => x) l = left_list ++ [mid] ++ right_list ->
      exists left_list' mid' right_list',
        length left_list = length left_list'
        /\ length right_list = length right_list'
        /\ l = combine (left_list ++ [mid] ++ right_list) (left_list' ++ [mid'] ++ right_list').
  Proof with auto.
    induction l;intros.
    - apply app_cons_not_nil in H.
      contradiction.
    - destruct a.
      destruct left_list, right_list;
        inv H.
      + exists [], y, [].
        apply map_eq_nil in H2.
        rewrite H2.
        splits...
      + destruct (IHl [] x0 right_list H2)
          as (left_list' & mid' & right_list' & ? & ? & ?).
        exists [], y, (left_list' ++ [mid'] ++ right_list').
        splits...
        * inv H.
          symmetry in H4.
          apply length_zero_iff_nil in H4.
          rewrite H4.
          rewrite 2 app_nil_l.
          rewrite map_length.
          rewrite combine_length.
          rewrite 2 app_length.
          simpl.
          rewrite H0.
          lia.
        * simpl.
          rewrite H1.
          rewrite combine_fst.
          { easy. }
          rewrite app_nil_l.
          rewrite 3 app_length.
          rewrite <- H.
          rewrite H0.
          easy.
      + destruct (IHl left_list mid [] H2)
          as (left_list' & mid' & right_list' & ? & ? & ?).
        exists (y::left_list'), mid', right_list'.
        splits...
        * inv H0.
          simpl.
          rewrite H.
          easy.
        * rewrite H1.
          easy.
      + destruct (IHl left_list mid (x1::right_list) H2)
          as (left_list' & mid' & right_list' & ? & ? & ?).
        exists (y::left_list'), mid', (right_list').
        splits...
        * simpl.
          rewrite H.
          easy.
        * rewrite H1.
          easy.
  Qed.

  (* I thought this would be useful, but turns out we usually need the length *)
  Corollary split_corresponding_list_no_length: forall (l: list (X*Y)) left_list mid right_list,
      map (fun '(x, _) => x) l = left_list ++ [mid] ++ right_list ->
      exists left_list' mid' right_list', l = combine (left_list ++ [mid] ++ right_list) (left_list' ++ [mid'] ++ right_list').
  Proof.
    intros.
    destruct (split_corresponding_list _ _ _ _ H)
      as (left_list' & mid' & right_list' & _ & _ & EQ).
    exists left_list', mid', right_list'.
    apply EQ.
  Qed.

End ListLemmas.

Lemma cons_neq {X:Type}: forall (x:X) l, x :: l <> l.
Proof.
  induction l; auto.
  intro.
  inversion H; subst.
  now apply IHl.
Qed.