Require Import Equations.Equations Equations.Prop.DepElim Arith Undecidability.Shared.Libs.PSL.Numbers List Setoid.
From Undecidability.Synthetic Require Export DecidabilityFacts EnumerabilityFacts ListEnumerabilityFacts ReducibilityFacts.
From Undecidability.FOLP Require Export FullSyntax.
From Undecidability.Shared Require Import ListAutomation.
Require Export Lia.
Import ListAutomationNotations.


Notation "phi - psi" := (Impl ) (right associativity, at level 55).
Notation "phi ∧ psi" := (Conj ) (right associativity, at level 55).
Notation "phi ∨ psi" := (Disj ) (right associativity, at level 55).
Notation "∀ phi" := (All ) (at level 56, right associativity).
Notation "∃ phi" := (Ex ) (at level 56, right associativity).
Notation "⊥" := (Fal).
Notation "¬ phi" := ( --> ) (at level 20).

Notation vector := Vector.t.
Import Vector.
Arguments nil {A}.
Arguments cons {A} _ {n}.
Derive Signature for vector.


Ltac capply H := eapply H; try eassumption.
Ltac comp := repeat (progress (cbn in *; autounfold in *; asimpl in *)).
Hint Unfold idsRen : core.

Ltac resolve_existT :=
  match goal with
     | [ : existT _ _ _ = existT _ _ _ |- _ ] rewrite (Eqdep.EqdepTheory.inj_pair2 _ _ _ _ _ ) in *
  | _ idtac
  end.

Ltac inv H :=
  inversion H; subst; repeat (progress resolve_existT).

