Require Import List Arith Bool Lia Eqdep_dec.

From Undecidability.Shared.Libs.DLW.Utils
  Require Import utils_tac utils_list utils_nat finite.

From Undecidability.Shared.Libs.DLW.Vec
  Require Import pos vec.

From Undecidability.Shared.Libs.DLW.Wf
  Require Import wf_finite.

From Undecidability.TRAKHTENBROT
  Require Import notations decidable fol_ops fo_sig fo_terms fo_logic fo_sat
                 membership hfs reln_hfs.

Set Implicit Arguments.


Local Notation ø := vec_nil.

Section Sign_Sig2_encoding.

  Variable (n : nat).

  Notation Σ2 := (Σrel 2).
  Notation Σn := (Σrel n).

  Infix "∈" := Σ2_mem.
  Infix "≈" := Σ2_equiv.
  Infix "⊆" := Σ2_incl.


  Fixpoint Σn_Σ2 (d r : nat) (A : fol_form Σn) : fol_form Σ2 :=
    match A with
      | =>
      | fol_atom _ v => Σ2_is_tuple_in r (vec_map (@Σrel_var _) v)
      | fol_bin b A B => fol_bin b (Σn_Σ2 d r A) (Σn_Σ2 d r B)
      | fol_quant fol_fa A => 0 (S d) Σn_Σ2 (S d) (S r) A
      | fol_quant fol_ex A => 0 (S d) Σn_Σ2 (S d) (S r) A
     end.

  Variable (X : Type) (M2 : fo_model Σ2 X).
  Variable (Y : Type) (Mn : fo_model Σn Y).

  Let mem a b := fom_rels M2 tt (a##b##ø).

  Infix "∈m" := mem (at level 59, no associativity).
  Notation P := (fun v => fom_rels Mn tt v).

  Variable R : Y -> X -> Prop.


  Let HR1 (d r : X) := forall y, exists x, x m d /\ R y x.
  Let HR2 (d r : X) := forall x, x m d -> exists y, R y x.
  Let HR3 (d r : X) := forall v w, (forall p, R (vec_pos v p) (vec_pos w p))
         -> P v <-> mb_is_tuple_in mem r w.

  Notation "⟪ A ⟫" := (fun ψ => fol_sem M2 ψ A).
  Notation "⟪ A ⟫'" := (fun φ => fol_sem Mn φ A) (at level 1, format "⟪ A ⟫'").


  Lemma Σn_Σ2_correct (A : fol_form Σn) d r φ ψ :
            HR1 (ψ d) (ψ r)
         -> HR2 (ψ d) (ψ r)
         -> HR3 (ψ d) (ψ r)
        -> (forall x, In x (fol_vars A) -> R (φ x) (ψ x))
        -> A ⟫' φ <-> Σn_Σ2 d r A ψ.
  Proof.
    revert d r φ ψ.
    induction A as [ | [] v | b A HA B HB | [] A HA ]; intros l r phi psy H1 H2 H3 H.
    + simpl; tauto.
    + simpl; red in H3.
      rewrite H3 with (w := vec_map (fun x => psy (Σrel_var x)) v).
      * apply (fol_quant_sem_ext fol_ex); intros x.
        apply (fol_bin_sem_ext fol_conj); try tauto.
        rewrite Σ2_is_tuple_spec; simpl.
        rewrite !vec_map_map; simpl; tauto.
      * intros p; rewrite !vec_pos_map.
        case_eq (vec_pos v p).
        2: intros [].
        simpl; intros x Hx; rewrite Hx; simpl.
        apply H; simpl; apply in_flat_map.
        exists (vec_pos v p); split.
        - apply in_vec_list, in_vec_pos.
        - simpl; rewrite Hx; simpl; auto.
    + simpl; apply fol_bin_sem_ext; [ apply HA | apply HB ];
        intros; auto; apply H, in_or_app; simpl; auto.
    + simpl; split.
      * intros (x & Hx).
        destruct (H1 x) as (y & G1 & G2).
        exists y; split.
        - rew fot; simpl; auto.
        - revert Hx; apply HA; auto.
          intros [ | n' ]; simpl; auto.
          intros; apply H; simpl; apply in_flat_map; exists (S n'); simpl; auto.
      * intros (y & G1 & G2); revert G1 G2; rew fot; simpl; intros G1 G2.
        destruct (H2 _ G1) as (x & G3).
        exists x; revert G2; apply HA; auto.
        intros [ | n' ]; simpl; auto.
        intros; apply H; simpl; apply in_flat_map; exists (S n'); simpl; auto.
    + simpl; split.
      * intros G1 y; rew fot; simpl; intros G2.
        destruct (H2 _ G2) as (x & G3).
        generalize (G1 x); apply HA; auto.
        intros [ | n' ]; simpl; auto.
        intros; apply H; simpl; apply in_flat_map; exists (S n'); simpl; auto.
      * intros G1 x.
        destruct (H1 x) as (y & G2 & G3).
        generalize (G1 _ G2); apply HA; auto.
        intros [ | n' ]; simpl; auto.
        intros; apply H; simpl; apply in_flat_map; exists (S n'); simpl; auto.
  Qed.

  Variable A : fol_form Σn.


  Let B := fol_subst (fun v => £ (2+v)) A.
  Let d := 0.
  Let r := 1.



  Definition Σn_Σ2_enc :=
                Σ2_extensional
               Σ2_non_empty d
               Σ2_list_in d (fol_vars B)
               Σn_Σ2 d r B.

End Sign_Sig2_encoding.

Section SAT2_SATn.

  Variable n : nat.


  Section nested.

    Variables (A : fol_form (Σrel n))
              (X : Type)
              (M2 : fo_model (Σrel 2) X)
              (M2fin : finite_t X)
              (M2dec : fo_model_dec M2)
              (ψ : nat -> X)
              (HA : fol_sem M2 ψ (Σn_Σ2_enc A)).

    Let mem a b := fom_rels M2 tt (a##b##ø).

    Let mem_dec : forall x y, { mem x y } + { ~ mem x y }.
    Proof. intros x y; apply (@M2dec tt). Qed.

    Let P x := (if mem_dec x (ψ 0) then true else false) = true.

    Let HP0 x : P x <-> mem x (ψ 0).
    Proof.
      unfold P.
      destruct (mem_dec x (ψ 0)); split; try tauto; discriminate.
    Qed.

    Let HP1 : finite_t (sig P).
    Proof.
      apply fin_t_finite_t.
      + intros; apply UIP_dec, bool_dec.
      + apply finite_t_fin_t_dec; auto.
        intro; apply bool_dec.
    Qed.

    Let Mn : fo_model (Σrel n) (sig P).
    Proof.
      exists.
      + intros [].
      + intros [] v.
        simpl in v.
        apply (@mb_is_tuple_in _ mem (ψ 1) n).
        apply (vec_map (@proj1_sig _ _) v).
    Defined.

    Let Mn_dec : fo_model_dec Mn.
    Proof. intros [] v; apply mb_is_tuple_in_dec; auto. Qed.

    Let R (x : sig P) (y : X) := proj1_sig x = y.

    Local Lemma SAT2_to_SATn : exists Y, fo_form_fin_dec_SAT_in A Y.
    Proof.
      exists (sig P).
      destruct HA as (H1 & H2 & H3 & H4).
      rewrite Σ2_non_empty_spec in H2.
      rewrite Σ2_list_in_spec in H3.
      revert H3 H4; set (B := Afun v : nat => in_var (2 + v)); intros H3 H4.
      assert (H5 : forall n, In n (fol_vars B) -> P (ψ n)).
      { intros; apply HP0, H3; auto. }
      destruct H2 as (x0 & H0).
      generalize H0; intros H2.
      apply HP0 in H0.
      set (phi := fun n : nat =>
        match in_dec eq_nat_dec n (fol_vars B) with
          | left H => (exist _ (ψ n) (H5 _ H) : sig P)
          | right _ => (exist _ x0 H0 : sig P)
        end).
      exists Mn, HP1, Mn_dec, (fun n => phi (2+n)).
      unfold B in *; clear B.
      rewrite <- Σn_Σ2_correct with (Mn := Mn) (φ := phi) (R := R) in H4.
      + rewrite fol_sem_subst in H4.
        revert H4; apply fol_sem_ext; intro; rew fot; auto.
      + intros (x & Hx); exists x; unfold R; simpl; split; auto.
        apply HP0 in Hx; auto.
      + intros x Hx; apply HP0 in Hx.
        exists (exist _ x Hx); red; simpl; auto.
      + intros v w Hvw. simpl.
        apply fol_equiv_ext; f_equal.
        apply vec_pos_ext; intros p.
        rewrite vec_pos_map; apply Hvw.
      + intros j Hj; red.
        unfold phi.
        destruct (in_dec eq_nat_dec j (fol_vars Afun v : nat => in_var (2 + v)))
          as [ H | [] ]; auto; simpl.
    Qed.

  End nested.

  Theorem SAT2_SATn A : fo_form_fin_dec_SAT (@Σn_Σ2_enc n A)
                     -> fo_form_fin_dec_SAT A.
  Proof.
    intros (X & M2 & H1 & H2 & psy & H3).
    apply SAT2_to_SATn with X M2 psy; auto.
  Qed.

End SAT2_SATn.

Section SATn_SAT2.

  Variable n : nat.


  Section nested.

    Variables (A : fol_form (Σrel n))
              (X : Type) (Mn : fo_model (Σrel n) X)
              (X_fin : finite_t X)
              (X_discr : discrete X)
              (Mn_dec : fo_model_dec Mn)
              (φ : nat -> X)
              (HA : fol_sem Mn φ A).

    Let R := fom_rels Mn tt.

    Local Lemma SATn_to_SAT2 : exists Y, fo_form_fin_dec_SAT_in (@Σn_Σ2_enc n A) Y.
    Proof.
      destruct reln_hfs with (R := fom_rels Mn tt)
        as (Y & H1 & H2 & mem & H3 & l & r & i & s & H4 & H5 & H6 & H7 & H8 & H9 & H10); auto.
      exists Y, (bin_rel_Σ2 mem), H1, (bin_rel_Σ2_dec _ H3),
        (fun n => match n with
           | 0 => l
           | 1 => r
           | S (S n) => i (φ n)
         end).
      unfold Σn_Σ2_enc; msplit 3; auto.
      + exists (i (φ 0)); simpl; rew fot; simpl; auto.
      + apply Σ2_list_in_spec.
        intros n'; simpl.
        rewrite fol_vars_map, in_map_iff.
        intros (m & <- & ?); auto.
      + rewrite <- Σn_Σ2_correct with (Mn := Mn) (R := fun x y => y = i x)
            (φ := fun n => match n with 0 => φ 0 | 1 => φ 1 | S (S n) => φ n end); auto.
        * rewrite fol_sem_subst.
          revert HA; apply fol_sem_ext.
          intros; simpl; rew fot; auto.
        * intros x; exists (i x); split; auto; apply H6.
        * intros v w E; rewrite H9.
          apply fol_equiv_ext; f_equal.
          apply vec_pos_ext; intro; rew vec.
        * intros n'; rewrite fol_vars_map, in_map_iff.
          intros (m & <- & Hm); simpl; auto.
    Qed.

  End nested.

  Theorem SATn_SAT2 A : fo_form_fin_discr_dec_SAT A
                     -> fo_form_fin_dec_SAT (@Σn_Σ2_enc n A).
  Proof.
    intros (X & H1 & Mn & H2 & H4 & psy & H5).
    apply SATn_to_SAT2 with X Mn psy; auto.
  Qed.

End SATn_SAT2.