Autosubst.Autosubst_Derive

Tactics to generate substitution operations and show the basic lemmas.
Require Import Autosubst_Basics Autosubst_MMap Autosubst_Classes Autosubst_Tactics Autosubst_Lemmas.

Ltac app_var := match goal with [ |- var] assumption end.

Ltac derive_Ids := intro; solve
  [ constructor 1; [app_var] | constructor 2; [app_var]
  | constructor 3; [app_var] | constructor 4; [app_var]
  | constructor 5; [app_var] | constructor 6; [app_var]
  | constructor 7; [app_var] | constructor 8; [app_var]
  | constructor 9; [app_var] | constructor 10; [app_var]
  | constructor 11; [app_var] | constructor 12; [app_var]
  | constructor 13; [app_var] | constructor 14; [app_var]
  | constructor 15; [app_var] | constructor 16; [app_var]
  | constructor 17; [app_var] | constructor 18; [app_var]
  | constructor 19; [app_var] | constructor 20; [app_var]].
Hint Extern 0 (Ids _) derive_Ids : derive.

Ltac derive_Rename :=
  match goal with [ |- Rename ?term ]
    hnf; fix inst 2; change _ with (Rename term) in inst;
    intros s; change (annot term s); destruct s;
    match goal with
      | [ |- annot _ ?t ]
        let rec map s :=
            (match s with
               | ? ?
                 let := map in
                 let T := typeof in
                 let :=
                     match T with
                       | term constr:(rename )
                       | var constr:( )
                       | _bind term _ 1 constr:(rename (upren ) )
                       | _bind term _ ?n
                           constr:(rename (iterate upren n ) )
                       | context[_bind term _ 1]
                           constr:(mmap (rename (upren )) )
                       | context[_bind term _ ?n]
                           constr:(mmap (rename (iterate upren n )) )
                       | context[term] constr:(mmap (rename ) )
                       | _
                     end in
                 constr:( )
               | _ s
             end) in
        let t := map t in exact t
    end
  end.
Hint Extern 0 (Rename _) derive_Rename : derive.

Ltac has_var s :=
  match s with
    | ? ?
      match has_var with
        | Some ?x constr:(Some x)
        | _
          match typeof with
            | var constr:(Some )
            | _ None
          end
      end
    | _ None
  end.

Ltac derive_Subst :=
  match goal with [ |- Subst ?term ]
    require_instance (Rename term);
    hnf; fix inst 2; change _ with (Subst term) in inst;
    intros s; change (annot term s); destruct s;
    match goal with
      | [ |- annot _ ?t ]
        let rec map s :=
            (match s with
               | ? ?
                 let := map in
                 let T := typeof in
                 let :=
                   match T with
                     | term constr:(subst )
                     | _bind term _ 1 constr:(subst (up ) )
                     | _bind term _ ?n constr:(subst (upn n ) )
                     | _bind ?hterm _ ?n
                         constr:(subst ( >>| (ren(+n) : var hterm)) )
                     | context[_bind term _ 1]
                         constr:(mmap (subst (up )) )
                     | context[_bind term _ ?n]
                         constr:(mmap (subst (upn n )) )
                     | context[_bind ?hterm _ ?n]
                         constr:(mmap
                                   (subst ( >>| (ren(+n) : var hterm)))
                                   )
                     | context[term] constr:(mmap (subst ) )
                     | _
                   end in
                 constr:( )
               | _ s
             end) in
        match has_var t with
          | Some ?x exact ( x)
          | _ let t := map t in exact t
        end
    end
  end.
Hint Extern 0 (Subst _) derive_Subst : derive.