Section FullFOL.
  Context {Sigma : Signature}.

  Lemma var_subst phi :
    [var_term] = .
  Proof.
    now asimpl.
  Qed.


  Lemma var_subst_L A :
    [[var_term] | phi A] = A.
  Proof.
    induction A; cbn; trivial.
    now rewrite var_subst, IHA.
  Qed.


  Definition form_shift n := var_term (S n).
  Notation "↑" := form_shift.


  Inductive sf : form form Prop :=
  | SImplL phi psi : sf ( --> )
  | SImplR phi psi : sf ( --> )
  
  | SDisjL phi psi : sf ( )
  | SDisjR phi psi : sf ( )
  | SConjL phi psi : sf ( )
  | SConjR phi psi : sf ( )
  | SAll phi t : sf ( [t .: ids]) ( )
  | SEx phi t : sf ( [t .: ids]) ( ).

  Lemma sf_acc phi rho :
    Acc sf ( []).
  Proof.
    revert ; induction ; intros ; constructor; intros Hpsi; asimpl in *; inversion Hpsi;
      subst; asimpl in *; eauto.
  Qed.


  Lemma sf_well_founded :
    well_founded sf.
  Proof.
    intros . pose proof (sf_acc ids) as H. comp. erewrite idSubst_form in H; firstorder.
  Qed.



  Fixpoint size (phi : form ) :=
    match with
    | Pred _ _ 0
    | Fal 0
    | Impl
    | Conj
    | Disj S (size + size )
    | All
    | Ex S (size )
    end.

  Lemma size_ind {X : Type} (f : X ) (P : X Prop) :
    ( x, ( y, f y < f x P y) P x) x, P x.
  Proof.
    intros H x. apply H.
    induction (f x).
    - intros y. .
    - intros y. intros [] % le_lt_or_eq.
      + apply IHn; .
      + apply H. injection . now intros .
  Qed.


  Lemma subst_size rho phi :
    size (subst_form ) = size .
  Proof.
    revert ; induction ; intros ; cbn; asimpl; try congruence.
  Qed.


  Lemma form_ind_subst (P : form Prop) :
    P ( p v, P (Pred p v))
    ( phi psi, P P P ( --> ))
    ( phi psi, P P P ( ))
    ( phi psi, P P P ( ))
    ( phi, ( t, P [t..]) P ( ))
    ( phi, ( t, P [t..]) P ( ))
     phi, P .
  Proof.
    intros .
    induction using (@size_induction _ size).
    destruct ; trivial.
    - apply ; apply H; cbn; .
    - apply ; apply H; cbn; .
    - apply ; apply H; cbn; .
    - apply . intros t. apply H.
      cbn. unfold . rewrite subst_size. .
    - apply . intros t. apply H.
      cbn. unfold . rewrite subst_size. .
  Qed.



  Inductive Forall (A : Type) (P : A Type) : n, vector A n Type :=
  | Forall_nil : Forall P (@Vector.nil A)
  | Forall_cons : n (x : A) (l : vector A n), P x Forall P l Forall P (@Vector.cons A x n l).

  Inductive vec_in (A : Type) (a : A) : n, vector A n Type :=
  | vec_inB n (v : vector A n) : vec_in a (cons a v)
  | vec_inS a' n (v :vector A n) : vec_in a v vec_in a (cons a' v).
  Hint Constructors vec_in : core.

  Lemma strong_term_ind' (p : term Type) :
    ( x, p (var_term x)) ( F v, (Forall p v) p (Func F v)) (t : term), p t.
  Proof.
    intros . fix strong_term_ind' 1. destruct t as [n|F v].
    - apply .
    - apply . induction v.
      + econstructor.
      + econstructor. now eapply strong_term_ind'. eauto.
  Qed.


  Lemma strong_term_ind (p : term Type) :
    ( x, p (var_term x)) ( F v, ( t, vec_in t v p t) p (Func F v)) (t : term), p t.
  Proof.
    intros . eapply strong_term_ind'.
    - apply .
    - intros. apply . intros t. induction 1; inv X; eauto.
  Qed.



  Inductive unused_term (n : ) : term Prop :=
  | uft_var m : n m unused_term n (var_term m)
  | uft_Func F v : ( t, vec_in t v unused_term n t) unused_term n (Func F v).

  Inductive unused (n : ) : form Prop :=
  | uf_Fal : unused n Fal
  | uf_Pred P v : ( t, vec_in t v unused_term n t) unused n (Pred P v)
  | uf_I phi psi : unused n unused n unused n (Impl )
  | uf_A phi psi : unused n unused n unused n (Conj )
  | uf_O phi psi : unused n unused n unused n (Disj )
  | uf_All phi : unused (S n) unused n (All )
  | uf_Ex phi : unused (S n) unused n (Ex ).

  Definition unused_L n A := phi, A unused n .
  Definition closed phi := n, unused n .

  Lemma vec_unused n (v : vector term n) :
    ( t, vec_in t v { n | m, n m unused_term m t })
    { k | t, vec_in t v m, k m unused_term m t }.
  Proof.
    intros Hun. induction v in Hun |-*.
    - exists 0. intros n H. inv H.
    - destruct IHv as [k H]. 1: eauto. destruct (Hun h (vec_inB h v)) as [k' H'].
      exists (k + k'). intros t . inv ; intros m Hm; [apply H' | apply H]; now try .
  Qed.


  Lemma find_unused_term t :
    { n | m, n m unused_term m t }.
  Proof.
    induction t using strong_term_ind.
    - exists (S x). intros m Hm. constructor. .
    - destruct (vec_unused X) as [k H]. exists k. eauto using unused_term.
  Qed.


  Lemma find_unused phi :
    { n | m, n m unused m }.
  Proof with eauto using unused.
    induction .
    - exists 0...
    - destruct (@vec_unused _ t) as [k H]. 1: eauto using find_unused_term. exists k. eauto using unused.
    - destruct , . exists (x + ). intros m Hm. constructor; [ apply u | apply ]; .
    - destruct , . exists (x + ). intros m Hm. constructor; [ apply u | apply ]; .
    - destruct , . exists (x + ). intros m Hm. constructor; [ apply u | apply ]; .
    - destruct IHphi. exists x. intros m Hm. constructor. apply u. .
    - destruct IHphi. exists x. intros m Hm. constructor. apply u. .
  Qed.


  Lemma find_unused_L A :
    { n | m, n m unused_L m A }.
  Proof.
    induction A.
    - exists 0. unfold unused_L. firstorder.
    - destruct IHA. destruct (find_unused a).
      exists (x + ). intros m Hm. intros []; subst.
      + apply . .
      + apply u. . auto.
  Qed.


  Definition capture n phi := nat_rect _ ( _ All) n.

  Lemma capture_captures n m phi :
    ( i, n i unused i ) ( i, n - m i unused i (capture m )).
  Proof.
    intros H. induction m; cbn; intros i Hi.
    - rewrite minus_n_O in *. intuition.
    - constructor. apply IHm. .
  Qed.


  Definition close phi := capture (proj1_sig (find_unused )) .

  Lemma close_closed phi :
    closed (close ).
  Proof.
    intros n. unfold close. destruct (find_unused ) as [m Hm]; cbn.
    apply (capture_captures Hm). .
  Qed.


  Fixpoint big_imp A phi :=
    match A with
    | List.nil
    | a :: A' a --> (big_imp A' )
    end.


  Definition shift_P P n :=
    match n with
    | O False
    | S n' P n'
    end.

  Lemma vec_map_ext X Y (f g : X Y) n (v : vector X n) :
    ( x, vec_in x v f x = g x) map f v = map g v.
  Proof.
    intros Hext; induction v in Hext |-*; cbn.
    - reflexivity.
    - rewrite IHv, (Hext h). 1: reflexivity. all: eauto.
  Qed.


  Lemma subst_unused_term xi sigma P t :
    ( x, dec (P x)) ( m, P m m = m) ( m, P m unused_term m t)
    subst_term t = subst_term t.
  Proof.
    intros Hdec Hext Hunused. induction t using strong_term_ind; cbn; asimpl.
    - destruct (Hdec x) as [H % Hunused | H % Hext].
      + inversion H; subst; congruence.
      + congruence.
    - f_equal. apply vec_map_ext. intros t H'. apply (H t H'). intros n % Hunused. inv . eauto.
  Qed.


  Lemma subst_unused_form xi sigma P phi :
    ( x, dec (P x)) ( m, P m m = m) ( m, P m unused m )
    subst_form = subst_form .
  Proof.
    induction in ,,P |-*; intros Hdec Hext Hunused; cbn; asimpl.
    - reflexivity.
    - f_equal. apply vec_map_ext. intros s H. apply (subst_unused_term Hdec Hext).
      intros m H' % Hunused. inv H'. eauto.
    - rewrite with ( := ) (P := P). rewrite with ( := ) (P := P).
      all: try tauto. all: intros m H % Hunused; now inversion H.
    - rewrite with ( := ) (P := P). rewrite with ( := ) (P := P).
      all: try tauto. all: intros m H % Hunused; now inversion H.
    - rewrite with ( := ) (P := P). rewrite with ( := ) (P := P).
      all: try tauto. all: intros m H % Hunused; now inversion H.
    - erewrite IHphi with (P := shift_P P). 1: reflexivity.
      + intros [| x]; [now right| now apply Hdec].
      + intros [| m]; [reflexivity|]. intros Heq % Hext; unfold ">>"; cbn; congruence.
      + intros [| m]; [destruct 1| ]. intros H % Hunused; now inversion H.
    - erewrite IHphi with (P := shift_P P). 1: reflexivity.
      + intros [| x]; [now right| now apply Hdec].
      + intros [| m]; [reflexivity|]. intros Heq % Hext; unfold ">>"; cbn; congruence.
      + intros [| m]; [destruct 1| ]. intros H % Hunused; now inversion H.
  Qed.


  Lemma subst_unused_single xi sigma n phi :
    unused n ( m, n m m = m) subst_form = subst_form .
  Proof.
    intros Hext Hunused. apply subst_unused_form with (P := m n = m). all: intuition.
    now subst.
  Qed.


  Lemma subst_unused_range xi sigma phi n :
    ( m, n m unused m ) ( x, x < n x = x) subst_form = subst_form .
  Proof.
    intros Hle Hext. apply subst_unused_form with (P := x n x); [apply le_dec| |assumption].
    intros ? ? % not_le; intuition.
  Qed.


  Lemma subst_unused_closed xi sigma phi :
    closed subst_form = subst_form .
  Proof.
    intros Hcl. apply subst_unused_range with (n := 0); intuition. .
  Qed.


  Lemma subst_unused_closed' xi phi :
    closed subst_form = .
  Proof.
    intros Hcl. rewrite idSubst_form with (sigmaterm := ids).
    apply subst_unused_range with (n := 0). all: intuition; .
  Qed.


  Lemma vec_forall_map X Y (f : X Y) n (v : vector X n) (p : Y Type) :
    ( x, vec_in x v p (f x)) y, vec_in y (map f v) p y.
  Proof.
    intros H y Hmap. induction v; cbn; inv Hmap; eauto.
  Qed.



  Definition theory := form Prop.
  Definition contains phi (T : theory) := T .
  Definition contains_L (A : list form) (T : theory) := f, f A contains f T.
  Definition subset_T (T1 T2 : theory) := (phi : form), contains contains .
  Definition list_T A : theory := phi A.

  Infix "⊏" := contains_L (at level 20).
  Infix "⊑" := subset_T (at level 20).
  Infix "∈" := contains (at level 70).

  Hint Unfold contains : core.
  Hint Unfold contains_L : core.
  Hint Unfold subset_T : core.

  Global Instance subset_T_trans : Transitive subset_T.
  Proof.
    intros . intuition.
  Qed.


  Definition extend T (phi : form) := psi T = .
  Infix "⋄" := extend (at level 20).

  Definition closed_T (T : theory) := phi n, contains T unused n .
  Lemma closed_T_extend T phi :
    closed_T T closed closed_T (T ).
  Proof.
    intros ? ? ? ? []; subst; intuition.
  Qed.


  Section ContainsAutomation.
    Lemma contains_nil T :
      List.nil T.
    Proof. firstorder. Qed.

    Lemma contains_cons a A T :
      a T A T (a :: A) T.
    Proof. intros ? ? ? []; subst; intuition. Qed.

    Lemma contains_cons2 a A T :
      (a :: A) T A T.
    Proof. firstorder. Qed.

    Lemma contains_app A B T :
      A T B T (A B) T.
    Proof. intros ? ? ? [] % in_app_or; intuition. Qed.

    Lemma contains_extend1 phi T :
       (T ).
    Proof. now right. Qed.

    Lemma contains_extend2 phi psi T :
       T (T ).
    Proof. intros ?. now left. Qed.

    Lemma contains_extend3 A T phi :
      A T A (T ).
    Proof.
      intros ? ? ?. left. intuition.
    Qed.

  End ContainsAutomation.
