Library mathcomp_eulerian.psi_cdindex_tree_shape


From mathcomp Require Import all_ssreflect.
Require Import mmtree psi_core psi_comm psi_descent_v2 psi_descent_thms.

Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.



Fixpoint mmtree_shape_fuel (fuel : nat) (s : seq nat) : seq nat :=
  match fuel with
  | 0 ⇒ [::]
  | fuel'.+1
      match s with
      | [::][::]
      | _ :: _
          let j := mm_pos s in
          mm_pos s ::
            (mmtree_shape_fuel fuel' (take j s) ++
             mmtree_shape_fuel fuel' (drop j.+1 s))
      end
  end.

Arguments mmtree_shape_fuel : simpl never.

Definition mmtree_shape (s : seq nat) : seq nat :=
  mmtree_shape_fuel (size s) s.


Lemma mmtree_shape_fuel_monotone fuel1 fuel2 s :
  size s fuel1 fuel1 fuel2
  mmtree_shape_fuel fuel2 s = mmtree_shape_fuel fuel1 s.
Proof.
elim: fuel1 fuel2 s ⇒ [|f1 IH] f2 s Hsz1 Hle.
  move: Hsz1; rewrite leqn0 ⇒ /nilP →.
  by case: f2 Hle ⇒ //=; case: f2 ⇒ //.
case: f2 Hle ⇒ // f2 Hle.
case: s Hsz1 ⇒ [//|a s0 Hsz1].
rewrite /mmtree_shape_fuel -/mmtree_shape_fuel.
set s := a :: s0.
have Hj : mm_pos s < size s by apply: mm_pos_lt.
set j := mm_pos s.
have Hj2 : j size s0 by rewrite -ltnS.
have Htake_sz : size (take j s) f1.
  rewrite size_take Hj.
  by apply: (leq_trans Hj2); rewrite -ltnS.
have Hdrop_sz : size (drop j.+1 s) f1.
  rewrite size_drop /= subSS.
  by apply: (leq_trans (leq_subr _ _)); rewrite -ltnS.
by rewrite (IH f2 _ Htake_sz Hle) (IH f2 _ Hdrop_sz Hle).
Qed.

Lemma mmtree_shape_nil : mmtree_shape [::] = [::].
Proof. by rewrite /mmtree_shape /mmtree_shape_fuel. Qed.

Lemma mmtree_shape_cons a s0 :
  let s := a :: s0 in
  let j := mm_pos s in
  mmtree_shape s =
    mm_pos s :: (mmtree_shape (take j s) ++ mmtree_shape (drop j.+1 s)).
Proof.
set s := a :: s0. set j := mm_pos s.
have Hj : j < size s by apply: mm_pos_lt.
have Hj2 : j size s0 by rewrite -ltnS.
have Htake_sz : size (take j s) size s0.
  by rewrite size_take Hj.
have Hdrop_sz : size (drop j.+1 s) size s0.
  by rewrite size_drop /= subSS; apply: leq_subr.
rewrite /mmtree_shape /mmtree_shape_fuel -/mmtree_shape_fuel /=.
congr (_ :: (_ ++ _)); apply: mmtree_shape_fuel_monotone ⇒ //.
Qed.

Opaque mmtree_shape_fuel mmtree_shape.

Lemma size_mmtree_shape : s, size (mmtree_shape s) = size s.
Proof.
suff H : n s, size s n size (mmtree_shape s) = size s.
  by moves; apply: H (leqnn _).
elim⇒ [|n IH] s Hsz.
  by move: Hsz; rewrite leqn0 ⇒ /nilP ->; rewrite mmtree_shape_nil.
case: s Hsz ⇒ [|a s0]; first by rewrite mmtree_shape_nil.
moveHsz.
rewrite (mmtree_shape_cons a s0).
set s := a :: s0; set j := mm_pos s.
have Hj : j < size s by apply: mm_pos_lt.
have Hj' : j size s0 by rewrite -ltnS.
have Hsz_n : size s0 n by rewrite -ltnS.
have Htake_sz : size (take j s) n.
  by rewrite size_take Hj; apply: leq_trans Hj' Hsz_n.
have Hdrop_sz : size (drop j.+1 s) n.
  rewrite size_drop /= subSS.
  by apply: leq_trans (leq_subr _ _) Hsz_n.
rewrite /= size_cat (IH _ Htake_sz) (IH _ Hdrop_sz).
rewrite size_take Hj size_drop /= subSS.
by rewrite (subnKC Hj').
Qed.


Lemma mmtree_shape_order_iso (s1 s2 : seq nat) :
  size s1 = size s2 uniq s1 uniq s2
  ( p q, p < size s1 q < size s1
    (nth 0 s1 p < nth 0 s1 q) =
    (nth 0 s2 p < nth 0 s2 q))
  mmtree_shape s1 = mmtree_shape s2.
Proof.
move: s1 s2.
suff Hgen : n s1 s2, size s1 n
  size s1 = size s2 uniq s1 uniq s2
  ( p q, p < size s1 q < size s1
    (nth 0 s1 p < nth 0 s1 q) =
    (nth 0 s2 p < nth 0 s2 q))
  mmtree_shape s1 = mmtree_shape s2.
  by moves1 s2; apply: (Hgen (size s1)); rewrite ?leqnn.
elim⇒ [|n IH] s1 s2 Hsz1 Hszeq Hu1 Hu2 Hord.
  have Hsz0 : size s1 = 0 by apply/eqP; rewrite -leqn0.
  by rewrite (size0nil Hsz0); rewrite Hsz0 in Hszeq;
     rewrite (size0nil (esym Hszeq)).
case Hs1 : s1 ⇒ [|a1 t1].
  by move: Hszeq; rewrite Hs1 /= ⇒ Hsz2;
     have → : s2 = [::] by apply/eqP; rewrite -size_eq0 -Hsz2.
case Hs2 : s2 ⇒ [|a2 t2].
  by move: Hszeq; rewrite Hs1 Hs2 /=.
have Hne1 : a1 :: t1 [::] by [].
have Hszeq' : size (a1 :: t1) = size (a2 :: t2) by rewrite -Hs1 -Hs2.
subst s1 s2.
have Hmm := mm_pos_order_iso Hszeq' Hu1 Hu2 Hne1 Hord.
set m := mm_pos (a1 :: t1).
rewrite (mmtree_shape_cons a1 t1) -/m.
rewrite (mmtree_shape_cons a2 t2) -Hmm -/m.
have Hm1 : m < size (a1 :: t1) by apply: mm_pos_lt.
have Hm2 : m < size (a2 :: t2) by rewrite -Hszeq'.
congr (_ :: (_ ++ _)).
- apply: (IH _ _ _ _ _ _).
  + rewrite (size_takel (ltnW Hm1)).
    exact: leq_trans Hm1 Hsz1.
  + by rewrite (size_takel (ltnW Hm1)) (size_takel (ltnW Hm2)).
  + exact: take_uniq.
  + exact: take_uniq.
  + exact: order_iso_take Hm1 Hszeq' Hord.
- apply: (IH _ _ _ _ _ _).
  + rewrite size_drop /= subSS.
    by apply: leq_trans (leq_subr _ _) _; rewrite -ltnS.
  + by rewrite !size_drop Hszeq'.
  + exact: drop_uniq.
  + exact: drop_uniq.
  + exact: order_iso_drop Hm1 Hszeq' Hord.
Qed.

Opaque mmtree_shape_order_iso.


Lemma seq_cat_left_eq (T : eqType) (s1 s2 t1 t2 : seq T) :
  size s1 = size t1 s1 ++ s2 = t1 ++ t2 s1 = t1.
Proof.
moveHsz Heq.
have Htake : take (size s1) (s1 ++ s2) = take (size s1) (t1 ++ t2)
  by rewrite Heq.
rewrite take_size_cat // in Htake.
by rewrite Htake Hsz take_size_cat.
Qed.

Lemma seq_cat_right_eq (T : eqType) (s1 s2 t1 t2 : seq T) :
  size s1 = size t1 s1 ++ s2 = t1 ++ t2 s2 = t2.
Proof.
moveHsz Heq.
have Hdrop : drop (size s1) (s1 ++ s2) = drop (size s1) (t1 ++ t2)
  by rewrite Heq.
rewrite drop_size_cat // in Hdrop.
by rewrite Hdrop Hsz drop_size_cat.
Qed.

Lemma mmtree_shape_decompose s1 s2 :
  size s1 = size s2
  s1 [::]
  mmtree_shape s1 = mmtree_shape s2
  [/\ mm_pos s1 = mm_pos s2,
      mmtree_shape (take (mm_pos s1) s1) =
        mmtree_shape (take (mm_pos s2) s2)
    & mmtree_shape (drop (mm_pos s1).+1 s1) =
        mmtree_shape (drop (mm_pos s2).+1 s2)].
Proof.
case: s1 ⇒ [|a1 t1]; first by move_ /(_ erefl).
case: s2 ⇒ [|a2 t2] /=; first by [].
moveHszeq _ Hshape.
move: Hshape.
rewrite (mmtree_shape_cons a1 t1) (mmtree_shape_cons a2 t2).
caseHhead Htail.
have Hm1lt : mm_pos (a1 :: t1) < size (a1 :: t1) by apply: mm_pos_lt.
have Hm2lt : mm_pos (a2 :: t2) < size (a2 :: t2) by apply: mm_pos_lt.
have Hsz_take :
    size (mmtree_shape (take (mm_pos (a1 :: t1)) (a1 :: t1))) =
    size (mmtree_shape (take (mm_pos (a2 :: t2)) (a2 :: t2))).
  by rewrite !size_mmtree_shape !size_take Hm1lt Hm2lt Hhead.
split.
- exact: Hhead.
- exact: seq_cat_left_eq Hsz_take Htail.
- exact: seq_cat_right_eq Hsz_take Htail.
Qed.


Lemma has_left_child_of_shape : (s1 s2 : seq nat) i,
  size s1 = size s2
  mmtree_shape s1 = mmtree_shape s2
  has_left_child i s1 = has_left_child i s2.
Proof.
suff H : n s1 s2 i, size s1 n
  size s1 = size s2
  mmtree_shape s1 = mmtree_shape s2
  has_left_child i s1 = has_left_child i s2.
  by moves1 s2 i; apply: (H (size s1)); rewrite ?leqnn.
elim⇒ [|n IH] s1 s2 i Hsz1 Hszeq Hshape.
  have Hsz0 : size s1 = 0 by apply/eqP; rewrite -leqn0.
  by rewrite (size0nil Hsz0); rewrite Hsz0 in Hszeq;
     rewrite (size0nil (esym Hszeq)).
case Hs1 : s1 ⇒ [|a1 t1].
  by move: Hszeq; rewrite Hs1 /= ⇒ Hsz2;
     have → : s2 = [::] by apply/eqP; rewrite -size_eq0 -Hsz2.
case Hs2 : s2 ⇒ [|a2 t2].
  by move: Hszeq; rewrite Hs1 Hs2 /=.
have Hne1 : (a1 :: t1) [::] by [].
have Hszeq' : size (a1 :: t1) = size (a2 :: t2) by rewrite -Hs1 -Hs2.
subst s1 s2.
have [Hmm Hltake Hldrop] := mmtree_shape_decompose Hszeq' Hne1 Hshape.
rewrite -Hmm in Hltake Hldrop.
set m := mm_pos (a1 :: t1).
rewrite -/m in Hltake Hldrop.
have Hm1 : m < size (a1 :: t1) by apply: mm_pos_lt.
have Hm2 : m < size (a2 :: t2) by rewrite -Hszeq'.
rewrite (has_left_child_cons i a1 t1) -/m.
rewrite (has_left_child_cons i a2 t2) -Hmm -/m.
case: (ltngtP i m) ⇒ Him.
-
  apply: (IH _ _ _ _ _ Hltake).
  + rewrite size_takel ?(ltnW Hm1) //.
    exact: leq_trans Hm1 Hsz1.
  + by rewrite (size_takel (ltnW Hm1)) (size_takel (ltnW Hm2)).
-
  apply: (IH _ _ _ _ _ Hldrop).
  + rewrite size_drop /= subSS.
    have Hszt : size t1 n by rewrite -ltnS.
    exact: leq_trans (leq_subr _ _) Hszt.
  + by rewrite !size_drop Hszeq'.
-
  by [].
Qed.

Lemma window_size_of_shape : (s1 s2 : seq nat) i,
  size s1 = size s2
  mmtree_shape s1 = mmtree_shape s2
  window_size i s1 = window_size i s2.
Proof.
suff H : n s1 s2 i, size s1 n
  size s1 = size s2
  mmtree_shape s1 = mmtree_shape s2
  window_size i s1 = window_size i s2.
  by moves1 s2 i; apply: (H (size s1)); rewrite ?leqnn.
elim⇒ [|n IH] s1 s2 i Hsz1 Hszeq Hshape.
  have Hsz0 : size s1 = 0 by apply/eqP; rewrite -leqn0.
  by rewrite (size0nil Hsz0); rewrite Hsz0 in Hszeq;
     rewrite (size0nil (esym Hszeq)).
case Hs1 : s1 ⇒ [|a1 t1].
  by move: Hszeq; rewrite Hs1 /= ⇒ Hsz2;
     have → : s2 = [::] by apply/eqP; rewrite -size_eq0 -Hsz2.
case Hs2 : s2 ⇒ [|a2 t2].
  by move: Hszeq; rewrite Hs1 Hs2 /=.
have Hne1 : (a1 :: t1) [::] by [].
have Hszeq' : size (a1 :: t1) = size (a2 :: t2) by rewrite -Hs1 -Hs2.
subst s1 s2.
have [Hmm Hltake Hldrop] := mmtree_shape_decompose Hszeq' Hne1 Hshape.
rewrite -Hmm in Hltake Hldrop.
set m := mm_pos (a1 :: t1).
rewrite -/m in Hltake Hldrop.
have Hm1 : m < size (a1 :: t1) by apply: mm_pos_lt.
have Hm2 : m < size (a2 :: t2) by rewrite -Hszeq'.
rewrite (window_size_cons i a1 t1) -/m.
rewrite (window_size_cons i a2 t2) -Hmm -/m.
case: (ltngtP i m) ⇒ Him.
-
  apply: (IH _ _ _ _ _ Hltake).
  + rewrite size_takel ?(ltnW Hm1) //.
    exact: leq_trans Hm1 Hsz1.
  + by rewrite (size_takel (ltnW Hm1)) (size_takel (ltnW Hm2)).
-
  apply: (IH _ _ _ _ _ Hldrop).
  + rewrite size_drop /= subSS.
    have Hszt : size t1 n by rewrite -ltnS.
    exact: leq_trans (leq_subr _ _) Hszt.
  + by rewrite !size_drop Hszeq'.
-
  by rewrite Hszeq'.
Qed.

Opaque has_left_child_of_shape window_size_of_shape.


Lemma has_left_child_order_iso (s1 s2 : seq nat) i :
  size s1 = size s2 uniq s1 uniq s2
  ( p q, p < size s1 q < size s1
    (nth 0 s1 p < nth 0 s1 q) =
    (nth 0 s2 p < nth 0 s2 q))
  has_left_child i s1 = has_left_child i s2.
Proof.
moveHsz Hu1 Hu2 Hord.
apply: has_left_child_of_shape; first exact: Hsz.
exact: mmtree_shape_order_iso.
Qed.

Opaque has_left_child_order_iso.


Lemma mmtree_shape_psi : (j : nat) (w : seq nat), uniq w
  mmtree_shape (psi j w) = mmtree_shape w.
Proof.
suff Hgen : n j w, size w n uniq w
  mmtree_shape (psi j w) = mmtree_shape w.
  by movej w Hu; apply: (Hgen (size w)); rewrite ?leqnn.
elim⇒ [|n IH] j w Hsz Huniq.
  by move: Hsz; rewrite leqn0 ⇒ /eqP/size0nil →.
have [Htriv | Hws_gt1] := leqP (window_size j w) 1.
  by rewrite (psi_id_trivial Htriv).
have Hjw := ws_lt_size Hws_gt1.
have Hw_ne : w [::] by case: (w) Hjw.
case: (w) Hw_ne Huniq Hws_gt1 Hjw Hsz
  [//|a s0] _ Huniq Hws_gt1 Hjw Hsz.
set s := a :: s0.
set m := mm_pos s.
have Hm : m < size s by apply: mm_pos_lt.
have Hs_ne : s [::] by discriminate.
have Hm' : mm_pos (psi j s) = m by apply: mm_pos_psi_eq.
have Hpsi_ne : psi j s [::].
  by moveE; move: Hjw; rewrite -(size_psi j) E.
have [a' [s0' Hpsi_eq]] : a' s0', psi j s = a' :: s0'.
  by case: (psi j s) Hpsi_ne ⇒ [//|x y] _; x, y.
have Hm'c : mm_pos (a' :: s0') = m by rewrite -Hpsi_eq.
rewrite Hpsi_eq.
rewrite (mmtree_shape_cons a' s0') Hm'c.
rewrite (mmtree_shape_cons a s0) -/m.
congr (_ :: (_ ++ _)).
-
  case: (ltngtP j m) ⇒ [Hjm | Hmj | Hjeqm].
  +
    rewrite -Hpsi_eq (take_mm_psi Hs_ne Huniq Hws_gt1 Hjm).
    apply: IH.
    × rewrite (size_takel (ltnW Hm)).
      exact: leq_trans Hm Hsz.
    × exact: take_uniq.
  +
    by rewrite -Hpsi_eq (@take_psi m j s (ltnW Hmj)).
  +
    by rewrite -Hpsi_eq Hjeqm (@take_psi m m s (leqnn m)).
-
  case: (ltngtP j m) ⇒ [Hjm | Hmj | Hjeqm].
  +
    suff → : drop m.+1 (a' :: s0') = drop m.+1 (a :: s0) by [].
    rewrite -Hpsi_eq.
    apply: drop_psi_above.
    apply: leq_trans (window_fits_left Hs_ne Hjm) _.
    exact: leqnSn.
  +
    suff → : drop m.+1 (a' :: s0') = psi (j - m - 1) (drop m.+1 (a :: s0)).
      apply: IH.
      × rewrite size_drop.
        apply: (leq_trans (leq_sub2r _ Hsz)).
        exact: leq_subr.
      × exact: drop_uniq.
    by rewrite -Hpsi_eq (drop_mm_psi Hs_ne Huniq Hws_gt1 Hmj).
  +
    subst j.
    have Hws_m : window_size m s = size s - m
      by rewrite (window_size_cons m a s0) -/m ltnn eqxx.
    have Hws_drop : 1 < size (drop m s)
      by rewrite size_drop -Hws_m.
    have Hdm_ne : drop m s [::]
      by case: (drop m s) Hws_drop.
    have Huniq_dm : uniq (drop m s) by exact: drop_uniq.
    have Hmm_drop : mm_pos (drop m s) = 0 by exact: mm_pos_drop_mm.
    have Hpsi_m_eq : psi m s = take m s ++ rank_shift_seq (drop m s).
      rewrite /psi.
      have Hwa_m : window_at m s = drop m s
        by rewrite (window_at_cons m a s0) -/m ltnn eqxx //.
      rewrite Hwa_m Hws_m.
      by rewrite subnKC ?drop_size ?cats0 // ltnW.
    have Hdrop_psi : drop m.+1 (a' :: s0') = behead (rank_shift_seq (drop m s)).
      rewrite -Hpsi_eq Hpsi_m_eq.
      rewrite drop_cat size_take Hm.
      have → : m.+1 < m = false by rewrite ltnNge leqnSn.
      by rewrite /= subSnn drop1.
    have Hdrop_orig : drop m.+1 (a :: s0) = behead (drop m s)
      by rewrite /s -drop1 drop_drop add1n.
    rewrite Hdrop_psi Hdrop_orig.
    apply: mmtree_shape_order_iso.
    × by rewrite !size_behead size_rank_shift_seq2.
    × rewrite -drop1.
      apply: (subseq_uniq (drop_subseq _ 1)).
      by rewrite (perm_uniq (rank_shift_perm_eq _)).
    × rewrite -drop1.
      exact: (subseq_uniq (drop_subseq _ 1)).
    ×
      move: Hdm_ne Hmm_drop Huniq_dm Hws_drop.
      case: (drop m s) ⇒ [//|dm_h dm_t] _ Hmm_drop Huniq_dm Hws_drop.
      movep q Hp Hq.
      have Hws_gt1_dm : 1 < window_size 0 (dm_h :: dm_t)
        by rewrite (window_size_cons 0 dm_h dm_t) Hmm_drop ltnn eqxx.
      have Hwat : window_at 0 (dm_h :: dm_t) = dm_h :: dm_t
        by rewrite (window_at_cons 0 dm_h dm_t) Hmm_drop ltnn eqxx drop0.
      have Hhead_ext :
        (head 0 (dm_h :: dm_t) == nth 0 (sort leq (dm_h :: dm_t)) 0) ||
        (head 0 (dm_h :: dm_t) ==
          nth 0 (sort leq (dm_h :: dm_t)) (size (dm_h :: dm_t)).-1).
        by have := window_head_extremum Huniq_dm Hws_gt1_dm; rewrite Hwat.
      have Hp1 : p.+1 < size (dm_h :: dm_t)
        by move: Hp; rewrite size_behead size_rank_shift_seq2 ltn_predRL.
      have Hq1 : q.+1 < size (dm_h :: dm_t)
        by move: Hq; rewrite size_behead size_rank_shift_seq2 ltn_predRL.
      rewrite -[behead (rank_shift_seq _)] (drop1 (rank_shift_seq (dm_h :: dm_t))).
      rewrite -[behead (dm_h :: dm_t)](drop1 (dm_h :: dm_t)).
      rewrite !nth_drop.
      have H := rank_shift_preserves_interior_order
        Huniq_dm Hws_drop Hhead_ext (ltn0Sn q) (ltn0Sn p) Hq1 Hp1.
      by rewrite -H.
Qed.

Opaque mmtree_shape_psi.


Lemma has_left_child_psi j i w :
  uniq w has_left_child i (psi j w) = has_left_child i w.
Proof.
moveHu.
apply: has_left_child_of_shape; first exact: size_psi.
exact: mmtree_shape_psi.
Qed.

Opaque has_left_child_psi.