From Undecidability.L Require Import Util.L_facts.
Import L_Notations.

Notation "A '.[' i ']'" := (elAt A i) (no associativity, at level 50).

Fixpoint appCross A B :=
match A with
| nil nil
| a :: A map (app a) B appCross A B
end.

Fixpoint T n :=
match n with
| 0 [#n]
| S n let A := T n in A [#(S n)] filter ( x Dec ( x A)) ( map lam A appCross A A )
end.

Lemma sublist_T : n : , (x : term) (B : list term), T (S n) = T n x :: B.
Proof.
  intros n. exists (# (S n)). simpl. eexists; reflexivity.
Qed.


Lemma T_S s n m : T n .[ m ] = Some s T (S n).[ m ] = Some s.
Proof.
  destruct (sublist_T n) as [x [B H]]. rewrite H. eapply elAt_app.
Qed.


Lemma T_ge s n1 n2 m : T .[ m ] = Some s T .[ m ] = Some s.
Proof.
  remember (S ) as . induction 1; inv ; eauto using T_S.
Qed.


Lemma appCross_correct A B s t : (s A t B) (app s t) appCross A B.
Proof.
  split.
  - induction A; simpl; intuition; subst; eauto using in_map.
  - induction A; simpl; try rewrite in_app_iff in *; intros H; try tauto; destruct H as [ | ]; intuition; rewrite in_map_iff in *; destruct ; destruct H; [left|]; congruence.
Qed.


Lemma T_var n : #n T n.
Proof.
  destruct n; simpl; auto.
Qed.


Lemma T_app s t n : s T n t T n s t T (S n).
Proof.
  intros; simpl. decide (s t T n); auto; simpl.
  rewrite in_app_iff; simpl.
  rewrite in_filter_iff, in_app_iff, appCross_correct.
  intuition.
Qed.


Lemma T_el_ge s n m : n m s T m s T n.
Proof.
  intros A B; destruct (el_elAt B); eapply elAt_el, T_ge; eassumption.
Qed.


Lemma T_lam s n : s T n lam s T (S n).
Proof.
  intros; simpl; decide (lam s T n); auto.
  rewrite in_app_iff; simpl; rewrite in_filter_iff, in_app_iff, in_map_iff.
  right. right. split; try left; eauto.
Qed.


Fixpoint exh_size s := match s with
| #n n
| app 1 + exh_size + exh_size
| lam s 1 + exh_size s
end.

Lemma T_exhaustive s : s (T (exh_size s)).
Proof with
  simpl; repeat rewrite in_app_iff; repeat rewrite in_filter_iff; try rewrite appCross_correct; eauto using in_filter_iff, in_app_iff, appCross_correct, el_elAt, elAt_el, T_ge, in_map.
  induction s.
  - destruct n; simpl; auto.
  - eapply T_app; eapply T_el_ge; try eassumption; fold exh_size plus; .
  - now eapply T_lam.
Qed.


Lemma T_longenough m : |T m| > m.
Proof.
  induction m.
  - simpl; .
  - simpl. rewrite app_length. simpl. .
Qed.


Definition g s := match pos s (T (exh_size s)) with None 0 | Some n n end.
Definition g_inv n := match elAt (T n) n with None #0 | Some n n end.

Lemma g_inv_g s : g_inv (g s) = s.
Proof.
  unfold g. destruct( pos s (T (exh_size s)) ) eqn:A.
  unfold g_inv. destruct (T n .[ n ] ) eqn:B.
  - eapply pos_elAt in A.
    destruct (lt_eq_lt_dec n (exh_size s)) as [[D | D] | D].
    + eapply T_ge in B. rewrite B in A. now inv A. .
    + subst. assert (Some s = Some t) by now rewrite A, B. congruence.
    + eapply T_ge in A. rewrite A in B. now inv B. .
  - eapply nth_error_none in B.
    assert (|T n| > n) by eapply T_longenough. .
  - assert (HIn : s T (exh_size s)) by eapply T_exhaustive.
    eapply el_elAt in HIn; eauto. destruct HIn. eapply elAt_el in H. eapply el_pos in H. destruct H. rewrite H in *; congruence.
Qed.


#[export] Hint Unfold left_inverse injective right_inverse surjective : core.

Lemma disjoint_symm X (A B : list X) : disjoint A B disjoint B A.
Proof.
  rewrite !disjoint_forall; firstorder.
Qed.


Lemma map_lam A : x, x map lam A t, x = lam t.
Proof.
  intros x B; rewrite in_map_iff in B; destruct B as [? [B _]]; subst; eauto.
Qed.


Lemma appCross_app A B : x, x appCross A B s t, x = app s t.
Proof.
  induction A.
  - inversion 1.
  - simpl; intros. rewrite in_app_iff in H. destruct H; eauto. rewrite in_map_iff in H. destruct H as [t [ ]]; eauto; subst; eauto.
Qed.


Lemma T_var_not n m : m > n #m T n.
Proof.
  induction n.
  - destruct m; destruct 2; try . congruence. auto.
  - simpl; intros A; rewrite !in_app_iff.
    intros [H | H].
    + eapply IHn; now try .
    + destruct H as [H | H]. inv H; .
      rewrite filter_app in H. rewrite in_app_iff in H.
      destruct H as [H | H]; rewrite in_filter_iff in H; destruct H as [ ].
      * rewrite in_map_iff in . destruct as [x [ _]]. inv .
      * destruct (appCross_app ) as [s [t ]]; inv .
Qed.


Lemma appCross_dupfree A B : dupfree A dupfree B dupfree (appCross A B).
Proof with eauto using dupfree; intuition.
  induction 1; intros dpf_B; simpl...
  eapply dupfree_app...
  -eapply disjoint_forall. intros y D Hy.
  edestruct (appCross_app Hy) as [ [ Hyy]]; subst. eapply appCross_correct in Hy as [].
  eapply in_map_iff in D. destruct D as [d [ ]]. inv ...
  -eapply dupfree_map... congruence.
Qed.


Lemma dupfree_T : n, dupfree (T n).
Proof.
  induction n.
  - simpl. repeat econstructor. eauto.
  - simpl. eapply dupfree_app.
    + eapply disjoint_symm, disjoint_cons. split.
      * eapply T_var_not; .
      * rewrite filter_app, disjoint_symm, disjoint_forall.
        intros s A B. eapply in_app_iff in B. destruct B; eapply in_filter_iff in H;rewrite Dec_reflect in *;tauto.
    + eassumption.
    + eapply dupfreeC. rewrite in_filter_iff.
      intros [A _]. rewrite in_app_iff in A. destruct A.
      eapply map_lam in H. destruct H; inv H.
      eapply appCross_app in H. destruct H as [s [t H]]. inv H.
      eapply dupfree_filter. eapply dupfree_app.
      rewrite disjoint_forall.
      intros x A B. destruct (map_lam A), (appCross_app B) as [ [ ]]. subst. inv .
      eapply dupfree_map; congruence.
      now eapply appCross_dupfree.
Qed.


Lemma T_dup n1 n2 m1 m2 x : T .[ ] = Some x T .[] = Some x = .
Proof.
  destruct (lt_eq_lt_dec ) as [[H | H] | H]; try subst;
  try eapply lt_le_weak in H;
  eauto using (T_ge), dupfree_elAt, dupfree_T.
Qed.


Lemma g_g_inv n : g(g_inv n) = n.
Proof.
  unfold g_inv. destruct (T n .[ n ] ) eqn:B.
  unfold g. destruct( pos t (T (exh_size t)) ) eqn:A.
  - eapply pos_elAt, T_dup in A; eauto.
  - assert (HIn : t T (exh_size t)) by eapply T_exhaustive.

    eapply el_elAt in HIn. destruct HIn.
    eapply elAt_el in H. eapply el_pos in H. destruct H. rewrite H in A. congruence.
  - eapply nth_error_none in B.
    assert (|T n| > n) by eapply T_longenough.
    .
Qed.


#[export] Hint Unfold left_inverse injective surjective g g_inv : core.

Lemma g_T : bijective g.
Proof.
  split.
  - eauto using left_inv_inj, g_inv_g.
  - eauto using right_inv_surjective, g_g_inv.
Qed.


Fixpoint C (n : ) :=
  match n with
    | 0 [(0,0)]
    | S n C n (S n, 0) :: filter ( x Dec (not (x C n))) (map ( p : * let (p1,p2) := p in (,S )) (C n))
  end.

Lemma sublist_C : n : , x B, C (S n) = C n x :: B.
Proof.
  repeat eexists.
Qed.


Lemma C_S p n m : C n .[ m ] = Some p C (S n).[ m ] = Some p.
Proof.
  destruct (sublist_C n) as [x [B H]]. rewrite H. eapply elAt_app.
Qed.


Lemma C_ge p n1 n2 m : C .[ m ] = Some p C .[ m ] = Some p.
Proof.
  remember (S ) as . induction 1; inv ; eauto using C_S.
Qed.


Definition eSize (p : * ) := let (n,m) := p in 1 + n + m.

Lemma C_exhaustive p : p C( eSize p ).
Proof.
  destruct p as [n m]. induction m.
  - simpl. rewrite plus_0_r. destruct n; simpl; auto.
  - simpl.
    decide ( (n,S m) C (n + S m) ).
    + auto.
    + eapply in_app_iff. right. right.
      eapply in_filter_iff. split.
      * eapply in_map_iff. exists (n, m). split. reflexivity. assert (n + S m = 1 + n + m) by . rewrite H. eassumption.
      * rewrite Dec_reflect. eassumption.
Qed.


Lemma C_longenough n : |C n| > n.
Proof.
  induction n; simpl.
  - .
  - rewrite app_length. simpl. .
Qed.


Definition c n : * := match elAt (C n) n with None (0,0) | Some p p end.
Definition c_inv p : := match pos p (C (eSize p)) with None 0 | Some p p end.

Lemma c_c_inv p : c (c_inv p) = p.
Proof.
  unfold c_inv. destruct( pos p (C (eSize p)) ) eqn:A.
  unfold c. destruct (elAt (C n) n ) eqn:B.
  - eapply pos_elAt in A.
    destruct (lt_eq_lt_dec n (eSize p)) as [[D | D] | D].
    + eapply C_ge in B. rewrite B in A. now inv A. .
    + subst. cut (Some p = Some ); try congruence. now rewrite A, B.
    + eapply C_ge in A. rewrite A in B. now inv B. .
  - eapply nth_error_none in B.
    assert (|C n| > n) by eapply C_longenough. .
  - assert (HIn : p C (eSize p)) by eapply C_exhaustive.
    eapply el_elAt in HIn. destruct HIn. eapply elAt_el in H. eapply el_pos in H. destruct H. rewrite H in *. congruence.
Qed.


Lemma c_surj : surjective c.
Proof.
  eapply right_inv_surjective. unfold right_inverse. eapply c_c_inv.
Qed.