Lvc.Infra.AutoIndTac

Require Import List.
Require Export Smpl LvcPlugin LengthEq MoreInversion.

Set Implicit Arguments.

(* fail 1 will break from the 'match H with', and indicate to
   the outer match that it should consider finding another
   hypothesis, see documentation on match goal and fail
   This tactic is a variation of Tobias Tebbi's revert_except_until *)


Smpl Create norevert.

Ltac OK_to_revert H := smpl norevert H.

Ltac revert_except E :=
  repeat match goal with
           [H : _ |- _] ⇒
           match H with
             | Efail 1
             | _first [ OK_to_revert H | revert H ]
               end
         end.

Ltac clear_except E :=
  repeat match goal with
           [H : _ |- _] ⇒
           match H with
             | Efail
             | _clear H
               end
         end.

Ltac clear_all :=
  repeat match goal with
           [H : _ |- _] ⇒ clear H
         end.

Ltac revert_all :=
  repeat match goal with
           [H : _ |- _] ⇒ revert H
         end.

(*
(* succeed if H has a function type, fail otherwise *)
Ltac is_ftype H :=
  let t := type of H in
    let t' := eval cbv in t in
      match t' with
        | _ -> _ => idtac
      end.
*)

(* match on the type of E and remember each of it's arguments
   that is not a variable by calling tac.
   tac needs to do a remember exactly if x is not a var, and
   fail otherwise. (We need to fail, otherwise the repeat will
   stop prematurely)
   try will silently ignore a fail 0, but will fail if a fail 1 or
   above occurs. Sequentialization makes sure fail 1 is executed
   if is_var is successful, hence try (is_var x; fail 1) will
   fail exactly when x is a var. *)


Class DoNotRemember (T:Type) := DNR { Q:Type }.

Ltac remember_arguments E :=
  let tac t x := ( try (is_param t 0; fail 1);
                  try (is_var x; fail 1);
                  try (assert (DoNotRemember x) by eauto with typeclass_instances; fail 1 );
                  remember (x))
  in
  repeat (match type of E with
    | ?t ?x _ _ _ _ _ _ _ _ _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _ _ _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _ _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _ _tac t x
    | ?t ?x _ _ _ _ _tac t x
    | ?t ?x _ _ _ _tac t x
    | ?t ?x _ _ _tac t x
    | ?t ?x _ _tac t x
    | ?t ?x _tac t x
    | ?t ?xtac t x
  end).

(* from Coq.Program.Tactics *)
Ltac clear_dup :=
(*  match goal with
  |  H : ?X, H' : ?X |- _  => clear H' || clear H
  end.*)

  match goal with
  | [ H : ?X |- _ ] ⇒
    match goal with
    | [ H' : ?Y |- _ ] ⇒
      match H with
      | H'fail 2
      | _unify X Y ; (clear H' || clear H)
      end
    end
  end.

Ltac clear_if_dup H :=
  match type of H with
    | ?X
      match goal with
      | [ H' : ?Y |- _ ] ⇒
        match H with
        | H'fail 2
        | _unify X Y ; (clear H' || clear H)
        end
      end
  end.

Ltac inductify H :=
  match type of H with
  | length _ = length _eapply length_length_eq in H
  end.

Ltac expose_inductive H :=
  first [ is_inductive H | hnf in H ].

Tactic Notation "general" "induction" hyp(H) :=
  (try inductify H);
  expose_inductive H;
  remember_arguments H; revert_except H;
  induction H; intros; (try clear_trivial_eqs);
  repeat (smpl inversion_cleanup).

Tactic Notation "indros" :=
  intros; (try inv_eqs); (try clear_trivial_eqs).

Module Test.

  Inductive decreasing : list nat Prop :=
  | base : decreasing nil
  | step m n L : decreasing (n::L) n m decreasing (m :: n :: L).

  Lemma all_zero_by_hand L
    : decreasing (0::L) x, In x L x = 0.
  Proof.
    intros. remember (0::L).
    revert dependent L. revert x. induction H; intros.
    inversion Heql.
    inversion Heql. subst. inversion H0; subst; firstorder.
  Qed.

  Lemma all_zero L
    : decreasing (0::L) x, In x L x = 0.
  Proof.
    intros. general induction H.
    firstorder.
  Qed.

End Test.