Ltac derive_HSubst :=
  match goal with [ |- HSubst ?inner ?outer ]
    require_instance (Subst inner);
    hnf; fix inst 2; change _ with (HSubst inner outer) in inst;
    intros s; change (annot outer s); destruct s;
    match goal with
      | [ |- annot _ ?t ]
        let rec map s :=
            (match s with
               | ? ?
                 let := map in
                 let T := typeof in
                 let :=
                   match T with
                     | inner constr:(subst )
                     | outer constr:(hsubst )
                     | _bind inner outer 1 constr:(hsubst (up ) )
                     | _bind inner inner 1 constr:(subst (up ) )
                     | _bind inner outer ?n
                         constr:(hsubst (upn n ) )
                     | _bind inner inner ?n
                         constr:(subst (upn n ) )
                     | _bind _ outer _ constr:(hsubst )
                     | _bind _ inner _ constr:(subst )
                     | context[_bind inner outer 1]
                         constr:(mmap (hsubst (up )) )
                     | context[_bind inner inner 1]
                         constr:(mmap (subst (up )) )
                     | context[_bind inner outer ?n]
                         constr:(mmap (hsubst (upn n )) )
                     | context[_bind inner inner ?n]
                         constr:(mmap (subst (upn n )) )
                     | context[_bind _ outer _]
                         constr:(mmap (hsubst ) )
                     | context[_bind _ inner _]
                         constr:(mmap (subst ) )
                     | context[inner] constr:(mmap (subst ) )
                     | context[outer] constr:(mmap (hsubst ) )
                     | _
                   end in
                 constr:( )
               | _ s
             end) in
        let t := map t in exact t
    end
  end.
Hint Extern 0 (HSubst _ _) derive_HSubst : derive.

Lemma mmap_id_ext {A B} {inst : MMap A B} `{@MMapLemmas A B inst}
  `{@MMapExt A B inst} (f : A A) (b : B) :
  ( x, f x = x) mmap f b = b.
Proof. intros E. rewrite (mmap_ext E). apply mmap_id. Defined.

Lemma iter_fp {A} (f : A A) x :
  f x = x n, iterate f n x = x.
Proof.
  intros E. induction n. reflexivity. now rewrite iterate_S, IHn.
Qed.


Lemma iter_param {A B} (f : A A) (g : B B) (h : A B) :
  ( x, g (h x) = h (f x))
  ( x n, iterate g n (h x) = h (iterate f n x)).
Proof.
  intros E x n. induction n. reflexivity. now rewrite !iterate_S, IHn, E.
Qed.


Section InternalLemmas.

Context {term : Type} {Ids_term : Ids term}
  {Rename_term : Rename term} {Subst_term : Subst term}.

Lemma up_upren_internal :
  ( x, rename (ids x) = ids ( x))
  ( : var var, up (ren ) = ren (upren )).
Proof.
  intros E . f_ext. intros [|x]. reflexivity. apply E.
Qed.


Lemma up_upren_n_internal :
  ( , up (ren ) = ren (upren ))
  ( n, upn n (ren ) = ren (iterate upren n )).
Proof.
  apply (iter_param upren up (fun x ren x)).
Qed.


Lemma up_id_internal :
  ( x, rename (ids x) = ids ( x))
  up ids = ids :> (var term).
Proof.
  intros h. f_ext. intros [|x]. reflexivity. apply h.
Qed.


Lemma up_id_n_internal :
  up ids = ids ( n, upn n ids = ids).
Proof.
  apply iter_fp.
Qed.


Lemma up_comp_ren_subst ( : var term) :
  up ( >>> ) = upren >>> up .
Proof. f_ext. intros [|x]; reflexivity. Qed.

Lemma up_comp_ren_subst_n ( : var term) n :
  upn n ( >>> ) = iterate upren n >>> upn n .
Proof.
  induction n. reflexivity. rewrite !iterate_S, IHn. apply up_comp_ren_subst.
Qed.


Lemma up_comp_subst_ren_internal :
  ( x, rename (ids x) = ids ( x))
  ( s, rename s = s.[ren ])
  ( s, (rename s).[] = s.[ >>> ])
  ( , up ( >>> rename ) = up >>> rename (upren )).
Proof.
  intros . f_ext. intros [|x]. unfold up; simpl.
  now rewrite . unfold up. simpl. now rewrite , , , .
Qed.


Lemma up_comp_subst_ren_n_internal :
  ( , up ( >>> rename ) = up >>> rename (upren ))->
  ( ( : var term) n,
    upn n ( >>> rename ) = upn n >>> rename (iterate upren n )).
Proof.
  intros E n. induction n. reflexivity. rewrite !iterate_S, IHn.
  apply E.
Qed.


Lemma up_comp_internal :
  ( x, (ids x).[] = x)
  ( s, (rename s).[] = s.[ >>> ])
  ( s, rename s.[] = s.[ >>> rename ])
  ( , up ( ) = up up ).
