From PslBase Require Import Base FinTypes.
From Undecidability Require Import L.Functions.EqBool.
From Undecidability.L.Complexity Require Export Problems.Cook.PR FlatFinTypes MorePrelim.
Require Import Lia.

Flat Parallel Rewriting


Inductive FlatPR := {
  Sigma : ;
  offset : ;
  width : ;
  init : list ;
  windows : list (PRWin );
  final : list (list );
  steps :
  }.

Definition PRWin_ofFlatType (win : PRWin ) k:= list_ofFlatType k (prem win) list_ofFlatType k (conc win).
Definition isValidFlatWindows (l : list (PRWin )) k := ( win, win l PRWin_ofFlatType win k).
Definition isValidFlatFinal (l : list (list )) k := ( s, s l list_ofFlatType k s).
Definition isValidFlatInitial (l : list ) k := list_ofFlatType k l.

Definition FlatPR_wellformed fpr :=
  width fpr > 0
   offset fpr > 0
   ( k, k > 0 width fpr = k * offset fpr)
   length (init fpr) width fpr
   ( win, win windows fpr PRWin_of_size win (width fpr))
   ( k, length (init fpr) = k * offset fpr).

Definition isValidFlattening fpr :=
 list_ofFlatType (Sigma fpr) (init fpr)
   ( s, s final fpr list_ofFlatType (Sigma fpr) s)
   ( win, win windows fpr PRWin_ofFlatType win (Sigma fpr)).


Definition FlatPRLang (C : FlatPR) := FlatPR_wellformed C isValidFlattening C
   (sf : list ), list_ofFlatType (Sigma C) sf
       relpower (valid (offset C) (width C) (windows C)) (steps C) (init C) sf
       satFinal (offset C) (length (init C)) (final C) sf.

