Require Import Omega.
Require Import Autosubst.Autosubst.
Require Import AutosubstExtra.
Require Import Autosubst_EOS.

(* -------------------------------------------------------------------------- *)

Class IdsLemmas (term : Type) {Ids_term : Ids term} := {
  (* The identity substitution is injective. *)
  ids_inj:
    forall x y,
    ids x = ids y ->
    x = y
}.

(* -------------------------------------------------------------------------- *)

Section FreeVars.

Context
  {A : Type}
  {Ids_A : Ids A}
  {Rename_A : Rename A}
  {Subst_A : Subst A}
  {IdsLemmas_A : IdsLemmas A}
  {SubstLemmas_A : SubstLemmas A}.

(* -------------------------------------------------------------------------- *)

(* A reformulation of [ids_inj]. *)

Lemma ids_inj_False:
  forall x y,
  ids x = ids y ->
  x <> y ->
  False.
Proof.
  intros.
  assert (x = y). { eauto using ids_inj. }
  unfold var in *.
  omega.
Qed.

(* -------------------------------------------------------------------------- *)

(* The predicate [fv k t] means that the free variables of the term [t] are
   contained in the semi-open interval [0..k). *)

Definition fv k t :=
  t.[upn k (ren (+1))] = t.

(* -------------------------------------------------------------------------- *)

(* The predicate [closed t] means that the term [t] is closed, that is, [t]
   has no free variables. *)

Definition closed :=
  fv 0.

(* -------------------------------------------------------------------------- *)

(* This technical lemma states that the renaming [+1] is injective. *)

Lemma lift_inj_ids:
  forall t x,
  t.[ren (+1)] = ids (S x) <-> t = ids x.
Proof.
  split; intros.
  { eapply lift_inj. autosubst. }
  { subst. autosubst. }
Qed.

(* -------------------------------------------------------------------------- *)

(* This lemma characterizes the meaning of [fv k] when applied to a variable. *)

Lemma fv_ids_eq:
  forall k x,
  fv k (ids x) <-> x < k.
Proof.
  unfold fv. induction k; intros.
  (* Base case. *)
  { asimpl. split; intros; elimtype False.
    { eauto using ids_inj_False. }
    { omega. }
  }
  (* Step. *)
  { destruct x; asimpl.
    { split; intros. { omega. } { reflexivity. } }
    { rewrite lift_inj_ids.
      rewrite <- id_subst.
      rewrite IHk. omega. }
  }
Qed.

(* -------------------------------------------------------------------------- *)

(* A simplification lemma. *)

Lemma fv_lift:
  forall k i t,
  fv (k + i) t.[ren (+i)] <-> fv k t.
Proof.
  unfold fv. intros. asimpl.
  rewrite Nat.add_comm.
  rewrite <- upn_upn.
  erewrite plus_upn by eauto.
  rewrite <- subst_comp.
  split; intros.
  { eauto using lift_injn. }
  { f_equal. eauto. }
Qed.

(* -------------------------------------------------------------------------- *)

(* If [t] has at most [n - 1] free variables,
   and if [x] is inserted among them,
   then we get [eos x t],
   which has at most [n] free variables. *)

Lemma fv_eos:
  forall x n t,
  x < n ->
  fv (n - 1) t ->
  fv n (eos x t).
Proof.
  unfold fv. intros x n t ? ht.
  rewrite eos_eq in ht.
  rewrite eos_eq.
  rewrite eos_eos_reversed by omega. (* nice! *)
  rewrite ht.
  reflexivity.
Qed.

Lemma fv_eos_eq:
  forall x n t,
  x < n ->
  fv n (eos x t) <->
  fv (n - 1) t.
Proof.
  unfold fv. intros x n t ?.
  rewrite eos_eq.
  rewrite eos_eq.
  rewrite eos_eos_reversed by omega. (* nice! *)
  split; intros h.
  { eauto using eos_injective. }
  { rewrite h. reflexivity. }
Qed.

(* -------------------------------------------------------------------------- *)

(* A substitution [sigma] is regular if and only if, for some [j], for
   sufficiently large [x], [sigma x] is [x + j]. *)

Definition regular (sigma : var -> A) :=
  exists i j,
  ren (+i) >> sigma = ren (+j).

Lemma regular_ids:
  regular ids.
Proof.
  exists 0. exists 0. autosubst.
Qed.

Lemma regular_plus:
  forall i,
  regular (ren (+i)).
Proof.
  intros. exists 0. exists i. autosubst.
Qed.

Lemma regular_upn:
  forall n sigma,
  regular sigma ->
  regular (upn n sigma).
Proof.
  intros ? ? (i&j&hsigma).
  exists (n + i). eexists (n + j).
  replace (ren (+(n + i))) with (ren (+i) >> ren (+n)) by autosubst.
  rewrite <- scompA.
  rewrite up_liftn.
  rewrite scompA.
  rewrite hsigma.
  autosubst.
Qed.

(* -------------------------------------------------------------------------- *)

