(**************************************************************)
(*   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 (fun s => ar_syms _ (proj1_sig s)).
+ exact (fun r => ar_rels _ (proj1_sig r)).
Defined.

Notation Σ' := Σ_fin.

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

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

Fact Σ_fin_discrete_syms : discrete (syms Σ').
Proof.
intros (s1 & H1) (s2 & H2).
destruct (HΣ1 s1 s2) 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 (r1 & H1) (r2 & H2).
destruct (HΣ2 r1 r2) 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)
(M1' : finite_t X)
(M2' : fo_model_dec M') (x0 : 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 x0.
+ 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' phi t = fo_term_sem M phi (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' phi A <-> fol_sem M phi (Σ_finite_rev A).
Proof.
revert phi; induction A as [ | (r & Hr) v | b A HA B HB | q A HA ]; intros phi.
+ 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)
(M1 : finite_t X)
(M2 : 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' phi t = fo_term_sem M phi (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' phi A <-> fol_sem M phi (Σ_finite_rev A).
Proof.
revert phi; induction A as [ | (r & Hr) v | b A HA B HB | q A HA ]; intros phi.
+ 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' & H1 & H2 & phi & H3).
exists X, (Σ_finite_rev_model1 M' (phi 0)), H1.
exists.
{ intros r v; simpl.
destruct (in_dec HΣ2 r lr).
+ apply H2.
+ tauto. }
exists phi.
apply Σ_finite_rev_sound; auto.
+ intros (X & M & H1 & H2 & phi & H3).
exists X, (Σ_finite_rev_model2 M), H1.
exists.
{ intros (r & Hr) v; simpl; apply H2. }
exists phi; 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 : forall 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 H1 H2.
+ exists ; simpl; auto.
+ assert (Hr : Re r).
{ apply HRe, H2; simpl; auto. }
assert (Hv : forall p, { t | vec_pos v p = fo_term_fin_rev t }).
{ intro p; apply fo_term_finite; intros s Hs; apply H1.
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 H1, in_app_iff; auto. }
{ intros ? ?; apply H2, in_app_iff; auto. }
destruct HB as (B' & HB').
{ intros ? ?; apply H1, in_app_iff; auto. }
{ intros ? ?; apply H2, in_app_iff; auto. }
exists (fol_bin b A' B'); simpl; f_equal; auto.
+ destruct HA as (A' & HA').
{ intros ? ?; apply H1; auto. }
{ intros ? ?; apply H2; 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 Σ &
{ _ : forall s s', is s = is s' -> s = s' &
{ _ : forall s, ar_syms _ (is s) = ar_syms _ s &
{ _ : forall s, In (is s) (fol_syms A) &
{ ir : rels Σ' -> rels Σ &
{ _ : forall r r', ir r = ir r' -> r = r' &
{ _ : forall r, ar_rels _ (ir r) = ar_rels _ r &
{ _ : forall 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 (fun p => ar_syms _ (js p)).
+ exact (fun 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)
(H1 : discrete (syms Σ)) (H2 : finite_t (syms Σ))
(H3 : discrete (rels Σ)) (H4 : finite_t (rels Σ)).

Let H5 := finite_t_discrete_bij_t_pos H2 H1.

Let n := projT1 H5.
Let is : syms Σ -> pos n := projT1 (projT2 H5).
Let js : pos n -> syms Σ := proj1_sig (projT2 (projT2 H5)).

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

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

Let H6 := finite_t_discrete_bij_t_pos H4 H3.

Let m := projT1 H6.
Let ir : rels Σ -> pos m := projT1 (projT2 H6).
Let jr : pos m -> rels Σ := proj1_sig (projT2 (projT2 H6)).

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

Let Hjir r : jr (ir r) = r.
Proof. apply (proj2_sig (projT2 (projT2 H6))). 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 (fun s => fom_syms M (js s)).
+ apply (fun 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)
(φ : nat -> 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)
(M'dec : fo_model_dec M')
(φ : nat -> 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 M'dec. }
exists φ.
revert HA; apply convert_complete.
Qed.

End completeness.

Local Definition Σ_finite_to_pos (A : fol_form Σ) :
{ n : nat &
{ m : nat &
{ is : pos n -> syms Σ &
{ ir : pos m -> rels Σ &
{ _ : forall s s', is s = is s' -> s = s' &
{ _ : forall s, ar_syms _ (is s) = ar_syms (Σpos Σ is ir) s &
{ _ : forall r r', ir r = ir r' -> r = r' &
{ _ : forall r, ar_rels _ (ir r) = ar_rels (Σpos Σ is ir) r &
{ B : fol_form (Σpos Σ is ir)
| forall 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 & G1 & G2 & phi & G3).
apply convert_soundness with M phi; auto.
+ intros (M & G1 & G2 & phi & G3).
apply convert_completeness with M phi; 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 : nat &
{ m : nat &
{ is : pos n -> syms Σ &
{ ir : pos m -> rels Σ &
{ _ : forall s s', is s = is s' -> s = s' &
{ _ : forall s, ar_syms _ (is s) = ar_syms (Σpos Σ is ir) s &
{ _ : forall s, In (is s) (fol_syms A) &
{ _ : forall r r', ir r = ir r' -> r = r' &
{ _ : forall r, ar_rels _ (ir r) = ar_rels (Σpos Σ is ir) r &
{ _ : forall 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 & F1 & F2 & F3 & F4 & C & HC).
+ apply Σ_fin_discrete_syms.
+ apply Σ_fin_finite_syms.
+ apply Σ_fin_discrete_rels.
+ apply Σ_fin_finite_rels.
+ exists n, m, (fun p => proj1_sig (i p)), (fun p => proj1_sig (j p)).
exists.
{ intros s s' E; apply F1; 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 F3; 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 : nat &
{ m : nat &
{ 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.