Section Proper.
Local Open Scope signature_scope.
(* SAZ: This proof is a bit annoying.  We can only rewrite under the "upto" paco2 predicate (see the eq_itree_paco instance in Eq), which means we have to introduce names, start the upto proof, do the rewrite, and then regeneralize for the CIH.  It would be nicer if we could rewrite under (paco2 _ r). *)
Instance proper_interpret_state {S R} : Proper ((@eq_itree (stateE S) R _ eq) ==> (@eq S) ==> (@eq_itree void1 (S * R) _ eq)) interpret_state.
End Proper.
(* Defining Predicates Over ITrees ------------------------------------------ *)
(* For the sake of example, let's prove that an itree that does not contain any Get events, when interpreted in the standard state monad, does not actually depend on the (initial) state.  *)
(* First, we define a predicate [NoGets], which characterizes the itrees that don't have [Get] events.  There are basically two options for doing this: (1) write a "native" CoInductive predicate, or (2) write a paco-compatible predicate.  Since we advocate using paco for coinductive proofs, we'll follow option (2). *)
(* Paco requires that we state our predicate "functorially", which means that we specify a _predicate transformer_ (i.e. function from relations to relations) and then prove that it is monotonic.  Paco then constructs the greatest fixed point, which we can use with the paco tactics pcofix, etc.  In this case, [NoGets] is a _unary_ predicate, so all of our "relation transformers" are "unary predicate transformers".  For more sophisticated examples of binary predicates, see the definitions of [eq_itree] and [eutt]. *)
(* Due to the separation of the [itree] definition into [itreeF] and [itree] proper, it is convenient to split up the [NoGets] predicate in an analogous way. *)
(* First, we define [NoGetsF]. It takes as input [rec], which will be instantiated to the [NoGets] predicate for subtrees.  It yields a predicate on [itreeF] nodes. *)
Variant NoGetsF {S R} (rec : itree (stateE S) R -> Prop) : itreeF (stateE S) R (itree (stateE S) R) -> Prop :=
| isRet : forall (r:R), NoGetsF rec (RetF r)
| isTau : forall t, rec t -> NoGetsF rec (TauF t)
| isPut : forall (k : unit -> itree (stateE S) R), forall (s:S), rec (k tt) -> NoGetsF rec (VisF (Put s) k).
Hint Constructors NoGetsF.
(* Next, we lift the [NoGetsF] predicate through the [itree] [go] constructor to obtain a predicate transformer on [itree stateE R].  This is achieved just by asking composing NoGetsF with [observe]. *)
(* SAZ: n.b. for some reason, we have adopted the [Foo_] notational convention for the predicate transformer whose greatest fixpoint is [Foo].  Is this good?  SAZ: Actually, I see that we're inconsistent between [eq_itree] and [eutt] with these naming conventions. *)
Definition NoGets_ {S R} (rec : itree (stateE S) R -> Prop) (t : itree (stateE S) R) : Prop :=
NoGetsF rec (observe t).
(* Next, we need to prove that [NoGets_] is a monotone function on relations, which means that paco can take its greatest fixpoint.  Monotonicity of [NoGets_] depends on monotonicity of [NoGetsF].  Fortunately, paco provides the tactic [pmonauto] which almost always discharges these proofs.  It also provides the definitions monotone1, monotone2, etc. for monotonicity at different arities of relations. *)
(* SAZ: Arguably, the LE fact should be defined via some named property.  In the Coq Relationclasses library, there is a definition of subrelation, which is the binary version of this.  It might be more uniform to have subrelation1, subrelation2, subrelation3, etc. for different arities.  SAZ: It's a bit of a wart that, due to NoGetsF transforming predicates on [itree (stateE S) R] to predicates on [itreeF (stateE S) (itree (stateE S) R)] that it isn't an instance of monotone1. *)

(* SAZ: we need to do a couple of reductions to expose the structure of the lemma so that pmonauto can work.  Note that [cbn] and [simple] don't work here because they don't unfold the definitions.  *)
Lemma monotone_NoGets_ : forall {S R}, monotone1 (@NoGets_ S R).