Library mathcomp_eulerian.psi_cdindex_support


From mathcomp Require Import all_ssreflect.
Require Import mmtree psi_core psi_comm psi_descent_v2 psi_descent_thms.
Require Import psi_cdindex_core psi_cdindex_witness.
Require Export psi_cdindex_support_defs.

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

Arguments window_size_fuel : simpl never.
Arguments has_left_child_fuel : simpl never.



Lemma S_w_seq_bound w k :
  k \in S_w_seq w k < (size w).-1.-1.
Proof.
rewrite /S_w_seq.
move⇒ /mapP [i].
rewrite mem_filter ⇒ /andP [Hd Hi] →.
move: Hi; rewrite mem_iota ⇒ /andP [Hi1 Hi2].
suff Hlt : i < (size w).-1.
  by case: i Hi1 Hi2 Hd Hlt ⇒ [//|i'] _ _ _ /=; case: (size w) ⇒ [|[|n]] //=.
rewrite ltn_neqAle.
have Hle : i (size w).-1.
  by rewrite -ltnS; case: (size w) Hi2 ⇒ [|n] //=.
rewrite Hle andbT.
apply/negP ⇒ /eqP Heqi.
move: Hd; rewrite Heqi /is_D_letter /classify_vertex_cde.
have Hws1 : window_size (size w).-1 w 1.
  apply: leq_trans (window_size_bound (size w).-1 w) _.
  case: (size w) Hi2 ⇒ [|[|n]] //= _.
  by rewrite subSS subSnn.
have Hnint : is_internal (size w).-1 w = false.
  rewrite /is_internal.
  case Hlt : ((size w).-1 < size w) ⇒ //=.
  by rewrite ltnNge Hws1.
by rewrite Hnint.
Qed.

Lemma classify_vertex_cde_psi j i w :
  uniq w
  classify_vertex_cde i (psi j w) = classify_vertex_cde i w.
Proof.
moveHu.
rewrite /classify_vertex_cde.
by rewrite (is_internal_apply_psis [:: j]) //
           (has_left_child_apply_psis [:: j]).
Qed.

Lemma S_w_seq_psi j w :
  uniq w S_w_seq (psi j w) = S_w_seq w.
Proof.
moveHu.
rewrite /S_w_seq size_psi.
congr map; apply: eq_in_filteri _.
by rewrite classify_vertex_cde_psi.
Qed.


Lemma mm_pos_lt_pred (s : seq nat) :
  uniq s 1 < size s mm_pos s < (size s).-1.
Proof.
case: s ⇒ [//|a s0] Hu Hsz.
set s := a :: s0. set j := mm_pos s.
have Hne : s [::] by [].
have Hj : j < size s := mm_pos_lt Hne.
apply/negP ⇒ /negP; rewrite -leqNgtHle.
have Heq : j = (size s).-1.
  apply/eqP; rewrite eqn_leq Hle andbT.
  by case: (size s) Hj ⇒ [|n] //=; rewrite ltnS.
have Hdrop_sz : size (drop j s) = 1.
  rewrite size_drop Heq.
  by case: (size s) Hsz Hj ⇒ [//|[//|n]] _ _ /=; rewrite subSS subSnn.
have [Hno_min Hno_max] := notin_take_mm Hne.
have Hmin_drop :
  foldr minn (head 0 s) (behead s) \in drop j s.
  have Hmin_in_s : foldr minn (head 0 s) (behead s) \in s.
    have := min_in (behead s) (head 0 s).
    by have ->: head 0 s :: behead s = s by [].
  rewrite -[X in _ \in X](cat_take_drop j s) mem_cat in Hmin_in_s.
  case/orP: Hmin_in_s ⇒ [Hin|//].
  by rewrite Hin in Hno_min.
have Hmax_drop :
  foldr maxn (head 0 s) (behead s) \in drop j s.
  have Hmax_in_s : foldr maxn (head 0 s) (behead s) \in s.
    have := max_in (behead s) (head 0 s).
    by have ->: head 0 s :: behead s = s by [].
  rewrite -[X in _ \in X](cat_take_drop j s) mem_cat in Hmax_in_s.
  case/orP: Hmax_in_s ⇒ [Hin|//].
  by rewrite Hin in Hno_max.
have [x Hdrop_eq] : x, drop j s = [:: x]
  by case: (drop j s) Hdrop_sz
     [//|x [|y t]] //= _; x.
have Hall_eq : y, y \in s y = x.
  movey Hy.
  have Hmin_x :
    foldr minn (head 0 s) (behead s) = x
    by move: Hmin_drop; rewrite Hdrop_eq mem_seq1
       ⇒ /eqP.
  have Hmax_x :
    foldr maxn (head 0 s) (behead s) = x
    by move: Hmax_drop; rewrite Hdrop_eq mem_seq1
       ⇒ /eqP.
  have Hmin' : x y.
    rewrite -Hmin_x; apply: psi_core.foldr_minn_le.
    by have ->: head 0 s :: behead s = s by [].
  have Hmax' : y x.
    rewrite -Hmax_x; apply: psi_core.foldr_maxn_ge.
    by have ->: head 0 s :: behead s = s by [].
  by apply/eqP; rewrite eqn_leq Hmax' Hmin'.
move: Hu; rewrite /s /= ⇒ /andP [Hnotin _].
have Ha_eq : a = x by apply: Hall_eq; rewrite mem_head.
have Hb : head 0 s0 \in s.
  rewrite [in X in _ \in X]/s !inE; apply/orP; right.
  by case: (s0) Hsz ⇒ [|b t] _ //=; rewrite mem_head.
have Hh_eq : head 0 s0 = x by apply: Hall_eq.
have Hsz0 : 0 < size s0 by case: (s0) Hsz.
have Hh_eq2 : head 0 s0 = a by rewrite Hh_eq.
have : a \in s0.
  rewrite -Hh_eq2.
  by case: (s0) Hsz0 ⇒ [|b t] //= _; rewrite mem_head.
moveHain; by rewrite Hain in Hnotin.
Qed.

Lemma classify_vertex_left i a s0 :
  let j := mm_pos (a :: s0) in
  i < j
  classify_vertex_cde i (a :: s0) =
  classify_vertex_cde i (take j (a :: s0)).
Proof.
move⇒ /= Hij.
set j := mm_pos (a :: s0). set s := a :: s0.
have Hj : j < size s
  := mm_pos_lt
     (fun H : s = [::]ltac:(discriminate H)).
rewrite /classify_vertex_cde /is_internal.
rewrite (window_size_cons i a s0) -/s -/j Hij.
rewrite (has_left_child_cons i a s0) -/s -/j Hij.
have → : i < size s = true
  by exact: ltn_trans Hij Hj.
have ->: size (take j s) = j by rewrite size_takel // ltnW.
by rewrite Hij.
Qed.

Lemma classify_vertex_right i a s0 :
  let j := mm_pos (a :: s0) in
  j < i
  classify_vertex_cde i (a :: s0) =
  classify_vertex_cde (i - j - 1)
    (drop j.+1 (a :: s0)).
Proof.
move⇒ /= Hji.
set j := mm_pos (a :: s0). set s := a :: s0.
have Hj : j < size s
  := mm_pos_lt
     (fun H : s = [::]ltac:(discriminate H)).
rewrite /classify_vertex_cde /is_internal.
rewrite (window_size_cons i a s0) -/s -/j.
rewrite (has_left_child_cons i a s0) -/s -/j.
have → : (i < j) = false by rewrite ltnNge ltnW.
have → : (i == j) = false by apply: gtn_eqF.
suff → : (i < size s) =
  (i - j - 1 < size (drop j.+1 s)) by done.
rewrite size_drop /s /=.
case: i Hji ⇒ [//|i] Hji /=.
rewrite ltnS subSS.
have ->: i.+1 - j - 1 = i - j by rewrite subnAC subn1.
by rewrite ltn_sub2rE // ltnW.
Qed.

Lemma classify_vertex_mm a s0 :
  let j := mm_pos (a :: s0) in
  uniq (a :: s0) 1 < size (a :: s0)
  classify_vertex_cde j (a :: s0) =
  (if 0 < j then D_letter else C_letter).
Proof.
move⇒ /= Hu Hsz.
set j := mm_pos (a :: s0). set s := a :: s0.
have Hj : j < size s
  := mm_pos_lt
     (fun H : s = [::]ltac:(discriminate H)).
have Hjp : j < (size s).-1 by apply: mm_pos_lt_pred; rewrite /s.
rewrite /classify_vertex_cde /is_internal.
rewrite (window_size_cons j a s0) -/s -/j
  ltnn eqxx.
have → : j < size s = true by [].
have → : 1 < size s - j = true
  by rewrite ltn_subRL addnC /= ltnS.
rewrite /=.
rewrite (has_left_child_cons j a s0) -/s -/j
  ltnn eqxx.
by case: (0 < j).
Qed.

Lemma phi_w_cons_mm0 a rest :
  mm_pos (a :: rest) = 0
  1 < size (a :: rest)
  phi_w (a :: rest) = C_letter :: phi_w rest.
Proof.
moveHmm Hsz.
rewrite /phi_w /phi'_w /=.
rewrite /classify_vertex_cde /is_internal.
rewrite (window_size_cons 0 a rest)
  -/(mm_pos (a :: rest)) Hmm ltnn eqxx subn0.
have → : 0 < size (a :: rest) = true by [].
have → : 1 < size (a :: rest) = true by [].
rewrite /= has_left_child_0 /=.
congr cons; congr (filter _).
apply: (@eq_from_nth _ E_letter).
  by rewrite !size_map !size_iota.
movek Hk.
rewrite size_map size_iota in Hk.
rewrite !(nth_map 0) ?size_iota //.
rewrite !nth_iota // add0n.
have H := classify_skip_mm0 Hmm (ltn0Sn k).
rewrite /classify_vertex_cde /is_internal in H.
rewrite -[1 + k]/(k.+1).
rewrite subn1 /= in H.
exact H.
Qed.

Lemma phi_w_decomp_mm a s0 :
  let s := a :: s0 in
  let j := mm_pos s in
  uniq s 1 < size s 0 < j
  phi_w s = phi_w (take j s) ++
    D_letter :: phi_w (drop j.+1 s).
Proof.
move⇒ /= Hu Hsz Hj0.
set s := a :: s0. set j := mm_pos s.
have Hne : s [::] by [].
have Hj : j < size s := mm_pos_lt Hne.
rewrite /phi_w /phi'_w.
have Hiota_split : iota 0 (size s) =
  iota 0 j ++ j :: iota j.+1 (size s - j.+1).
  rewrite -cat1s catA.
  have ->: iota 0 j ++ [:: j] = iota 0 j.+1.
    by rewrite -addn1 iotaD /= add0n.
  by rewrite -iotaD subnKC.
rewrite Hiota_split !map_cat /= !filter_cat /=.
rewrite (classify_vertex_mm Hu Hsz) Hj0 /=.
congr (_ ++ _ :: _).
- congr (filter _ _).
  have Hsz_take : size (take j s) = j by rewrite size_takel // ltnW.
  rewrite Hsz_take.
  apply/eq_in_mapi; rewrite mem_iota add0n ⇒ /andP [_ Hi].
  by apply: classify_vertex_left Hi.
- congr (filter _ _).
  rewrite -[RHS]/(map _ (iota 0
    (size (drop j.+1 s)))).
  rewrite size_drop.
  apply: (@eq_from_nth _ E_letter).
    by rewrite !size_map !size_iota.
  movek Hk.
  rewrite size_map size_iota in Hk.
  rewrite !(nth_map 0) ?size_iota //.
  rewrite !nth_iota ?add0n //.
  have Hjk : j < j.+1 + k
    by rewrite addSn ltnS leq_addr.
  rewrite (classify_vertex_right Hjk).
  by rewrite addSn -addnS addKn subn1.
Qed.

Lemma D_offsets_cons_C m :
  D_offsets (C_letter :: m) =
  [seq x.+1 | x <- D_offsets m].
Proof.
rewrite /D_offsets /=.
rewrite -map_comp.
have Hiota : iota 1 (size m) = [seq j.+1 | j <- iota 0 (size m)].
  by elim: (size m) 1 0 ⇒ [|n IH] m1 m2 //=; rewrite IH.
rewrite Hiota filter_map -map_comp.
rewrite (eq_filter (a2 := fun iis_D_letter (nth C_letter m i))); last by movei.
apply/eq_in_mapi Hi /=.
by rewrite /cde_offset /= /cde_width add1n.
Qed.

Lemma D_offsets_cat_D m1 m2 :
  D_offsets (m1 ++ D_letter :: m2) =
  D_offsets m1 ++
  cde_total_width m1 ::
  [seq cde_total_width m1 + 2 + x
  | x <- D_offsets m2].
Proof.
rewrite /D_offsets.
have Hsz : size (m1 ++ D_letter :: m2) =
  size m1 + (size m2).+1
  by rewrite size_cat /=.
rewrite Hsz iotaD add0n filter_cat map_cat.
have Hleft :
  [seq cde_offset (m1 ++ D_letter :: m2) i
  | i <- [seq i <- iota 0 (size m1)
  | is_D_letter
      (nth C_letter
        (m1 ++ D_letter :: m2) i)]]
= D_offsets m1.
  rewrite /D_offsets.
  rewrite (eq_in_filter
    (a2 := fun iis_D_letter (nth C_letter m1 i))); last first.
    movei; rewrite mem_iota add0n ⇒ /andP [_ Hi].
    by rewrite nth_cat Hi.
  apply/eq_in_mapi Hi.
  have Hi_lt : i < size m1.
    move: Hi; rewrite mem_filter mem_iota add0n.
    by case/andP_ /andP [].
  by rewrite /cde_offset take_cat Hi_lt.
have Hmid_f : is_D_letter (nth C_letter
  (m1 ++ D_letter :: m2) (size m1)) = true
  by rewrite nth_cat ltnn subnn.
have Hmid_o : cde_offset (m1 ++ D_letter :: m2)
  (size m1) = cde_total_width m1.
  rewrite /cde_offset /cde_total_width.
  by rewrite take_cat ltnn subnn take0 cats0.
have Hcons : iota (size m1) (size m2).+1 =
  size m1 :: iota (size m1).+1 (size m2) by [].
rewrite Hleft Hcons /= Hmid_f /= Hmid_o.
congr (cat (D_offsets m1)); congr (cons (cde_total_width m1)).
have Hshift : iota (size m1).+1 (size m2) =
  [seq (size m1).+1 + i | i <- iota 0 (size m2)].
  by rewrite -iotaDl addn0.
rewrite Hshift filter_map -map_comp /D_offsets -map_comp.
rewrite (eq_filter (a2 := fun i
  is_D_letter (nth C_letter m2 i))); last first.
  movek /=.
  rewrite nth_cat.
  have HltF : ((size m1).+1 + k < size m1) = false
    by rewrite ltnNge
       (leq_trans (leqnSn (size m1)) (leq_addr k (size m1).+1)).
  by rewrite HltF addSn -addnS addKn.
apply/eq_in_mapk Hk /=.
have Hk_lt : k < size m2.
  move: Hk; rewrite mem_filter mem_iota add0n.
  by case/andP_ /andP [].
rewrite /cde_offset /cde_total_width take_cat.
have HltF : ((size m1).+1 + k < size m1) = false
  by rewrite ltnNge
     (leq_trans (leqnSn (size m1)) (leq_addr k (size m1).+1)).
by rewrite HltF addSn -addnS addKn /= map_cat sumn_cat addnA.
Qed.

Lemma S_w_seq_decomp_mm a s0 :
  let s := a :: s0 in
  let j := mm_pos s in
  uniq s 1 < size s 0 < j
  S_w_seq s =
  S_w_seq (take j s) ++ j.-1 ::
  [seq x + j.+1 | x <- S_w_seq (drop j.+1 s)].
Proof.
move⇒ /= Hu Hsz Hj0.
set s := a :: s0. set j := mm_pos s.
have Hne : s [::] by [].
have Hj : j < size s := mm_pos_lt Hne.
rewrite /S_w_seq.
have Hsplit : iota 1 (size s).-1 =
  iota 1 j.-1 ++ j :: iota j.+1 ((size s).-1 - j).
  have Hjpred : j (size s).-1 by
    rewrite -ltnS (prednK (ltnW Hsz)).
  have Hlen : (size s).-1 = j.-1 + ((size s).-1 - j).+1 by
    rewrite -addn1 addnA addnAC addn1 (prednK Hj0) subnKC.
  by rewrite {1}Hlen iotaD add1n (prednK Hj0).
rewrite Hsplit !filter_cat /= !map_cat /=.
have Hleft_filter : i,
  i \in iota 1 j.-1
  is_D_letter (classify_vertex_cde i s) =
  is_D_letter (classify_vertex_cde i (take j s)).
  movei; rewrite mem_iota ⇒ /andP [_ Hij].
  have Hij' : i < j.
    have Heq : 1 + j.-1 = j by rewrite add1n prednK.
    by rewrite -Heq.
  by rewrite (classify_vertex_left Hij').
rewrite (eq_in_filter Hleft_filter).
have Hmid :
  is_D_letter (classify_vertex_cde j s) = true
  by rewrite (classify_vertex_mm Hu Hsz) Hj0.
rewrite Hmid /=.
have Hright_filter : i,
  i \in iota j.+1 ((size s).-1 - j)
  is_D_letter (classify_vertex_cde i s) =
  is_D_letter (classify_vertex_cde (i - j - 1)
    (drop j.+1 s)).
  movei; rewrite mem_iota ⇒ /andP [Hji _].
  by rewrite (classify_vertex_right Hji).
rewrite (eq_in_filter Hright_filter).
congr (_ ++ _ :: _).
- by rewrite size_takel // ltnW.
-
  set d := drop j.+1 s.
  have Hq0 : is_D_letter (classify_vertex_cde 0 d) = false.
    rewrite /classify_vertex_cde /is_internal.
    case E : (0 < size d) ⇒ //=.
    case E2 : (1 < window_size 0 d) ⇒ //=.
    by rewrite has_left_child_0.
  have Hnd : (size s).-1 - j = size d.
    rewrite /d size_drop.
    case: (size s) Hsz ⇒ [//|[//|m]] _; by rewrite !subSS.
  rewrite Hnd.
  have Hshift : iota j.+1 (size d) = [seq j.+1 + k | k <- iota 0 (size d)]
    by rewrite -iotaDl addn0.
  rewrite Hshift filter_map -map_comp.
  rewrite (eq_filter (a2 := fun k
    is_D_letter (classify_vertex_cde k d))); last first.
    by movek /=; rewrite addSn -addnS addKn subn1.
  have Hfilt :
    filter (fun kis_D_letter (classify_vertex_cde k d)) (iota 0 (size d)) =
    filter (fun kis_D_letter (classify_vertex_cde k d)) (iota 1 (size d).-1).
    case: (size d) ⇒ [//|m]; by rewrite /= Hq0.
  rewrite Hfilt /S_w_seq -map_comp.
  apply: (@eq_in_map nat nat _ _ _).1k Hk.
  rewrite mem_filter mem_iota in Hk.
  move: Hk ⇒ /andP [_ /andP [Hk1 _]].
  case: k Hk1 ⇒ [//|k'] _.
  unfold comp. simpl. by rewrite addnC addnS.
Qed.


Lemma cde_total_width_phi_w_all w :
  uniq w
  cde_total_width (phi_w w) = (size w).-1.
Proof.
moveHu.
suff Hgen : n w, size w n uniq w
  cde_total_width (phi_w w) = (size w).-1.
  exact: Hgen (leqnn _) Hu.
move: w Hu_ _.
elim ⇒ [|n IH] w Hsz Hu.
  by move: Hsz; rewrite leqn0 ⇒ /eqP/size0nil →.
case Hsz1 : (size w 1).
  case: w Hu Hsz Hsz1 ⇒ [//|a [|b s]] //= _ _ _.
  rewrite /cde_total_width /phi_w /phi'_w
    /classify_vertex_cde /is_internal
    /window_size /window_size_fuel
    /mm_pos /min_pos /max_pos /= eqxx /=.
  by [].
have Hsz2 : 1 < size w
  by case: (size w) Hsz1 ⇒ [|[|m]].
case Hszn : (size w n).
  exact: IH w Hszn Hu.
case: w Hsz Hu Hsz1 Hsz2 Hszn
  [//|a rest] Hsz Hu _ Hsz2 Hszn.
set s := a :: rest. set j := mm_pos s.
have Hne : s [::] by [].
have Hj : j < size s := mm_pos_lt Hne.
have HuL : uniq (take j s) := take_uniq j Hu.
have HuR : uniq (drop j.+1 s)
  := drop_uniq j.+1 Hu.
have Hsw : size s = n.+1.
  apply/eqP; rewrite eqn_leq Hsz /=.
  by rewrite ltnNge Hszn.
have HszL : size (take j s) n.
  rewrite size_takel; last exact: ltnW.
  by rewrite -ltnS -Hsw.
have HszR : size (drop j.+1 s) n.
  rewrite size_drop Hsw /=. exact: leq_subr.
have IHL := IH _ HszL HuL.
have IHR := IH _ HszR HuR.
case Hj0 : (0 < j).
  rewrite (phi_w_decomp_mm Hu Hsz2 Hj0).
  have HtotalL : cde_total_width (phi_w (take j s)) = (size (take j s)).-1 := IHL.
  have HtotalR : cde_total_width (phi_w (drop j.+1 s)) = (size (drop j.+1 s)).-1 := IHR.
  rewrite cde_total_width_cat HtotalL.
  have → : cde_total_width (D_letter :: phi_w (drop j.+1 s)) =
    2 + cde_total_width (phi_w (drop j.+1 s)).
    by rewrite /cde_total_width /=.
  rewrite HtotalR.
  rewrite size_takel; last exact: ltnW.
  rewrite size_drop.
  have HszR1 : 0 < size s - j.+1.
    rewrite subn_gt0.
    exact: mm_pos_lt_pred Hu Hsz2.
  have Hstep1 : j.-1 + 2 = j.+1 by rewrite addn2 (prednK Hj0).
  have Hstep2 : (size s - j.+1).-1.+1 = size s - j.+1 := prednK HszR1.
  rewrite [j.-1 + _]addnA Hstep1.
  rewrite -subn1 (addnBA _ HszR1) subnKC; last exact: Hj.
  by rewrite subn1.
move: Hj0; rewrite lt0n ⇒ /negbFE /eqP Hj0.
rewrite (phi_w_cons_mm0 Hj0 Hsz2).
have HszR' : size rest n.
  by move: Hsz; rewrite /s /= ltnS.
have HuR' : uniq rest.
  by move: Hu; rewrite /s cons_uniq ⇒ /andP [].
have HconsC : cde_total_width (C_letter :: phi_w rest) =
  1 + cde_total_width (phi_w rest) by rewrite /cde_total_width /=.
have H0rest : 0 < size rest
  by move: Hsz2; rewrite /s /= ltnS.
rewrite HconsC (IH rest HszR' HuR') /s /=.
by rewrite add1n prednK.
Qed.

Lemma cde_total_width_phi_w w :
  uniq w 2 size w
  cde_total_width (phi_w w) = (size w).-1.
Proof.
by moveHu _; exact: cde_total_width_phi_w_all.
Qed.


Lemma D_offsets_phi_w_eq_S_w_seq w :
  uniq w 2 size w
  D_offsets (phi_w w) = S_w_seq w.
Proof.
moveHu Hsz2.
suff Hgen : n w, size w n uniq w
  D_offsets (phi_w w) = S_w_seq w.
  exact: Hgen (leqnn _) Hu.
move: w Hu Hsz2_ _ _.
elim ⇒ [|n IH] w Hsz Hu.
  by move: Hsz; rewrite leqn0 ⇒ /eqP/size0nil →.
case Hsz1 : (size w 1).
  case: w Hu Hsz Hsz1 ⇒ [//|a [|b s]] //= _ _ _.
  rewrite /D_offsets /S_w_seq /phi_w /phi'_w
    /classify_vertex_cde /is_internal
    /window_size /window_size_fuel
    /mm_pos /min_pos /max_pos /= eqxx /=.
  by [].
have Hsz2 : 1 < size w
  by case: (size w) Hsz1 ⇒ [|[|m]].
case Hszn : (size w n).
  exact: IH w Hszn Hu.
case: w Hsz Hu Hsz1 Hsz2 Hszn
  [//|a rest] Hsz Hu _ Hsz2 Hszn.
set s := a :: rest. set j := mm_pos s.
have Hne : s [::] by [].
have Hj : j < size s := mm_pos_lt Hne.
have HuL : uniq (take j s) := take_uniq j Hu.
have HuR : uniq (drop j.+1 s)
  := drop_uniq j.+1 Hu.
have Hsw : size s = n.+1.
  apply/eqP; rewrite eqn_leq Hsz /=.
  by rewrite ltnNge Hszn.
have HszL : size (take j s) n.
  rewrite size_takel; last exact: ltnW.
  by rewrite -ltnS -Hsw.
have HszR : size (drop j.+1 s) n.
  rewrite size_drop Hsw /=. exact: leq_subr.
have IHL := IH _ HszL HuL.
have IHR := IH _ HszR HuR.
case Hj0 : (0 < j).
  rewrite (phi_w_decomp_mm Hu Hsz2 Hj0).
  rewrite D_offsets_cat_D.
  rewrite (S_w_seq_decomp_mm Hu Hsz2 Hj0).
  rewrite IHL IHR.
  have HwL := cde_total_width_phi_w_all HuL.
  rewrite HwL size_takel; last exact: ltnW.
  congr (_ ++ _ :: _).
  apply: eq_mapx /=.
  by rewrite addnC addn2 prednK.
move: Hj0; rewrite lt0n ⇒ /negbFE /eqP Hj0.
rewrite (phi_w_cons_mm0 Hj0 Hsz2).
rewrite D_offsets_cons_C.
rewrite (S_w_seq_shift Hj0).
have HszR' : size rest n.
  by move: Hsz; rewrite /s /= ltnS.
have HuR' : uniq rest.
  by move: Hu; rewrite /s cons_uniq ⇒ /andP [].
by rewrite (IH rest HszR' HuR').
Qed.


Lemma phi_w_support_general (w : seq nat) (X : seq bool) :
  uniq w 2 size w size X = (size w).-1
  (X \in expand_cde (phi_w w)) =
  all (fun kk \in omega_seq [seq i <- iota 0 (size w).-1 | nth false X i])
      (S_w_seq w).
Proof.
moveHu Hsz2 HszX.
have Hwidth := cde_total_width_phi_w Hu Hsz2.
have HszX2 : size X = cde_total_width (phi_w w) by rewrite Hwidth.
rewrite (expand_cde_mem_iff HszX2).
rewrite /all_D_transitions (D_offsets_phi_w_eq_S_w_seq Hu Hsz2).
apply/allP/allP.
- moveHall k Hk.
  have Hkb := S_w_seq_bound Hk.
  rewrite -(has_transition_omega_seq X Hkb).
  exact: Hall Hk.
- moveHall k Hk.
  have Hkb := S_w_seq_bound Hk.
  rewrite (has_transition_omega_seq X Hkb).
  exact: Hall Hk.
Qed.


Lemma uniq_expand_cde m : uniq (expand_cde m).
Proof.
elim: m ⇒ [|[||] l IH] //=.
- rewrite cat_uniq !map_inj_uniq // ?IH ?andbT;
    try by movea b [].
  apply/hasPnx /mapP [y Hy ->].
  apply/negP ⇒ /mapP [z Hz]. by case.
- rewrite cat_uniq !map_inj_uniq //; try by movea b [].
  rewrite ?IH ?andbT.
  apply/hasPnx /mapP [y Hy ->].
  apply/negP ⇒ /mapP [z Hz]. by case.
Qed.

Lemma perm_eq_from_subset (T : eqType) (s1 s2 : seq T) :
  uniq s1 uniq s2 size s1 = size s2
  {subset s1 s2} perm_eq s1 s2.
Proof.
moveHu1 Hu2 Hsz Hsub.
suff Hmem : x, x \in s1 = (x \in s2).
  by apply: uniq_perm.
movex; apply/idP/idP; first exact: Hsub.
moveHx2; apply/negPn/negPHx1.
have : size (x :: s1) size s2.
  apply: uniq_leq_size.
    by rewrite /= Hx1.
  movey; rewrite in_cons ⇒ /orP [/eqP → | /Hsub] //.
by rewrite /= Hsz ltnn.
Qed.

Lemma check_fact3_of_perm_eq w :
  perm_eq [seq char_mono (apply_psis ss w) | ss <- powerset_internal w]
          (expand_cde (phi_w w))
  check_fact3 w.
Proof.
moveHp; rewrite /check_fact3.
by rewrite (sort_perm_eq_leq_seqb Hp) eqxx.
Qed.


Lemma D_vertex_descent_transition w i :
  uniq w 0 < i is_internal i w has_left_child i w
  is_descent_seq w i.-1 != is_descent_seq w i.
Proof.
moveHu Hi0 Hint Hlc.
have Hws : 1 < window_size i w
  by move: Hint; rewrite /is_internal ⇒ /andP [_ ->].
have Hxor := exactly_one_descent_LR Hu Hi0 Hlc Hws.
move: Hxor; case: (is_descent_seq w i.-1);
  by case: (is_descent_seq w i).
Qed.


Lemma char_mono_self_mem w :
  uniq w 2 size w
  char_mono w \in expand_cde (phi_w w).
Proof.
moveHu Hsz2.
have Hszx : size (char_mono w) = (size w).-1 by rewrite size_char_mono.
rewrite (phi_w_support_general Hu Hsz2 Hszx).
apply/allPk Hk.
have Hkb := S_w_seq_bound Hk.
rewrite -(has_transition_omega_seq (char_mono w) Hkb).
rewrite /has_transition.
move: Hk; rewrite /S_w_seq ⇒ /mapP [i].
rewrite mem_filter ⇒ /andP [Hid Hiota] Hkeq.
rewrite mem_iota /= in Hiota.
have Hi_pos : 0 < i by move: Hiota ⇒ /andP [].
have Hi_lt : i < size w.
  move: Hiota ⇒ /andP [_ Hi].
  by rewrite add1n (prednK (ltn_trans _ Hsz2)) in Hi.
have Hint : is_internal i w.
  move: Hid; rewrite /classify_vertex_cde.
  by case: (is_internal i w) ⇒ //=.
have Hlc : has_left_child i w.
  move: Hid; rewrite /classify_vertex_cde.
  case: (is_internal i w) ⇒ //=.
  by case: (has_left_child i w).
have Htrans := D_vertex_descent_transition Hu Hi_pos Hint Hlc.
have Hk_lt : k < (size w).-1.
  rewrite Hkeq -ltnS (prednK Hi_pos).
  by case: (size w) Hsz2 Hi_lt ⇒ [//|[//|m]].
have Hk1_lt : k.+1 < (size w).-1.
  have Hpred_pos : 0 < (size w).-1
    by case: (size w) Hsz2 ⇒ [|[|m]].
  by rewrite -[in X in _ < X](prednK Hpred_pos) ltnS.
rewrite (nth_char_mono Hk_lt) (nth_char_mono Hk1_lt).
by rewrite Hkeq prednK.
Qed.


Lemma char_mono_apply_psis_mem w ss :
  uniq w 2 size w
  char_mono (apply_psis ss w) \in expand_cde (phi_w w).
Proof.
moveHu Hsz2.
rewrite -(phi_w_apply_psis ss Hu).
apply: char_mono_self_mem.
  exact: uniq_apply_psis.
by rewrite size_apply_psis.
Qed.


Lemma in_internal_vertices i w :
  (i \in internal_vertices w) = is_internal i w.
Proof.
rewrite /internal_vertices mem_filter.
case Hi: (is_internal i w) ⇒ /=; last by [].
rewrite mem_iota /=.
by move: Hi ⇒ /andP [-> _].
Qed.

Lemma char_mono_apply_psis_inj w ss1 ss2 :
  uniq w
  ss1 \in powerset_internal w
  ss2 \in powerset_internal w
  char_mono (apply_psis ss1 w) = char_mono (apply_psis ss2 w)
  ss1 = ss2.
Proof.
moveHu Hss1 Hss2 Heq.
have Hivs_uniq : uniq (internal_vertices w).
  by rewrite /internal_vertices filter_uniq // iota_uniq.
have Hsub1 : subseq ss1 (internal_vertices w) := subseq_powerset_internal Hss1.
have Hsub2 : subseq ss2 (internal_vertices w) := subseq_powerset_internal Hss2.
have Hu1 : uniq ss1 := subseq_uniq Hsub1 Hivs_uniq.
have Hu2 : uniq ss2 := subseq_uniq Hsub2 Hivs_uniq.
have Hint1 : i, i \in ss1 is_internal i w.
  by movei Hi; rewrite -in_internal_vertices; exact: (mem_subseq Hsub1).
have Hint2 : i, i \in ss2 is_internal i w.
  by movei Hi; rewrite -in_internal_vertices; exact: (mem_subseq Hsub2).
have Hmem : ss1 =i ss2.
  movex.
  case Hin: (x \in internal_vertices w); last first.
    have HF1 : (x \in ss1) = false.
      apply/negPHx.
      by have := mem_subseq Hsub1 Hx; rewrite Hin.
    have HF2 : (x \in ss2) = false.
      apply/negPHx.
      by have := mem_subseq Hsub2 Hx; rewrite Hin.
    by rewrite HF1 HF2.
  rewrite in_internal_vertices in Hin.
  have Hbit : k, k < (size w).-1
    nth false (char_mono (apply_psis ss1 w)) k =
    nth false (char_mono (apply_psis ss2 w)) k by movek Hk; rewrite Heq.
  case Hlc: (has_left_child x w).
  -
    have Hxlt := is_internal_lt Hin.
    have Hxpos : 0 < x.
      case Hx0: x Hlc ⇒ [|n] //=.
      by rewrite has_left_child_0.
    have H1 := char_mono_apply_psis_D_bit_self Hu Hu1 Hint1 Hin Hlc.
    have H2 := char_mono_apply_psis_D_bit_self Hu Hu2 Hint2 Hin Hlc.
    have Hxx := Hbit _ Hxlt.
    rewrite H1 H2 in Hxx.
    have Hdiff := D_vertex_descent_transition Hu Hxpos Hin Hlc.
    case E1: (x \in ss1); case E2: (x \in ss2) ⇒ //.
    + rewrite E1 E2 /= in Hxx.
      by rewrite Hxx eqxx in Hdiff.
    + rewrite E1 E2 /= in Hxx.
      by rewrite -Hxx eqxx in Hdiff.
  -
    have Hcv : ~~ has_left_child x w by rewrite Hlc.
    have Hxlt := is_internal_lt Hin.
    have H1 := char_mono_apply_psis_C_bit Hu Hu1 Hint1 Hin Hcv.
    have H2 := char_mono_apply_psis_C_bit Hu Hu2 Hint2 Hin Hcv.
    have Hxx := Hbit _ Hxlt.
    rewrite H1 H2 in Hxx.
    by move: Hxx; case: (is_descent_seq w x);
      case: (x \in ss1); case: (x \in ss2).
have Eq1 : ss1 = [seq x <- internal_vertices w | x \in ss1].
  exact: (elimT (subseq_uniqP Hivs_uniq) Hsub1).
have Eq2 : ss2 = [seq x <- internal_vertices w | x \in ss2].
  exact: (elimT (subseq_uniqP Hivs_uniq) Hsub2).
rewrite Eq1 Eq2.
by apply: eq_in_filterx _; exact: Hmem.
Qed.

Lemma uniq_map_char_mono_powerset w :
  uniq w 2 size w
  uniq [seq char_mono (apply_psis ss w) | ss <- powerset_internal w].
Proof.
moveHu _.
rewrite map_inj_in_uniq; first exact: uniq_powerset_internal.
movess1 ss2 Hss1 Hss2 Heq.
exact: char_mono_apply_psis_inj Hu Hss1 Hss2 Heq.
Qed.


Lemma check_fact3_true w :
  uniq w check_fact3 w.
Proof.
moveHu.
case Hsz : (size w 1).
  exact: check_fact3_size1.
have Hsz2 : 2 size w
  by case: (size w) Hsz ⇒ [|[|m]].
apply: check_fact3_of_perm_eq.
apply: perm_eq_from_subset.
- exact: uniq_map_char_mono_powerset.
- exact: uniq_expand_cde.
- by rewrite size_map size_powerset_internal -size_expand_cde_phi_w.
- movex /mapP [ss _ ->].
  exact: char_mono_apply_psis_mem.
Qed.

Lemma fact3 : (w : seq nat),
  uniq w
  sort leq_seqb
    [seq char_mono (apply_psis ss w)
    | ss <- powerset_internal w]
  =
  sort leq_seqb (expand_cde (phi_w w)).
Proof.
movew Hu.
by apply/check_fact3P; apply: check_fact3_true.
Qed.


Example fact3_ex_315426 :
  let w := [:: 3; 1; 5; 4; 2; 6] in
  sort leq_seqb [seq char_mono (apply_psis ss w) | ss <- powerset_internal w]
  = sort leq_seqb (expand_cde (phi_w w)).
Proof. by vm_compute. Qed.

Example fact3_ex_213 :
  let w := [:: 2; 1; 3] in
  sort leq_seqb [seq char_mono (apply_psis ss w) | ss <- powerset_internal w]
  = sort leq_seqb (expand_cde (phi_w w)).
Proof. by vm_compute. Qed.