(* If the free variables of the term [t] are below [k], then [t] is unaffected
   by a substitution of the form [upn k sigma]. *)

(* Unfortunately, in this file, where the definition of type [A] is unknown, I
   am unable to establish this result for arbitrary substitutions [sigma]. I
   am able to establish it for *regular* substitutions, where The proof is somewhat interesting, so it is given here, even
   though, once the definition of the type [A] is known, a more direct proof,
   without a regularity hypothesis, can usually be given. *)

(* An intermediate result states that, since [upn k (ren (+1))] does not
   affect [t], then (by iteration) neither does [upn k (ren (+j))]. *)

Lemma fv_unaffected_lift:
  forall j t k,
  fv k t ->
  t.[upn k (ren (+j))] = t.
Proof.
  induction j as [| j ]; intros t k ht.
  { asimpl. rewrite up_id_n. autosubst. }
  { replace (ren (+S j)) with (ren (+1) >> ren (+j)) by autosubst.
    rewrite <- up_comp_n.
    replace (t.[upn k (ren (+1)) >> upn k (ren (+j))])
       with (t.[upn k (ren (+1))].[upn k (ren (+j))]) by autosubst.
    rewrite ht.
    rewrite IHj by eauto.
    eauto. }
Qed.

(* There follows that a substitution of the form [upn k sigma], where [sigma]
   is regular, does not affect [t]. The proof is slightly subtle but very
   short. The previous lemma is used twice. *)

Lemma fv_unaffected_regular:
  forall k t sigma,
  fv k t ->
  regular sigma ->
  t.[upn k sigma] = t.
Proof.
  intros k t sigma ? (i&j&hsigma).
  rewrite <- (fv_unaffected_lift i t k) at 1 by eauto.
  asimpl. rewrite up_comp_n.
  rewrite hsigma.
  rewrite fv_unaffected_lift by eauto.
  reflexivity.
Qed.

(* A corollary. *)

Lemma closed_unaffected_regular:
  forall t sigma,
  closed t ->
  regular sigma ->
  t.[sigma] = t.
Proof.
  unfold closed. intros.
  rewrite <- (upn0 sigma).
  eauto using fv_unaffected_regular.
Qed.

(*One might also wish to prove a result along the following lines:

  Goal
    forall t k sigma1 sigma2,
    fv k t ->
    (forall x, x < k -> sigma1 x = sigma2 x) ->
    t.[sigma1] = t.[sigma2].

  I have not yet investigated how this could be proved. *)

(* -------------------------------------------------------------------------- *)

(* If some term [t] has free variables under [j], then it also has free
   variables under [k], where [j <= k]. *)

Lemma fv_monotonic:
  forall j k t,
  fv j t ->
  j <= k ->
  fv k t.
Proof.
  intros. unfold fv.
  replace k with (j + (k - j)) by omega.
  rewrite <- upn_upn.
  eauto using fv_unaffected_regular, regular_upn, regular_plus.
Qed.

(* -------------------------------------------------------------------------- *)

(* These little lemmas may be occasionally useful. *)

Lemma use_fv_length_cons:
  forall A (x : A) (xs : list A) n t,
  (forall x, fv (length (x :: xs)) t) ->
  n = length xs ->
  fv (n + 1) t.
Proof.
  intros. subst.
  replace (length xs + 1) with (length (x :: xs)) by (simpl; omega).
  eauto.
Qed.

Lemma prove_fv_length_cons:
  forall A (x : A) (xs : list A) n t,
  n = length xs ->
  fv (n + 1) t ->
  fv (length (x :: xs)) t.
Proof.
  intros. subst.
  replace (length (x :: xs)) with  (length xs + 1) by (simpl; omega).
  eauto.
Qed.

(* -------------------------------------------------------------------------- *)

(* [closed t] is equivalent to [t.[ren (+1)] = t]. *)

Lemma closed_eq:
  forall t,
  closed t <-> t.[ren (+1)] = t.
Proof.
  unfold closed, fv. asimpl. tauto.
Qed.

(* -------------------------------------------------------------------------- *)

(* A variable is not closed. *)

Lemma closed_ids:
  forall x,
  ~ closed (ids x).
Proof.
  unfold closed, fv. intros. asimpl. intro.
  eauto using ids_inj_False.
Qed.

End FreeVars.

(* -------------------------------------------------------------------------- *)

(* The tactic [fv] is intended to use a number of lemmas as rewriting rules.
   The hint database [fv] can be extended with language-specific lemmas. *)

Hint Rewrite @fv_ids_eq @fv_lift @fv_eos_eq : fv.

Ltac fv :=
  autorewrite with fv in *;
  eauto with typeclass_instances.

(* -------------------------------------------------------------------------- *)

(* A hint database to prove goals of the form [~ (closed _)] or [closed _]. *)

Hint Resolve closed_ids : closed.

(* -------------------------------------------------------------------------- *)

Hint Resolve regular_ids regular_plus regular_upn : regular.
