Library mathcomp_eulerian.psi_cdindex_core


From mathcomp Require Import all_ssreflect.
Require Import mmtree psi_core psi_comm psi_descent_v2 psi_descent_thms.
Require Export psi_cdindex_defs psi_cdindex_tree_hlc psi_cdindex_tree.

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



Lemma window_size_apply_psis ops i w :
  uniq w
  window_size i (apply_psis ops w) = window_size i w.
Proof.
moveHu; elim: ops w Hu ⇒ [//|j ops IH] w Hu /=.
by rewrite IH ?uniq_psi // window_size_psi.
Qed.

Lemma has_left_child_apply_psis ops i w :
  uniq w
  has_left_child i (apply_psis ops w) = has_left_child i w.
Proof.
moveHu; elim: ops w Hu ⇒ [//|j ops IH] w Hu /=.
by rewrite IH ?uniq_psi // has_left_child_psi.
Qed.

Lemma is_internal_apply_psis ops i w :
  uniq w
  is_internal i (apply_psis ops w) = is_internal i w.
Proof.
moveHu; rewrite /is_internal size_apply_psis.
by rewrite window_size_apply_psis.
Qed.

Lemma internal_vertices_apply_psis ops w :
  uniq w
  internal_vertices (apply_psis ops w) = internal_vertices w.
Proof.
moveHu; rewrite /internal_vertices size_apply_psis.
apply: eq_in_filteri _.
exact: is_internal_apply_psis.
Qed.

Lemma phi_w_apply_psis ops w :
  uniq w phi_w (apply_psis ops w) = phi_w w.
Proof.
moveHu; rewrite /phi_w /phi'_w size_apply_psis.
congr [seq _ <- _ | _].
apply: eq_mapi; rewrite /classify_vertex_cde.
by rewrite is_internal_apply_psis //
           has_left_child_apply_psis.
Qed.


Lemma phi_w_as_map w :
  phi_w w =
  [seq (if has_left_child i w then D_letter else C_letter)
  | i <- internal_vertices w].
Proof.
rewrite /phi_w /phi'_w /internal_vertices.
elim: (iota 0 (size w)) ⇒ [//|i s IH] /=.
rewrite /classify_vertex_cde /is_internal.
case: (i < size w) ⇒ /=;
  case: (1 < window_size i w) ⇒ /=;
  case: (has_left_child i w) ⇒ /=;
  by rewrite ?IH.
Qed.


Lemma leq_seqb_total : total leq_seqb.
Proof.
movex y; elim: x y
  [|b1 s1 IH] [|b2 s2] //=.
by case: b1; case: b2 ⇒ //=.
Qed.

Lemma leq_seqb_trans : transitive leq_seqb.
Proof.
movey x z; elim: x y z
  [|b1 s1 IH] [|b2 s2] [|b3 s3] //=.
by case: b1; case: b2; case: b3 ⇒ //=; exact: IH.
Qed.

Lemma leq_seqb_anti : antisymmetric leq_seqb.
Proof.
movex y; elim: x y
  [|b1 s1 IH] [|b2 s2] //=.
by case: b1; case: b2 ⇒ //= H;
   congr (_ :: _); exact: IH.
Qed.

Lemma sort_perm_eq_leq_seqb (s1 s2 : seq (seq bool)) :
  perm_eq s1 s2
  sort leq_seqb s1 = sort leq_seqb s2.
Proof.
moveHp; apply/perm_sort_inP ⇒ //.
- by movea b _ _; exact: leq_seqb_total.
- by movea b c _ _ _; exact: leq_seqb_trans.
- by movea b _ _; exact: leq_seqb_anti.
Qed.


Lemma descent_psi_effect v w k :
  uniq w is_internal v w k < (size w).-1
  is_descent_seq (psi v w) k =
    if ~~ has_left_child v w then
      if k == v then ~~ is_descent_seq w v
      else is_descent_seq w k
    else
      if k == v then is_descent_seq w v.-1
      else if k == v.-1 then is_descent_seq w v
      else is_descent_seq w k.
Proof.
moveHu Hint Hk.
have Hws : 1 < window_size v w
  by move: Hint; rewrite /is_internal ⇒ /andP [_ ->].
case Hlc: (has_left_child v w) ⇒ /=.
- case Hd: (is_descent_seq w v).
  +
    
    have := descent_psi_LR_swap2 Hu Hws Hlc Hd Hk ⇒ →.
    case: (k == v) ⇒ //.
    have Hi0 : 0 < v.
      case Hv0: v ⇒ [|n0]; last by [].
      by exfalso; rewrite Hv0 has_left_child_0 in Hlc.
    have := exactly_one_descent_LR Hu Hi0 Hlc Hws.
    rewrite Hd addbT.
    by case: (is_descent_seq w v.-1).
  +
    have := descent_psi_LR_swap1 Hu Hws Hlc (negbT Hd) Hk ⇒ →.
    have Hi0 : 0 < v.
      case Hv0: v ⇒ [|n0]; last by [].
      by exfalso; rewrite Hv0 has_left_child_0 in Hlc.
    have := exactly_one_descent_LR Hu Hi0 Hlc Hws.
    rewrite Hd /= addbFHd1.
    case: (k == v); first by rewrite Hd1.
    by case: (k == v.-1).
- case Hd: (is_descent_seq w v).
  + have := descent_psi_R_remove Hu Hws (negbT Hlc) Hd Hk ⇒ →.
    case Hkv: (k == v); first by move/eqP: Hkv ⇒ ->; rewrite Hd.
    by [].
  + have := descent_psi_R_add Hu Hws (negbT Hlc) (negbT Hd) Hk ⇒ →.
    case Hkv: (k == v); first by move/eqP: Hkv ⇒ ->; rewrite Hd.
    by [].
Qed.


Lemma nth_char_mono w k :
  k < (size w).-1
  nth false (char_mono w) k = is_descent_seq w k.
Proof.
moveHk; rewrite /char_mono (nth_map 0);
  last by rewrite size_iota.
by rewrite nth_iota // add0n.
Qed.

Lemma size_char_mono w :
  size (char_mono w) = (size w).-1.
Proof. by rewrite /char_mono size_map size_iota. Qed.

Lemma is_internal_lt v w : is_internal v w v < (size w).-1.
Proof.
move⇒ /andP [Hvlt Hws].
have Hwbd := window_size_bound v w.
have Hsub : 1 < size w - v by exact: leq_trans Hws Hwbd.
rewrite ltn_subRL in Hsub.
case: (size w) Hvlt Hsub ⇒ [//|n] Hvlt /=.
by rewrite addnC.
Qed.

Lemma char_mono_psi_effect v w k :
  uniq w is_internal v w k < (size w).-1
  nth false (char_mono (psi v w)) k =
    if ~~ has_left_child v w then
      if k == v then ~~ nth false (char_mono w) v
      else nth false (char_mono w) k
    else
      if k == v then nth false (char_mono w) v.-1
      else if k == v.-1 then nth false (char_mono w) v
      else nth false (char_mono w) k.
Proof.
moveHu Hint Hk.
have Hvlt := is_internal_lt Hint.
have Hvm1 : v.-1 < (size w).-1 := leq_ltn_trans (leq_pred v) Hvlt.
have HkP : k < (size (psi v w)).-1 by rewrite size_psi.
rewrite (nth_char_mono HkP) (descent_psi_effect Hu Hint Hk).
case: (~~ has_left_child v w); case Hkv: (k == v).
- by rewrite (nth_char_mono Hvlt).
- by rewrite (nth_char_mono Hk).
- by rewrite (nth_char_mono Hvm1).
case Hkv1: (k == v.-1).
  by rewrite (nth_char_mono Hvlt).
by rewrite (nth_char_mono Hk).
Qed.


Lemma index_rcons_eq_size (T : eqType) (s : seq T) y x :
  index x (rcons s y) = size s x \notin s.
Proof.
elim: s ⇒ [|a s IH] //=.
case Hax : (a == x) ⇒ /= Hidx; first by [].
injection HidxHidx'.
have Hxn := IH Hidx'.
by rewrite in_cons eq_sym Hax /=; exact: Hxn.
Qed.

Lemma mm_pos_rcons_lt sl x : 0 < size sl
  mm_pos (rcons sl x) < size sl.
Proof.
moveHsl.
have Hne : rcons sl x [::] by case: sl Hsl.
have Hjlt := mm_pos_lt Hne.
rewrite size_rcons ltnS leq_eqVlt in Hjlt.
case/orP: Hjlt ⇒ [/eqP Hjeq|//].
exfalso.
move: Hjeq; rewrite /mm_pos.
case Hcmp: (min_pos (rcons sl x) max_pos (rcons sl x)) ⇒ Hjeq.
- have Hmax_eq : max_pos (rcons sl x) = size sl.
    apply/eqP; rewrite eqn_leq.
    have Hmax_lt := max_pos_lt Hne.
    rewrite size_rcons ltnS in Hmax_lt.
    by rewrite Hmax_lt -Hjeq Hcmp.
  rewrite /min_pos in Hjeq.
  rewrite /max_pos in Hmax_eq.
  set m := foldr minn (head 0 (rcons sl x)) (behead (rcons sl x)).
  set M := foldr maxn (head 0 (rcons sl x)) (behead (rcons sl x)).
  have Hm_nin : foldr minn (head 0 (rcons sl x)) (behead (rcons sl x)) \notin sl
    by apply: index_rcons_eq_size Hjeq.
  have HM_nin : foldr maxn (head 0 (rcons sl x)) (behead (rcons sl x)) \notin sl
    by apply: index_rcons_eq_size Hmax_eq.
  have Hrcons_form : rcons sl x = head 0 (rcons sl x) :: behead (rcons sl x)
    by case: (rcons sl x) Hne.
  have Hm_in : m \in rcons sl x
    by rewrite [in X in _ \in X]Hrcons_form; apply: min_in.
  have HM_in : M \in rcons sl x
    by rewrite [in X in _ \in X]Hrcons_form; apply: max_in.
  have Hm_eq : m = x.
    move: Hm_in; rewrite mem_rcons in_cons.
    case/orP ⇒ [/eqP → //|Hin].
    by rewrite Hin in Hm_nin.
  have HM_eq : M = x.
    move: HM_in; rewrite mem_rcons in_cons.
    case/orP ⇒ [/eqP → //|Hin].
    by rewrite Hin in HM_nin.
  case Hsl_eq : sl Hsl ⇒ [//|a sl0] _.
  have Ha_in_sl : a \in sl by rewrite Hsl_eq in_cons eqxx.
  have Ha_in : a \in rcons sl x
    by rewrite mem_rcons in_cons; apply/orP; right.
  have Hm_le_a : m a
    by rewrite -/m; apply: foldr_minn_le; rewrite -Hrcons_form.
  have Ha_le_M : a M
    by rewrite -/M; apply: foldr_maxn_ge; rewrite -Hrcons_form.
  rewrite Hm_eq in Hm_le_a.
  rewrite HM_eq in Ha_le_M.
  have Ha_eq_x : a = x by apply/eqP; rewrite eqn_leq Ha_le_M Hm_le_a.
  move/negP: Hm_nin; apply.
  by rewrite -/m Hm_eq -Ha_eq_x.
- rewrite /max_pos in Hjeq.
  have Hcmp' : ~~ (min_pos (rcons sl x) max_pos (rcons sl x))
    by rewrite Hcmp.
  rewrite -ltnNge in Hcmp'.
  have Hmin_eq : min_pos (rcons sl x) = size sl.
    apply/eqP; rewrite eqn_leq.
    have Hmin_lt := min_pos_lt Hne.
    rewrite size_rcons ltnS in Hmin_lt.
    by rewrite Hmin_lt /max_pos -Hjeq ltnW //.
  rewrite /min_pos in Hmin_eq.
  set m := foldr minn (head 0 (rcons sl x)) (behead (rcons sl x)).
  set M := foldr maxn (head 0 (rcons sl x)) (behead (rcons sl x)).
  have Hm_nin : m \notin sl by apply: index_rcons_eq_size Hmin_eq.
  have HM_nin : M \notin sl by apply: index_rcons_eq_size Hjeq.
  have Hrcons_form : rcons sl x = head 0 (rcons sl x) :: behead (rcons sl x)
    by case: (rcons sl x) Hne.
  have Hm_in : m \in rcons sl x
    by rewrite [in X in _ \in X]Hrcons_form; apply: min_in.
  have HM_in : M \in rcons sl x
    by rewrite [in X in _ \in X]Hrcons_form; apply: max_in.
  have Hm_eq : m = x.
    move: Hm_in; rewrite mem_rcons in_cons.
    case/orP ⇒ [/eqP → //|Hin].
    by rewrite Hin in Hm_nin.
  have HM_eq : M = x.
    move: HM_in; rewrite mem_rcons in_cons.
    case/orP ⇒ [/eqP → //|Hin].
    by rewrite Hin in HM_nin.
  case Hsl_eq : sl Hsl ⇒ [//|a sl0] _.
  have Ha_in_sl : a \in sl by rewrite Hsl_eq in_cons eqxx.
  have Ha_in : a \in rcons sl x
    by rewrite mem_rcons in_cons; apply/orP; right.
  have Hm_le_a : m a
    by rewrite -/m; apply: foldr_minn_le; rewrite -Hrcons_form.
  have Ha_le_M : a M
    by rewrite -/M; apply: foldr_maxn_ge; rewrite -Hrcons_form.
  rewrite Hm_eq in Hm_le_a.
  rewrite HM_eq in Ha_le_M.
  have Ha_eq_x : a = x by apply/eqP; rewrite eqn_leq Ha_le_M Hm_le_a.
  move/negP: Hm_nin; apply.
  by rewrite -/m Hm_eq -Ha_eq_x.
Qed.


Lemma has_left_child_t_last_valid : t : mmtree nat,
  valid_mm t
  0 < size (mmtree_to_seq t)
  has_left_child_t (size (mmtree_to_seq t)).-1 t = false.
Proof.
elim ⇒ [|l IHl x r IHr] //=.
caseHmm [Hvl Hvr] _.
rewrite size_cat /= addnS /=.
case Hszr : (size (mmtree_to_seq r)) ⇒ [|n].
-
  have Hszl_zero : size (mmtree_to_seq l) = 0.
    have Hszr_nil : mmtree_to_seq r = [::]
      by move: Hszr; case: (mmtree_to_seq r).
    rewrite Hszr_nil cats1 in Hmm.
    case Hszl : (size (mmtree_to_seq l)) Hmm ⇒ [//|m] Hmm.
    exfalso.
    have Hsl_pos : 0 < size (mmtree_to_seq l) by rewrite Hszl.
    have Hjlt := mm_pos_rcons_lt x Hsl_pos.
    by rewrite Hmm Hszl ltnn in Hjlt.
  rewrite Hszl_zero /= addn0 /=.
  exact: has_left_child_t_0.
-
  rewrite addnS /=.
  have Hgt2 : size (mmtree_to_seq l) < (size (mmtree_to_seq l) + n).+1
    by rewrite ltnS leq_addr.
  rewrite (@has_left_child_t_Node_gt l x r _ Hgt2).
  rewrite subSS.
  have → : size (mmtree_to_seq l) + n - size (mmtree_to_seq l) = n
    by rewrite addnC addnK.
  have → : n = (size (mmtree_to_seq r)).-1 by rewrite Hszr.
  apply: IHr; first exact: Hvr.
  by rewrite Hszr.
Qed.

Lemma has_left_child_last w :
  0 < size w
  has_left_child (size w).-1 w = false.
Proof.
moveHne.
have Hroundtrip : mmtree_to_seq (mmtree_of_seq_mm w) = w
  by exact: mmtree_of_seq_mmK.
have Hv : valid_mm (mmtree_of_seq_mm w) by exact: valid_mm_build.
rewrite -Hroundtrip (has_left_child_t_eq _ Hv).
apply: has_left_child_t_last_valid; first exact: Hv.
by rewrite Hroundtrip.
Qed.

Lemma has_left_child_last_fuel :
   fuel w, size w fuel 0 < size w
  has_left_child_fuel fuel (size w).-1 w = false.
Proof.
movefuel w Hsz Hne.
have Heq : has_left_child_fuel fuel (size w).-1 w =
           has_left_child_fuel (size w) (size w).-1 w
  by apply: has_left_child_fuel_monotone Hsz.
rewrite Heq.
exact: (has_left_child_last Hne).
Qed.


Lemma last_vertex_internal w :
  uniq w 2 size w
  is_internal (size w).-2 w.
Proof.
moveHu Hsz.
apply/negPn/negPHep.
have Hk : (size w).-2.+1 < size w
  by case: (size w) Hsz ⇒ [|[|n]].
have Hklast : (size w).-2.+1 = (size w).-1
  by case: (size w) Hsz ⇒ [|[|n]].
have := endpoint_implies_next_has_left_child
  Hu Hk Hep.
rewrite HklastHlc.
have Hne : 0 < size w
  by apply: leq_ltn_trans _ Hsz.
by rewrite has_left_child_last // in Hlc.
Qed.



Lemma has_left_child_is_internal i w :
  i < size w has_left_child i w is_internal i w.
Proof.
have Hgen : t : mmtree nat, valid_mm t
   i0, i0 < size (mmtree_to_seq t)
  has_left_child_t i0 t 1 < window_size_t i0 t.
  elim ⇒ [//|l IHl x r IHr] /=.
  caseHmm [Hvl Hvr] i0 Hi0 Hlc0.
  rewrite size_cat /= addnS in Hi0.
  case: (ltngtP i0 (size (mmtree_to_seq l))) ⇒ Hil.
  + rewrite (@window_size_t_Node_lt l x r i0 Hil).
    apply: IHl ⇒ //.
    by rewrite (@has_left_child_t_Node_lt l x r i0 Hil) in Hlc0.
  + rewrite (@window_size_t_Node_gt l x r i0 Hil).
    rewrite (@has_left_child_t_Node_gt l x r i0 Hil) in Hlc0.
    apply: IHr ⇒ //.
    rewrite ltn_subLR; last exact: Hil.
    by rewrite addSn.
  + rewrite Hil (@window_size_t_Node_eq l x r) ltnS.
    rewrite Hil (@has_left_child_t_Node_eq l x r) in Hlc0.
    case Hszr : (size (mmtree_to_seq r)) ⇒ [|n0]; last by [].
    exfalso.
    have Hszr_nil : mmtree_to_seq r = [::]
      by move: Hszr; case: (mmtree_to_seq r).
    rewrite Hszr_nil cats1 in Hmm.
    have Hsl_pos : 0 < size (mmtree_to_seq l) by exact: Hlc0.
    have Hjlt := mm_pos_rcons_lt x Hsl_pos.
    by rewrite Hmm ltnn in Hjlt.
moveHi Hlc.
rewrite /is_internal Hi /=.
have Hroundtrip : mmtree_to_seq (mmtree_of_seq_mm w) = w
  by exact: mmtree_of_seq_mmK.
have Hv : valid_mm (mmtree_of_seq_mm w) by exact: valid_mm_build.
rewrite -Hroundtrip (window_size_t_eq i Hv).
apply: (Hgen _ Hv); first by rewrite Hroundtrip.
by rewrite -(has_left_child_t_eq i Hv) Hroundtrip.
Qed.

Lemma endpoint_succ_is_D_internal k w :
  uniq w k.+1 < size w ~~ is_internal k w
  is_internal k.+1 w && has_left_child k.+1 w.
Proof.
moveHu Hk1 Hep.
have Hlc := endpoint_implies_next_has_left_child Hu Hk1 Hep.
by rewrite (has_left_child_is_internal Hk1 Hlc) Hlc.
Qed.



Lemma size_powerset_of (ivs : seq nat) :
  size (foldl (fun acc i
    acc ++ [seq s ++ [:: i] | s <- acc]) [:: [::]] ivs)
  = 2 ^ size ivs.
Proof.
have Hgen : acc : seq (seq nat),
  size (foldl (fun (acc' : seq (seq nat)) (i : nat) ⇒
    acc' ++ [seq s ++ [:: i] | s <- acc']) acc ivs) =
  size acc × 2 ^ size ivs.
  elim: ivs ⇒ [|v ivs IH] acc //=.
    by rewrite expn0 muln1.
  rewrite IH size_cat size_map.
  rewrite expnS mulnCA.
  by rewrite mulnA mul2n addnn.
by rewrite Hgen /= mul1n.
Qed.

Lemma size_powerset_internal w :
  size (powerset_internal w) = 2 ^ size (internal_vertices w).
Proof. exact: size_powerset_of. Qed.

Lemma uniq_powerset_of (ivs : seq nat) :
  uniq ivs
  uniq (foldl (fun acc i
    acc ++ [seq s ++ [:: i] | s <- acc]) [:: [::]] ivs).
Proof.
have Hgen : (ivs0 : seq nat) (acc : seq (seq nat)),
  uniq ivs0 uniq acc
  ( s, s \in acc all (fun x : natx \notin ivs0) s)
  uniq (foldl (fun (acc' : seq (seq nat)) (i : nat) ⇒
    acc' ++ [seq s ++ [:: i] | s <- acc']) acc ivs0).
  elim⇒ [|j ivs0 IH] acc //=.
  move⇒ /andP[Hj Hivs] Huacc Hdisj.
  apply: IH ⇒ //.
    rewrite cat_uniq Huacc /=.
    apply/andP; split.
      apply/hasPnx /mapP [s Hs ->{x}].
      apply/negPHxa.
      have := Hdisj _ Hxa ⇒ /allP /(_ j).
      have Hjmem : j \in s ++ [:: j] by rewrite mem_cat inE eqxx orbT.
      by move/(_ Hjmem); rewrite inE eqxx.
    have → : [seq s ++ [:: j] | s <- acc] = [seq rcons s j | s <- acc].
      by apply: eq_maps; rewrite cats1.
    by rewrite map_inj_uniq //; exact: rcons_injl.
  moves' /[!mem_cat] /orP [Hs'a | /mapP [s Hs ->{s'}]].
    have := Hdisj _ Hs'a ⇒ /allP Hall.
    apply/allPx Hx; have := Hall _ Hx.
    by rewrite inE negb_or ⇒ /andP [_ ->].
  apply/allPx; rewrite mem_cat ⇒ /orP [Hxs | Hxj].
    have := Hdisj _ Hs ⇒ /allP /(_ x Hxs).
    by rewrite inE negb_or ⇒ /andP [_ ->].
  by rewrite mem_seq1 in Hxj; move/eqP: Hxj ⇒ → .
moveHuniq; apply: Hgen ⇒ // s.
by rewrite mem_seq1 ⇒ /eqP → .
Qed.

Lemma uniq_powerset_internal w : uniq (powerset_internal w).
Proof. by apply: uniq_powerset_of; apply: filter_uniq; apply: iota_uniq. Qed.

Lemma subset_powerset_of (ivs : seq nat) :
   ss, ss \in foldl (fun acc i
    acc ++ [seq s ++ [:: i] | s <- acc]) [:: [::]] ivs
  {subset ss ivs}.
Proof.
have Hgen : (ivs0 : seq nat) (acc : seq (seq nat)) (used : seq nat),
  ( s, s \in acc {subset s used})
   ss, ss \in foldl (fun (acc' : seq (seq nat)) (i : nat) ⇒
    acc' ++ [seq s ++ [:: i] | s <- acc']) acc ivs0
  {subset ss used ++ ivs0}.
  elim⇒ [|j ivs0 IH] acc used Hacc ss /=.
    by rewrite cats0; exact: Hacc.
  moveHss.
  have HIH : {subset ss (used ++ [:: j]) ++ ivs0}.
    apply: IH Hsss.
    rewrite mem_cat ⇒ /orP [Hsa | /mapP [s' Hs' Hseq]].
      movex Hxs; rewrite mem_cat (Hacc _ Hsa _ Hxs) //.
    movex; rewrite Hseq mem_cat ⇒ /orP [Hxs' | Hxj].
      by rewrite mem_cat (Hacc _ Hs' _ Hxs').
    rewrite mem_seq1 in Hxj; move/eqP: Hxj ⇒ →.
    by rewrite !mem_cat mem_seq1 eqxx /= orbT.
  by movex /HIH; rewrite -catA.
movess Hss.
have := Hgen ivs [:: [::]] [::] _ ss Hss.
rewrite cat0s; apply.
by moves; rewrite mem_seq1 ⇒ /eqPx //.
Qed.

Lemma subset_powerset_internal w ss :
  ss \in powerset_internal w {subset ss internal_vertices w}.
Proof. exact: subset_powerset_of. Qed.

Lemma subseq_powerset_of (ivs : seq nat) :
   ss, ss \in foldl (fun acc i
    acc ++ [seq s ++ [:: i] | s <- acc]) [:: [::]] ivs
  subseq ss ivs.
Proof.
have Hgen : (ivs0 : seq nat) (acc : seq (seq nat)) (used : seq nat),
  ( s, s \in acc subseq s used)
   ss, ss \in foldl (fun (acc' : seq (seq nat)) (i : nat) ⇒
    acc' ++ [seq s ++ [:: i] | s <- acc']) acc ivs0
  subseq ss (used ++ ivs0).
  elim⇒ [|j ivs0 IH] acc used Hacc ss /=.
    by rewrite cats0; exact: Hacc.
  moveHss.
  have HIH : subseq ss ((used ++ [:: j]) ++ ivs0).
    apply: IH Hsss.
    rewrite mem_cat ⇒ /orP [Hsa | /mapP [s' Hs' Hseq]].
      apply: subseq_trans (Hacc _ Hsa) _.
      exact: prefix_subseq.
    rewrite Hseq.
    by apply: cat_subseq; [exact: Hacc | exact: subseq_refl].
  by rewrite -catA /= in HIH.
movess Hss.
have := Hgen ivs [:: [::]] [::] _ ss Hss.
rewrite cat0s; apply.
by moves; rewrite mem_seq1 ⇒ /eqP →.
Qed.

Lemma subseq_powerset_internal w ss :
  ss \in powerset_internal w subseq ss (internal_vertices w).
Proof. exact: subseq_powerset_of. Qed.

Lemma char_mono_apply_psis_C_bit ss w v :
  uniq w uniq ss
  ( i, i \in ss is_internal i w)
  is_internal v w
  ~~ has_left_child v w
  nth false (char_mono (apply_psis ss w)) v =
    is_descent_seq w v (+) (v \in ss).
Proof.
elim: ss w ⇒ [|u ss IH] w Hu Huss Hint Hintv Hcv.
  rewrite /= addbF.
  by rewrite (nth_char_mono (is_internal_lt Hintv)).
move: Huss ⇒ /= /andP [Hunin Husss].
have Hintu : is_internal u w by apply: Hint; rewrite mem_head.
have Hupsi : uniq (psi u w) := uniq_psi u Hu.
have Hint_psi : i, i \in ss is_internal i (psi u w).
  movei Hi.
  rewrite -[psi u w]/(apply_psis [::u] w) is_internal_apply_psis //.
  by apply: Hint; rewrite in_cons Hi orbT.
have Hintv_psi : is_internal v (psi u w).
  by rewrite -[psi u w]/(apply_psis [::u] w) is_internal_apply_psis.
have Hcv_psi : ~~ has_left_child v (psi u w).
  by rewrite -[psi u w]/(apply_psis [::u] w) has_left_child_apply_psis.
rewrite /= -/(apply_psis _ _).
rewrite (IH (psi u w) Hupsi Husss Hint_psi Hintv_psi Hcv_psi).
rewrite (descent_psi_effect Hu Hintu (is_internal_lt Hintv)).
rewrite in_cons.
case Hlcu: (has_left_child u w) ⇒ /=.
- have Hvu_neq : (v == u) = false.
    apply: negbTE; apply: contraTneq Hcv ⇒ →.
    by rewrite Hlcu.
  have Hvum1_neq : (v == u.-1) = false.
    apply: negbTE; apply: contraTneq Hintv ⇒ →.
    have Hu_pos : 0 < u.
      case Hu0: u Hlcu ⇒ [|n] //=.
      by rewrite has_left_child_0.
    by have := LR_pred_is_endpoint Hu Hu_pos Hintu Hlcu.
  by rewrite Hvu_neq Hvum1_neq.
case Hvu: (v == u) ⇒ /=.
- move/eqP: HvuHvu.
  by rewrite Hvu (negbTE Hunin) /= addbT addbF.
by [].
Qed.

Lemma char_mono_apply_psis_D_bit_pred ss w v :
  uniq w uniq ss
  ( i, i \in ss is_internal i w)
  is_internal v w
  has_left_child v w
  nth false (char_mono (apply_psis ss w)) v.-1 =
    is_descent_seq w (if v \in ss then v else v.-1).
Proof.
elim: ss w ⇒ [|u ss IH] w Hu Huss Hint Hintv Hdv.
  rewrite /=.
  by rewrite (nth_char_mono (leq_ltn_trans (leq_pred v) (is_internal_lt Hintv))).
move: Huss ⇒ /= /andP [Hunin Husss].
have Hintu : is_internal u w by apply: Hint; rewrite mem_head.
have Hupsi : uniq (psi u w) := uniq_psi u Hu.
have Hint_psi : i, i \in ss is_internal i (psi u w).
  movei Hi.
  rewrite -[psi u w]/(apply_psis [::u] w) is_internal_apply_psis //.
  by apply: Hint; rewrite in_cons Hi orbT.
have Hintv_psi : is_internal v (psi u w).
  by rewrite -[psi u w]/(apply_psis [::u] w) is_internal_apply_psis.
have Hdv_psi : has_left_child v (psi u w).
  by rewrite -[psi u w]/(apply_psis [::u] w) has_left_child_apply_psis.
rewrite /= -/(apply_psis _ _).
rewrite (IH (psi u w) Hupsi Husss Hint_psi Hintv_psi Hdv_psi).
have Hv_pos : 0 < v.
  case Hv0: v Hdv ⇒ [|n] //=.
  by rewrite has_left_child_0.
have Hv_lt := is_internal_lt Hintv.
have Hvm1_lt : v.-1 < (size w).-1 := leq_ltn_trans (leq_pred v) Hv_lt.
have Hvm1u_neq : u != v.-1.
  apply: contraTneq Hintu ⇒ →.
  by have := LR_pred_is_endpoint Hu Hv_pos Hintv Hdv.
rewrite in_cons.
case Hvss: (v \in ss) ⇒ /=.
- rewrite orbT.
  rewrite (descent_psi_effect Hu Hintu Hv_lt).
  case Hvu: (v == u).
    by move/eqP: HvuHvu; move: Hunin; rewrite -Hvu Hvss.
  case Hlcu: (has_left_child u w) ⇒ //.
  have Hu_pos : 0 < u.
    case Hu0: u Hlcu ⇒ [|n] //=.
    by rewrite has_left_child_0.
  have Hvum1 : (v == u.-1) = false.
    apply: negbTE; apply: contraTneq Hintv ⇒ →.
    by have := LR_pred_is_endpoint Hu Hu_pos Hintu Hlcu.
  by rewrite Hvum1.
- rewrite orbF.
  rewrite (descent_psi_effect Hu Hintu Hvm1_lt).
  case Hvu: (v == u) ⇒ /=.
  + move/eqP: HvuHeq.
    have Hlcu : has_left_child u w by rewrite -Heq.
    rewrite Hlcu.
    have Hvm1_neq_v : (v.-1 == v) = false.
      by apply: negbTE; rewrite neq_ltn ltn_predL Hv_pos.
    by rewrite -Heq Hvm1_neq_v eqxx.
  + have Hvm1u : (v.-1 == u) = false.
      by apply: negbTE; rewrite eq_sym; exact: Hvm1u_neq.
    rewrite Hvm1u.
    case Hlcu: (has_left_child u w) ⇒ //.
    have Hu_pos : 0 < u.
      case Hu0: u Hlcu ⇒ [|n] //=.
      by rewrite has_left_child_0.
    have Hvm1um1 : (v.-1 == u.-1) = false.
      apply: negbTE; apply/eqPH.
      move: Hvu; suff → : v = u by rewrite eqxx.
      by rewrite -(prednK Hv_pos) -(prednK Hu_pos) H.
    by rewrite Hvm1um1.
Qed.

Lemma char_mono_apply_psis_D_bit_self ss w v :
  uniq w uniq ss
  ( i, i \in ss is_internal i w)
  is_internal v w
  has_left_child v w
  nth false (char_mono (apply_psis ss w)) v =
    is_descent_seq w (if v \in ss then v.-1 else v).
Proof.
elim: ss w ⇒ [|u ss IH] w Hu Huss Hint Hintv Hdv.
  rewrite /=.
  by rewrite (nth_char_mono (is_internal_lt Hintv)).
move: Huss ⇒ /= /andP [Hunin Husss].
have Hintu : is_internal u w by apply: Hint; rewrite mem_head.
have Hupsi : uniq (psi u w) := uniq_psi u Hu.
have Hint_psi : i, i \in ss is_internal i (psi u w).
  movei Hi.
  rewrite -[psi u w]/(apply_psis [::u] w) is_internal_apply_psis //.
  by apply: Hint; rewrite in_cons Hi orbT.
have Hintv_psi : is_internal v (psi u w).
  by rewrite -[psi u w]/(apply_psis [::u] w) is_internal_apply_psis.
have Hdv_psi : has_left_child v (psi u w).
  by rewrite -[psi u w]/(apply_psis [::u] w) has_left_child_apply_psis.
rewrite /= -/(apply_psis _ _).
rewrite (IH (psi u w) Hupsi Husss Hint_psi Hintv_psi Hdv_psi).
have Hv_pos : 0 < v.
  case Hv0: v Hdv ⇒ [|n] //=.
  by rewrite has_left_child_0.
have Hv_lt := is_internal_lt Hintv.
have Hvm1_lt : v.-1 < (size w).-1 := leq_ltn_trans (leq_pred v) Hv_lt.
have Hvm1u_neq : u != v.-1.
  apply: contraTneq Hintu ⇒ →.
  by have := LR_pred_is_endpoint Hu Hv_pos Hintv Hdv.
rewrite in_cons.
case Hvss: (v \in ss) ⇒ /=.
- rewrite orbT.
  rewrite (descent_psi_effect Hu Hintu Hvm1_lt).
  case Hvu: (v == u).
    by move/eqP: HvuHvu; move: Hunin; rewrite -Hvu Hvss.
  have Hvm1u : (v.-1 == u) = false.
    by apply: negbTE; rewrite eq_sym; exact: Hvm1u_neq.
  rewrite Hvm1u.
  case Hlcu: (has_left_child u w) ⇒ //.
  have Hu_pos : 0 < u.
    case Hu0: u Hlcu ⇒ [|n] //=.
    by rewrite has_left_child_0.
  have Hvm1um1 : (v.-1 == u.-1) = false.
    apply: negbTE; apply/eqPH.
    move: Hvu; suff → : v = u by rewrite eqxx.
    by rewrite -(prednK Hv_pos) -(prednK Hu_pos) H.
  by rewrite Hvm1um1.
- rewrite orbF.
  rewrite (descent_psi_effect Hu Hintu Hv_lt).
  case Hvu: (v == u) ⇒ /=.
  + move/eqP: HvuHeq.
    have Hlcu : has_left_child u w by rewrite -Heq.
    by rewrite Hlcu -Heq.
  + case Hlcu: (has_left_child u w) ⇒ //.
    have Hu_pos : 0 < u.
      case Hu0: u Hlcu ⇒ [|n] //=.
      by rewrite has_left_child_0.
    have Hvum1 : (v == u.-1) = false.
      apply: negbTE; apply: contraTneq Hintv ⇒ →.
      by have := LR_pred_is_endpoint Hu Hu_pos Hintu Hlcu.
    by rewrite Hvum1.
Qed.

Lemma size_expand_cde letters :
  size (expand_cde letters) =
  2 ^ size [seq l <- letters
           | match l with E_letterfalse
                        | _true end].
Proof.
elim: letters ⇒ [|[||] l IH] //=.
- rewrite size_cat !size_map IH expnS.
  by rewrite mul2n -addnn.
- rewrite size_cat !size_map IH expnS.
  by rewrite mul2n -addnn.
Qed.

Lemma size_phi_w w :
  size (phi_w w) = size (internal_vertices w).
Proof. by rewrite phi_w_as_map size_map. Qed.

Lemma size_expand_cde_phi_w w :
  size (expand_cde (phi_w w)) =
  2 ^ size (internal_vertices w).
Proof.
rewrite size_expand_cde.
congr (2 ^ _).
rewrite phi_w_as_map filter_map size_map size_filter.
rewrite -[RHS](count_predT (internal_vertices w)).
apply: eq_in_counti _ /=.
by case: (has_left_child i w).
Qed.


Definition check_fact3 (w : seq nat) : bool :=
  sort leq_seqb
    [seq char_mono (apply_psis ss w)
    | ss <- powerset_internal w]
  ==
  sort leq_seqb (expand_cde (phi_w w)).

Lemma check_fact3P w :
  reflect
    (sort leq_seqb
      [seq char_mono (apply_psis ss w)
      | ss <- powerset_internal w]
    =
    sort leq_seqb (expand_cde (phi_w w)))
    (check_fact3 w).
Proof. exact: eqP. Qed.

Lemma check_fact3_size1 w :
  size w 1 check_fact3 w.
Proof.
case: w ⇒ [|a [|b s]] //= _.
apply/eqP.
have Hws : window_size 0 [:: a] = 1.
  apply/eqP; rewrite eqn_leq.
  apply/andP; split.
    have := window_size_bound 0 [:: a].
    by rewrite subn0.
  by apply: window_size_gt0.
have Hphi : phi_w [:: a] = [::].
  rewrite /phi_w /phi'_w /= /classify_vertex_cde /is_internal /=.
  by rewrite Hws.
have Hpw : powerset_internal [:: a] = [:: [::]].
  rewrite /powerset_internal /internal_vertices /=.
  by rewrite /is_internal /= Hws.
rewrite Hphi Hpw /=.
by rewrite /apply_psis /char_mono /=.
Qed.