(**************************************************************)
(*   Copyright Dominique Larchey-Wendling *                 *)
(*                                                            *)
(*                             * Affiliation LORIA -- CNRS  *)
(**************************************************************)
(*      This file is distributed under the terms of the       *)
(*         CeCILL v2 FREE SOFTWARE LICENSE AGREEMENT          *)
(**************************************************************)

Require Import List Arith Bool Lia Eqdep_dec Max.

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.TRAKHTENBROT
  Require Import notations utils decidable fol_ops fo_sig fo_terms fo_logic fo_sat.

Import fol_notations.

Set Implicit Arguments.

(* * Mapping a formula into a finitary signature *)

Local Notation ø := vec_nil.

Section discrete_to_finite_fix.

  Variables (Σ : fo_signature)
            (HΣ1 : discrete (syms Σ))
            (HΣ2 : discrete (rels Σ))

            (ls : list (syms Σ))
            (lr : list (rels Σ)).

  Let Fn s := (if in_dec HΣ1 s ls then true else false) = true.
  Let Re r := (if in_dec HΣ2 r lr then true else false) = true.

  Let HFn s : Fn s In s ls.
  Proof.
    unfold Fn; destruct (in_dec HΣ1 s ls); split; try tauto; discriminate.
  Qed.


  Let HRe r : Re r In r lr.
  Proof.
    unfold Re; destruct (in_dec HΣ2 r lr); split; try tauto; discriminate.
  Qed.


  Definition Σ_fin : fo_signature.
  Proof.
    exists (sig Fn) (sig Re).
    + exact ( s ar_syms _ (proj1_sig s)).
    + exact ( r ar_rels _ (proj1_sig r)).
  Defined.


  Notation Σ' := Σ_fin.

  Fact Σ_fin_syms : s : syms Σ', In (proj1_sig s) ls.
  Proof. intros (s & Hs); apply HFn, Hs. Qed.

  Fact Σ_fin_rels : r : rels Σ', In (proj1_sig r) lr.
  Proof. intros (r & Hr); apply HRe, Hr. Qed.

  Fact Σ_fin_discrete_syms : discrete (syms Σ').
  Proof.
    intros ( & ) ( & ).
    destruct (HΣ1 ) as [ | C ].
    + left; subst; f_equal; apply eq_bool_pirr.
    + right; contradict C; injection C; auto.
  Qed.


  Fact Σ_fin_discrete_rels : discrete (rels Σ').
  Proof.
    intros ( & ) ( & ).
    destruct (HΣ2 ) as [ | C ].
    + left; subst; f_equal; apply eq_bool_pirr.
    + right; contradict C; injection C; auto.
  Qed.


  Fact Σ_fin_finite_syms : finite_t (syms Σ').
  Proof.
    set (f s Hs := exist Fn s (proj2 (HFn s) Hs)).
    exists (list_in_map ls f).
    intros (s & Hs).
    replace (exist Fn s Hs) with (f s (proj1 (HFn s) Hs)).
    + apply In_list_in_map.
    + unfold f; f_equal; apply eq_bool_pirr.
  Qed.


  Fact Σ_fin_finite_rels : finite_t (rels Σ').
  Proof.
    set (f r Hr := exist Re r (proj2 (HRe r) Hr)).
    exists (list_in_map lr f).
    intros (r & Hr).
    replace (exist Re r Hr) with (f r (proj1 (HRe r) Hr)).
    + apply In_list_in_map.
    + unfold f; f_equal; apply eq_bool_pirr.
  Qed.


  Fixpoint fo_term_fin_rev (t : fo_term (ar_syms Σ')) : fo_term (ar_syms Σ) :=
    match t with
      | in_var n in_var n
      | in_fot s v in_fot (proj1_sig s) (vec_map fo_term_fin_rev v)
    end.

  Fixpoint Σ_finite_rev (A : fol_form Σ') : fol_form Σ :=
    match A with
      |
      | fol_atom r v fol_atom (proj1_sig r) (vec_map fo_term_fin_rev v)
      | fol_bin b A B fol_bin b (Σ_finite_rev A) (Σ_finite_rev B)
      | fol_quant q A fol_quant q (Σ_finite_rev A)
    end.

  Section Σ_finite_rev_sem.

    Variable (X : Type) (M' : fo_model Σ' X)
             ( : finite_t X)
             ( : fo_model_dec M') ( : X).

    Local Definition Σ_finite_rev_model1 : fo_model Σ X.
    Proof.
      split.
      + intros s.
        destruct (in_dec HΣ1 s ls) as [ H | H ].
        * apply HFn in H.
          apply (fom_syms M' (exist _ s H)).
        * intro; exact .
      + intros r.
        destruct (in_dec HΣ2 r lr) as [ H | H ].
        * apply HRe in H.
          apply (fom_rels M' (exist _ r H)).
        * intro; exact True.
    Defined.


    Notation M := Σ_finite_rev_model1.

    Local Fact fo_term_fin_rev_sound t phi : fo_term_sem M' t = fo_term_sem M (fo_term_fin_rev t).
    Proof.
      induction t as [ n | (s & Hs) v IHv ]; simpl; auto.
      destruct (in_dec HΣ1 s ls) as [ H | [] ].
      2: { apply HFn; auto. }
      replace (match HFn s with | conj _ x x end H) with Hs by apply eq_bool_pirr.
      f_equal; apply vec_pos_ext; intros p; rewrite !vec_pos_map; auto.
    Qed.


    Hint Resolve fo_term_fin_rev_sound : core.

    Local Fact Σ_finite_rev_sound A phi : fol_sem M' A fol_sem M (Σ_finite_rev A).
    Proof.
      revert ; induction A as [ | (r & Hr) v | b A HA B HB | q A HA ]; intros .
      + simpl; tauto.
      + simpl.
        destruct (in_dec HΣ2 r lr) as [ | [] ].
        2: { apply HRe; auto. }
        replace (match HRe r with | conj _ x x end i) with Hr.
        2: apply eq_bool_pirr.
        apply fol_equiv_ext; f_equal.
        rewrite vec_map_map; apply vec_pos_ext; intros p.
        rewrite !vec_pos_map; auto.
      + simpl; apply fol_bin_sem_ext; auto.
      + simpl; apply fol_quant_sem_ext; intro; auto.
    Qed.


  End Σ_finite_rev_sem.

  Section Σ_finite_rev_sem'.

    Variable (X : Type) (M : fo_model Σ X)
             ( : finite_t X)
             ( : fo_model_dec M).

    Local Definition Σ_finite_rev_model2 : fo_model Σ' X.
    Proof.
      split.
      + intros (s & ?); apply (fom_syms M s).
      + intros (r & ?); apply (fom_rels M r).
    Defined.


    Notation M' := Σ_finite_rev_model2.

    Local Fact fo_term_fin_rev_complete t phi : fo_term_sem M' t = fo_term_sem M (fo_term_fin_rev t).
    Proof.
      induction t as [ n | (s & Hs) v IHv ]; simpl; auto.
      rewrite vec_map_map; f_equal; apply vec_pos_ext.
      intros p; rewrite !vec_pos_map; auto.
    Qed.


    Hint Resolve fo_term_fin_rev_complete : core.

    Local Fact Σ_finite_rev_complete A phi : fol_sem M' A fol_sem M (Σ_finite_rev A).
    Proof.
      revert ; induction A as [ | (r & Hr) v | b A HA B HB | q A HA ]; intros .
      + simpl; tauto.
      + simpl.
        apply fol_equiv_ext; f_equal.
        rewrite vec_map_map; apply vec_pos_ext; intros p.
        rewrite !vec_pos_map; auto.
      + simpl; apply fol_bin_sem_ext; auto.
      + simpl; apply fol_quant_sem_ext; intro; auto.
    Qed.


  End Σ_finite_rev_sem'.

  Fact Σ_finite_rev_correct A : fo_form_fin_dec_SAT A fo_form_fin_dec_SAT (Σ_finite_rev A).
  Proof.
    split.
    + intros (X & M' & & & & ).
      exists X, (Σ_finite_rev_model1 M' ( 0)), .
      exists.
      { intros r v; simpl.
        destruct (in_dec HΣ2 r lr).
        + apply .
        + tauto. }
      exists .
      apply Σ_finite_rev_sound; auto.
    + intros (X & M & & & & ).
      exists X, (Σ_finite_rev_model2 M), .
      exists.
      { intros (r & Hr) v; simpl; apply . }
      exists ; apply Σ_finite_rev_complete; auto.
  Qed.


  Definition fo_term_finite (t : fo_term (ar_syms Σ)) :
              incl (fo_term_syms t) ls { t' | t = fo_term_fin_rev t' }.
  Proof.
    induction t as [ n | s v IHv ]; intros H.
    + exists (in_var n); simpl; auto.
    + assert (Hv : p, { t | vec_pos v p = fo_term_fin_rev t}).
      { intros p; apply IHv; intros x Hx; apply H; rew fot.
        right; apply in_flat_map; exists (vec_pos v p); split; auto.
        apply in_vec_list, in_vec_pos. }
      apply vec_reif_t in Hv; destruct Hv as (w & Hw).
      assert (Hs : Fn s).
      { apply HFn, H; rew fot; simpl; left; auto. }
      exists (in_fot (exist _ s Hs) w); simpl; f_equal.
      apply vec_pos_ext; intros p.
      rewrite vec_pos_map; auto.
  Qed.


  Local Fact Σ_finite_full (A : fol_form Σ) : incl (fol_syms A) ls
                                     incl (fol_rels A) lr
                                     { B | A = Σ_finite_rev B }.
  Proof.
    induction A as [ | r v | b A HA B HB | q A HA ]; intros .
    + exists ; simpl; auto.
    + assert (Hr : Re r).
      { apply HRe, ; simpl; auto. }
      assert (Hv : p, { t | vec_pos v p = fo_term_fin_rev t }).
      { intro p; apply fo_term_finite; intros s Hs; apply .
        simpl; apply in_flat_map; exists (vec_pos v p); split; auto.
        apply in_vec_list, in_vec_pos. }
      apply vec_reif_t in Hv; destruct Hv as (w & Hw).
      exists (@fol_atom Σ' (exist _ r Hr) w); simpl; f_equal.
      apply vec_pos_ext; intros p; rewrite vec_pos_map; auto.
    + destruct HA as (A' & HA').
      { intros ? ?; apply , in_app_iff; auto. }
      { intros ? ?; apply , in_app_iff; auto. }
      destruct HB as (B' & HB').
      { intros ? ?; apply , in_app_iff; auto. }
      { intros ? ?; apply , in_app_iff; auto. }
      exists (fol_bin b A' B'); simpl; f_equal; auto.
    + destruct HA as (A' & HA').
      { intros ? ?; apply ; auto. }
      { intros ? ?; apply ; auto. }
      exists (fol_quant q A'); simpl; f_equal; auto.
  Qed.


End discrete_to_finite_fix.

Section discrete_to_finite.

  Variables (Σ : fo_signature)
            (HΣ1 : discrete (syms Σ))
            (HΣ2 : discrete (rels Σ)).

  Hint Resolve incl_refl : core.

  Local Definition Σ_finite (A : fol_form Σ) :
              { Σ' : fo_signature &
              { _ : finite_t (syms Σ') &
              { _ : finite_t (rels Σ') &
              { _ : discrete (syms Σ') &
              { _ : discrete (rels Σ') &
              { is : syms Σ' syms Σ &
              { _ : s s', is s = is s' s = s' &
              { _ : s, ar_syms _ (is s) = ar_syms _ s &
              { _ : s, In (is s) (fol_syms A) &
              { ir : rels Σ' rels Σ &
              { _ : r r', ir r = ir r' r = r' &
              { _ : r, ar_rels _ (ir r) = ar_rels _ r &
              { _ : r, In (ir r) (fol_rels A) &
              { B : fol_form Σ'
              | fo_form_fin_dec_SAT A fo_form_fin_dec_SAT B } } } } } } } } } } } } } }.
  Proof.
    exists (Σ_fin Σ HΣ1 HΣ2 (fol_syms A) (fol_rels A)).
    exists. { apply Σ_fin_finite_syms. }
    exists. { apply Σ_fin_finite_rels. }
    exists. { apply Σ_fin_discrete_syms. }
    exists. { apply Σ_fin_discrete_rels. }
    exists (@proj1_sig _ _).
    exists. { intros (? & ?) (? & ?); simpl; intros ; f_equal; apply eq_bool_pirr. }
    exists. { intros (? & ?); reflexivity. }
    exists. { apply Σ_fin_syms. }
    exists (@proj1_sig _ _).
    exists. { intros (? & ?) (? & ?); simpl; intros ; f_equal; apply eq_bool_pirr. }
    exists. { intros (? & ?); reflexivity. }
    exists. { apply Σ_fin_rels. }
    destruct (@Σ_finite_full Σ HΣ1 HΣ2 (fol_syms A) (fol_rels A) A) as (B & HB); auto.
    exists B.
    revert B HB.
    generalize (fol_syms A) (fol_rels A); intros ls lr B .
    symmetry; apply Σ_finite_rev_correct.
  Qed.


End discrete_to_finite.

Definition Σpos (Σ : fo_signature) n m (js : pos n syms Σ) (jr : pos m rels Σ) : fo_signature.
Proof.
  exists (pos n) (pos m).
  + exact ( p ar_syms _ (js p)).
  + exact ( p ar_rels _ (jr p)).
Defined.


Section discr_finite_to_pos.

  (* Strangely this does not lead to transport hell ... I should
      probably rework fol_transport_hell.v ... maybe there is a better way
      like in here *)


  Variables (Σ : fo_signature)
            ( : discrete (syms Σ)) ( : finite_t (syms Σ))
            ( : discrete (rels Σ)) ( : finite_t (rels Σ)).

  Let := finite_t_discrete_bij_t_pos .

  Let n := .
  Let is : syms Σ pos n := ( ).
  Let js : pos n syms Σ := proj1_sig ( ( )).

  Let Hijs p : is (js p) = p.
  Proof. apply (proj2_sig ( ( ))). Qed.

  Let Hjis s : js (is s) = s.
  Proof. apply (proj2_sig ( ( ))). Qed.

  Let := finite_t_discrete_bij_t_pos .

  Let m := .
  Let ir : rels Σ pos m := ( ).
  Let jr : pos m rels Σ := proj1_sig ( ( )).

  Let Hijr p : ir (jr p) = p.
  Proof. apply (proj2_sig ( ( ))). Qed.

  Let Hjir r : jr (ir r) = r.
  Proof. apply (proj2_sig ( ( ))). Qed.

  Notation Σ' := (Σpos Σ js jr).

  Local Fixpoint convert_t (t : fo_term (ar_syms Σ)) : fo_term (ar_syms Σ').
  Proof.
    refine (match t with
      | in_var i in_var i
      | in_fot s v @in_fot _ (ar_syms Σ') (is s) (vec_map convert_t (cast v _))
    end).
    simpl; rewrite Hjis; trivial.
  Defined.


  Local Fixpoint convert (A : fol_form Σ) : fol_form Σ'.
  Proof.
    refine (match A with
      |
      | fol_atom r v @fol_atom Σ' (ir r) (vec_map convert_t (cast v _))
      | fol_bin b A B fol_bin b (convert A) (convert B)
      | fol_quant q A fol_quant q (convert A)
    end).
    simpl; rewrite Hjir; trivial.
  Defined.


  Section soundness.

    Variable (X : Type) (M : fo_model Σ X).

    Let M' : fo_model Σ' X.
    Proof.
      split.
      + apply ( s fom_syms M (js s)).
      + apply ( r fom_rels M (jr r)).
    Defined.


    Local Fact convert_t_sound t φ : fo_term_sem M φ t = fo_term_sem M' φ (convert_t t).
    Proof.
      induction t as [ i | s v IHv ]; simpl; auto.
      rewrite (Hjis s); simpl; f_equal.
      apply vec_pos_ext; intro; rew vec.
      rewrite !vec_pos_map; auto.
    Qed.


    Local Fact convert_sound A φ : fol_sem M φ A fol_sem M' φ (convert A).
    Proof.
      revert φ; induction A as [ | r v | b A HA B HB | q A HA ]; intros φ.
      + simpl; tauto.
      + simpl; rewrite Hjir; simpl; rewrite vec_map_map.
        apply fol_equiv_ext; f_equal.
        apply vec_pos_ext; intro; rew vec.
        apply convert_t_sound.
      + apply fol_bin_sem_ext; auto.
      + apply fol_quant_sem_ext; intro; auto.
    Qed.


    Hypothesis (Xfin : finite_t X)
               (Mdec : fo_model_dec M)
               (φ : X)
               (A : fol_form Σ)
               (HA : fol_sem M φ A).

    Local Fact convert_soundness : fo_form_fin_dec_SAT_in (convert A) X.
    Proof.
      exists M', Xfin.
      exists. { intros ? ?; apply Mdec. }
      exists φ.
      revert HA; apply convert_sound.
    Qed.


  End soundness.

  Section completeness.

    Variable (X : Type) (M' : fo_model Σ' X).

    Let M : fo_model Σ X.
    Proof.
      split.
      + intros s v.
        apply (fom_syms M' (is s)); simpl.
        rewrite Hjis; trivial.
      + intros r v.
        apply (fom_rels M' (ir r)); simpl.
        rewrite Hjir; trivial.
    Defined.


    Local Fact convert_t_complete t φ : fo_term_sem M φ t = fo_term_sem M' φ (convert_t t).
    Proof.
      induction t as [ i | s v IHv ]; simpl; auto.
      f_equal; unfold eq_rect_r.
      generalize (Hjis s); intros ; simpl.
      apply vec_pos_ext; intro; rew vec.
      rewrite !vec_pos_map; auto.
    Qed.


    Local Fact convert_complete A φ : fol_sem M φ A fol_sem M' φ (convert A).
    Proof.
      revert φ; induction A as [ | r v | b A HA B HB | q A HA ]; intros φ.
      + simpl; tauto.
      + simpl; apply fol_equiv_ext; f_equal; unfold eq_rect_r.
        generalize (Hjir r); intros ; simpl.
        apply vec_pos_ext; intro; rew vec.
        rewrite vec_pos_map; apply convert_t_complete.
      + apply fol_bin_sem_ext; auto.
      + apply fol_quant_sem_ext; intro; auto.
    Qed.


    Hypothesis (Xfin : finite_t X)
               ( : fo_model_dec M')
               (φ : X)
               (A : fol_form Σ)
               (HA : fol_sem M' φ (convert A)).

    Local Fact convert_completeness : fo_form_fin_dec_SAT_in A X.
    Proof.
      exists M, Xfin.
      exists. { intros ? ?; apply . }
      exists φ.
      revert HA; apply convert_complete.
    Qed.


  End completeness.

  Local Definition Σ_finite_to_pos (A : fol_form Σ) :
              { n : &
              { m : &
              { is : pos n syms Σ &
              { ir : pos m rels Σ &
              { _ : s s', is s = is s' s = s' &
              { _ : s, ar_syms _ (is s) = ar_syms (Σpos Σ is ir) s &
              { _ : r r', ir r = ir r' r = r' &
              { _ : r, ar_rels _ (ir r) = ar_rels (Σpos Σ is ir) r &
              { B : fol_form (Σpos Σ is ir)
              | X, fo_form_fin_dec_SAT_in A X
                       fo_form_fin_dec_SAT_in B X } } } } } } } } }.
  Proof.
    exists n, m, js, jr.
    exists. { intros s s' E; rewrite (Hijs s), E, Hijs; auto. }
    exists. { simpl; auto. }
    exists. { intros r r' E; rewrite (Hijr r), E, Hijr; auto. }
    exists. { simpl; auto. }
    exists (convert A); intros X; split.
    + intros (M & & & & ).
      apply convert_soundness with M ; auto.
    + intros (M & & & & ).
      apply convert_completeness with M ; auto.
  Qed.


End discr_finite_to_pos.

Section combine_the_two.

  Variables (Σ : fo_signature)
            (HΣ1 : discrete (syms Σ))
            (HΣ2 : discrete (rels Σ)).

  Local Definition Σ_discrete_to_pos' (A : fol_form Σ) :
              { n : &
              { m : &
              { is : pos n syms Σ &
              { ir : pos m rels Σ &
              { _ : s s', is s = is s' s = s' &
              { _ : s, ar_syms _ (is s) = ar_syms (Σpos Σ is ir) s &
              { _ : s, In (is s) (fol_syms A) &
              { _ : r r', ir r = ir r' r = r' &
              { _ : r, ar_rels _ (ir r) = ar_rels (Σpos Σ is ir) r &
              { _ : r, In (ir r) (fol_rels A) &
              { B : fol_form (Σpos Σ is ir)
              | fo_form_fin_dec_SAT A
             fo_form_fin_dec_SAT B } } } } } } } } } } }.
  Proof.
    destruct (Σ_finite_full HΣ1 HΣ2 A (incl_refl _) (incl_refl _)) as (B & HB).
    destruct Σ_finite_to_pos with (A := B)
      as (n & m & i & j & & & & & C & HC).
    + apply Σ_fin_discrete_syms.
    + apply Σ_fin_finite_syms.
    + apply Σ_fin_discrete_rels.
    + apply Σ_fin_finite_rels.
    + exists n, m, ( p proj1_sig (i p)), ( p proj1_sig (j p)).
      exists.
      { intros s s' E; apply ; revert E; generalize (i s) (i s').
        intros (? & ?) (? & ?); simpl; intros ; f_equal; apply eq_bool_pirr. }
      exists. { intros; simpl; auto. }
      exists. { intro; apply Σ_fin_syms. }
      exists.
      { intros r r' E; apply ; revert E; generalize (j r) (j r').
        intros (? & ?) (? & ?); simpl; intros ; f_equal; apply eq_bool_pirr. }
      exists. { intros; simpl; auto. }
      exists. { intro; apply Σ_fin_rels. }
      exists C.
      transitivity (fo_form_fin_dec_SAT B).
      * clear C HC; revert B HB.
        generalize (fol_syms A) (fol_rels A); intros ls lr B .
        symmetry; apply Σ_finite_rev_correct.
      * apply exists_equiv; auto.
  Qed.


  Definition Sig_discrete_to_pos (A : fol_form Σ) :
              { n : &
              { m : &
              { i : pos n syms Σ &
              { j : pos m rels Σ &
              { B : fol_form (Σpos Σ i j)
              | fo_form_fin_dec_SAT A
             fo_form_fin_dec_SAT B } } } } }.
  Proof.
    destruct (Σ_discrete_to_pos' A) as (n & m & i & j & _ & _ & _ & _ & _ & _ & B & HB).
    exists n, m, i, j, B; auto.
  Qed.


End combine_the_two.