From Coq Require Import Vector List.
From Undecidability.Shared.Libs.PSL Require Import FinTypes Vectors.
From Undecidability.L Require Import Datatypes.Lists Tactics.LTactics Util.L_facts.
From Undecidability.TM Require Import TM_facts ProgrammingTools .

Import ListNotations VectorNotations.

From Undecidability.TM.L.CompilerBoolFuns Require Import Compiler_spec.

Lemma encBoolsTM_inj {Σ} (sym_s sym_b : Σ) n1 n2 :
  sym_s sym_b encBoolsTM sym_s sym_b = encBoolsTM sym_s sym_b = .
Proof.
  intros Hdiff.
  induction in |- *.
  - destruct ; now inversion 1.
  - destruct ; inversion 1.
    destruct a, b.
    + f_equal. eapply . unfold encBoolsTM. congruence.
    + now exfalso.
    + now exfalso.
    + f_equal. eapply . unfold encBoolsTM. congruence.
Qed.


Lemma encBoolsL_inj l1 l2 :
  encBoolsL = encBoolsL = .
Proof.
  induction in |- *; intros H.
  - destruct ; cbn in *; congruence.
  - destruct ; cbn in *; try congruence.
    inversion H. f_equal; eauto.
    destruct a, b; now inversion .
Qed.


Lemma L_bool_computable_function {k} R :
  @L_bool_computable k R functional R.
Proof.
  intros [s Hs] v .
  eapply Hs in . eapply Hs in .
  rewrite eval_iff in , .
  destruct as [ ], as [ ].
  eapply encBoolsL_inj, L_facts.unique_normal_forms; eauto.
  now rewrite , .
Qed.


From Undecidability.TM Require Import Hoare.

Definition TM_bool_computable_hoare_in {k n Σ} s b (v: Vector.t (list bool) k): SpecV Σ (1+k+n)
  := ([Custom (eq niltape)] Vector.map ( bs Custom (eq (encBoolsTM s b bs))) v) Vector.const (Custom (eq niltape)) _.

Lemma TM_bool_computable_hoare_in_spec {k n Σ} (s b:_ + Σ) (v: Vector.t (list bool) k):
  ([niltape] Vector.map (encBoolsTM s b) v) const niltape n
    ≃≃ ([]%list, TM_bool_computable_hoare_in s b v).
