(** ** 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 ?y |- _ => rewrite H in *; clear H
        end).(** ** 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).Ltac fsetdec_rec :=
      auto with FSet_Auto;
      subst++;
      try (match goal with
      | H: E.eq ?x ?x -> False |- _ => destruct H
      end);
      (reflexivity ||
      contradiction ||
      (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 :=
      inst_FSet_hypotheses;
      autorewrite with set_simpl in *;
      push not in * using FSet_decidability;
      substFSet;
      assert_decidability;
      auto with FSet_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;
    (** 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;

    (** SCW: this autorewrite can introduce [=], we need to to
        it before the next step and not just in the body. *)
    autorewrite with set_simpl in *;

    (** 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 =>
      if 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_conj : 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.