Section FOL_SYNTAX. (* Reference: "https://github.com/ernius/formalmetatheory-stoughton" *)
#[local] Hint Unfold compose : simplication_hints.
#[local] Open Scope program_scope.
Definition renaming : Set :=
ivar -> ivar.
Definition subst (L : language) : Set :=
ivar -> trm L.
Context {L : language}.
Fixpoint fvs_trm (t : trm L) : list ivar :=
match t with
| Var_trm x => [x]
| Fun_trm f ts => fvs_trms ts
| Con_trm c => []
end
with fvs_trms {n : nat} (ts : trms L n) : list ivar :=
match ts with
| O_trms => []
| S_trms n t ts => fvs_trm t ++ fvs_trms ts
end.
Fixpoint fvs_frm (p : frm L) : list ivar :=
match p with
| Rel_frm r ts => fvs_trms ts
| Eqn_frm t1 t2 => fvs_trm t1 ++ fvs_trm t2
| Neg_frm p1 => fvs_frm p1
| Imp_frm p1 p2 => fvs_frm p1 ++ fvs_frm p2
| All_frm y p1 => List.remove eq_dec y (fvs_frm p1)
end.
#[local] Hint Rewrite fv_is_free_in_trm fv_is_free_in_trms : simplication_hints.
#[local] Hint Rewrite fv_is_free_in_frm : simplication_hints.
Definition is_not_free_in_trm (x : ivar) (t : trm L) : Prop :=
is_free_in_trm x t = false.
Definition is_not_free_in_trms {n : nat} (x : ivar) (ts : trms L n) : Prop :=
is_free_in_trms x ts = false.
Definition is_not_free_in_frm (x : ivar) (p : frm L) : Prop :=
is_free_in_frm x p = false.
#[local] Hint Unfold is_not_free_in_frm is_free_in_trms is_not_free_in_frm : simplication_hints.
Definition fvs_frms (ps : ensemble (frm L)) : ensemble ivar :=
ps >>= E.fromList ∘ fvs_frm.
#[local] Hint Rewrite fvs_is_free_in_frms : simplication_hints.
Definition is_not_free_in_frms (x : ivar) (ps : ensemble (frm L)) : Prop :=
forall p, p \in ps -> is_free_in_frm x p = false.
#[local] Hint Unfold is_not_free_in_frms : simplication_hints.
Definition last_ivar_trm (t : trm L) : ivar :=
maxs (fvs_trm t).
Fixpoint last_ivar_trms {n : nat} (ts : trms L n) : ivar :=
match ts with
| O_trms => 0
| S_trms n t ts => max (last_ivar_trm t) (last_ivar_trms (n := n) ts)
end.
Definition last_ivar_frm (p : frm L) : ivar :=
maxs (fvs_frm p).
#[local] Hint Unfold last_ivar_trm last_ivar_frm : simplication_hints.
#[local] Hint Rewrite <- last_ivar_trms_eq_maxs_fvs : simplication_hints.
Definition chi_frm (s : subst L) (p : frm L) : ivar :=
1 + maxs (List.map (last_ivar_trm ∘ s) (fvs_frm p)).
Definition nil_subst : subst L :=
fun z : ivar => Var_trm z.
Definition cons_subst (y : ivar) (t : trm L) (s : subst L) : subst L :=
fun z : ivar => if eq_dec z y then t else s z.
Definition one_subst (x1 : ivar) (t1 : trm L) : subst L :=
cons_subst x1 t1 nil_subst.
#[local] Hint Unfold nil_subst cons_subst one_subst : simplication_hints.
Fixpoint subst_trm (s : subst L) (t : trm L) : trm L :=
match t with
| Var_trm x => s x
| Fun_trm f ts => Fun_trm f (subst_trms s ts)
| Con_trm c => Con_trm c
end
with subst_trms {n : nat} (s : subst L) (ts : trms L n) : trms L n :=
match ts with
| O_trms => O_trms
| S_trms n t ts => S_trms n (subst_trm s t) (subst_trms (n := n) s ts)
end.
Fixpoint subst_frm (s : subst L) (p : frm L) : frm L :=
let chi : ivar := chi_frm s p in
match p with
| Rel_frm R ts => Rel_frm R (subst_trms s ts)
| Eqn_frm t1 t2 => Eqn_frm (subst_trm s t1) (subst_trm s t2)
| Neg_frm p1 => Neg_frm (subst_frm s p1)
| Imp_frm p1 p2 => Imp_frm (subst_frm s p1) (subst_frm s p2)
| All_frm y p1 => All_frm chi (subst_frm (cons_subst y (Var_trm chi) s) p1)
end.
Definition subst_compose (s : subst L) (s' : subst L) : subst L :=
fun z : ivar => subst_trm s' (s z).
#[local] Hint Unfold subst_compose : simplication_hints.
Definition equiv_subst_in_frm (s1 : subst L) (s2 : subst L) (p : frm L) : Prop :=
forall z : ivar, forall FREE : is_free_in_frm z p = true, s1 z = s2 z.
Definition free_in_trm_wrt (x : ivar) (s : subst L) (t : trm L) : Prop :=
exists y : ivar, is_free_in_trm y t = true /\ is_free_in_trm x (s y) = true.
Definition free_in_trms_wrt {n : nat} (x : ivar) (s : subst L) (ts : trms L n) : Prop :=
exists y : ivar, is_free_in_trms y ts = true /\ is_free_in_trm x (s y) = true.
Definition free_in_frm_wrt (x : ivar) (s : subst L) (p : frm L) : Prop :=
exists y : ivar, is_free_in_frm y p = true /\ is_free_in_trm x (s y) = true.

Theorem free_in_trm_wrt_iff (t : trm L) (z : ivar) (s : subst L)
: free_in_trm_wrt z s t <-> is_free_in_trm z (subst_trm s t) = true
with free_in_trms_wrt_iff n (ts : trms L n) (z : ivar) (s : subst L)
: free_in_trms_wrt z s ts <-> is_free_in_trms z (subst_trms s ts) = true.