Proof.
  intros . f_ext. intros [|x]; unfold up; simpl.
  now rewrite . now rewrite , .
Qed.


Lemma up_comp_n_internal :
  ( , up ( ) = up up )
  ( n,
    upn n ( ) = upn n upn n ).
Proof.
  intros h sigam n. induction n. reflexivity. rewrite !iterate_S, IHn.
  apply h.
Qed.


End InternalLemmas.

Section InternalLemmasHSubst.

Context {inner : Type} {Ids_inner : Ids inner}
  {Rename_inner : Rename inner} {Subst_inner : Subst inner}
  {SubstLemmas_inner : SubstLemmas inner}.

Context {outer : Type} {Ids_outer : Ids outer}
  {Rename_outer : Rename outer} {HSubst_inst : HSubst inner outer}
  {HSubstLemmas_inst : HSubstLemmas inner outer}.

Implicit Types ( : var outer) ( : var inner) (s : outer).

Lemma up_hcomp_internal :
  ( s, rename s.|[] = (rename s).|[])
  ( , up ( >>| ) = up >>| ).
Proof.
  intros E . f_ext. intros [|x]; unfold up; simpl.
  now rewrite id_hsubst. apply E.
Qed.


Lemma up_hcomp_n_internal :
  ( , up ( >>| ) = up >>| )
   n, upn n ( >>| ) = upn n >>| .
Proof.
  intros E n. induction n. reflexivity.
  rewrite !iterate_S, IHn. apply E.
Qed.


Lemma hcomp_lift_n_internal n :
  ( >>| ) >>| ren (+n) = ( >>| ren (+n)) >>| upn n .
Proof.
  f_ext. intros x; simpl. now rewrite !hsubst_comp, up_liftn.
Qed.


Lemma hcomp_lift_internal :
  ( >>| ) >>| ren (+1) = ( >>| ren (+1)) >>| up .
Proof. apply hcomp_lift_n_internal. Qed.

Context {Subst_outer : Subst outer}
  {SubstHSubstComp_inst : SubstHSubstComp inner outer}.

Lemma hcomp_ren_internal :
  ( s, rename s = s.[ren ])
  ( >>> rename ) >>| = ( >>| ) >>> rename .
Proof.
  intros E. f_ext. intros x. simpl. rewrite !E, subst_hsubst_comp.
  f_equal. f_ext. intros y. simpl. now rewrite id_hsubst.
Qed.


Lemma hcomp_dist_internal :
  ( ) >>| = ( >>| ) ( >>| ).
Proof.
  f_ext. intros x. simpl. apply subst_hsubst_comp.
Qed.


End InternalLemmasHSubst.