Proof.
  eapply tspecI;cbn;[easy|]; unfold TM_bool_computable_hoare_in;intros i;
  destruct_fin i;cbn;repeat (rewrite Vector_nth_L + rewrite Vector_nth_R + rewrite nth_map' + rewrite const_nth);cbn; reflexivity.
Qed.


Lemma nth_error_to_list {X n} (v : Vector.t X n) i k :
  k = (proj1_sig (Fin.to_nat i))
  nth_error (Vector.to_list v) k = Some (Vector.nth v i).
Proof.
  intros . induction v; cbn.
  - inversion i.
  - dependent destruct i; cbn.
    + reflexivity.
    + destruct (Fin.to_nat p) as (i, P) eqn:E. cbn.
      fold (to_list v).
      specialize (IHv (Fin.of_nat_lt P)). cbn in *.
      rewrite Fin.to_nat_of_nat in IHv. cbn in IHv.
      now rewrite (Fin.of_nat_to_nat_inv p), E.
Qed.


Definition TM_bool_computable_hoare_out {k n Σ} s b (bs :list bool): SpecV Σ (1+k+n)
  := ([Custom (eq (encBoolsTM s b bs)) ] Vector.const (Custom ( _ True)) _)++ Vector.const (Custom ( _ True)) _.

Lemma TM_bool_computable_hoare_out_spec {k n Σ} (s b:_ + Σ) bs t':
  t' ≃≃ ([]%list, TM_bool_computable_hoare_out (k:=k) (n:=n)s b bs)
   Vector.hd t' = encBoolsTM s b bs.
Proof.
  intros ([]&)%tspecE. specialize ( ()).
  unfold TM_bool_computable_hoare_out in .
  cbn in . setoid_rewrite . now apply (Vector.caseS' t').
Qed.


Definition TM_bool_computable_hoare {k} (R : Vector.t (list bool) k (list bool) Prop) :=
   n : , Σ : finType, s b , s b
     M : pTM (Σ^+) unit (1 + k + n),
     v,
      Triple ≃≃([],TM_bool_computable_hoare_in s b v) M ( y t res, t ≃≃ ([R v res]%list,TM_bool_computable_hoare_out s b res))
       res, R v res
           k__steps, TripleT ≃≃([],TM_bool_computable_hoare_in s b v) k__steps M ( y ≃≃([]%list,TM_bool_computable_hoare_out s b res)).

Lemma TM_bool_computable_hoare_spec k R :
  functional R
  @TM_bool_computable_hoare k R @TM_bool_computable k R.
Proof.
  intros Hfun (n & Σ & s & b & Hdiff & [M lab] & H).
  eexists n, _, s, b. split. exact Hdiff. exists M.
  intros v. specialize (H v) as [ ]. split.
  - intros m. split.
    + intros HR.
      specialize as [k__steps %TripleT_TerminatesIn]. eassumption.
      cbn in .
      destruct ( (([niltape] Vector.map (encBoolsTM s b) v) Vector.const niltape n) k__steps) as [[q' t'] Hconf].
      * split. 2:easy. now apply TM_bool_computable_hoare_in_spec.
      * exists q', t'. split. eapply TM_eval_iff. eexists. eapply Hconf.
        eapply in Hconf as (m' & & %TM_bool_computable_hoare_out_spec).
        now apply TM_bool_computable_hoare_in_spec.
        pose proof (Hfun _ _ _ HR ) as .
        easy.
    + intros (q' & t' & [steps Hter] % TM_eval_iff & Hout).
      eapply in Hter as (m' & & %TM_bool_computable_hoare_out_spec).
      now apply TM_bool_computable_hoare_in_spec.
      cbn -[to_list] in *.
      enough (m = m') by now subst.
      eapply encBoolsTM_inj; eauto. congruence.
  - intros q t [steps Hter] % TM_eval_iff.
    eapply in Hter as (m & & %TM_bool_computable_hoare_out_spec).
    now apply TM_bool_computable_hoare_in_spec. eauto.
Qed.


Definition TM_bool_computable_hoare_in' {k n Σ} s b (v: Vector.t (list bool) k): SpecV Σ (1+n+k)
  := Custom (eq niltape) :: Vector.const (Custom (eq niltape)) _ Vector.map ( bs Custom (eq (encBoolsTM s b bs))) v.

Definition TM_bool_computable_hoare_out' {n Σ} s b (bs :list bool): SpecV Σ (1+n)
  := Custom (eq (encBoolsTM s b bs)) :: Vector.const (Custom ( _ True)) _.

Definition TM_bool_computable_hoare' {k} (R : Vector.t (list bool) k (list bool) Prop) :=
   n : , Σ : finType, s b , s b
     M : pTM (Σ^+) unit (1 + n + k),
     v,
      Triple ≃≃([],TM_bool_computable_hoare_in' s b v) M ( y t res, t ≃≃ ([R v res]%list,TM_bool_computable_hoare_out' s b res))
       res, R v res
           k__steps, TripleT ≃≃([],TM_bool_computable_hoare_in' s b v) k__steps M ( y ≃≃([]%list,TM_bool_computable_hoare_out' s b res)).

Local Definition tapeOrderSwap n k:=
  ( ::: tabulate (n := n) ( x Fin.R (1 + k) x) (tabulate (n := k) ( x Fin.L n (Fin.R 1 x)))).

Local Lemma tapeOrderSwap_dupfree k n : VectorDupfree.dupfree (tapeOrderSwap n k).
Proof.
  unfold tapeOrderSwap.
  econstructor. intros [ [i Hi % (f_equal ( x proj1_sig (Fin.to_nat x)))] % in_tabulate | [i Hi % (f_equal ( x proj1_sig (Fin.to_nat x)))] % in_tabulate ] % Vector_in_app.
  3: eapply Vector_dupfree_app; repeat split.
  + rewrite !Fin.R_sanity in Hi. cbn in Hi. .
  + rewrite !Fin.L_sanity, !Fin.R_sanity in Hi. destruct (Fin.to_nat i); cbn in *; .
  + eapply dupfree_tabulate_injective. eapply Fin_R_fillive.
  + eapply dupfree_tabulate_injective. intros. eapply Fin_R_fillive. eapply Fin_L_fillive. eassumption.
  + intros ? (? & ?) % in_tabulate (? & ?) % in_tabulate. subst.
    eapply (f_equal ( H proj1_sig (Fin.to_nat H))) in . rewrite Fin.R_sanity, !Fin.L_sanity in .
    destruct Fin.to_nat, Fin.to_nat. cbn in *. .
Qed.


Lemma TM_bool_computable_hoare_in'_spec {k n Σ} s b (v: Vector.t (list bool) k):
  TM_bool_computable_hoare_in' (Σ:=Σ) s b v = Downlift (TM_bool_computable_hoare_in s b v) (tapeOrderSwap n k).
Proof.
  unfold TM_bool_computable_hoare_in,TM_bool_computable_hoare_in'.
  eapply eq_nth_iff';intros i.
  destruct_fin i;cbn.
  all:repeat (rewrite Vector_nth_L + rewrite Vector_nth_R + rewrite nth_map' + rewrite nth_tabulate + rewrite const_nth + cbn);cbn.
  all:reflexivity.
Qed.


Lemma helper n m' m f:
  injective f
    lookup_index_vector (n:=n)
  (tabulate ( i : Fin.t m' f i))
  (f m) = Some m.
Proof.
  revert n m f. induction m';cbn. easy.
  intros n m f Hinj. specialize (Fin.eqb_eq _ (f m) (f )) as H'.
  destruct Fin.eqb.
  -destruct H' as [H' _]. specialize (H' eq_refl). apply Hinj in H' as . easy.
  -destruct H' as [_ H']. destruct (fin_destruct_S m) as [[i' ]| ]. 2:now discriminate H'.
  specialize IHm' with (f:= m f (Fin.FS m) ). cbn in IHm'. rewrite IHm'. easy. intros ? ? ?%Hinj. now apply Fin.FS_inj.
Qed.


Lemma TM_bool_computable_hoare_out'_spec {k n Σ} s b bs w:
  TM_bool_computable_hoare_out (Σ:=Σ) s b bs = Frame w (tapeOrderSwap n k) (TM_bool_computable_hoare_out' s b bs).
Proof.
  unfold TM_bool_computable_hoare_out,TM_bool_computable_hoare_out'.
  eapply eq_nth_iff';intros i. unfold Frame,fill.
  rewrite nth_tabulate.
  destruct_fin i; cbn -[lookup_index_vector].
  all:repeat (rewrite Vector_nth_L + rewrite Vector_nth_R + rewrite nth_map' + rewrite nth_tabulate + rewrite const_nth).
  - erewrite lookup_index_vector_Some with (j := ); try reflexivity. eapply tapeOrderSwap_dupfree.
  - erewrite lookup_index_vector_Some with (j := Fin.FS (Fin.R n )).
    + cbn. now rewrite const_nth.
    + eapply tapeOrderSwap_dupfree.
    + cbn. now rewrite Vector_nth_R, nth_tabulate.
  - erewrite lookup_index_vector_Some with (j := Fin.FS (Fin.L k )).
    + cbn. now rewrite const_nth.
    + eapply tapeOrderSwap_dupfree.
    + cbn. now rewrite Vector_nth_L, nth_tabulate.
Qed.


Local Set Keyed Unification.

Lemma TM_bool_computable_hoare'_spec k R :
  functional R
  @TM_bool_computable_hoare' k R TM_bool_computable R.
Proof.
  intros Hfun (n & Σ & s & b & Hdiff & M & H).
  eapply TM_bool_computable_hoare_spec. eassumption.
  exists n, Σ, s, b. split. exact Hdiff.
  eexists (LiftTapes M (tapeOrderSwap n k)).
  intros v. specialize (H v) as [ ].
  rewrite TM_bool_computable_hoare_in'_spec in ,.
  split.
  - refine (Consequence _ _ _). refine (LiftTapes_Spec_ex _ _). now apply tapeOrderSwap_dupfree.
    exact . reflexivity. intro. eapply EntailsI. intros ? []. eexists.
    erewrite TM_bool_computable_hoare_out'_spec. eassumption.
  - intros. specialize as [x ]. easy. exists x.
    refine (ConsequenceT _ _ _ _). refine (LiftTapes_SpecT _ _). now apply tapeOrderSwap_dupfree.
    exact . reflexivity. cbn . intros. 2:easy. now rewrite TM_bool_computable_hoare_out'_spec.
Qed.


Lemma closed_compiler_helper s n v: closed s
closed
  (Vector.fold_left (n:=n)
     ( (s0 : term) (l_i : list bool) L.app (encBoolsL l_i)) s v).
Proof.
  revert s. induction v. all:cbn. easy.
  intros. eapply IHv. change (encBoolsL h) with (enc h). Lproc.
Qed.