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. *)
Hint Resolve monotone_NoGets_ : paco.
(* Finally, we can define the [NoGets] predicate by simply applying paco1 starting from bot1 (the least prediate).  We would use paco2 and bot2 for a binary relation, paco3 and bot3 for ternary, etc. *)
Definition NoGets {S R} : itree (stateE S) R -> Prop := paco1 NoGets_ bot1.
(* Using a coinductive predicate -------------------------------------------- *)
(* Now that we have defined [NoGets], we can use that predicate to do a coinductive proof.  Intuitively, if we interpret an itree of type [itree (stateE S) R] that satisfies the [NoGets] predicate, it does not matter what initial state it runs in.  To state this correctly, we have to "project away" the final state component that is produced by the monad, since the two final states might differ.  Some notes about this proof:  - In general, rewriting up to ≅ or ≈ can take place only under the "up to" paco context.  This means that to do the [rewrite (itree_eta ...)] steps, we have to have done [pupto2_init] first. *)

Theorem state_independent': forall {S R} (t:itree (stateE S) R)
(H: NoGets t),
forall s s', ('(s,x) <- interpret_state t s ;; ret x) ≅ ('(s,x) <- interpret_state t s' ;; ret x).