Ltac derive_SubstLemmas :=
  match goal with
    [ |- @SubstLemmas ?term ?Ids_term ?Rename_term ?Subst_term]
    let rename := constr:(@rename term Rename_term) in
    let subst := constr:(@subst term Subst_term) in
    let ids := constr:(@ids term Ids_term) in
    let up := constr:(@up term Ids_term Rename_term) in

    (* rename subst *)

    assert (rename_subst :
               (s : term), rename s = subst (ren ) s) by (
     assert (up_upren :
                , up (ren ) = ren (upren )) by
       (apply up_upren_internal; reflexivity);
     assert (up_upren_n :
                n, upn n (ren ) = ren (iterate upren n )) by
       (apply up_upren_n_internal, up_upren);
     fix ih 2; intros s; destruct s; try reflexivity; simpl; f_equal;
     try apply mmap_ext; intros; rewrite ?up_upren, ?up_upren_n; apply ih);

    (* subst id *)

    assert (subst_id : (s : term), subst ids s = id s) by (
     assert (up_id : up ids = ids) by
       (apply up_id_internal; reflexivity);
     assert (up_id_n : n, upn n ids = ids) by
       (apply up_id_n_internal, up_id);
     fix ih 1; intros s; destruct s; simpl; f_equal; try reflexivity;
     rewrite ?up_id, ?up_id_n; try apply mmap_id_ext; intros; apply ih);

    (* subst comp *)

    assert (ren_subst_comp :
        (s : term), (rename s).[] = s.[ >>> ]) by(
     fix ih 3; intros s; destruct s; try reflexivity; simpl; f_equal;
     rewrite ?up_comp_ren_subst, ?up_comp_ren_subst_n, ?mmap_comp;
     try apply mmap_ext; intros; apply ih);

    assert (subst_ren_comp : (s : term),
      rename s.[] = s.[ >>> rename ]) by (
     assert (up_comp_subst_ren :
       , up ( >>> rename ) = up >>>rename (upren ))
      by (apply up_comp_subst_ren_internal; [reflexivity|
          apply rename_subst| apply ren_subst_comp]);
     assert (up_comp_subst_ren_n :
       n, upn n ( >>> rename ) = upn n >>> rename (iterate upren n ))
      by (apply up_comp_subst_ren_n_internal; apply up_comp_subst_ren);
     fix ih 3; intros s; destruct s; try reflexivity; simpl;
     f_equal; rewrite ?up_comp_subst_ren, ?up_comp_subst_ren_n, ?mmap_comp;
     try (rewrite hcomp_ren_internal; [|apply rename_subst]);
     try apply mmap_ext; intros; apply ih);

    assert (subst_comp :
           (s : term), s.[].[] = s.[ ]) by (
     assert (up_comp : ( : var term), up ( ) = up up )
      by (apply up_comp_internal; [reflexivity|apply ren_subst_comp|apply subst_ren_comp]);
     assert (up_comp_n : n, upn n ( ) = upn n upn n )
      by (apply up_comp_n_internal; apply up_comp);
     fix ih 3; intros s; destruct s; try reflexivity; simpl; f_equal;
     rewrite ?up_comp, ?up_comp_n, ?mmap_comp, ?hcomp_dist_internal;
     try apply mmap_ext; intros; apply ih);

  constructor; hnf;
    [apply rename_subst|apply subst_id|reflexivity|apply subst_comp]
  end.
Hint Extern 0 (SubstLemmas _) derive_SubstLemmas : derive.

Ltac derive_HSubstLemmas :=
  match goal with [|- HSubstLemmas ?inner ?outer ]
  let ids := constr:(ids : var inner) in

  assert (hsubst_id : (s : outer), s.|[ids] = s) by (
    fix ih 1; intros s; destruct s; try reflexivity; simpl; f_equal;
    rewrite ?up_id, ?up_id_n; try apply mmap_id_ext; intros;
    (apply subst_id || apply ih)
  );

  assert (hsubst_comp : ( : var inner) (s : outer),
    s.|[].|[] = s.|[ ])
  by (
    fix ih 3; intros s; destruct s; try reflexivity; simpl; f_equal;
    rewrite ?up_comp, ?up_comp_n, ?mmap_comp; try apply mmap_ext; intros;
    (apply subst_comp || apply ih)
  );

  constructor; hnf; [exact hsubst_id|reflexivity|exact hsubst_comp]
  end.
Hint Extern 0 (HSubstLemmas _ _) derive_HSubstLemmas : derive.

Ltac derive_SubstHSubstComp :=
  match goal with [|- SubstHSubstComp ?inner ?outer ] hnf;

  assert (ren_hsubst_comp : ( : var inner) (s : outer),
    rename s.|[] = (rename s).|[]
  ) by (
    fix ih 3; intros s; destruct s; try reflexivity; simpl; f_equal;
    rewrite ?mmap_comp; try apply mmap_ext; intros; simpl; apply ih
  );

  assert (up_hcomp : ( : var outer) ( : var inner),
    up ( >>| ) = up >>|
  ) by (
    apply up_hcomp_internal; apply ren_hsubst_comp
  );

  assert (up_hcomp_n : ( : var outer) ( : var inner) n,
    upn n ( >>| ) = upn n >>|
  ) by (
    apply up_hcomp_n_internal; apply up_hcomp
  );

  fix ih 3; intros s; destruct s; try reflexivity; simpl; f_equal;
  rewrite ?up_hcomp, ?up_hcomp_n, ?hcomp_lift_n_internal, ?mmap_comp;
  try apply mmap_ext; intros; apply ih
  end.
Hint Extern 0 (SubstHSubstComp _ _) derive_SubstHSubstComp : derive.

(* Local Variables: *)
(* coq-load-path: (("." "Autosubst")) *)
(* End: *)