End FullFOL.

Definition tmap {S1 S2 : Signature} (f : @form @form ) (T : @theory ) : @theory :=
   phi psi, T f = .

Lemma enum_tmap {S1 S2 : Signature} (f : @form @form ) (T : @theory ) L :
  list_enumerator L T list_enumerator(L >> List.map f) (tmap f T).
Proof.
  intros . unfold ">>".
  intros x; split.
  + intros ( & [m Hin] % & ). exists m. apply in_map_iff. firstorder.
  + intros (m & ( & & Hphi) % in_map_iff). firstorder.
Qed.


Lemma tmap_contains_L {S1 S2 : Signature} (f : @form @form ) T A :
  contains_L A (tmap f T) B, A = List.map f B contains_L B T.
Proof.
  induction A.
  - intros. now exists List.nil.
  - intros H. destruct IHA as (B & & HB). 1: firstorder.
    destruct (H a (or_introl eq_refl)) as (b & Hb & ).
    exists (b :: B). split. 1: auto. intros ? []; subst; auto.
Qed.


Hint Constructors vec_in : core.

Infix "⊏" := contains_L (at level 20).
Infix "⊑" := subset_T (at level 20).
Infix "∈" := contains (at level 70).
Infix "⋄" := extend (at level 20).

Hint Resolve contains_nil contains_cons contains_cons2 contains_app : contains_theory.
Hint Resolve contains_extend1 contains_extend2 contains_extend3 : contains_theory.
Ltac use_theory A := exists A; split; [eauto 15 with contains_theory|].


