Set Implicit Arguments.
Require Import Morphisms Lia FinFun.
From Undecidability.HOU Require Import std.std.
From Undecidability.HOU.calculus Require Import
  prelim terms syntax semantics confluence.

Set Default Proof Using "Type".

Section Equivalence.
  Context {X: Const}.
  Notation "s ≡ t" := (equiv (@step X) s t) (at level 70).

  Section CompatibilityProperties.

    Global Instance equiv_lam_proper:
      Proper (equiv step ++> equiv step) (@lam X).
    Proof.
      intros ? ? (v & & ) % church_rosser; eauto.
      rewrite , . reflexivity.
    Qed.


    Global Instance equiv_app_proper:
      Proper (equiv step ++> equiv step ++> equiv step) (@app X).
    Proof.
      intros ? ? (v & & ) % church_rosser ? ? (v' & & ) % church_rosser;
        eauto.
      rewrite , , , . reflexivity.
    Qed.


    Lemma ren_equiv s t delta:
      s t ren s ren t.
    Proof.
      intros (v & ? & ?) % church_rosser; eauto.
      transitivity (ren v); [| symmetry].
      all: eapply equiv_star, ren_steps; eauto.
    Qed.


    Global Instance ren_equiv_proper:
      Proper (eq ++> equiv step ++> equiv step) (@ren X).
    Proof.
      intros ? s t H; now eapply ren_equiv.
    Qed.


    Lemma subst_equiv s t sigma:
      s t s t.
    Proof.
      intros (v & ? & ?) % church_rosser; eauto.
      transitivity ( v); [| symmetry].
      all: eapply equiv_star, subst_steps; eauto.
    Qed.


    Global Instance subst_equiv_proper:
      Proper (eq ++> equiv step ++> equiv step) (@subst_exp X).
    Proof.
      intros ? s t H; now eapply subst_equiv.
    Qed.


    Lemma subst_pointwise_equiv (s: exp X) sigma tau:
      ( x, x vars s x x) s s.
    Proof.
      induction s in , |-*; cbn -[vars]; eauto.
      - intros H; eapply equiv_lam_proper, IHs.
        intros []; cbn -[vars]. reflexivity.
        intros ? % vars_varof % varofLambda % varof_vars % H.
        unfold funcomp.
        now eapply ren_equiv.
      - intros H; rewrite , ; eauto.
    Qed.


  End CompatibilityProperties.

  Section InjectivityProperties.

    Lemma equiv_var_eq (x y: fin):
      var x var y x = y.
    Proof.
      intros; eapply equiv_unique_normal_forms in H; eauto.
      congruence.
    Qed.


    Lemma equiv_const_eq (x y: X):
      const x const y x = y.
    Proof.
      intros; eapply equiv_unique_normal_forms in H; eauto.
      congruence.
    Qed.


    Lemma equiv_lam_elim (s t: exp X):
       s t s t.
    Proof.
      intros (v & [] %steps_lam & [] %steps_lam) % church_rosser; intuition; subst.
      injection H as ; eauto.
    Qed.


    Lemma equiv_app_elim (s s' t t': exp X):
      s t s' t' isAtom (head s) isAtom (head s') s s' t t'.
    Proof.
      intros (v & [] % steps_app & [] % steps_app) % church_rosser ; eauto.
      * do 2 destruct H, ; intuition; subst;
          injection H as ; eauto.
      * do 2 destruct H; destruct ; intuition; subst.
        all: destruct (head s'); cbn in *; intuition.
      * do 2 destruct ; destruct H; intuition; subst.
        all: destruct (head s); cbn in *; intuition.
      * destruct H, ; intuition.
        all: destruct (head s); cbn in *; intuition.
    Qed.


    Lemma equiv_anti_ren delta (s t: exp X):
      Injective ren s ren t s t.
    Proof.
      intros ? (v & & ) % church_rosser; eauto.
      eapply steps_anti_ren in as [].
      eapply steps_anti_ren in as [].
      intuition; subst.
      eapply anti_ren in ; eauto.
      subst; eauto.
    Qed.


  End InjectivityProperties.

  Section DisjointnessProperties.

    Lemma equiv_neq_var_app (x: ) (s t: exp X):
      var x s t isAtom (head s) False.
    Proof.
      intros EQ.
      destruct (head s) as [y | | | ] eqn: H'; intuition.
      all: eapply church_rosser in EQ as (v & L & R); eauto.
      all: inv L; firstorder using normal_var.
      all: eapply steps_app in R as [R|R].
      1, 3: destruct R as (? & ? & ? & ?); discriminate.
      all: destruct R; intuition; rewrite H' in *; syn.
    Qed.


    Lemma equiv_neq_lambda_app (s' s t: exp X):
       s' s t isAtom (head s) False.
    Proof.
      intros EQ.
      destruct (head s) as [y | | | ] eqn: H'; intuition.
      all: eapply church_rosser in EQ as (v & L & R); eauto.
      all: eapply steps_lam in L as (v' & ? & ?); subst.
      all: eapply steps_app in R as [R|R].
      1, 3: destruct R as (? & ? & ? & ?); discriminate.
      all: destruct R; intuition; rewrite H' in *; syn.
    Qed.


    Lemma equiv_neq_var_lambda (x: ) s: var x s.
    Proof.
      intros (v & ? & ?) % church_rosser; eauto.
      inv H; firstorder using normal_var.
      eapply steps_lam in as []; intuition; discriminate.
    Qed.


    Lemma equiv_neq_var_const (x: ) c: var x const c.
    Proof.
      intros (v & ? & ?) % church_rosser; eauto.
      inv H; firstorder using normal_var.
      inv . inv H.
    Qed.


    Lemma equiv_neq_const_lam s c: const c s.
    Proof.
      intros (v & ? & ?) % church_rosser; eauto.
      inv H; firstorder using normal_var.
      eapply steps_lam in as []; intuition; discriminate.
      inv .
    Qed.


    Lemma equiv_neq_const_app (s t: exp X) c:
      const c s t isAtom (head s) False.
    Proof.
      intros EQ.
      destruct (head s) as [y | | | ] eqn: H'; intuition.
      all: eapply church_rosser in EQ as (v & L & R); eauto.
      all: inv L; firstorder using normal_const.
      all: eapply steps_app in R as [R|R].
      1, 3: destruct R as (? & ? & ? & ?); discriminate.
      all: destruct R; intuition; rewrite H' in *; syn.
    Qed.


  End DisjointnessProperties.

  Section HuetDefinition.
    Variable (s t : exp X).
    Hypothesis (: s ) (: t ).

    Lemma equiv_huet_forward:
      s t = .
    Proof using .
      destruct as [ ], as [ ].
      intros H; eapply equiv_unique_normal_forms; eauto.
      now rewrite , .
    Qed.


    Lemma equiv_huet_backward:
       = s t.
    Proof using .
      intros; subst; destruct , ; eapply equiv_join; eauto.
    Qed.

  End HuetDefinition.

End Equivalence.

Notation "s ≡ t" := (equiv step s t) (at level 70).

#[export] Hint Resolve equiv_neq_var_app equiv_neq_var_lambda equiv_neq_var_const
     equiv_neq_const_app equiv_neq_const_lam equiv_neq_lambda_app : core.

Ltac ClearRefl H :=
  let T := type of H in
  match T with
  | ?X ?X clear H
  | _ idtac
  end.

Ltac Injection H :=
  let T := type of H in
  let := fresh "H" in
  let := fresh "H" in
  match T with
  | var _ var _ eapply equiv_var_eq in H as ; ClearRefl
  | const _ const _ eapply equiv_const_eq in H as ; ClearRefl
  | _ _ eapply equiv_lam_elim in H as ; ClearRefl
  | app _ _ app _ _ eapply equiv_app_elim in H as [ ]; [| atom..]; ClearRefl ; ClearRefl
  end.

Ltac Discriminate :=
  match goal with
  | [H: var _ const _ |- _] eapply equiv_neq_var_const in H as []
  | [H: const _ var _ |- _] symmetry in H; eapply equiv_neq_var_const in H as []
  | [H: var _ app _ _ |- _] solve [eapply equiv_neq_var_app in H as []; atom]
  | [H: app _ _ var _ |- _] solve [symmetry in H; eapply equiv_neq_var_app in H as []; atom]
  | [H: var _ _ |- _] eapply equiv_neq_var_lambda in H as []
  | [H: _ var _ |- _] symmetry in H; eapply equiv_neq_var_lambda in H as []
  | [H: const _ _ |- _] eapply equiv_neq_const_lam in H as []
  | [H: _ const _ |- _] symmetry in H; eapply equiv_neq_const_lam in H as []
  | [H: const _ app _ _ |- _] solve [eapply equiv_neq_const_app in H as []; atom]
  | [H: app _ _ const _ |- _] solve [symmetry in H; eapply equiv_neq_const_app in H as []; atom]
  | [H: _ app _ _ |- _] solve [eapply equiv_neq_lambda_app in H as []; atom]
  | [H: app _ _ _ |- _] solve [symmetry in H; eapply equiv_neq_lambda_app in H as []; atom]
  | [H: var _ var _ |- _] solve [eapply equiv_var_eq in H as ?; discriminate]
  | [H: const _ const _ |- _] solve [eapply equiv_const_eq in H as ?; discriminate]
  end.

Lemma equiv_head_equal X (s t: exp X):
  s t isAtom (head s) isAtom (head t) head s = head t.
Proof.
  induction s in t |-*; destruct t; intros; try Discriminate; Injection H; subst; eauto.
  - cbn in *; intuition.
  - cbn; eapply ; eauto.
Qed.