(** ** Normalizing Propositions About Equality
        We have to deal with the fact that [E.eq] may be
        convertible with Coq's equality.  Thus, we will find the
        following tactics useful to replace one form with the
        other everywhere. *)

    (** The next tactic, [Logic_eq_to_E_eq], mentions the term
        [E.t]; thus, we must ensure that [E.t] is used in favor
        of any other convertible but syntactically distinct
        term. *)
    Ltac change_to_E_t :=
      repeat (
        match goal with
        | H : ?T |- _ =>
          progress (change T with E.t in H);
          repeat (
            match goal with
            | J : _ |- _ => progress (change T with E.t in J)
            | |- _ => progress (change T with E.t)
            end )
        | H : forall x : ?T, _ |- _ =>
          progress (change T with E.t in H);
          repeat (
            match goal with
            | J : _ |- _ => progress (change T with E.t in J)
            | |- _ => progress (change T with E.t)
            end )
       end).(** These two tactics take us from Coq's built-in equality
        to [E.eq] (and vice versa) when possible. *)

    Ltac Logic_eq_to_E_eq :=
      repeat (
        match goal with
        | H: _ |- _ =>
          progress (change (@Logic.eq E.t) with E.eq in H)
        | |- _ =>
          progress (change (@Logic.eq E.t) with E.eq)
        end).Ltac E_eq_to_Logic_eq :=
      repeat (
        match goal with
        | H: _ |- _ =>
          progress (change E.eq with (@Logic.eq E.t) in H)
        | |- _ =>
          progress (change E.eq with (@Logic.eq E.t))
        end).(** This tactic works like the built-in tactic [subst], but
        at the level of set element equality (which may not be
        the convertible with Coq's equality). *)
    Ltac substFSet :=
      repeat (
        match goal with
        | H: E.eq ?x ?x |- _ => clear H
        | H: E.eq ?x ?y |- _ => rewrite H in *; clear H
        end);
      autorewrite with set_eq_simpl in *.(** ** Considering Decidability of Base Propositions
        This tactic adds assertions about the decidability of
        [E.eq] and [In] to the context.  This is necessary for
        the completeness of the [fsetdec] tactic.  However, in
        order to minimize the cost of proof search, we should be
        careful to not add more than we need.  Once negations
        have been pushed to the leaves of the propositions, we
        only need to worry about decidability for those base
        propositions that appear in a negated form. *)
    Ltac assert_decidability :=
      (** We actually don't want these rules to fire if the
          syntactic context in the patterns below is trivially
          empty, but we'll just do some clean-up at the
          afterward.  *)
      repeat (
        match goal with
        | H: context [~ E.eq ?x ?y] |- _ =>
          assert new (E.eq x y \/ ~ E.eq x y) by (apply dec_eq)
        | H: context [~ In ?x ?s] |- _ =>
          assert new (In x s \/ ~ In x s) by (apply dec_In)
        | |- context [~ E.eq ?x ?y] =>
          assert new (E.eq x y \/ ~ E.eq x y) by (apply dec_eq)
        | |- context [~ In ?x ?s] =>
          assert new (In x s \/ ~ In x s) by (apply dec_In)
        end);
      (** Now we eliminate the useless facts we added (because
          they would likely be very harmful to performance). *)
      repeat (
        match goal with
        | _: ~ ?P, H : ?P \/ ~ ?P |- _ => clear H
        end).(** ** Handling [Empty], [Subset], and [Equal]
        This tactic instantiates universally quantified
        hypotheses (which arise from the unfolding of [Empty],
        [Subset], and [Equal]) for each of the set element
        expressions that is involved in some membership or
        equality fact.  Then it throws away those hypotheses,
        which should no longer be needed. *)
    Ltac inst_FSet_hypotheses :=
      repeat (
        match goal with
        | H : forall a : E.t, _,
          _ : context [ In ?x _ ] |- _ =>
          let P := type of (H x) in
          assert new P by (exact (H x))
        | H : forall a : E.t, _
          |- context [ In ?x _ ] =>
          let P := type of (H x) in
          assert new P by (exact (H x))
        | H : forall a : E.t, _,
          _ : context [ E.eq ?x _ ] |- _ =>
          let P := type of (H x) in
          assert new P by (exact (H x))
        | H : forall a : E.t, _
          |- context [ E.eq ?x _ ] =>
          let P := type of (H x) in
          assert new P by (exact (H x))
        | H : forall a : E.t, _,
          _ : context [ E.eq _ ?x ] |- _ =>
          let P := type of (H x) in
          assert new P by (exact (H x))
        | H : forall a : E.t, _
          |- context [ E.eq _ ?x ] =>
          let P := type of (H x) in
          assert new P by (exact (H x))
        end);
      repeat (
        match goal with
        | H : forall a : E.t, _ |- _ =>
          clear H
        end).(** ** The Core [fsetdec] Auxiliary Tactics *)

    (** Here is the crux of the proof search.  Recursion through
        [intuition]!  (This will terminate if I correctly
        understand the behavior of [intuition].) *)
    Ltac fsetdec_rec := progress substFSet; intuition fsetdec_rec.(** If we add [unfold Empty, Subset, Equal in *; intros;] to
        the beginning of this tactic, it will satisfy the same
        specification as the [fsetdec] tactic; however, it will
        be much slower than necessary without the pre-processing
        done by the wrapper tactic [fsetdec]. *)
    Ltac fsetdec_body :=
      autorewrite with set_eq_simpl in *;
      inst_FSet_hypotheses;
      autorewrite with set_simpl set_eq_simpl in *;
      push not in * using FSet_decidability;
      substFSet;
      assert_decidability;
      auto;
      (intuition fsetdec_rec) ||
      fail 1
        "because the goal is beyond the scope of this tactic".(** * The [fsetdec] Tactic
      Here is the top-level tactic (the only one intended for
      clients of this library).  It's specification is given at
      the top of the file. *)
  Ltac fsetdec :=
    (** We first unfold any occurrences of [iff]. *)
    unfold iff in *;
    (** We fold occurrences of [not] because it is better for
        [intros] to leave us with a goal of [~ P] than a goal of
        [False]. *)
    fold any not; intros;
    (** We don't care about the value of elements : complex ones are
        abstracted as new variables (avoiding potential dependencies,
        see bug #2464) *)
    abstract_elements;
    (** We remove dependencies to logical hypothesis. This way,
        later "clear" will work nicely (see bug #2136) *)
    no_logical_interdep;
    (** Now we decompose conjunctions, which will allow the
        [discard_nonFSet] and [assert_decidability] tactics to
        do a much better job. *)
    decompose records;
    discard_nonFSet;
    (** We unfold these defined propositions on finite sets.  If
        our goal was one of them, then have one more item to
        introduce now. *)
    unfold Empty, Subset, Equal in *; intros;
    (** We now want to get rid of all uses of [=] in favor of
        [E.eq].  However, the best way to eliminate a [=] is in
        the context is with [subst], so we will try that first.
        In fact, we may as well convert uses of [E.eq] into [=]
        when possible before we do [subst] so that we can even
        more mileage out of it.  Then we will convert all
        remaining uses of [=] back to [E.eq] when possible.  We
        use [change_to_E_t] to ensure that we have a canonical
        name for set elements, so that [Logic_eq_to_E_eq] will
        work properly.  *)
    change_to_E_t; E_eq_to_Logic_eq; subst++; Logic_eq_to_E_eq;
    (** The next optimization is to swap a negated goal with a
        negated hypothesis when possible.  Any swap will improve
        performance by eliminating the total number of
        negations, but we will get the maximum benefit if we
        swap the goal with a hypotheses mentioning the same set
        element, so we try that first.  If we reach the fourth
        branch below, we attempt any swap.  However, to maintain
        completeness of this tactic, we can only perform such a
        swap with a decidable proposition; hence, we first test
        whether the hypothesis is an [FSet_elt_Prop], noting
        that any [FSet_elt_Prop] is decidable. *)
    pull not using FSet_decidability;
    unfold not in *;
    match goal with
    | H: (In ?x ?r) -> False |- (In ?x ?s) -> False =>
      contradict H; fsetdec_body
    | H: (In ?x ?r) -> False |- (E.eq ?x ?y) -> False =>
      contradict H; fsetdec_body
    | H: (In ?x ?r) -> False |- (E.eq ?y ?x) -> False =>
      contradict H; fsetdec_body
    | H: ?P -> False |- ?Q -> False =>
      tryif prop (FSet_elt_Prop P) holds by
        (auto 100 with FSet_Prop)
      then (contradict H; fsetdec_body)
      else fsetdec_body
    | |- _ =>
      fsetdec_body
    end.

Lemma test_not_In_disj : forall x y s1 s2 s3 s4, ~ In x (union s1 (union s2 (union s3 (add y s4)))) -> ~ (In x s1 \/ In x s4 \/ E.eq y x).