Section fixInstance.
  Variable (fpr : FlatPR).
  Notation Sigma := (Sigma fpr).
  Notation offset := (offset fpr).
  Notation width := (width fpr).
  Notation init := (init fpr).
  Notation windows := (windows fpr).
  Notation final := (final fpr).
  Notation steps := (steps fpr).

  Context (A : FlatPR_wellformed fpr).
  Context (B : isValidFlattening fpr).

  Lemma p_invariant (p : list Prop) (a b : list ) :
    p a
     ( x y, p (x y) p x p y)
     |a| width
     ( x y u v win, rewritesHead win (u x) (v y) win windows |u| = offset |v| = offset p v)
     ( win, win windows p (conc win))
     valid offset width windows a b
     p b.
  Proof.
    intros H .
    assert (valid offset width windows a b |a| width) as %validDirect_valid by tauto. 2-4: apply A.
    clear . induction .
    - clear .
      destruct A as (_ & _ & (k & _ & ) & _ & & _).
      destruct B as ( _ & _ & ).
      specialize ( _ ) as ( & ).
      specialize as ((rest' & ) & (rest & )).       specialize ( _ ) as ( & ).
      subst. rewrite app_length in .
      assert (rest = []) as .
      {
        destruct rest'; cbn in ; [ | lia]. rewrite !app_length in .
        destruct rest; [ easy | cbn in ; lia].
      }
      rewrite app_nil_r. now apply .
    - rewrite . apply in H. split; [ | apply IHvalidDirect; easy]. now eapply .
  Qed.


  Corollary valid_list_ofFlatType_invariant a b :
    list_ofFlatType Sigma a |a| width valid offset width windows a b list_ofFlatType Sigma b.
  Proof.
    intros H . eapply (@p_invariant (list_ofFlatType Sigma)).
    - apply H.
    - intros. apply list_ofFlatType_app.
    - apply .
    - intros. destruct as (_ & (c & )).
        destruct A as (_ & _ & & _ & & _ ). destruct B as ( _ & _ & ).
        specialize ( _ ) as (_ & ). specialize ( _ ) as (_ & ).
        apply app_length_split in as (u' & ).
        * rewrite in . now apply list_ofFlatType_app in .
        * destruct as (ak & & ). nia.
    - intros. destruct B as (_ & _ & ).
      apply in as (_ & ). apply .
    - apply .
  Qed.


  Lemma relpower_valid_list_ofFlatType_invariant steps a b:
    list_ofFlatType Sigma a |a| width relpower (valid offset width windows) steps a b list_ofFlatType Sigma b.
  Proof.
    intros. induction ; [easy | ].
    apply IHrelpower. eapply valid_list_ofFlatType_invariant, ; [ apply H | ].
    - apply .
    - apply valid_length_inv in . lia.
  Qed.


End fixInstance.

Definition PRWin_ofFlatType_dec ( : ) (win : PRWin ) := list_ofFlatType_dec (prem win) && list_ofFlatType_dec (conc win).
Lemma PRWin_ofFlatType_dec_correct win : PRWin_ofFlatType_dec win = true PRWin_ofFlatType win .
Proof.
  unfold PRWin_ofFlatType_dec, PRWin_ofFlatType. rewrite andb_true_iff.
  rewrite !list_ofFlatType_dec_correct. easy.
Qed.


Definition FlatPR_wf_dec (fpr : FlatPR) :=
  leb 1 (width fpr)
  && leb 1 (offset fpr)
  && Nat.eqb (Nat.modulo (width fpr) (offset fpr)) 0
  && leb (width fpr) (|init fpr|)
  && forallb (PRWin_of_size_dec (width fpr)) (windows fpr)
  && Nat.eqb (Nat.modulo (|init fpr|) (offset fpr)) 0.
Definition isValidFlattening_dec (fpr : FlatPR) :=
  list_ofFlatType_dec (Sigma fpr) (init fpr)
  && forallb (list_ofFlatType_dec (Sigma fpr)) (final fpr)
  && forallb (PRWin_ofFlatType_dec (Sigma fpr)) (windows fpr).

Lemma FlatPR_wf_dec_correct (fpr : FlatPR) : FlatPR_wf_dec fpr = true FlatPR_wellformed fpr.
Proof.
  unfold FlatPR_wf_dec, FlatPR_wellformed. rewrite !andb_true_iff, !and_assoc.
  rewrite !leb_iff. rewrite !(reflect_iff _ _ (Nat.eqb_spec _ _ )).
  rewrite !forallb_forall.
  setoid_rewrite PRWin_of_size_dec_correct.
  split; intros; repeat match goal with [H : _ _ |- _] destruct H end;
  repeat match goal with [ |- _ _ ] split end; try easy.
  - apply Nat.mod_divide in as (k & ); [ | lia].
    exists k; split; [ | apply ]. destruct k; cbn in *; lia.
  - apply Nat.mod_divide in as (k & ); [ | lia]. exists k; apply .
  - apply Nat.mod_divide; [ lia | ]. destruct as (k & _ & ). exists k; apply .
  - apply Nat.mod_divide; [ lia | ]. apply .
Qed.


Lemma isValidFlattening_dec_correct (fpr : FlatPR) : isValidFlattening_dec fpr = true isValidFlattening fpr.
Proof.
  unfold isValidFlattening_dec, isValidFlattening.
  rewrite !andb_true_iff, !and_assoc.
  rewrite !forallb_forall.
  setoid_rewrite PRWin_ofFlatType_dec_correct.
  setoid_rewrite list_ofFlatType_dec_correct.
  split; intros; repeat match goal with [H : _ _ |- _] destruct H end;
  repeat match goal with [ |- _ _ ] split end; try easy.
Qed.


Inductive isFlatPRWinOf (X : finType) (flatwin : PRWin ) (win : PRWin X) :=
  mkIsFlatPRWinO (Hprem : isFlatListOf (prem flatwin) (prem win))
    (Hconc : isFlatListOf (conc flatwin) (conc win))
  : isFlatPRWinOf flatwin win.

Inductive isFlatWindowsOf (X : finType) (flatwindows : list (PRWin )) (windows : list (PRWin X)) :=
  mkIsFlatWindowsOf (Hsound : flatwin, flatwin flatwindows win, win windows isFlatPRWinOf flatwin win)
    (Hcomplete : win, win windows flatwin, flatwin flatwindows isFlatPRWinOf flatwin win)
  : isFlatWindowsOf flatwindows windows.

Inductive isFlatFinalOf (X : finType) (flatfinal : list (list )) (final : list (list X)) :=
  mkIsFlatFinalOf (Hsound : fin, fin flatfinal fin', fin' final isFlatListOf fin fin')
    (Hcomplete : fin, fin final fin', fin' flatfinal isFlatListOf fin' fin)
  : isFlatFinalOf flatfinal final.

Inductive isFlatPROf (fpr : FlatPR) (pr : PR) :=
  mkIsFlatPROf (HSigma : finRepr (PR.Sigma pr) (Sigma fpr))
    (HOffset : offset fpr = PR.offset pr)
    (HWidth : width fpr = PR.width pr)
    (HInit : isFlatListOf (init fpr) (PR.init pr))
    (HWindows : isFlatWindowsOf (windows fpr) (PR.windows pr))
    (HFinal : isFlatFinalOf (final fpr) (PR.final pr))
    (HSteps : steps fpr = PR.steps pr)
  : isFlatPROf fpr pr.

Lemma isFlatPROf_isValidFlattening (fpr : FlatPR) (pr : PR) : isFlatPROf fpr pr isValidFlattening fpr.
Proof.
  intros. destruct H.
  repeat split.
  - rewrite HSigma. now eapply isFlatListOf_list_ofFlatType.
  - intros s %HFinal. rewrite HSigma. destruct as (s' & & ).
    eapply isFlatListOf_list_ofFlatType. unfold isFlatListOf. reflexivity.
  - apply HWindows in H. destruct H as (win' & & ).
    destruct as ( & ). rewrite HSigma.
    eapply isFlatListOf_list_ofFlatType, .
  - apply HWindows in H. destruct H as (win' & & ).
    destruct as ( & ). rewrite HSigma.
    eapply isFlatListOf_list_ofFlatType, .
Qed.


we show that FlatPR instances "behave in the same way" as PR instances

Lemma rewritesHead_flat_agree (X : finType) (windowsFin : list (PRWin X)) windowsFlat finStr finStr' flatStr flatStr' :
  isFlatListOf flatStr finStr
   isFlatListOf flatStr' finStr'
   isFlatWindowsOf windowsFlat windowsFin
   ( win, win windowsFin rewritesHead win finStr finStr') ( win, win windowsFlat rewritesHead win flatStr flatStr').
Proof.
  intros. split; intros (win & & ).
  - apply in as (win' & & ). exists win'. split; [apply | ].
    unfold rewritesHead, prefix in *. destruct as (( & ) & ( & )).
    unfold isFlatListOf in H, .
    rewrite map_app in H, . split.
    + exists (map index ). rewrite H. enough (map index (prem win) = prem win') as .
      { now setoid_rewrite . }
      destruct win; cbn.
      destruct . cbn in *. now rewrite Hprem.
    + exists (map index ). rewrite . enough (map index (conc win) = conc win') as .
      { now setoid_rewrite . }
      destruct win; cbn.
      destruct . cbn in *. now rewrite Hconc.
  - apply in as (win' & & ). exists win'. split; [ apply | ].
    unfold rewritesHead, prefix in *. destruct as (( & ) & ( & )).
    unfold isFlatListOf in H, . split.
    + symmetry in H. apply map_eq_app in H as ( & & & ? & ?).
      exists .
      enough ( = prem win') as by (now setoid_rewrite ).
      destruct . rewrite Hprem in H.
      apply map_injective in H; [easy | unfold injective; apply injective_index].
    + symmetry in . apply map_eq_app in as ( & & & ? & ?).
      exists .
      enough ( = conc win') as by (now setoid_rewrite ).
      destruct . rewrite Hconc in .
      apply Prelim.map_inj in ; [easy | unfold injective; apply injective_index].
Qed.


Section fixFPRInstance.
  Variable (fpr : FlatPR).
  Notation Sigma := (Sigma fpr).
  Notation offset := (offset fpr).
  Notation width := (width fpr).
  Notation init := (init fpr).
  Notation windows := (windows fpr).
  Notation final := (final fpr).
  Notation steps := (steps fpr).

  Context (A : FlatPR_wellformed fpr).
  Context (B : isValidFlattening fpr).

  Lemma valid_flat_agree (X : finType) (fwindows : list (PRWin X)) :
    isFlatListOf
     isFlatListOf
     isFlatWindowsOf windows fwindows
     valid offset width windows valid offset width fwindows .
  Proof.
    intros H .
    split.
    - intros . revert H . induction ; intros.
      + destruct ; [ | now unfold isFlatListOf in H].
        destruct ; [ | now unfold isFlatListOf in ].
        constructor.
      + unfold isFlatListOf in , .
        symmetry in . apply map_eq_app in as ( & & & & ).
        symmetry in . apply map_eq_app in as ( & & & & ).
        constructor 2.
        2-4: now rewrite map_length in *.
        apply IHvalid; easy.
      + unfold isFlatListOf in , .
        symmetry in . apply map_eq_app in as ( & & & & ).
        symmetry in . apply map_eq_app in as ( & & & & ).
        assert ( w, w windows rewritesHead w (map index map index ) (map index map index )) as by eauto.
        eapply rewritesHead_flat_agree in as (finwin & & ).
        * econstructor 3. 2-3: now rewrite map_length in *.
          -- eapply IHvalid; easy.
          -- apply .
          -- apply .
        * clear . apply isFlatListOf_app; easy.
        * clear . apply isFlatListOf_app; easy.
        * clear . apply .
    - intros . revert H . induction ; intros.
      + rewrite H, ; cbn; constructor 1.
      + rewrite , . rewrite !map_app. constructor. 2-4: rewrite map_length; auto.
        now eapply IHvalid.
      + rewrite , . rewrite !map_app.
        assert ( w, w fwindows rewritesHead w (u a) (vb)) as by eauto.
        eapply rewritesHead_flat_agree in as (finwin & & ). 2-4: eauto.
        econstructor 3. 2-3: rewrite map_length; auto.
        * now eapply IHvalid.
        * apply .
        * rewrite , in . now rewrite !map_app in .
  Qed.


  Lemma valid_flat_isFlatListOf_invariant (X : finType) ( : list X) :
    finRepr X Sigma
     isFlatListOf
     || width
     valid offset width windows
     ( : list X), isFlatListOf .
  Proof.
    intros.
    apply isFlatListOf_list_ofFlatType in . rewrite H in .
    specialize (@valid_list_ofFlatType_invariant fpr A B ) as .
    apply (finRepr_exists_list H) in . destruct as ( & ); easy.
  Qed.


  Lemma relpower_valid_flat_agree (X : finType) (finwindows : list (PRWin X)) n:
    finRepr X Sigma
     || width
     isFlatListOf
     isFlatListOf
     isFlatWindowsOf windows finwindows
     relpower (valid offset width windows) n relpower (valid offset width finwindows) n .
  Proof.
    intros . split.
    - intros . revert . induction ; intros.
      + specialize (isFlatListOf_functional ) as . eauto.
      + specialize (valid_flat_isFlatListOf_invariant H) as ( & ).
        specialize (valid_length_inv H) as . rewrite in .
        specialize (IHrelpower _ _ ).
        econstructor.
        * apply (valid_flat_agree ) in H. apply H.
        * apply IHrelpower.
    - intros . clear .
      revert . induction ; intros.
      + specialize (isFlatListOf_injective ) as . constructor.
      + assert (isFlatListOf (map index b) b) as by (unfold isFlatListOf; easy).
        specialize (IHrelpower _ _ ).
        apply (valid_flat_agree ) in H. eauto.
  Qed.


  Lemma final_flat_agree (X : finType) (F : list (list X)) (f : list (list )) l:
    isFlatFinalOf f F s fs, isFlatListOf fs s satFinal offset l f fs satFinal offset l F s.
  Proof.
    intros. split.
    - intros (subs & k & & & ). apply H in as (subs' & & ).
      exists subs', k. split; [ apply | split; [ apply | ]].
      unfold isFlatListOf in . rewrite in . rewrite in .
      destruct as (b & ). rewrite utils.map_skipn in .
      apply map_eq_app in as ( & & & & ).
      rewrite .
      apply map_injective in ; [ | apply injective_index].
      rewrite . now exists .
    - intros (subs & k & & & ). apply H in as (subs' & &).
      exists subs', k. split; [ apply | split; [ apply | ]].
      rewrite , . destruct as (b & ).
      exists (map index b). rewrite utils.map_skipn. rewrite .
      now rewrite !map_app.
  Qed.

End fixFPRInstance.

Lemma isFlatPROf_wf_equivalent (pr : PR) (fpr : FlatPR) :
  isFlatPROf fpr pr (FlatPR_wellformed fpr PR_wellformed pr).
Proof.
  intros [ ]. split; intros; unfold FlatPR_wellformed, PR_wellformed in *.
  - destruct H as ( & & & & & ). repeat split.
    + easy.
    + easy.
    + destruct as (k & & ). exists k. nia.
    + rewrite in . rewrite map_length in . lia.
    + apply HWindows in H as (flatwin & H & ). apply in H.
      destruct . destruct H as (H & _). rewrite Hprem in H. rewrite map_length in H. lia.
    + apply HWindows in H as (flatwin & H & ). apply in H.
      destruct . destruct H as (_ & H). rewrite Hconc in H. rewrite map_length in H. lia.
    + destruct as (k & ). rewrite in . rewrite map_length in . exists k; nia.
  - destruct H as ( & & & & & ). repeat split.
    + easy.
    + easy.
    + destruct as (k & & ). exists k. nia.
    + rewrite , map_length. lia.
    + apply HWindows in H as (finwin & H & ). apply in H.
      destruct . destruct H as (H & _). rewrite Hprem, map_length. lia.
    + apply HWindows in H as (finwin & H & ). apply in H.
      destruct . destruct H as (_ & H). rewrite Hconc, map_length. lia.
    + destruct as (k & ). rewrite , map_length. exists k. nia.
Qed.


Lemma isFlatPROf_equivalence (pr : PR) (fpr : FlatPR) :
  isFlatPROf fpr pr (FlatPRLang fpr PRLang pr).
Proof.
  intros. split.
  - intros ( & & ). split; [ now eapply isFlatPROf_wf_equivalent | ].
    destruct H as [ ].
    destruct as (sf & & & ).
    apply (finRepr_exists_list ) in as (SF & ).
    exists SF. split.
    + rewrite , , HSteps. eapply relpower_valid_flat_agree; eauto. apply .
    + rewrite . rewrite , map_length in . eapply final_flat_agree; eauto.
  - intros ( & ). split; [ now eapply isFlatPROf_wf_equivalent | ].
    split; [now eapply isFlatPROf_isValidFlattening | ].
    destruct H as [ ].
    destruct as (sf & & ).
    exists (map index sf). repeat split.
    + unfold list_ofFlatType, ofFlatType.
      setoid_rewrite in_map_iff. intros a (x & & H). rewrite . apply index_le.
    + eapply relpower_valid_flat_agree; eauto.
      * now rewrite isFlatPROf_wf_equivalent.
      * now eapply isFlatPROf_isValidFlattening with (pr := pr).
      * rewrite , map_length, . apply .
      * unfold isFlatListOf. reflexivity.
      * rewrite , , HSteps. apply .
    + eapply final_flat_agree; eauto.
      * unfold isFlatListOf; reflexivity.
      * rewrite , , map_length. apply .
Qed.



Require Import PslBase.FiniteTypes.VectorFin PslBase.FiniteTypes.Cardinality.
Import Coq.Init.Specif.
Lemma unflattenString (f : list ) k : list_ofFlatType k f {f' : list (finType_CS (Fin.t k)) & isFlatListOf f f'}.
Proof.
  intros H.
  eapply finRepr_exists_list with (X := finType_CS (Fin.t k)) in H as (a' & H).
  2: { unfold finRepr. specialize (Card_Fint k). unfold Cardinality. easy. }
  eauto.
Qed.


Lemma unflattenWindow (w : PRWin ) k : PRWin_ofFlatType w k {w' : PRWin (finType_CS (Fin.t k)) & isFlatPRWinOf w w'}.
Proof.
  intros. destruct w. destruct H as ( & ). cbn in *.
  apply unflattenString in as (prem' & ).
  apply unflattenString in as (conc' & ).
  exists (Build_PRWin prem' conc'). split; easy.
Qed.


Lemma unflattenWindows (l : list (PRWin )) k : isValidFlatWindows l k {l' : list (PRWin (finType_CS (Fin.t k))) & isFlatWindowsOf l l'}.
Proof.
  intros. unfold isValidFlatWindows in H. induction l.
  - exists []. easy.
  - edestruct IHl as (l' & IH);[ easy | ]. specialize (H a (or_introl eq_refl)).
    apply unflattenWindow in H as (a' & H). exists (a' :: l'). split; intros.
    + destruct as [ | ]; [easy | ]. apply IH in as (win & ? & ?); eauto.
    + destruct as [ | ]; [ easy | ]. apply IH in as (win' & ? & ?); eauto.
Qed.


Lemma unflattenFinal (f : list (list )) k : isValidFlatFinal f k {f' : list (list (finType_CS (Fin.t k))) & isFlatFinalOf f f'}.
Proof.
  intros. unfold isValidFlatFinal in H. induction f; intros.
  - exists []; easy.
  - edestruct IHf as (f' & IH); [easy | ]. specialize (H a (or_introl eq_refl)).
    apply unflattenString in H as (a' &H).
    exists (a'::f'). split; intros.
    + destruct as [ | ]; [easy | ]. apply IH in as (? & ? & ?); eauto.
    + destruct as [ | ]; [easy | ]. apply IH in as (? & ? & ?); eauto.
Qed.


Lemma unflattenPR (f : FlatPR) : isValidFlattening f {f' : PR & isFlatPROf f f'}.
Proof.
  intros ( & & ).
  apply unflattenWindows in as (w' & ).
  apply unflattenFinal in as (f' & ).
  apply unflattenString in as (i' & ).
  exists (Build_PR (offset f) (width f) i' w' f' (steps f)).
  constructor; eauto.
  cbn. unfold finRepr. specialize (Card_Fint (Sigma f)). easy.
Qed.


extraction


From Undecidability.L.Tactics Require Import LTactics GenEncode.
From Undecidability.L.Datatypes Require Import LProd LOptions LLNat LLists.

Section fix_X.
  Variable (X:Type).
  Context `{X_registered : registered X}.

  Run TemplateProgram (tmGenEncode "PRWin_enc" (PRWin X)).
  Hint Resolve PRWin_enc_correct : Lrewrite.

  Global Instance term_Build_PRWin : computableTime' (@Build_PRWin X) ( _ _ (1, _ _ (1, tt))).
  Proof.
    extract constructor. solverec.
  Defined.


  Definition cnst_prem := 5.
  Global Instance term_prem : computableTime' (@prem X) ( _ _ (cnst_prem, tt)).
  Proof.
    extract. unfold cnst_prem. solverec.
  Defined.


  Definition cnst_conc := 5.
  Global Instance term_conc : computableTime' (@conc X) ( _ _ (cnst_conc, tt)).
  Proof.
    extract. unfold cnst_conc. solverec.
  Defined.


  Definition c__sizePRWin := 4.
  Lemma PRWin_enc_size (win : PRWin X) : size (enc win) = size (enc (prem win)) + size (enc (conc win)) + c__sizePRWin.
  Proof.
    destruct win. cbn. unfold enc, c__sizePRWin. cbn. nia.
  Qed.

End fix_X.

Hint Resolve PRWin_enc_correct : Lrewrite.

Run TemplateProgram (tmGenEncode "FlatPR_enc" (FlatPR)).
Hint Resolve FlatPR_enc_correct : Lrewrite.

From Undecidability.L.Complexity Require Import PolyBounds.

Instance term_Build_FlatPR : computableTime' Build_FlatPR ( _ _ (1, _ _ (1, _ _ (1, _ _ (1, _ _ (1, _ _ (1, _ _ (1, tt)))))))).
Proof.
  extract constructor. solverec.
Defined.


Definition c__Sigma := 10.
Instance term_FlatPR_Sigma : computableTime' Sigma ( _ _ (c__Sigma, tt)).
Proof.
  extract. unfold c__Sigma. solverec.
Defined.


Definition c__offset := 10.
Instance term_FlatPR_offset : computableTime' offset ( _ _ (c__offset, tt)).
Proof.
  extract. unfold c__offset. solverec.
Defined.


Definition c__width := 10.
Instance term_FlatPR_width : computableTime' width ( _ _ (c__width, tt)).
Proof.
  extract. unfold c__width. solverec.
Defined.


Definition c__init := 10.
Instance term_FlatPR_init : computableTime' init ( _ _ (c__init, tt)).
Proof.
  extract. unfold c__init. solverec.
Defined.


Definition c__windows := 10.
Instance term_FlatPR_windows : computableTime' windows ( _ _ (c__windows, tt)).
Proof.
  extract. unfold c__windows. solverec.
Defined.


Definition c__final := 10.
Instance term_FlatPR_final : computableTime' final ( _ _ (c__final, tt)).
Proof.
  extract. unfold c__final. solverec.
Defined.


Definition c__steps := 10.
Instance term_FlatPR_steps : computableTime' steps ( _ _ (c__steps, tt)).
Proof.
  extract. unfold c__steps. solverec.
Defined.


Lemma FlatPR_enc_size (fpr : FlatPR) : size (enc fpr) = size (enc (Sigma fpr)) + size(enc (offset fpr)) + size (enc (width fpr)) + size (enc (init fpr)) + size (enc (windows fpr)) + size (enc (final fpr)) + size (enc (steps fpr)) + 9.
Proof.
  destruct fpr. cbn. unfold enc. cbn. nia.
Qed.


Section PRWin_of_size.
  Variable ( X : Type).
  Context `{X_registered : registered X}.

  Definition c__prwinOfSizeDec := 2 * (cnst_prem + 2 * 5 + cnst_conc + 6 + c__length).
  Definition PRWin_of_size_dec_time (width : ) (win : PRWin X) := c__prwinOfSizeDec * (1 + |prem win| + |conc win|) + eqbTime (X := ) (size (enc (|prem win|))) (size (enc width)) + eqbTime (X := ) (size (enc (|conc win|))) (size (enc width)).
  Global Instance term_PRWin_of_size_dec : computableTime' (@PRWin_of_size_dec X) ( width _ (1, win _ (PRWin_of_size_dec_time width win, tt))).
  Proof.
    extract. solverec. unfold PRWin_of_size_dec_time, c__prwinOfSizeDec. nia.
  Defined.


  Definition c__prwinOfSizeDecBound := c__prwinOfSizeDec + c__eqbComp .
  Lemma PRWin_of_size_dec_time_bound width (win : PRWin X) : PRWin_of_size_dec_time width win (size(enc win) + 1) * c__prwinOfSizeDecBound.
  Proof.
    unfold PRWin_of_size_dec_time. rewrite !eqbTime_le_l. rewrite !list_size_enc_length, !list_size_length.
    rewrite PRWin_enc_size. unfold c__prwinOfSizeDecBound, c__sizePRWin. nia.
  Qed.

End PRWin_of_size.

Definition c__FlatPRWfDec := 3 * c__leb2 + 4 * c__width + 3 * c__offset + 2 * 5 + 2 * c__init + 2 * c__length + c__windows + 32 + 4 * c__leb + 2 * c__eqbComp * size (enc 0).
Definition FlatPR_wf_dec_time x := 2 * c__length * (|init x|) + leb_time (width x) (|init x|) + forallb_time (@PRWin_of_size_dec_time (width x)) (windows x) + modulo_time (|init x|) (offset x) + modulo_time (width x) (offset x) + c__FlatPRWfDec.

Instance term_FlatPR_wf_dec : computableTime' FlatPR_wf_dec ( fpr _ (FlatPR_wf_dec_time fpr, tt)).
Proof.
  extract. solverec. unfold FlatPR_wf_dec_time, c__FlatPRWfDec, leb_time. rewrite !eqbTime_le_r.
  rewrite Nat.le_min_l. rewrite Nat.le_min_l.
  leq_crossout.
Defined.


Definition c__FlatPRWfDecBound := 2*(2 * c__length + c__leb + c__prwinOfSizeDecBound + c__forallb + 2 * c__moduloBound + c__FlatPRWfDec).
Definition poly__FlatPRWfDec n := (n*n + 2* n + 1) * c__FlatPRWfDecBound.

Lemma FlatPR_wf_dec_time_bound fpr :
  FlatPR_wf_dec_time fpr poly__FlatPRWfDec (size (enc fpr)).
Proof.
  unfold FlatPR_wf_dec_time. rewrite leb_time_bound_l.
  rewrite !modulo_time_bound. rewrite list_size_enc_length.
  rewrite list_size_length.
  erewrite forallb_time_bound_env.
  2: {
    split; [intros | ].
    - specialize (PRWin_of_size_dec_time_bound (X := ) y a) as .
      instantiate (2:= n (n + 1) * c__prwinOfSizeDecBound). nia.
    - smpl_inO.
  }
  rewrite list_size_length.
  replace_le (size(enc (windows fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia).
  replace_le (size(enc (init fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia).
  replace_le (size(enc (width fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia).
  replace_le (size(enc(offset fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia).
  unfold poly__FlatPRWfDec, c__FlatPRWfDecBound. nia.
Qed.

Lemma FlatPR_wf_dec_poly : monotonic poly__FlatPRWfDec inOPoly poly__FlatPRWfDec.
Proof.
  unfold poly__FlatPRWfDec; split; smpl_inO.
Qed.



Definition c__PRWinOfFlatTypeDec := cnst_prem + cnst_conc +8.
Definition PRWin_ofFlatType_dec_time (sig : ) (w : PRWin ):= list_ofFlatType_dec_time sig (prem w) + list_ofFlatType_dec_time sig (conc w) + c__PRWinOfFlatTypeDec.
Instance term_PRWin_ofFlatType_dec : computableTime' PRWin_ofFlatType_dec ( sig _ (1, w _ (PRWin_ofFlatType_dec_time sig w, tt))).
Proof.
  extract. solverec. unfold PRWin_ofFlatType_dec_time, c__PRWinOfFlatTypeDec. nia.
Defined.


Definition c__PRWinOfFlatTypeDecBound := 2 * (c__PRWinOfFlatTypeDec + 1).
Definition poly__PRWinOfFlatTypeDec n := (poly__listOfFlatTypeDec n + 1) * c__PRWinOfFlatTypeDecBound.
Lemma PRWin_ofFlatType_dec_time_bound sig w : PRWin_ofFlatType_dec_time sig w poly__PRWinOfFlatTypeDec (size (enc sig) + size (enc w)).
Proof.
  unfold PRWin_ofFlatType_dec_time. rewrite !list_ofFlatType_dec_time_bound.
  unfold poly__PRWinOfFlatTypeDec.
  poly_mono list_ofFlatType_dec_poly at 2.
  2: (replace_le (size (enc (conc w))) with (size (enc w)) by (rewrite PRWin_enc_size; lia)); reflexivity.
  poly_mono list_ofFlatType_dec_poly at 1.
  2: (replace_le (size (enc (prem w))) with (size (enc w)) by (rewrite PRWin_enc_size; lia)); reflexivity.
  unfold c__PRWinOfFlatTypeDecBound. nia.
Qed.

Lemma PRWin_ofFlatType_dec_poly : monotonic poly__PRWinOfFlatTypeDec inOPoly poly__PRWinOfFlatTypeDec.
Proof.
  split; unfold poly__PRWinOfFlatTypeDec; smpl_inO; apply list_ofFlatType_dec_poly.
Qed.


Definition c__isValidFlatteningDec := 3 * c__Sigma + c__init + c__final + c__windows + 16.
Definition isValidFlattening_dec_time x := list_ofFlatType_dec_time (Sigma x) (init x) + forallb_time (list_ofFlatType_dec_time (Sigma x)) (final x)+ forallb_time (PRWin_ofFlatType_dec_time (Sigma x)) (windows x) + c__isValidFlatteningDec.
Instance term_isValidFlattening_dec : computableTime' isValidFlattening_dec ( fpr _ (isValidFlattening_dec_time fpr, tt)).
Proof.
  extract. solverec. unfold isValidFlattening_dec_time, c__isValidFlatteningDec.
  repeat change ( x ?h x) with h. solverec.
Defined.


Definition c__isValidFlatteningDecBound := 2 * c__forallb + c__isValidFlatteningDec.
Definition poly__isValidFlatteningDec n :=(n + 2) * poly__listOfFlatTypeDec n + (n + 1) * poly__PRWinOfFlatTypeDec n + (n + 1) * c__isValidFlatteningDecBound.
Lemma isValidFlattening_dec_time_bound fpr : isValidFlattening_dec_time fpr poly__isValidFlatteningDec (size (enc fpr)).
Proof.
  unfold isValidFlattening_dec_time.
  rewrite list_ofFlatType_dec_time_bound.
  erewrite forallb_time_bound_env.
  2: {
    split; [intros | ].
    - rewrite list_ofFlatType_dec_time_bound, Nat.add_comm; reflexivity.
    - apply list_ofFlatType_dec_poly.
  }
  erewrite forallb_time_bound_env.
  2 : {
    split; [intros | ].
    - rewrite PRWin_ofFlatType_dec_time_bound, Nat.add_comm; reflexivity.
    - apply PRWin_ofFlatType_dec_poly.
  }

  rewrite !list_size_length.
  poly_mono list_ofFlatType_dec_poly at 1.
  2: (replace_le (size (enc (Sigma fpr)) + size (enc (init fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia)); reflexivity.
  poly_mono list_ofFlatType_dec_poly at 2.
  2: (replace_le (size (enc (final fpr)) + size (enc (Sigma fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia)); reflexivity.
  replace_le (size (enc (final fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia) at 1.
  replace_le (size (enc (windows fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia) at 1.
  poly_mono PRWin_ofFlatType_dec_poly at 1.
  2: (replace_le (size (enc (windows fpr)) + size (enc (Sigma fpr))) with (size (enc fpr)) by (rewrite FlatPR_enc_size; lia)); reflexivity.
  unfold poly__isValidFlatteningDec, c__isValidFlatteningDecBound. nia.
Qed.

Lemma isValidFlatteningDec_poly : monotonic poly__isValidFlatteningDec inOPoly poly__isValidFlatteningDec.
Proof.
  split; (unfold poly__isValidFlatteningDec; smpl_inO; [apply list_ofFlatType_dec_poly |apply PRWin_ofFlatType_dec_poly ]).
Qed.