Lemma dec_vec_in X n (v : vector X n) :
  ( x, vec_in x v y, dec (x = y)) v', dec (v = v').
Proof with subst; try (now left + (right; intros[=]; resolve_existT; congruence)).
  intros Hv. induction v; intros v'; dependent destruction v'...
  destruct (Hv h (vec_inB h v) )... destruct (IHv ( x H Hv x (vec_inS H)) v')...
Qed.


Instance dec_vec X {HX : eq_dec X} n : eq_dec (vector X n).
Proof.
  intros v. refine (dec_vec_in _).
Qed.


Section EqDec.
  Context {Sigma : Signature}.

  Hypothesis eq_dec_Funcs : eq_dec Funcs.
  Hypothesis eq_dec_Preds : eq_dec Preds.

  Global Instance dec_term : eq_dec term.
  Proof with subst; try (now left + (right; intros[=]; resolve_existT; congruence)).
    intros t. induction t using strong_term_ind; intros []...
    - decide (x = n)...
    - decide (F = f)... destruct (dec_vec_in X t)...
  Qed.


  Global Instance dec_form : eq_dec form.
  Proof with subst; try (now left + (right; intros[=]; resolve_existT; congruence)).
    intros . induction ; intros []...
    - decide (P = )... decide (t = )...
    - decide ( = f)... decide ( = )...
    - decide ( = f)... decide ( = )...
    - decide ( = f)... decide ( = )...
    - decide ( = f)...
    - decide ( = f)...
  Qed.

End EqDec.

Notation "↑" := form_shift.
Notation "A ⟹ phi" := (big_imp A ) (at level 60).