(** To streamline the proof (which you are to fill in), the [omega] tactic, which is enabled by the following [Require], is helpful in several places for automatically completing tedious low-level arguments involving equalities or inequalities over natural numbers.  We'll return to [omega] in a later chapter, but feel free to experiment with it now if you like.  The first case of the induction gives an example of how it is used. *)
(** In the first branch after [destruct], we explicitly apply the [beq_nat_true_iff] lemma to the equation generated by destructing [beq_nat n m], to convert the assumption [beq_nat n m = true] into the assumption [n = m]; then we had to [rewrite] using this assumption to complete the case.  We can streamline this by defining an inductive proposition that yields a better case-analysis principle for [beq_nat n m]. Instead of generating an equation such as [beq_nat n m = true], which is generally not directly useful, this principle gives us right away the assumption we really need: [n = m].  We'll actually define something a bit more general, which can be used with arbitrary properties (and not just equalities): *)
Module FirstTry.
Inductive reflect : Prop -> bool -> Prop :=
| ReflectT : forall (P:Prop), P -> reflect P true
| ReflectF : forall (P:Prop), ~ P -> reflect P false.
(** Before explaining this, let's rearrange it a little: Since the types of both [ReflectT] and [ReflectF] begin with [forall (P:Prop)], we can make the definition a bit more readable and easier to work with by making [P] a parameter of the whole Inductive declaration. *)
End FirstTry.
Inductive reflect (P : Prop) : bool -> Prop :=
| ReflectT : P -> reflect P true
| ReflectF : ~ P -> reflect P false.
(** The [reflect] property takes two arguments: a proposition [P] and a boolean [b].  Intuitively, it states that the property [P] is _reflected_ in (i.e., equivalent to) the boolean [b]: [P] holds if and only if [b = true].  To see this, notice that, by definition, the only way we can produce evidence that [reflect P true] holds is by showing that [P] is true and using the [ReflectT] constructor.  If we invert this statement, this means that it should be possible to extract evidence for [P] from a proof of [reflect P true].  Conversely, the only way to show [reflect P false] is by combining evidence for [~ P] with the [ReflectF] constructor.  It is easy to formalize this intuition and show that the two statements are indeed equivalent: *)

(* Specifications: *)
Theorem iff_reflect : forall P b, (P <-> b = true) -> reflect P b.
(** **** Exercise: 2 stars, recommended (reflect_iff)  *)
Theorem reflect_iff : forall P b, reflect P b -> (P <-> b = true).
(** The new proof of [filter_not_empty_In] now goes as follows. Notice how the calls to [destruct] and [apply] are combined into a single call to [destruct]. *)
(** (To see this clearly, look at the two proofs of [filter_not_empty_In] with Coq and observe the differences in proof state at the beginning of the first case of the [destruct].) *)
Theorem filter_not_empty_In' : forall n l,
filter (beq_nat n) l <> [] ->
In n l.
Theorem iff_reflect : forall P b, (P <-> b = true) -> reflect P b.
(** **** Exercise: 2 stars, recommended (reflect_iff)  *)
Theorem reflect_iff : forall P b, reflect P b -> (P <-> b = true).
(** The new proof of [filter_not_empty_In] now goes as follows. Notice how the calls to [destruct] and [apply] are combined into a single call to [destruct]. *)
(** (To see this clearly, look at the two proofs of [filter_not_empty_In] with Coq and observe the differences in proof state at the beginning of the first case of the [destruct].) *)
Theorem filter_not_empty_In' : forall n l,
filter (beq_nat n) l <> [] ->
In n l.