Library iris.algebra.upred

From iris.algebra Require Export cmra.
Local Hint Extern 1 (_ _) ⇒ etrans; [eassumption|].
Local Hint Extern 1 (_ _) ⇒ etrans; [|eassumption].
Local Hint Extern 10 (_ _) ⇒ omega.

Record uPred (M : ucmraT) : Type := IProp {
  uPred_holds :> nat M M Prop;
  uPred_mono n x1 y1 x2 y2:
    uPred_holds n x1 y1 x1 ≼{n} x2 y1 ≡{n}≡ y2 uPred_holds n x2 y2;
  uPred_closed n1 n2 x y :
    uPred_holds n1 x y n2 n1 ✓{n2} x ✓{n2} y uPred_holds n2 x y
}.
Arguments uPred_holds {_} _ _ _ _ : simpl never.
Add Printing Constructor uPred.
Instance: Params (@uPred_holds) 3.

Delimit Scope uPred_scope with I.
Bind Scope uPred_scope with uPred.
Arguments uPred_holds {_} _%I _ _ _.

Section cofe.
  Context {M : ucmraT}.

  Inductive uPred_equiv' (P Q : uPred M) : Prop :=
    { uPred_in_equiv : n x y, ✓{n} x ✓{n} y P n x y Q n x y }.
  Instance uPred_equiv : Equiv (uPred M) := uPred_equiv'.
  Inductive uPred_dist' (n : nat) (P Q : uPred M) : Prop :=
    { uPred_in_dist : n' x y, n' n ✓{n'} x ✓{n'} y P n' x y Q n' x y}.
  Instance uPred_dist : Dist (uPred M) := uPred_dist'.
  Program Instance uPred_compl : Compl (uPred M) := λ c,
    {| uPred_holds n x y := c n n x y |}.
  Next Obligation. naive_solver eauto using uPred_mono. Qed.
  Next Obligation.
    intros c n1 n2 x y ????; simpl in ×.
    apply (chain_cauchy c n2 n1); eauto using uPred_closed.
  Qed.
  Definition uPred_cofe_mixin : CofeMixin (uPred M).
  Proof.
    split.
    - intros P Q; split.
      + by intros HPQ n; spliti x ??; apply HPQ.
      + intros HPQ; splitn x ?; apply HPQ with n; auto.
    - intros n; split.
      + by intros P; splitx i.
      + by intros P Q HPQ; splitx i ??; symmetry; apply HPQ.
      + intros P Q Q' HP HQ; spliti x y ???.
        by trans (Q i x y);[apply HP|apply HQ].
    - intros n P Q HPQ; spliti x ??; apply HPQ; auto.
    - intros n c; spliti x ??; symmetry; apply (chain_cauchy c i n); auto.
  Qed.
  Canonical Structure uPredC : cofeT := CofeT (uPred M) uPred_cofe_mixin.
End cofe.
Arguments uPredC : clear implicits.

Instance uPred_ne {M} (P : uPred M) n : Proper (dist n ==> dist n ==> iff) (P n).
Proof. intros x1 x2 Hx y1 y2 Hy; split⇒ ?; eapply uPred_mono; eauto; by rewrite Hx. Qed.
Instance uPred_proper {M} (P : uPred M) n : Proper ((≡) ==> (≡) ==> iff) (P n).
Proof. by intros x1 x2 Hx y1 y2 Hy; apply uPred_ne; apply equiv_dist. Qed.

Lemma uPred_holds_ne {M} (P Q : uPred M) n1 n2 x y :
  P ≡{n2}≡ Q n2 n1 ✓{n2} x ✓{n2} y Q n1 x y P n2 x y.
Proof.
  intros [Hne] ????. eapply Hne; try done.
  eapply uPred_closed; eauto using cmra_validN_le.
Qed.

functor
Program Definition uPred_map {M1 M2 : ucmraT} (f : M2 -n> M1)
  `{!CMRAMonotone f} (P : uPred M1) :
  uPred M2 := {| uPred_holds n x y := P n (f x) (f y)|}.
Next Obligation.
  intros M1 M2 f ? P n y1 y2 x1 x2 ? Hx Hy. rewrite /=.
  rewrite -Hy. naive_solver eauto using uPred_mono, cmra_monotoneN.
Qed.
Next Obligation. naive_solver eauto using uPred_closed, validN_preserving. Qed.

Instance uPred_map_ne {M1 M2 : ucmraT} (f : M2 -n> M1)
  `{!CMRAMonotone f} n : Proper (dist n ==> dist n) (uPred_map f).
Proof.
  intros x1 x2 Hx; splitn' y ??.
  split; apply Hx; auto using validN_preserving.
Qed.
Lemma uPred_map_id {M : ucmraT} (P : uPred M): uPred_map cid P P.
Proof. by splitn x ?. Qed.
Lemma uPred_map_compose {M1 M2 M3 : ucmraT} (f : M1 -n> M2) (g : M2 -n> M3)
    `{!CMRAMonotone f, !CMRAMonotone g} (P : uPred M3):
  uPred_map (g f) P uPred_map f (uPred_map g P).
Proof. by splitn x Hx. Qed.
Lemma uPred_map_ext {M1 M2 : ucmraT} (f g : M1 -n> M2)
      `{!CMRAMonotone f} `{!CMRAMonotone g}:
  ( x, f x g x) x, uPred_map f x uPred_map g x.
Proof. intros Hf P; splitn x y Hx Hy /=; by rewrite /uPred_holds /= ?Hf. Qed.
Definition uPredC_map {M1 M2 : ucmraT} (f : M2 -n> M1) `{!CMRAMonotone f} :
  uPredC M1 -n> uPredC M2 := CofeMor (uPred_map f : uPredC M1 uPredC M2).
Lemma uPredC_map_ne {M1 M2 : ucmraT} (f g : M2 -n> M1)
    `{!CMRAMonotone f, !CMRAMonotone g} n :
  f ≡{n}≡ g uPredC_map f ≡{n}≡ uPredC_map g.
Proof.
  by intros Hfg P; splitn' x y ???;
    rewrite /uPred_holds /= ?(dist_le _ _ _ _(Hfg x)) //= ?(dist_le _ _ _ _(Hfg y)); last lia.
Qed.

Program Definition uPredCF (F : urFunctor) : cFunctor := {|
  cFunctor_car A B := uPredC (urFunctor_car F B A);
  cFunctor_map A1 A2 B1 B2 fg := uPredC_map (urFunctor_map F (fg.2, fg.1))
|}.
Next Obligation.
  intros F A1 A2 B1 B2 n P Q HPQ.
  apply uPredC_map_ne, urFunctor_ne; split; by apply HPQ.
Qed.
Next Obligation.
  intros F A B P; simpl. rewrite -{2}(uPred_map_id P).
  apply uPred_map_exty. by rewrite urFunctor_id.
Qed.
Next Obligation.
  intros F A1 A2 A3 B1 B2 B3 f g f' g' P; simpl. rewrite -uPred_map_compose.
  apply uPred_map_exty; apply urFunctor_compose.
Qed.

Instance uPredCF_contractive F :
  urFunctorContractive F cFunctorContractive (uPredCF F).
Proof.
  intros ? A1 A2 B1 B2 n P Q HPQ.
  apply uPredC_map_ne, urFunctor_contractivei ?; split; by apply HPQ.
Qed.

logical entailement
Inductive uPred_entails {M} (P Q : uPred M) : Prop :=
  { uPred_in_entails : n x y, ✓{n} x ✓{n} y P n x y Q n x y}.
Hint Extern 0 (uPred_entails _ _) ⇒ reflexivity.
Instance uPred_entails_rewrite_relation M : RewriteRelation (@uPred_entails M).

Hint Resolve uPred_mono uPred_closed : uPred_def.

logical connectives
Program Definition uPred_pure_def {M} (φ : Prop) : uPred M :=
  {| uPred_holds n x y := φ|}.
Solve Obligations with (intros; cofe_subst; done).

Definition uPred_pure_aux : { x | x = @uPred_pure_def}. by eexists. Qed.
Definition uPred_pure {M} := proj1_sig uPred_pure_aux M.
Definition uPred_pure_eq :
  @uPred_pure = @uPred_pure_def := proj2_sig uPred_pure_aux.

Instance uPred_inhabited M : Inhabited (uPred M) := populate (uPred_pure True).

Program Definition uPred_and_def {M} (P Q : uPred M) : uPred M :=
  {| uPred_holds n x y := P n x y Q n x y|}.
Solve Obligations with naive_solver eauto 2 with uPred_def.
Definition uPred_and_aux : { x | x = @uPred_and_def }. by eexists. Qed.
Definition uPred_and {M} := proj1_sig uPred_and_aux M.
Definition uPred_and_eq: @uPred_and = @uPred_and_def := proj2_sig uPred_and_aux.

Program Definition uPred_or_def {M} (P Q : uPred M) : uPred M :=
  {| uPred_holds n x y := P n x y Q n x y |}.
Solve Obligations with naive_solver eauto 2 with uPred_def.
Definition uPred_or_aux : { x | x = @uPred_or_def }. by eexists. Qed.
Definition uPred_or {M} := proj1_sig uPred_or_aux M.
Definition uPred_or_eq: @uPred_or = @uPred_or_def := proj2_sig uPred_or_aux.

Program Definition uPred_impl_def {M} (P Q : uPred M) : uPred M :=
  {| uPred_holds n x y := n' x',
       x x' n' n ✓{n'} x' ✓{n'} y P n' x' y Q n' x' y |}.
Next Obligation.
  intros M P Q n1 x1 x1' y1 y1' HPQ [x2 Hx1'] Hx2' n2 x3 [x4 Hx3] ?; simpl in ×.
  rewrite Hx3 (dist_le _ _ _ _ Hx1') // -(dist_le _ _ _ _ Hx2'); auto. intros ???.
  eapply HPQ; auto. (x2 x4); by rewrite assoc.
Qed.
Next Obligation. intros M P Q [|n1] [|n2] x; auto with lia. Qed.
Definition uPred_impl_aux : { x | x = @uPred_impl_def }. by eexists. Qed.
Definition uPred_impl {M} := proj1_sig uPred_impl_aux M.
Definition uPred_impl_eq :
  @uPred_impl = @uPred_impl_def := proj2_sig uPred_impl_aux.

Program Definition uPred_forall_def {M A} (Ψ : A uPred M) : uPred M :=
  {| uPred_holds n x y := a, Ψ a n x y|}.
Solve Obligations with naive_solver eauto 2 with uPred_def.
Definition uPred_forall_aux : { x | x = @uPred_forall_def }. by eexists. Qed.
Definition uPred_forall {M A} := proj1_sig uPred_forall_aux M A.
Definition uPred_forall_eq :
  @uPred_forall = @uPred_forall_def := proj2_sig uPred_forall_aux.

Program Definition uPred_exist_def {M A} (Ψ : A uPred M) : uPred M :=
  {| uPred_holds n x y := a, Ψ a n x y|}.
Solve Obligations with naive_solver eauto 2 with uPred_def.
Definition uPred_exist_aux : { x | x = @uPred_exist_def }. by eexists. Qed.
Definition uPred_exist {M A} := proj1_sig uPred_exist_aux M A.
Definition uPred_exist_eq: @uPred_exist = @uPred_exist_def := proj2_sig uPred_exist_aux.

Program Definition uPred_eq_def {M} {A : cofeT} (a1 a2 : A) : uPred M :=
  {| uPred_holds n x y := a1 ≡{n}≡ a2|}.
Solve Obligations with naive_solver eauto 2 using (dist_le (A:=A)).
Definition uPred_eq_aux : { x | x = @uPred_eq_def }. by eexists. Qed.
Definition uPred_eq {M A} := proj1_sig uPred_eq_aux M A.
Definition uPred_eq_eq: @uPred_eq = @uPred_eq_def := proj2_sig uPred_eq_aux.

Program Definition uPred_sep_def {M} (P Q : uPred M) : uPred M :=
  {| uPred_holds n x y := x1 x2 y1 y2, x ≡{n}≡ x1 x2 y ≡{n}≡ y1 y2
                                        P n x1 y1 Q n x2 y2 |}.
Next Obligation.
  intros M P Q n x1 y1 x2 y2 (x1'&x2'&y1'&y2'&Hx&Hy&?&?) [z Hz] ?.
   x1', (x2' z), y1', y2'.
  split_and?; eauto using uPred_mono, cmra_includedN_l.
  - by rewrite Hz Hx assoc.
  - by rewrite -Hy.
Qed.
Next Obligation.
  intros M P Q n1 n2 x y (x1&x2&y1&y2&Hx&Hy&?&?) Hlt.
  rewrite {1}(dist_le _ _ _ _ Hx) // =>?.
  rewrite {1}(dist_le _ _ _ _ Hy) // =>?.
   x1, x2, y1, y2; cofe_subst; split_and!;
  eauto using dist_le, uPred_closed, cmra_validN_op_l, cmra_validN_op_r.
Qed.
Definition uPred_sep_aux : { x | x = @uPred_sep_def }. by eexists. Qed.
Definition uPred_sep {M} := proj1_sig uPred_sep_aux M.
Definition uPred_sep_eq: @uPred_sep = @uPred_sep_def := proj2_sig uPred_sep_aux.

Program Definition uPred_wand_def {M} (P Q : uPred M) : uPred M :=
  {| uPred_holds n x y := n' x' y',
       n' n ✓{n'} (x x') ✓{n'} (y y') P n' x' y' Q n' (x x') (y y') |}.
Next Obligation.
  intros M P Q n1 x1 y1 x2 y2 HPQ Hx Hy n2 x3 y3 ????; simpl in ×.
  rewrite -(dist_le _ _ _ _ Hy) //.
  apply uPred_mono with (x1 x3) (y1 y3); first apply HPQ;
    eauto using cmra_validN_includedN, cmra_monoN_r, cmra_includedN_le.
  by rewrite (dist_le _ _ _ _ Hy).
Qed.
Next Obligation. naive_solver. Qed.
Definition uPred_wand_aux : { x | x = @uPred_wand_def }. by eexists. Qed.
Definition uPred_wand {M} := proj1_sig uPred_wand_aux M.
Definition uPred_wand_eq :
  @uPred_wand = @uPred_wand_def := proj2_sig uPred_wand_aux.

Program Definition uPred_relevant_def {M} (P : uPred M) : uPred M :=
  {| uPred_holds n x y:= P n (core x) (core y) y ≡{n}≡ core y |}.
Next Obligation.
  intros M P n x1 x2 y1 y2 (?&?) Hx Hy. rewrite -Hy.
  naive_solver eauto using uPred_mono, @cmra_core_monoN.
Qed.
Next Obligation.
  intros M P n1 n2 x y (?&Hc) ???.
  naive_solver eauto using uPred_closed, @cmra_core_mono, @cmra_core_validN, dist_le.
Qed.
Definition uPred_relevant_aux : { x | x = @uPred_relevant_def }. by eexists. Qed.
Definition uPred_relevant {M} := proj1_sig uPred_relevant_aux M.
Definition uPred_relevant_eq :
  @uPred_relevant = @uPred_relevant_def := proj2_sig uPred_relevant_aux.

Program Definition uPred_affine_def {M} (P : uPred M) : uPred M :=
  {| uPred_holds n x y:= P n x y y ≡{n}≡ |}.
Next Obligation.
  intros M P n x1 y1 x2 y2 (?&Hempty) Hx Hy.
  rewrite -Hy. naive_solver eauto 2 with uPred_def.
Qed.
Next Obligation.
  intros M P n1 n2 x y (?&?) ???. naive_solver eauto 2 using uPred_closed, dist_le.
Qed.
Definition uPred_affine_aux : { x | x = @uPred_affine_def }. by eexists. Qed.
Definition uPred_affine {M} := proj1_sig uPred_affine_aux M.
Definition uPred_affine_eq :
  @uPred_affine = @uPred_affine_def := proj2_sig uPred_affine_aux.

Program Definition uPred_later_def {M} (P : uPred M) : uPred M :=
  {| uPred_holds n x y := match n return _ with 0 True | S n' P n' x y end |}.
Next Obligation.
  intros M P [|n] ??; eauto using uPred_mono, cmra_includedN_S,(dist_le (A:=M)).
Qed.
Next Obligation.
  intros M P [|n1] [|n2] x1 x2 y1 y2; eauto using uPred_closed,cmra_validN_S; try lia.
Qed.
Definition uPred_later_aux : { x | x = @uPred_later_def }. by eexists. Qed.
Definition uPred_later {M} := proj1_sig uPred_later_aux M.
Definition uPred_later_eq :
  @uPred_later = @uPred_later_def := proj2_sig uPred_later_aux.

Program Definition uPred_ownM_def {M: ucmraT} (a : M) : uPred M :=
  {| uPred_holds n x y := a ≼{n} x|}.
Next Obligation.
  intros M a n x1 y1 x2 y2 [a' Hx1] [x2' ->] Hy;
     (a' x2'). by rewrite (assoc op) Hx1.
Qed.
Next Obligation. naive_solver eauto using cmra_includedN_le. Qed.
Definition uPred_ownM_aux : { x | x = @uPred_ownM_def }. by eexists. Qed.
Definition uPred_ownM {M} := proj1_sig uPred_ownM_aux M.
Definition uPred_ownM_eq :
  @uPred_ownM = @uPred_ownM_def := proj2_sig uPred_ownM_aux.

Program Definition uPred_ownMl_def {M: ucmraT} (a : M) : uPred M :=
  {| uPred_holds n x y := a ≡{n}≡ y|}.
Next Obligation.
  intros M a n x1 y1 x2 y2 Hequiv Hx Hy.
  rewrite Hequiv //=.
Qed.
Next Obligation.
  intros M a n1 n2 x1 x2 Hequiv Hle ?.
  rewrite -(dist_le _ _ _ _ Hequiv) //=.
Qed.
Definition uPred_ownMl_aux : { x | x = @uPred_ownMl_def }. by eexists. Qed.
Definition uPred_ownMl {M} := proj1_sig uPred_ownMl_aux M.
Definition uPred_ownMl_eq :
  @uPred_ownMl = @uPred_ownMl_def := proj2_sig uPred_ownMl_aux.

Program Definition uPred_valid_def {M : ucmraT} {A : cmraT} (a : A) : uPred M :=
  {| uPred_holds n x y := ✓{n} a|}.
Solve Obligations with (intros; cofe_subst; naive_solver eauto 2 using cmra_validN_le, dist_le).
Definition uPred_valid_aux : { x | x = @uPred_valid_def }. by eexists. Qed.
Definition uPred_valid {M A} := proj1_sig uPred_valid_aux M A.
Definition uPred_valid_eq :
  @uPred_valid = @uPred_valid_def := proj2_sig uPred_valid_aux.

Program Definition uPred_stopped_def {M: ucmraT} : uPred M :=
  {| uPred_holds n x y := n', n' n nf (stepN n') y|}.
Next Obligation.
  rewrite /nf /red; intros M n x1 x2 y1 y2 Hns Heq1 Heq2; simpl.
  intros n' Hle (y&Hs').
  eapply Hns; eauto. y. by rewrite (dist_le _ _ _ _ Heq2).
Qed.
Next Obligation. naive_solver. Qed.
Definition uPred_stopped_aux : { x | x = @uPred_stopped_def }. by eexists. Qed.
Definition uPred_stopped {M} := proj1_sig uPred_stopped_aux M.
Definition uPred_stopped_eq :
  @uPred_stopped = @uPred_stopped_def := proj2_sig uPred_stopped_aux.

Program Definition uPred_emp_def {M: ucmraT} : uPred M :=
  {| uPred_holds n x y := y ≡{n}≡ |}.
Solve Obligations with (intros; cofe_subst; naive_solver eauto 2 using cmra_validN_le, dist_le).
Definition uPred_emp_aux : { x | x = @uPred_emp_def }. by eexists. Qed.
Definition uPred_emp {M} := proj1_sig uPred_emp_aux M.
Definition uPred_emp_eq :
  @uPred_emp = @uPred_emp_def := proj2_sig uPred_emp_aux.

Notation "P ⊢ Q" := (uPred_entails P%I Q%I)
  (at level 99, Q at level 200, right associativity) : C_scope.
Notation "(⊢)" := uPred_entails (only parsing) : C_scope.
Notation "P ⊣⊢ Q" := (equiv (A:=uPred _) P%I Q%I)
  (at level 95, no associativity) : C_scope.
Notation "(⊣⊢)" := (equiv (A:=uPred _)) (only parsing) : C_scope.
Notation "■ φ" := (uPred_pure φ%C%type)
  (at level 20, right associativity) : uPred_scope.
Notation "x = y" := (uPred_pure (x%C%type = y%C%type)) : uPred_scope.
Notation "x ⊥ y" := (uPred_pure (x%C%type y%C%type)) : uPred_scope.
Notation "'False'" := (uPred_pure False) : uPred_scope.
Notation "'True'" := (uPred_pure True) : uPred_scope.
Notation "'Emp'" := (uPred_emp) : uPred_scope.
Infix "∧" := uPred_and : uPred_scope.
Notation "(∧)" := uPred_and (only parsing) : uPred_scope.
Infix "∨" := uPred_or : uPred_scope.
Notation "(∨)" := uPred_or (only parsing) : uPred_scope.
Infix "→" := uPred_impl : uPred_scope.
Infix "★" := uPred_sep (at level 80, right associativity) : uPred_scope.
Notation "(★)" := uPred_sep (only parsing) : uPred_scope.
Notation "P -★ Q" := (uPred_wand P Q)
  (at level 99, Q at level 200, right associativity) : uPred_scope.
Notation "∀ x .. y , P" :=
  (uPred_forall (λ x, .. (uPred_forall (λ y, P)) ..)%I) : uPred_scope.
Notation "∃ x .. y , P" :=
  (uPred_exist (λ x, .. (uPred_exist (λ y, P)) ..)%I) : uPred_scope.
Notation "□ P" := (uPred_relevant P)
  (at level 20, right associativity) : uPred_scope.
Notation "⧆ P" := (uPred_affine P)
  (at level 20, right associativity) : uPred_scope.
Notation "⧈ P" := (uPred_affine (uPred_relevant P))
  (at level 20, right associativity) : uPred_scope.
Notation "▷ P" := (uPred_later P)
  (at level 20, right associativity) : uPred_scope.
Infix "≡" := (uPred_eq) : uPred_scope.
Notation "✓ x" := (uPred_valid x) (at level 20) : uPred_scope.

Definition uPred_iff {M} (P Q : uPred M) : uPred M := ((P Q) (Q P))%I.
Instance: Params (@uPred_iff) 1.
Infix "↔" := uPred_iff : uPred_scope.

Definition uPred_relevant_if {M} (p : bool) (P : uPred M) : uPred M :=
  (if p then P else P)%I.
Instance: Params (@uPred_relevant_if) 2.
Arguments uPred_relevant_if _ !_ _/.
Notation "□? p P" := (uPred_relevant_if p P)
  (at level 20, p at level 0, P at level 20, format "□? p P").

Definition uPred_laterN {M} (n : nat) (P : uPred M) : uPred M :=
  Nat.iter n uPred_later P.
Instance: Params (@uPred_laterN) 2.
Notation "▷^ n P" := (uPred_laterN n P)
  (at level 20, n at level 9, right associativity,
   format "▷^ n P") : uPred_scope.

Class TimelessP {M} (P : uPred M) := timelessP : P (P False).
Arguments timelessP {_} _ {_}.

Class ATimelessP {M: ucmraT} (P : uPred M)
  := atimelessP : P (P False).
Arguments atimelessP {_} _ {_}.

Class RelevantP {M: ucmraT} (P : uPred M) := relevantP : P P.
Arguments relevantP {_} _ {_}.

Class AffineP {M: ucmraT} (P : uPred M) := affineP : P P.
Arguments affineP {_} _ {_}.

Class UnlimitedP {M} (P : uPred M) := {
  unlimited_relevant :> RelevantP P;
  unlimited_affine :> AffineP P
}.

Module uPred.
Definition unseal :=
  (uPred_pure_eq, uPred_and_eq, uPred_or_eq, uPred_impl_eq, uPred_forall_eq,
  uPred_exist_eq, uPred_eq_eq, uPred_sep_eq, uPred_wand_eq, uPred_relevant_eq, uPred_affine_eq,
  uPred_later_eq, uPred_ownM_eq, uPred_ownMl_eq, uPred_valid_eq, uPred_emp_eq, uPred_stopped_eq).
Ltac unseal := rewrite !unseal /=.

Section uPred_logic.
Context {M : ucmraT}.
Implicit Types φ : Prop.
Implicit Types P Q : uPred M.
Implicit Types A : Type.
Notation "P ⊢ Q" := (@uPred_entails M P%I Q%I). Notation "P ⊣⊢ Q" := (equiv (A:=uPred M) P%I Q%I). Arguments uPred_holds {_} !_ _ _ _ /.
Hint Immediate uPred_in_entails.

Global Instance: PreOrder (@uPred_entails M).
Proof.
  split.
  × by intros P; splitx i.
  × by intros P Q Q' HP HQ; splitx y i ???; apply HQ, HP.
Qed.
Global Instance: AntiSymm (⊣⊢) (@uPred_entails M).
Proof. intros P Q HPQ HQP; splitx n; by split; [apply HPQ|apply HQP]. Qed.
Lemma equiv_spec P Q : (P ⊣⊢ Q) (P Q) (Q P).
Proof.
  split; [|by intros [??]; apply (anti_symm (⊢))].
  intros HPQ; split; splitx i; apply HPQ.
Qed.
Lemma equiv_entails P Q : (P ⊣⊢ Q) (P Q).
Proof. apply equiv_spec. Qed.
Lemma equiv_entails_sym P Q : (Q ⊣⊢ P) (P Q).
Proof. apply equiv_spec. Qed.
Global Instance entails_proper :
  Proper ((⊣⊢) ==> (⊣⊢) ==> iff) ((⊢) : relation (uPred M)).
Proof.
  moveP1 P2 /equiv_spec [HP1 HP2] Q1 Q2 /equiv_spec [HQ1 HQ2]; split; intros.
  - by trans P1; [|trans Q1].
  - by trans P2; [|trans Q2].
Qed.
Lemma entails_equiv_l (P Q R : uPred M) : (P ⊣⊢ Q) (Q R) (P R).
Proof. by intros →. Qed.
Lemma entails_equiv_r (P Q R : uPred M) : (P Q) (Q ⊣⊢ R) (P R).
Proof. by intros ? <-. Qed.

Non-expansiveness and setoid morphisms
Global Instance pure_proper : Proper (iff ==> (⊣⊢)) (@uPred_pure M).
Proof. intros φ1 φ2 Hφ. by unseal; split⇒ -[|n] ?; try apply Hφ. Qed.
Global Instance and_ne n : Proper (dist n ==> dist n ==> dist n) (@uPred_and M).
Proof.
  intros P P' HP Q Q' HQ; unseal; splitx n' ????.
  split; (intros [??]; split; [by apply HP|by apply HQ]).
Qed.
Global Instance and_proper :
  Proper ((⊣⊢) ==> (⊣⊢) ==> (⊣⊢)) (@uPred_and M) := ne_proper_2 _.
Global Instance or_ne n : Proper (dist n ==> dist n ==> dist n) (@uPred_or M).
Proof.
  intros P P' HP Q Q' HQ; splitx n' ??.
  unseal; split; (intros [?|?]; [left; by apply HP|right; by apply HQ]).
Qed.
Global Instance or_proper :
  Proper ((⊣⊢) ==> (⊣⊢) ==> (⊣⊢)) (@uPred_or M) := ne_proper_2 _.
Global Instance impl_ne n :
  Proper (dist n ==> dist n ==> dist n) (@uPred_impl M).
Proof.
  intros P P' HP Q Q' HQ; splitn' x y ???;
  by unseal; split; intros HPQ; intros n'' x' ?????;
  apply HQ, HPQ, HP; auto.
Qed.
Global Instance impl_proper :
  Proper ((⊣⊢) ==> (⊣⊢) ==> (⊣⊢)) (@uPred_impl M) := ne_proper_2 _.
Global Instance sep_ne n : Proper (dist n ==> dist n ==> dist n) (@uPred_sep M).
Proof.
  intros P P' HP Q Q' HQ; splitn' x y ???.
  unseal; split; intros (x1&x2&y1&y2&?&?&?&?); cofe_subst x; cofe_subst y;
     x1, x2, y1, y2; split_and!; try (apply HP || apply HQ);
    eauto using cmra_validN_op_l, cmra_validN_op_r.
Qed.
Global Instance sep_proper :
  Proper ((⊣⊢) ==> (⊣⊢) ==> (⊣⊢)) (@uPred_sep M) := ne_proper_2 _.
Global Instance wand_ne n :
  Proper (dist n ==> dist n ==> dist n) (@uPred_wand M).
Proof.
  intros P P' HP Q Q' HQ; splitn' x y ???; unseal; split; intros HPQ n'' x' y' ????;
    apply HQ, HPQ, HP; eauto using cmra_validN_op_r.
Qed.
Global Instance wand_proper :
  Proper ((⊣⊢) ==> (⊣⊢) ==> (⊣⊢)) (@uPred_wand M) := ne_proper_2 _.
Global Instance eq_ne (A : cofeT) n :
  Proper (dist n ==> dist n ==> dist n) (@uPred_eq M A).
Proof.
  intros x x' Hx y y' Hy; splitn' z; unseal; split; intros; simpl in ×.
  × by rewrite -(dist_le _ _ _ _ Hx) -?(dist_le _ _ _ _ Hy); auto.
  × by rewrite (dist_le _ _ _ _ Hx) ?(dist_le _ _ _ _ Hy); auto.
Qed.
Global Instance eq_proper (A : cofeT) :
  Proper ((≡) ==> (≡) ==> (⊣⊢)) (@uPred_eq M A) := ne_proper_2 _.
Global Instance forall_ne A n :
  Proper (pointwise_relation _ (dist n) ==> dist n) (@uPred_forall M A).
Proof.
  by intros Ψ1 Ψ2 ; unseal; splitn' x; split; intros HP a; apply .
Qed.
Global Instance forall_proper A :
  Proper (pointwise_relation _ (⊣⊢) ==> (⊣⊢)) (@uPred_forall M A).
Proof.
  by intros Ψ1 Ψ2 ; unseal; splitn' x; split; intros HP a; apply .
Qed.
Global Instance exist_ne A n :
  Proper (pointwise_relation _ (dist n) ==> dist n) (@uPred_exist M A).
Proof.
  intros Ψ1 Ψ2 .
  unseal; splitn' x ??; split; intros [a ?]; a; by apply .
Qed.
Global Instance exist_proper A :
  Proper (pointwise_relation _ (⊣⊢) ==> (⊣⊢)) (@uPred_exist M A).
Proof.
  intros Ψ1 Ψ2 .
  unseal; splitn' x ?; split; intros [a ?]; a; by apply .
Qed.
Global Instance later_contractive : Contractive (@uPred_later M).
Proof.
  intros n P Q HPQ; unseal; split⇒ -[|n'] x y ???; simpl; [done|].
  apply (HPQ n'); eauto using cmra_validN_S.
Qed.
Global Instance later_proper' :
  Proper ((⊣⊢) ==> (⊣⊢)) (@uPred_later M) := ne_proper _.
Global Instance relevant_ne n : Proper (dist n ==> dist n) (@uPred_relevant M).
Proof.
  intros P1 P2 HP.
  unseal; splitn' x y; split;
  (intros (?&?); split; first apply HP; eauto using @cmra_core_validN).
Qed.
Global Instance relevant_proper :
  Proper ((⊣⊢) ==> (⊣⊢)) (@uPred_relevant M) := ne_proper _.
Global Instance affine_ne n : Proper (dist n ==> dist n) (@uPred_affine M).
Proof.
  intros P1 P2 HP.
  unseal; splitn' x y ???.
  split; intros (?&?); split; auto; apply HP; eauto.
Qed.
Global Instance affine_proper :
  Proper ((⊣⊢) ==> (⊣⊢)) (@uPred_affine M) := ne_proper _.
Global Instance ownM_ne n : Proper (dist n ==> dist n) (@uPred_ownM M).
Proof.
  intros a b Ha.
  unseal; splitn' x y ?? /=. by rewrite (dist_le _ _ _ _ Ha); last lia.
Qed.
Global Instance ownM_proper: Proper ((≡) ==> (⊣⊢)) (@uPred_ownM M) := ne_proper _.
Global Instance ownMl_ne n : Proper (dist n ==> dist n) (@uPred_ownMl M).
Proof.
  intros a b Ha.
  unseal; splitn' x y ?? /=. by rewrite (dist_le _ _ _ _ Ha); last lia.
Qed.
Global Instance ownMl_proper: Proper ((≡) ==> (⊣⊢)) (@uPred_ownMl M) := ne_proper _.
Global Instance valid_ne {A : cmraT} n :
Proper (dist n ==> dist n) (@uPred_valid M A).
Proof.
  intros a b Ha; unseal; splitn' x y ?? /=.
  by rewrite (dist_le _ _ _ _ Ha); last lia.
Qed.
Global Instance valid_proper {A : cmraT} :
  Proper ((≡) ==> (⊣⊢)) (@uPred_valid M A) := ne_proper _.

Introduction and elimination rules
Lemma pure_intro φ P : φ P φ.
Proof. by intros ?; unseal; split. Qed.
Lemma pure_elim φ Q R : (Q φ) (φ Q R) Q R.
Proof.
  unseal; intros HQP HQR; splitn x y ?? HQ; apply HQR; eauto.
  eapply HQP; last apply HQ; eauto.
Qed.
Lemma and_elim_l P Q : P Q P.
Proof. by unseal; splitn x y ?? [??]. Qed.
Lemma and_elim_r P Q : P Q Q.
Proof. by unseal; splitn x y ?? [??]. Qed.
Lemma and_intro P Q R : (P Q) (P R) P Q R.
Proof. intros HQ HR; unseal; splitn x ??; by split; [apply HQ|apply HR]. Qed.
Lemma or_intro_l P Q : P P Q.
Proof. unseal; splitn x y ???; left; auto. Qed.
Lemma or_intro_r P Q : Q P Q.
Proof. unseal; splitn x y ???; right; auto. Qed.
Lemma or_elim P Q R : (P R) (Q R) P Q R.
Proof. intros HP HQ; unseal; splitn x y ?? [?|?]. by apply HP. by apply HQ. Qed.
Lemma impl_intro_r P Q R : (P Q R) P Q R.
Proof.
  unseal; intros HQ; splitn x y ??? n' x' ?????.
  apply HQ; try naive_solver eauto using uPred_mono, uPred_closed, cmra_included_includedN.
Qed.
Lemma impl_elim P Q R : (P Q R) (P Q) P R.
Proof. by unseal; intros HP HP'; splitn x y ???; apply HP with n x, HP'. Qed.
Lemma forall_intro {A} P (Ψ : A uPred M): ( a, P Ψ a) P a, Ψ a.
Proof. unseal; intros HPΨ; splitn x y ??? a; by apply HPΨ. Qed.
Lemma forall_elim {A} {Ψ : A uPred M} a : ( a, Ψ a) Ψ a.
Proof. unseal; splitn x y ?? HP; apply HP. Qed.
Lemma exist_intro {A} {Ψ : A uPred M} a : Ψ a a, Ψ a.
Proof. unseal; splitn x y ???; by a. Qed.
Lemma exist_elim {A} (Φ : A uPred M) Q : ( a, Φ a Q) ( a, Φ a) Q.
Proof. unseal; intros HΦΨ; splitn x y ?? [a ?]; by apply HΦΨ with a. Qed.
Lemma eq_refl {A : cofeT} (a : A) : True (a a).
Proof. unseal; splitn x y ???; simpl. naive_solver. Qed.
Lemma eq_rewrite {A : cofeT} a b (Ψ : A uPred M) P
  { : n, Proper (dist n ==> dist n) Ψ} : (P a b) (P Ψ a) P Ψ b.
Proof.
  unseal; intros Hab Ha; splitn x y ???.
  apply with n a; auto.
  - symmetry. unfold uPred_eq_def in Hab.
    eapply Hab; last eauto; auto.
  - eauto.
Qed.
Lemma eq_equiv {A : cofeT} (a b : A) : (True a b) a b.
Proof.
  unsealHab; apply equiv_dist; intros n.
  apply Hab with ; last done;
  apply cmra_valid_validN, ucmra_unit_valid.
Qed.
Lemma eq_rewrite_contractive {A : cofeT} a b (Ψ : A uPred M) P
  { : Contractive Ψ} : (P (a b)) (P Ψ a) P Ψ b.
Proof.
  unseal; intros Hab Ha; splitn x y ???. apply with n a; auto.
  - destruct n; intros m ?; first omega. apply (dist_le n); last omega.
    symmetry. by destruct Hab as [Hab]; eapply (Hab (S n) x y).
  - by apply Ha.
Qed.

Lemma affine_equiv P: P ⊣⊢ (P Emp).
Proof.
  unseal; apply (anti_symm (⊢)); splitn x y ??; naive_solver.
Qed.

Global Instance iff_ne n : Proper (dist n ==> dist n ==> dist n) (@uPred_iff M).
Proof. unfold uPred_iff; solve_proper. Qed.
Global Instance iff_proper :
  Proper ((⊣⊢) ==> (⊣⊢) ==> (⊣⊢)) (@uPred_iff M) := ne_proper_2 _.

Lemma False_elim P : False P.
Proof. by apply (pure_elim False). Qed.
Lemma True_intro P : P True.
Proof. by apply pure_intro. Qed.
Lemma and_elim_l' P Q R : (P R) P Q R.
Proof. by rewrite and_elim_l. Qed.
Lemma and_elim_r' P Q R : (Q R) P Q R.
Proof. by rewrite and_elim_r. Qed.
Lemma or_intro_l' P Q R : (P Q) P Q R.
Proof. intros ->; apply or_intro_l. Qed.
Lemma or_intro_r' P Q R : (P R) P Q R.
Proof. intros ->; apply or_intro_r. Qed.
Lemma exist_intro' {A} P (Ψ : A uPred M) a : (P Ψ a) P a, Ψ a.
Proof. intros ->; apply exist_intro. Qed.
Lemma forall_elim' {A} P (Ψ : A uPred M) : (P a, Ψ a) a, P Ψ a.
Proof. moveHP a. by rewrite HP forall_elim. Qed.

Hint Resolve or_elim or_intro_l' or_intro_r'.
Hint Resolve and_intro and_elim_l' and_elim_r'.
Hint Immediate True_intro False_elim.

Lemma impl_intro_l P Q R : (Q P R) P Q R.
Proof. intros HR; apply impl_intro_r; rewrite -HR; auto. Qed.
Lemma impl_elim_l P Q : (P Q) P Q.
Proof. apply impl_elim with P; auto. Qed.
Lemma impl_elim_r P Q : P (P Q) Q.
Proof. apply impl_elim with P; auto. Qed.
Lemma impl_elim_l' P Q R : (P Q R) P Q R.
Proof. intros; apply impl_elim with Q; auto. Qed.
Lemma impl_elim_r' P Q R : (Q P R) P Q R.
Proof. intros; apply impl_elim with P; auto. Qed.
Lemma impl_entails P Q : (True P Q) P Q.
Proof. intros HPQ; apply impl_elim with (P)%I; auto. rewrite -HPQ; auto. Qed.
Lemma entails_impl P Q : (P Q) True P Q.
Proof. auto using impl_intro_l. Qed.

Lemma iff_refl Q P : Q P P.
Proof. rewrite /uPred_iff; apply and_intro; apply impl_intro_l; auto. Qed.
Lemma iff_equiv P Q : (True P Q) (P ⊣⊢ Q).
Proof.
  intros HPQ; apply (anti_symm (⊢));
    apply impl_entails; rewrite HPQ /uPred_iff; auto.
Qed.
Lemma equiv_iff P Q : (P ⊣⊢ Q) True P Q.
Proof. intros ->; apply iff_refl. Qed.

Lemma pure_mono φ1 φ2 : (φ1 φ2) φ1 φ2.
Proof. intros; apply pure_elim with φ1; eauto using pure_intro. Qed.
Lemma pure_iff φ1 φ2 : (φ1 φ2) φ1 ⊣⊢ φ2.
Proof. intros [??]; apply (anti_symm _); auto using pure_mono. Qed.
Lemma and_mono P P' Q Q' : (P Q) (P' Q') P P' Q Q'.
Proof. auto. Qed.
Lemma and_mono_l P P' Q : (P Q) P P' Q P'.
Proof. by intros; apply and_mono. Qed.
Lemma and_mono_r P P' Q' : (P' Q') P P' P Q'.
Proof. by apply and_mono. Qed.
Lemma or_mono P P' Q Q' : (P Q) (P' Q') P P' Q Q'.
Proof. auto. Qed.
Lemma or_mono_l P P' Q : (P Q) P P' Q P'.
Proof. by intros; apply or_mono. Qed.
Lemma or_mono_r P P' Q' : (P' Q') P P' P Q'.
Proof. by apply or_mono. Qed.
Lemma impl_mono P P' Q Q' : (Q P) (P' Q') (P P') Q Q'.
Proof.
  intros HP HQ'; apply impl_intro_l; rewrite -HQ'.
  apply impl_elim with P; eauto.
Qed.
Lemma forall_mono {A} (Φ Ψ : A uPred M) :
  ( a, Φ a Ψ a) ( a, Φ a) a, Ψ a.
Proof.
  intros HP. apply forall_introa; rewrite -(HP a); apply forall_elim.
Qed.
Lemma exist_mono {A} (Φ Ψ : A uPred M) :
  ( a, Φ a Ψ a) ( a, Φ a) a, Ψ a.
Proof. intros . apply exist_elima; rewrite ( a); apply exist_intro. Qed.
Lemma affine_mono P Q: (P Q) P Q.
Proof. by intros HP; rewrite ?affine_equiv; apply and_mono_l. Qed.
Global Instance pure_mono' : Proper (impl ==> (⊢)) (@uPred_pure M).
Proof. intros φ1 φ2; apply pure_mono. Qed.
Global Instance and_mono' : Proper ((⊢) ==> (⊢) ==> (⊢)) (@uPred_and M).
Proof. by intros P P' HP Q Q' HQ; apply and_mono. Qed.
Global Instance and_flip_mono' :
  Proper (flip (⊢) ==> flip (⊢) ==> flip (⊢)) (@uPred_and M).
Proof. by intros P P' HP Q Q' HQ; apply and_mono. Qed.
Global Instance or_mono' : Proper ((⊢) ==> (⊢) ==> (⊢)) (@uPred_or M).
Proof. by intros P P' HP Q Q' HQ; apply or_mono. Qed.
Global Instance or_flip_mono' :
  Proper (flip (⊢) ==> flip (⊢) ==> flip (⊢)) (@uPred_or M).
Proof. by intros P P' HP Q Q' HQ; apply or_mono. Qed.
Global Instance impl_mono' :
  Proper (flip (⊢) ==> (⊢) ==> (⊢)) (@uPred_impl M).
Proof. by intros P P' HP Q Q' HQ; apply impl_mono. Qed.
Global Instance forall_mono' A :
  Proper (pointwise_relation _ (⊢) ==> (⊢)) (@uPred_forall M A).
Proof. intros P1 P2; apply forall_mono. Qed.
Global Instance exist_mono' A :
  Proper (pointwise_relation _ (⊢) ==> (⊢)) (@uPred_exist M A).
Proof. intros P1 P2; apply exist_mono. Qed.
Global Instance affine_mono' : Proper ((⊢) ==> (⊢)) (@uPred_affine M).
Proof. intros P1 P2; apply affine_mono. Qed.

Global Instance and_idem : IdemP (⊣⊢) (@uPred_and M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance or_idem : IdemP (⊣⊢) (@uPred_or M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance and_comm : Comm (⊣⊢) (@uPred_and M).
Proof. intros P Q; apply (anti_symm (⊢)); auto. Qed.
Global Instance True_and : LeftId (⊣⊢) True%I (@uPred_and M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance and_True : RightId (⊣⊢) True%I (@uPred_and M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance False_and : LeftAbsorb (⊣⊢) False%I (@uPred_and M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance and_False : RightAbsorb (⊣⊢) False%I (@uPred_and M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance True_or : LeftAbsorb (⊣⊢) True%I (@uPred_or M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance or_True : RightAbsorb (⊣⊢) True%I (@uPred_or M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance False_or : LeftId (⊣⊢) False%I (@uPred_or M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance or_False : RightId (⊣⊢) False%I (@uPred_or M).
Proof. intros P; apply (anti_symm (⊢)); auto. Qed.
Global Instance and_assoc : Assoc (⊣⊢) (@uPred_and M).
Proof. intros P Q R; apply (anti_symm (⊢)); auto. Qed.
Global Instance or_comm : Comm (⊣⊢) (@uPred_or M).
Proof. intros P Q; apply (anti_symm (⊢)); auto. Qed.
Global Instance or_assoc : Assoc (⊣⊢) (@uPred_or M).
Proof. intros P Q R; apply (anti_symm (⊢)); auto. Qed.
Global Instance True_impl : LeftId (⊣⊢) True%I (@uPred_impl M).
Proof.
  intros P; apply (anti_symm (⊢)).
  - by rewrite -(left_id True%I uPred_and (_ _)%I) impl_elim_r.
  - by apply impl_intro_l; rewrite left_id.
Qed.

Lemma or_and_l P Q R : P Q R ⊣⊢ (P Q) (P R).
Proof.
  apply (anti_symm (⊢)); first auto.
  do 2 (apply impl_elim_l', or_elim; apply impl_intro_l); auto.
Qed.
Lemma or_and_r P Q R : P Q R ⊣⊢ (P R) (Q R).
Proof. by rewrite -!(comm _ R) or_and_l. Qed.
Lemma and_or_l P Q R : P (Q R) ⊣⊢ P Q P R.
Proof.
  apply (anti_symm (⊢)); last auto.
  apply impl_elim_r', or_elim; apply impl_intro_l; auto.
Qed.
Lemma and_or_r P Q R : (P Q) R ⊣⊢ P R Q R.
Proof. by rewrite -!(comm _ R) and_or_l. Qed.
Lemma and_exist_l {A} P (Ψ : A uPred M) : P ( a, Ψ a) ⊣⊢ a, P Ψ a.
Proof.
  apply (anti_symm (⊢)).
  - apply impl_elim_r'. apply exist_elima. apply impl_intro_l.
    by rewrite -(exist_intro a).
  - apply exist_elima. apply and_intro; first by rewrite and_elim_l.
    by rewrite -(exist_intro a) and_elim_r.
Qed.
Lemma and_exist_r {A} P (Φ: A uPred M) : ( a, Φ a) P ⊣⊢ a, Φ a P.
Proof.
  rewrite -(comm _ P) and_exist_l. apply exist_propera. by rewrite comm.
Qed.

Lemma impl_curry P Q R : ((P Q) R)%I ⊣⊢ (P Q R)%I.
Proof.
  apply (anti_symm (⊢)).
  - apply impl_intro_r.
    apply impl_intro_r.
    rewrite -assoc.
    apply impl_elim_l.
  - apply impl_intro_r.
    by rewrite assoc impl_elim_l impl_elim_l.
Qed.

Lemma pure_intro_l φ Q R : φ ( φ Q R) Q R.
Proof. intros ? <-; auto using pure_intro. Qed.
Lemma pure_intro_r φ Q R : φ (Q φ R) Q R.
Proof. intros ? <-; auto using pure_intro. Qed.
Lemma pure_intro_impl φ Q R : φ (Q φ R) Q R.
Proof. intros ? →. eauto using pure_intro_l, impl_elim_r. Qed.
Lemma pure_elim_l φ Q R : (φ Q R) φ Q R.
Proof. intros; apply pure_elim with φ; eauto. Qed.
Lemma pure_elim_r φ Q R : (φ Q R) Q φ R.
Proof. intros; apply pure_elim with φ; eauto. Qed.
Lemma pure_equiv (φ : Prop) : φ φ ⊣⊢ True.
Proof. intros; apply (anti_symm _); auto using pure_intro. Qed.

Lemma eq_refl' {A : cofeT} (a : A) P : P a a.
Proof. rewrite (True_intro P). apply eq_refl. Qed.
Hint Resolve eq_refl'.
Lemma equiv_eq {A : cofeT} P (a b : A) : a b P a b.
Proof. by intros →. Qed.
Lemma eq_sym {A : cofeT} (a b : A) : a b b a.
Proof. apply (eq_rewrite a b (λ b, b a)%I); auto. solve_proper. Qed.
Lemma eq_iff P Q : P Q P Q.
Proof.
  apply (eq_rewrite P Q (λ Q, P Q))%I; first solve_proper; auto using iff_refl.
Qed.

Lemma sep_mono P P' Q Q' : (P Q) (P' Q') P P' Q Q'.
Proof.
  intros HQ HQ'; unseal.
  split; intros n' x y ?? (x1&x2&y1&y2&?&?&?&?); x1,x2,y1,y2; cofe_subst x; cofe_subst y.
    split_and!; eauto 7 using cmra_validN_op_l, cmra_validN_op_r, uPred_in_entails.
Qed.
Global Instance Emp_sep : LeftId (⊣⊢) Emp%I (@uPred_sep M).
Proof.
  intros P; unseal; splitn x y Hvalid Hvalidy; split.
  - intros (x1&x2&y1&y2&?&?&?&?); cofe_subst.
    rewrite left_id in Hvalidy *; eauto using uPred_mono, cmra_includedN_r.
  - intros ?; (core x), x, , y; rewrite cmra_core_l left_id.
    split_and!; eauto. simpl; reflexivity.
Qed.
Global Instance sep_comm : Comm (⊣⊢) (@uPred_sep M).
Proof.
  intros P Q; unseal; splitn x y ??; split;
    intros (x1&x2&y1&y2&?&?&?&?); x2, x1, y2, y1;
  by rewrite (comm op x2) (comm op y2).
Qed.
Global Instance sep_assoc : Assoc (⊣⊢) (@uPred_sep M).
Proof.
  intros P Q R; unseal; splitn x y ??; split.
  - intros (x1&x2&y1&y2&Hx&Hy&?&z1&z2&w1&w2&Hz&Hw&?&?); (x1 z1), z2, (y1 w1), w2;
    split_and?; auto.
    + by rewrite -(assoc op) -Hz -Hx.
    + by rewrite -(assoc op) -Hw -Hy.
    + by x1, z1, y1, w1.
  - intros (x1&x2&y1&y2&Hx&Hy&(z1&z2&w1&w2&Hz&Hw&?&?)&?).
     z1, (z2 x2), w1, (w2 y2); split_and?; auto.
    + by rewrite (assoc op) -Hz -Hx.
    + by rewrite (assoc op) -Hw -Hy.
    + by z2, x2, w2, y2.
Qed.
Lemma wand_intro_r P Q R : (P Q R) P Q -★ R.
Proof.
  unsealHPQR; splitn x y ??? n' x' y' ????. apply HPQR; auto.
   x, x', y, y'; split_and?; auto.
  eapply uPred_closed with n; eauto using cmra_validN_op_l.
Qed.
Lemma wand_intro_affine_r P Q R : (P Q R) P ⧆(Q -★ R).
Proof.
  unsealHPQR. splitn x y ?? Haffine; split; auto.
  intros n' x' y' ????. apply HPQR; auto.
   x, x', y, y'; split_and?; auto.
  eapply uPred_closed with n; eauto using cmra_validN_op_l.
  destruct Haffine; auto.
Qed.
Lemma wand_elim_l' P Q R : (P Q -★ R) P Q R.
  unsealHPQR. split; intros n x y ?? (?&?&?&?&?&?&?&?). cofe_subst.
  eapply HPQR; eauto using cmra_validN_op_l.
Qed.

Hint Resolve sep_mono.
Lemma sep_mono_l P P' Q : (P Q) P P' Q P'.
Proof. by intros; apply sep_mono. Qed.
Lemma sep_mono_r P P' Q' : (P' Q') P P' P Q'.
Proof. by apply sep_mono. Qed.
Global Instance sep_mono' : Proper ((⊢) ==> (⊢) ==> (⊢)) (@uPred_sep M).
Proof. by intros P P' HP Q Q' HQ; apply sep_mono. Qed.
Global Instance sep_flip_mono' :
  Proper (flip (⊢) ==> flip (⊢) ==> flip (⊢)) (@uPred_sep M).
Proof. by intros P P' HP Q Q' HQ; apply sep_mono. Qed.
Lemma wand_mono P P' Q Q' : (Q P) (P' Q') (P -★ P') Q -★ Q'.
Proof.
  intros HP HQ; apply wand_intro_r. rewrite HP -HQ. by apply wand_elim_l'.
Qed.
Global Instance wand_mono' : Proper (flip (⊢) ==> (⊢) ==> (⊢)) (@uPred_wand M).
Proof. by intros P P' HP Q Q' HQ; apply wand_mono. Qed.

Lemma emp_True: Emp ⊣⊢ True.
Proof. rewrite affine_equiv left_id. auto. Qed.
Global Instance sep_Emp : RightId (⊣⊢) Emp%I (@uPred_sep M).
Proof. by intros P; rewrite comm left_id. Qed.
Lemma sep_elim_l P Q : P Q P.
Proof. by rewrite (True_intro Q) -emp_True right_id. Qed.
Lemma sep_elim_r P Q : P Q Q.
Proof. by rewrite (comm (★))%I; apply sep_elim_l. Qed.
Lemma sep_elim_l' P Q R : (P R) P Q R.
Proof. intros ->; apply sep_elim_l. Qed.
Lemma sep_elim_r' P Q R : (Q R) P Q R.
Proof. intros ->; apply sep_elim_r. Qed.
Hint Resolve sep_elim_l' sep_elim_r'.
Lemma sep_intro_Emp_l P Q R : (Emp P) (R Q) (R P Q).
Proof. by intros; rewrite -(left_id Emp%I uPred_sep R); apply sep_mono. Qed.
Lemma sep_intro_Emp_r P Q R : (R P) (Emp Q) (R P Q).
Proof. by intros; rewrite -(right_id Emp%I uPred_sep R); apply sep_mono. Qed.
Lemma sep_elim_Emp_l P Q R : (Emp P) (P R Q) R Q.
Proof. by intros HP; rewrite -HP left_id. Qed.
Lemma sep_elim_Emp_r P Q R : (Emp P) (R P Q) R Q.
Proof. by intros HP; rewrite -HP right_id. Qed.
Lemma sep_elim_l_equiv P Q {A: cofeT} (a b: A): (P a b) (P Q a b).
Proof.
  unseal. intros HPre. split. intros n x y ?? (x1&x2&y1&y2&Hx&Hy&HP&HQ).
  simpl. eapply HPre with x1 y1; eauto.
  - eapply cmra_validN_op_l with x2. rewrite -Hx; auto.
  - eapply cmra_validN_op_l with y2. rewrite -Hy; auto.
Qed.
Lemma sep_elim_l_valid P Q {A: cmraT} (a: A): (P a) (P Q a).
Proof.
  unseal. intros HPre. split. intros n x y ?? (x1&x2&y1&y2&Hx&Hy&HP&HQ).
  simpl. eapply HPre with x1 y1; eauto.
  - eapply cmra_validN_op_l with x2. rewrite -Hx; auto.
  - eapply cmra_validN_op_l with y2. rewrite -Hy; auto.
Qed.
Lemma sep_elim_l_ownM P Q (a: M): (P uPred_ownM a) (P Q uPred_ownM a).
Proof.
  unseal. intros HPre. split. intros n x y ?? (x1&x2&y1&y2&Hx&Hy&HP&HQ).
  simpl. destruct HPre as [HPre']. etransitivity; first (eapply (HPre' n x1 y1); eauto).
  - eapply cmra_validN_op_l with x2. rewrite -Hx; auto.
  - eapply cmra_validN_op_l with y2. rewrite -Hy; auto.
  - eexists; eauto.
Qed.
Lemma wand_intro_l P Q R : (Q P R) P Q -★ R.
Proof. rewrite comm; apply wand_intro_r. Qed.
Lemma wand_intro_affine_l P Q R : (Q P R) P ⧆(Q -★ R).
Proof. rewrite comm; apply wand_intro_affine_r. Qed.
Lemma wand_elim_l P Q : (P -★ Q) P Q.
Proof. by apply wand_elim_l'. Qed.
Lemma wand_elim_r P Q : P (P -★ Q) Q.
Proof. rewrite (comm _ P); apply wand_elim_l. Qed.
Lemma wand_elim_r' P Q R : (Q P -★ R) P Q R.
Proof. intros ->; apply wand_elim_r. Qed.
Lemma wand_apply_l P Q Q' R R' : (P Q' -★ R') (R' R) (Q Q') P Q R.
Proof. intros → → <-; apply wand_elim_l. Qed.
Lemma wand_apply_r P Q Q' R R' : (P Q' -★ R') (R' R) (Q Q') Q P R.
Proof. intros → → <-; apply wand_elim_r. Qed.
Lemma wand_apply_l' P Q Q' R : (P Q' -★ R) (Q Q') P Q R.
Proof. intros → <-; apply wand_elim_l. Qed.
Lemma wand_apply_r' P Q Q' R : (P Q' -★ R) (Q Q') Q P R.
Proof. intros → <-; apply wand_elim_r. Qed.
Lemma wand_frame_l P Q R : (Q -★ R) P Q -★ P R.
Proof. apply wand_intro_l. rewrite -assoc. apply sep_mono_r, wand_elim_r. Qed.
Lemma wand_frame_r P Q R : (Q -★ R) Q P -★ R P.
Proof.
  apply wand_intro_l. rewrite ![(_ P)%I]comm -assoc.
  apply sep_mono_r, wand_elim_r.
Qed.
Lemma wand_iff_emp P : Emp ((P -★ P) (P -★ P))%I.
Proof. by apply and_intro; apply wand_intro_l; rewrite ?right_id. Qed.
Lemma equiv_wand_iff P Q : P ⊣⊢ Q Emp ((P -★ Q) (Q -★ P)).
Proof. intros →. apply wand_iff_emp. Qed.

Lemma wand_diag P : ⧆(P -★ P) ⊣⊢ Emp.
Proof.
  apply (anti_symm _); auto.
  - rewrite emp_True. apply affine_mono; auto.
  - rewrite affine_equiv. apply and_intro; auto.
    apply wand_intro_l. by rewrite right_id.
Qed.
Lemma wand_Emp P : (Emp -★ P) ⊣⊢ P.
Proof.
  apply (anti_symm _).
  - eapply sep_elim_Emp_l; first reflexivity. by rewrite wand_elim_r.
  - apply wand_intro_l. rewrite left_id. auto.
Qed.
Lemma wand_entails P Q : (Emp P -★ Q) P Q.
Proof.
  intros HPQ. eapply sep_elim_Emp_r; first exact: HPQ. by rewrite wand_elim_r.
Qed.
Lemma entails_wand P Q : (P Q) Emp P -★ Q.
Proof. by intros; eapply wand_intro_l; rewrite right_id. Qed.
Lemma wand_curry P Q R : (P -★ Q -★ R) ⊣⊢ (P Q -★ R).
Proof.
  apply (anti_symm _).
  - apply wand_intro_l. by rewrite (comm _ P) -assoc !wand_elim_r.
  - do 2 apply wand_intro_l. by rewrite assoc (comm _ Q) wand_elim_r.
Qed.

Lemma sep_and P Q : P Q ⧆(P Q).
Proof.
  unseal. split. intros n x y ?? (x1&x2&y1&y2&Hx&Hy&(HP&Hy1)&(HQ&Hy2)).
  rewrite Hy1 Hy2 left_id in Hy HP HQ ×.
  intros Hy HP HQ. rewrite Hy Hx.
  simpl; split_and!; auto;
  eapply uPred_mono; eauto using cmra_includedN_r, cmra_includedN_l.
Qed.
Lemma pure_elim_spare φ Q Q' R : (Q φ Q') (φ (Q R)) Q R.
Proof.
  unseal; intros HQP HQR. splitn x ??? HQ; apply HQR; eauto.
  edestruct HQP as (HQP').
  edestruct HQP' as (?&?&?&?&?); try eapply HQ; eauto.
  naive_solver.
Qed.
Lemma pure_elim_sep_l φ Q R : (φ Q R) φ Q R.
Proof.
  intros. eapply (pure_elim_spare φ _ Q); auto.
Qed.
Lemma pure_elim_sep_r φ Q R : (φ Q R) Q φ R.
Proof. intros; eapply (pure_elim_spare φ _ Q); eauto. rewrite comm; eauto. Qed.

Global Instance sep_False : LeftAbsorb (⊣⊢) False%I (@uPred_sep M).
Proof. intros P; apply (anti_symm _); auto.
       unseal; split; naive_solver.
Qed.
Global Instance False_sep : RightAbsorb (⊣⊢) False%I (@uPred_sep M).
Proof. intros P; apply (anti_symm _); auto.
       unseal; split; naive_solver.
Qed.

Lemma sep_and_l P Q R : P (Q R) (P Q) (P R).
Proof. auto. Qed.
Lemma sep_and_r P Q R : (P Q) R (P R) (Q R).
Proof. auto. Qed.
Lemma sep_or_l P Q R : P (Q R) ⊣⊢ (P Q) (P R).
Proof.
  apply (anti_symm (⊢)); last by eauto 8.
  apply wand_elim_r', or_elim; apply wand_intro_l; auto.
Qed.
Lemma sep_or_r P Q R : (P Q) R ⊣⊢ (P R) (Q R).
Proof. by rewrite -!(comm _ R) sep_or_l. Qed.
Lemma sep_exist_l {A} P (Ψ : A uPred M) : P ( a, Ψ a) ⊣⊢ a, P Ψ a.
Proof.
  intros; apply (anti_symm (⊢)).
  - apply wand_elim_r', exist_elima. apply wand_intro_l.
    by rewrite -(exist_intro a).
  - apply exist_elima; apply sep_mono; auto using exist_intro.
Qed.
Lemma sep_exist_r {A} (Φ: A uPred M) Q: ( a, Φ a) Q ⊣⊢ a, Φ a Q.
Proof. setoid_rewrite (comm _ _ Q); apply sep_exist_l. Qed.
Lemma sep_forall_l {A} P (Ψ : A uPred M) : P ( a, Ψ a) a, P Ψ a.
Proof. by apply forall_introa; rewrite forall_elim. Qed.
Lemma sep_forall_r {A} (Φ : A uPred M) Q : ( a, Φ a) Q a, Φ a Q.
Proof. by apply forall_introa; rewrite forall_elim. Qed.
Lemma sep_affine_1 P Q: (P Q) ⧆(P Q).
Proof.
  unseal. constructor. intros n x y ?? (x1&x2&y1&y2&Hx&Hy&(?&Hy2)&(?&Hy1)).
    split.
    × x1, x2, y1, y2. split_and!; auto.
    × by rewrite Hy Hy1 Hy2 right_id.
Qed.

Lemma affine_elim P : P P.
Proof. by rewrite affine_equiv and_elim_l. Qed.
Lemma affine_affine0 P : ⧆⧆P ⊣⊢ P.
Proof.
  rewrite ?affine_equiv. apply (anti_symm (⊢)); auto.
Qed.
Lemma sep_affine_2 P Q: (P Q) ⊣⊢ ⧆(⧆P Q).
Proof.
  apply (anti_symm (⊢)).
  - rewrite -{1}(affine_affine0 P) -{1}(affine_affine0 Q) sep_affine_1 //=.
  - rewrite ?affine_equiv. auto.
Qed.
Lemma sep_affine_3 P Q: (P Q) ⊣⊢ ⧆(⧆P Q).
Proof.
  apply (anti_symm (⊢)).
  - rewrite -{1}(affine_affine0 P) -{1}(affine_affine0 Q) sep_affine_1 (affine_elim Q) //=.
  - unseal; constructor.
    intros n x y ?? ((x1&x2&y1&y2&Hx&Hy&(?&Hy1)&HQ)&Hempty).
    assert (y2 ≡{n}≡ ) as Hy2.
    { rewrite -Hempty Hy Hy1 left_id //=. }
     x1, x2, y1, y2. split_and!; auto.
    × split; auto.
    × split; auto.
Qed.

Lemma relevant_pure φ : ( φ) ⊣⊢ (φ).
Proof.
  unseal. splitn x y ??. simpl; split.
  - intros ((?&?)&?). split; auto. etransitivity; eauto.
  - intros (?&Hempty). split_and!; auto;
    rewrite Hempty persistent_core //=.
Qed.
Lemma relevant_elim P : P P.
Proof.
  unseal; splitn x y ?? /=.
  intros (HP&Hy). rewrite Hy.
  eapply uPred_mono; eauto using @cmra_included_core, cmra_included_includedN.
Qed.
Lemma relevant_intro' P Q : ( P Q) P Q.
Proof.
  unsealHPQ.
  splitn x y ?? [? ?].
  split; auto. apply HPQ; simpl; auto using @cmra_core_validN. by rewrite ?cmra_core_idemp.
Qed.
Lemma relevant_and P Q : ( (P Q)) ⊣⊢ ( P Q).
Proof. unseal; splitn x y ??; naive_solver. Qed.
Lemma relevant_or P Q : ( (P Q)) ⊣⊢ ( P Q).
Proof. unseal; splitn x y ??; naive_solver. Qed.
Lemma relevant_forall `{Inhabited A} (Ψ : A uPred M) : ( a, Ψ a) ⊣⊢ ( a, Ψ a).
Proof.
  unseal; splitn x y ??.
  split; simpl; try naive_solver.
  intros HP. split; first naive_solver.
  edestruct (HP inhabitant); eauto.
Qed.
Lemma relevant_exist {A} (Ψ : A uPred M) : ( a, Ψ a) ⊣⊢ ( a, Ψ a).
Proof.
  unseal; splitn x y ??; try naive_solver.
Qed.
Lemma relevant_and_sep_1 P Q : (P Q) (P Q).
Proof.
  unseal; splitn x y ?? [??].
  split; auto.
   (core x), (core x), (core y), (core y); rewrite -?cmra_core_dup; auto.
Qed.
Lemma relevant_and_sep_l_1' P Q : ( P Q) ( P Q).
Proof.
  unseal; splitn x y ?? [[??]?]; (core x), x, (core y), y; simpl in ×.
  by rewrite ?cmra_core_l ?cmra_core_idemp.
Qed.
Lemma relevant_relevant_later P : ( P) ⊣⊢ ( P).
Proof. unseal. split=>-[|n] x y ??; auto.
       simpl. rewrite ?cmra_core_idemp. naive_solver.
Qed.
Lemma relevant_later_1 P : ( P) ( P).
Proof. rewrite relevant_relevant_later. rewrite relevant_elim //=. Qed.
Lemma relevant_relevant_later_contra P Q: ( P -★ Q) (□ P -★ Q).
Proof.
  apply relevant_intro'. apply wand_intro_l.
  rewrite relevant_later_1.
  rewrite {1}(relevant_elim (_ -★ _)).
  apply wand_elim_r.
Qed.
Lemma relevant_mono P Q : (P Q) P Q.
Proof. unseal. intro HPQ. econstructor; intros n x y ?? [? ?].
       split; auto. eapply HPQ; eauto using @cmra_core_validN.
Qed.
Lemma relevant_eq {A:cofeT} (a b : A) : ( ⧆(a b)) ⊣⊢ ⧆(a b).
Proof.
  unseal; apply (anti_symm (⊢)); auto; split.
  - intros n x y ?? ((Hequiv&Hempty)&Hcore).
    split; auto. etransitivity; eauto.
  - intros n x y ?? (Hequiv&Hempty).
    split; rewrite ?Hempty ?persistent_core; naive_solver.
Qed.

Hint Resolve relevant_mono.

Global Instance relevant_mono' : Proper ((⊢) ==> (⊢)) (@uPred_relevant M).
Proof. intros P Q; apply relevant_mono. Qed.
Global Instance relevant_flip_mono' :
  Proper (flip (⊢) ==> flip (⊢)) (@uPred_relevant M).
Proof. intros P Q; apply relevant_mono. Qed.
Lemma relevant_impl P Q : (P Q) ( P Q).
Proof.
  apply impl_intro_l; rewrite -relevant_and.
  apply relevant_mono, impl_elim with P; auto.
Qed.
Lemma relevant_affine_and_sep P Q : ( (P Q)) ⊣⊢ ( (P Q)).
Proof.
  apply (anti_symm (⊢)).
  - rewrite relevant_and_sep_1; auto.
  - unseal. split; intros n x y ?? ((x1&x2&y1&y2&Hx&Hy&(?&Hy1)&(?&Hy2))&?).
    simpl. rewrite Hy Hy1 Hy2 right_id; split_and!; auto.
    × rewrite -Hy1 Hx.
      eapply uPred_mono; eauto using cmra_included_l, cmra_included_includedN.
    × rewrite -Hy2 Hx.
      eapply uPred_mono; eauto using cmra_included_r, cmra_included_includedN.
    × rewrite Hy1 Hy2 right_id in Hy *; etransitivity; eauto.
Qed.
Lemma relevant_sep P Q : ( P Q) (□(P Q)).
Proof.
  unseal; split. intros n x y ?? (x1&x2&y1&y2&Hx&Hy&(HP&Hcore1)&(HQ&Hcore2)).
  split.
  - destruct (cmra_core_monoN n x1 (x1 x2)) as (x1'&Hx1'); eauto using cmra_includedN_l.
    destruct (cmra_core_monoN n x2 (x1 x2)) as (x2'&Hx2'); eauto using cmra_includedN_r.
     (core x1 x1'), (core x2 x2'), (core y1), (core y2).
    rewrite -Hx1' -Hx2'.
    rewrite ?Hx ?Hy {1}Hcore1 {1}Hcore2.
    rewrite -?cmra_core_dup ?cmra_core_distrib -?Hy //=.
    split_and!; auto.
    × rewrite ?Hx1'.
      eapply uPred_mono; eauto using cmra_included_l, cmra_included_r;
      rewrite -Hx1'. apply cmra_core_monoN, cmra_includedN_l.
    × rewrite ?Hx2';
      eapply uPred_mono; eauto using cmra_included_l, cmra_included_r;
      rewrite -Hx2'. apply cmra_core_monoN, cmra_includedN_r.
  - rewrite Hy Hcore1 Hcore2 cmra_core_distrib; auto.
    rewrite -Hy; auto.
Qed.
Lemma relevant_sep_dup'_1 P : ( P) ( P P).
Proof.
  unseal. split; intros n x y ?? (HP&?).
   x, (core x), y, (core y); split; auto.
  × rewrite cmra_core_r; auto.
  × split_and!; auto.
    ** by rewrite cmra_core_r.
    ** simpl in *; rewrite ?cmra_core_idemp; auto.
    ** simpl. rewrite ?cmra_core_idemp; auto.
Qed.
Lemma relevant_affine_sep P Q : ( (P Q) ⊣⊢ (P Q)).
Proof.
  apply (anti_symm (⊢)).
  - rewrite -relevant_affine_and_sep. rewrite relevant_sep_dup'_1.
    apply sep_mono; auto.
  - rewrite relevant_sep. auto.
Qed.
Lemma relevant_wand_1 P Q: (P -★ Q) (P -★ Q).
Proof.
  apply wand_intro_r.
  rewrite relevant_sep.
  rewrite wand_elim_l. auto.
Qed.
Lemma relevant_wand_impl_1 P Q : ( (P -★ Q)) ( (P Q)).
Proof.
  unseal; split; intros n x y ?? (HPre&?).
  split; auto. intros n' ? Hincluded ????.
  specialize (HPre n' x' (core (core y))).
  rewrite ?cmra_core_r in HPre *; intros HPre.
  destruct Hincluded as (x''&Hin).
  eapply uPred_mono; first eapply HPre; eauto.
  × rewrite Hin assoc -cmra_core_dup -Hin //=.
  × rewrite cmra_core_idemp; auto.
  × rewrite Hin assoc -cmra_core_dup //=.
Qed.
Lemma relevant_entails_l' P Q : (P Q) P ( Q P).
Proof.
  unseal. intros HPQ. split. intros n x ? HP.
  destruct HPQ as [HPQ'].
  simpl. (core x), x, (core y), y.
  split_and!; auto.
  × rewrite cmra_core_l. auto.
  × rewrite cmra_core_l. auto.
  × rewrite ?cmra_core_idemp. eapply HPQ'; eauto.
  × by rewrite cmra_core_idemp.
Qed.
Lemma relevant_entails_r' P Q : (P Q) P (P Q).
Proof. intros; rewrite comm. apply relevant_entails_l'; auto. Qed.


Hint Resolve or_elim or_intro_l' or_intro_r'.

Lemma affine_relevant P : P P.
Proof.
  unseal; auto; econstructor; intros n x y ?? ((?&Hcore)&Heqy); auto.
  split; auto. split; auto.
  by rewrite Heqy persistent_core.
Qed.
Lemma affine_and P Q : (⧆(P Q)) ⊣⊢ (P Q).
Proof. unseal; splitn x y ??; naive_solver. Qed.
Lemma affine_and_l P Q : (P Q) ⊣⊢ (P Q).
Proof. unseal; splitn x y ??; naive_solver. Qed.
Lemma affine_and_r P Q : (P Q) ⊣⊢ (P Q).
Proof. unseal; splitn x y ??; naive_solver. Qed.
Lemma affine_or P Q : (⧆(P Q)) ⊣⊢ (P Q).
Proof. unseal; splitn x y ??; naive_solver. Qed.
Lemma affine_forall `{Inhabited A} (Ψ : A uPred M) : ( a, Ψ a) ⊣⊢ ( a, Ψ a).
Proof.
  unseal; splitn x y ??.
  split; simpl; try naive_solver.
  intros HP. split; first naive_solver.
  edestruct (HP inhabitant); eauto.
Qed.
Lemma affine_exist {A} (Ψ : A uPred M) : ( a, Ψ a) ⊣⊢ ( a, Ψ a).
Proof.
  unseal; splitn x y ??; try naive_solver.
Qed.
Lemma affine_affine_later P : ( P) ⊣⊢ ( P).
Proof. unseal. split=>-[|n] x y ??; auto.
       simpl. split; intros; naive_solver eauto 2 using dist_le.
Qed.
Lemma affine_later_1 P : ( P) ( P).
Proof. rewrite affine_affine_later. rewrite affine_elim //=. Qed.
Lemma affine_later_distrib P Q: ( (P Q) ( P Q)).
Proof.
  unseal. split=>-[|n] x y ?? [HL ?]; auto; simpl.
  - x, , , .
    split_and!; rewrite ?right_id; auto.
  - destruct HL as (x1&x2&y1&y2&?&?&(?&Hay1)&(?&Hay2)).
    destruct (cmra_extend n x x1 x2)
      as ([x1' x2']&Hx'&Hx1'&Hx2'); eauto using cmra_validN_S; simpl in ×.
    destruct (cmra_extend (S n) y )
      as ([y1' y2']&Hy'&Hy1'&Hy2'); eauto using cmra_validN_S; simpl in ×.
      { rewrite ?right_id; eauto using dist_le. }
     x1', x2', y1', y2'; split_and!; auto.
    × by rewrite Hx1' (dist_le _ _ _ _ Hy1') //= -?Hay1; last lia.
    × by rewrite Hx2' (dist_le _ _ _ _ Hy2') //= -?Hay2; last lia.
Qed.
Lemma relevant_affine P : P ⊣⊢ P.
Proof.
  apply (anti_symm (⊢)).
  - unseal; auto; econstructor; intros n x y ?? ((?&Hcore)&Heqy); auto.
    split.
    × split; auto.
    × by rewrite Heqy Hcore.
  - unseal; auto; econstructor; intros n x y ?? ((?&Hcore)&Heqy); auto.
    split; auto. split; auto. rewrite -Heqy; auto.
Qed.

Lemma affine_relevant_later P: P ⊣⊢ P.
Proof.
  apply (anti_symm (⊢)).
  - unseal; auto; econstructor; intros n x y ??.
    intros (HP&Haff). split; auto.
    rewrite Haff in HP ×. destruct n as [|n']; simpl; rewrite ?persistent_core; auto.
    intros (HP&_). split; auto.
  - apply affine_mono, relevant_later_1.
Qed.

Lemma unlimited_elim P : P P.
Proof.
  by rewrite affine_elim relevant_elim.
Qed.
Lemma unlimited_intro' P Q : ( P Q) P Q.
Proof.
  unsealHPQ.
  splitn x y ?? HP.
  assert (y ≡{n}≡ ) as Hequiv.
  { naive_solver. }
  split; [split |]; auto.
  - rewrite Hequiv persistent_core. eapply HPQ; simpl; auto using @cmra_core_validN.
    × apply cmra_valid_validN, ucmra_unit_valid.
    × by rewrite Hequiv //= ?persistent_core in HP ×.
  - by rewrite Hequiv persistent_core.
Qed.
Lemma unlimited_and P Q : (⧈(P Q)) ⊣⊢ ( P Q).
Proof. by rewrite relevant_and affine_and. Qed.
Lemma unlimited_or P Q : ( (P Q)) ⊣⊢ ( P Q).
Proof. by rewrite relevant_or affine_or. Qed.
Lemma unlimited_forall `{Inhabited A} (Ψ : A uPred M) : ( a, Ψ a) ⊣⊢ ( a, Ψ a).
Proof. by rewrite relevant_forall affine_forall. Qed.
Lemma unlimited_exist {A} (Ψ : A uPred M) : ( a, Ψ a) ⊣⊢ ( a, Ψ a).
Proof. by rewrite relevant_exist affine_exist. Qed.
Lemma unlimited_and_sep_1 P Q : (P Q) (P Q).
Proof.
  unseal; splitn x y ?? [[[HP HQ] Hcore] Hempty].
  rewrite Hempty.
  split; [| auto].
  split; [| by rewrite persistent_core].
   (core x), (core x), , .
  rewrite -Hcore Hempty in HP HQ ×.
  rewrite -cmra_core_dup ?right_id persistent_core; auto.
Qed.
Lemma unlimited_and_sep_l_1 P Q : ( P Q) (P Q).
Proof.
  unseal; splitn x y ?? [[[HP Hcore] Hempty] HQ]; (core x), x, , ; simpl in ×.
  rewrite cmra_core_l cmra_core_idemp right_id persistent_core.
  by rewrite Hempty persistent_core in HP HQ ×.
Qed.
Lemma unlimited_unlimited_later P : ( P) ⊣⊢ ( P).
Proof.
  rewrite -(relevant_affine P) -(relevant_relevant_later).
  rewrite -(relevant_affine ( P)) -(affine_affine_later P).
  by rewrite relevant_affine.
Qed.

Lemma unlimited_mono P Q : (P Q) P Q.
Proof. intros. apply unlimited_intro'. by rewrite unlimited_elim. Qed.
Hint Resolve unlimited_mono.
Lemma unlimited_impl P Q : ⧈(P Q) ( P Q).
Proof.
  apply impl_intro_l; rewrite -unlimited_and.
  apply unlimited_mono, impl_elim with P; auto.
Qed.
Lemma unlimited_sep_dup' P : (P) ⊣⊢ (P P).
Proof.
  apply (anti_symm (⊢)); auto.
  unseal. splitn x y ?? [[HP ?] Hempty].
  simpl in ×. x, (core x), , . rewrite cmra_core_r right_id ?persistent.
  by rewrite Hempty ?persistent_core in HP ×.
Qed.

Global Instance relevant_if_ne n p : Proper (dist n ==> dist n) (@uPred_relevant_if M p).
Proof. solve_proper. Qed.
Global Instance relevant_if_proper p : Proper ((⊣⊢) ==> (⊣⊢)) (@uPred_relevant_if M p).
Proof. solve_proper. Qed.
Global Instance relevant_if_mono p : Proper ((⊢) ==> (⊢)) (@uPred_relevant_if M p).
Proof. solve_proper. Qed.

Lemma relevant_if_elim p P : □?p P P.
Proof. destruct p; simpl; auto using relevant_elim. Qed.
Lemma always_elim_if p P : P □?p P.
Proof. destruct p; simpl; auto using relevant_elim. Qed.
Lemma relevant_if_and p P Q : □?p (P Q) ⊣⊢ □?p P □?p Q.
Proof. destruct p; simpl; auto using relevant_and. Qed.
Lemma relevant_if_or p P Q : □?p (P Q) ⊣⊢ □?p P □?p Q.
Proof. destruct p; simpl; auto using relevant_or. Qed.
Lemma relevant_if_exist {A} p (Ψ : A uPred M) : (□?p a, Ψ a) ⊣⊢ a, □?p Ψ a.
Proof. destruct p; simpl; auto using relevant_exist. Qed.

Lemma later_mono P Q : (P Q) P Q.
Proof.
  unsealHP; split=>-[|n] x y ???; [done|apply HP; eauto using cmra_validN_S].
Qed.
Lemma later_intro P : P P.
Proof.
  unseal; split⇒ -[|n] x y ???; simpl in *; [done|].
  apply uPred_closed with (S n); eauto using cmra_validN_S.
Qed.
Lemma löb P : ( P P) P.
Proof.
  unseal; splitn x y ?? HP; induction n as [|n IH]; [by apply HP|].
  apply HP, IH, uPred_closed with (S n); eauto using cmra_validN_S.
Qed.
Lemma wand_löb P : □( P -★ P) P.
Proof.
  unseal; splitn x y ?? HP; induction n as [|n IH].
  - destruct HP as (HP&Hcore). specialize (HP 0 ). rewrite ?right_id in HP ×. simpl; auto.
    intros HP'. rewrite Hcore. rewrite ?cmra_core_idemp.
    split; auto.
    eapply uPred_closed with 0; first apply HP';
    eauto using @cmra_core_validN, cmra_included_includedN, cmra_included_core.
  - destruct HP as (HP&Hcore).
    simpl. split; auto. simpl in HP.
    generalize (HP (S n) (core x) (core y)). rewrite -?cmra_core_dup. simpl. intros HP'.
    apply HP'; eauto using cmra_core_validN.
    apply IH; eauto using cmra_validN_S.
    split; eauto using dist_le.
    simpl. intros n' x' y' ???. eapply HP; eauto.
Qed.

Lemma relevant_löb_1 P : □(□ P -★ P) P.
Proof.
  unseal; splitn x y ?? HP; induction n as [|n IH].
  - destruct HP as (HP&Hcore); split; auto.
    edestruct HP as (HP'&Heq); eauto using @cmra_core_validN.
    × erewrite cmra_core_l; auto.
    × erewrite cmra_core_l; auto.
    × simpl. auto.
    × rewrite -Heq in HP' ×.
      rewrite ?cmra_core_l {1}Hcore. auto.
  - destruct HP as (HP&Hcore). rewrite Hcore.
    eapply uPred_closed with (S n); eauto using cmra_validN_S, @cmra_core_validN.
    edestruct HP as (HP'&Heq); eauto.
    × erewrite cmra_core_l; auto.
    × erewrite cmra_core_l; auto.
    × simpl. split; auto.
      eapply IH; eauto using cmra_validN_S.
      split; eauto using dist_S.
      eapply uPred_closed with (S n); eauto using cmra_validN_S, @cmra_core_validN.
    × split.
      ** rewrite ?cmra_core_l ?cmra_core_idemp in HP' ×. eauto.
      ** rewrite cmra_core_idemp; trivial.
Qed.
Lemma unlimited_löb_1 P : □( P -★ P) P.
Proof.
  unseal; splitn x y ?? HP; induction n as [|n IH].
  - destruct HP as ((HP&Hcore)&Haff); split; auto.
    simpl in HP.
    edestruct (HP 0 x ) as (HP'&Heq); eauto using cmra_core_validN.
    × erewrite cmra_core_l; auto.
    × rewrite right_id. apply cmra_core_validN; eauto.
    × simpl. split; rewrite ?persistent_core; auto.
    × rewrite -Heq in HP' ×.
      rewrite ?cmra_core_l ?right_id ?cmra_core_idemp {1}Hcore.
      rewrite cmra_core_idemp -cmra_core_dup cmra_core_idemp.
      intros (?&?); auto.
  - destruct HP as ((HP&Hcore)&Haff). rewrite Hcore.
    eapply uPred_closed with (S n); eauto using cmra_validN_S, @cmra_core_validN.
    edestruct HP as (HP'&?); eauto.
    × erewrite cmra_core_l; auto.
    × erewrite cmra_core_l; auto.
    × simpl. split; auto.
      split; auto.
      eapply IH; eauto using cmra_validN_S.
      split; eauto using dist_S.
      split; eauto using dist_S.
      eapply uPred_closed with (S n); eauto using cmra_validN_S, @cmra_core_validN.
    × split.
      ** rewrite ?cmra_core_l ?cmra_core_idemp in HP' ×. simpl. intros (?&?); eauto.
      ** rewrite cmra_core_idemp; trivial.
Qed.
Lemma relevant_löb_2 P : □(□ P P) P.
Proof.
  unseal; splitn x y ?? HP; induction n as [|n IH].
  - destruct HP as (HP&Hcore); split; auto.
    edestruct HP as (HP'&?); eauto using @cmra_core_validN.
    × split; simpl; auto.
      by rewrite cmra_core_idemp.
    × by rewrite ?cmra_core_idemp in HP' ×.
  - edestruct HP as (HP'&Hcore).
    simpl; split; auto.
    rewrite -(cmra_core_idemp x).
    rewrite -(cmra_core_idemp y).
    apply HP'; eauto using @cmra_core_validN.
    split; last (by rewrite cmra_core_idemp).

    rewrite ?cmra_core_idemp. apply IH; eauto using cmra_validN_S.
    split; last (by eauto using dist_le).
    eapply uPred_closed with (S n); eauto using cmra_validN_S, @cmra_core_validN.
Qed.
Lemma relevant_löb_3 P : □( P P) P.
Proof.
  apply relevant_mono. apply löb.
Qed.
Lemma later_and P Q : ( (P Q)) ⊣⊢ ( P Q).
Proof. unseal; split⇒ -[|n] x y; by split. Qed.
Lemma later_or P Q : ( (P Q)) ⊣⊢ ( P Q).
Proof. unseal; split⇒ -[|n] x y; simpl; tauto. Qed.
Lemma later_forall {A} (Φ : A uPred M) : ( a, Φ a) ⊣⊢ ( a, Φ a).
Proof. unseal; by split⇒ -[|n] x y. Qed.
Lemma later_exist_1 {A} (Φ : A uPred M) : ( a, Φ a) ( a, Φ a).
Proof. unseal; by split⇒ -[|[|n]] x y. Qed.
Lemma later_exist_2 `{Inhabited A} (Φ : A uPred M) :
  ( a, Φ a)%I ( a, Φ a)%I.
Proof. unseal; split⇒ -[|[|n]] x y; done || by inhabitant. Qed.
Lemma later_sep P Q : (P Q) ⊣⊢ P Q.
Proof.
  unseal; splitn x z ??; split.
  - destruct n as [|n]; simpl.
    { by x, (core x), z, (core z); rewrite ?cmra_core_r. }
    intros (x1&x2&z1&z2&Hx&Hz&?&?);
      destruct (cmra_extend n x x1 x2) as ([y1 y2]&Hx'&Hy1&Hy2);
      eauto using cmra_validN_S; simpl in ×.
      destruct (cmra_extend n z z1 z2) as ([q1 q2]&Hz'&Hq1&Hq2);
      eauto using cmra_validN_S; simpl in ×.
     y1, y2, q1, q2; split_and!; [by rewrite Hx'| by rewrite Hz'
                                        | by rewrite Hy1 Hq1 | by rewrite Hy2 Hq2].
  - destruct n as [|n]; simpl; [done|intros (x1&x2&z1&z2&Hx&Hz&?&?)].
     x1, x2, z1, z2; split_and!; eauto using dist_S.
Qed.
Lemma later_sep_affine_l P Q : ( (P Q)) ⊣⊢ ( P Q).
Proof.
  unseal; splitn x z ??; split.
  - destruct n as [|n]; simpl.
    { by intros; x, (core x), , z; rewrite ?cmra_core_r ?left_id. }
    intros (x1&x2&z1&z2&Hx&Hz&(?&Haff)&?);
      destruct (cmra_extend n x x1 x2) as ([y1 y2]&Hx'&Hy1&Hy2);
      eauto using cmra_validN_S; simpl in ×.
      destruct (cmra_extend n z z1 z2) as ([q1 q2]&Hz'&Hq1&Hq2);
      eauto using cmra_validN_S; simpl in ×.
     y1, y2, , (q1 q2); split_and!.
    × by rewrite Hx'.
    × by rewrite left_id Hz'.
    × by rewrite -Haff Hy1.
    × done.
    × by rewrite Hq1 Hq2 Hy2 Haff ?left_id.
  - destruct n as [|n]; simpl; [done|intros (x1&x2&z1&z2&Hx&Hz&(?&?)&?)].
     x1, x2, z1, z2; split_and!; eauto using dist_S.
Qed.

Lemma later_proper P Q : (P ⊣⊢ Q) P ⊣⊢ Q.
Proof. by intros →. Qed.
Hint Resolve later_mono later_proper.
Global Instance later_mono' : Proper ((⊢) ==> (⊢)) (@uPred_later M).
Proof. intros P Q; apply later_mono. Qed.
Global Instance later_flip_mono' :
  Proper (flip (⊢) ==> flip (⊢)) (@uPred_later M).
Proof. intros P Q; apply later_mono. Qed.
Lemma later_True : True ⊣⊢ True.
Proof. apply (anti_symm (⊢)); auto using later_intro. Qed.
Lemma later_impl P Q : (P Q) P Q.
Proof. apply impl_intro_l; rewrite -later_and; eauto using impl_elim. Qed.
Lemma later_exist `{Inhabited A} (Φ : A uPred M) :
   ( a, Φ a) ⊣⊢ ( a, Φ a).
Proof. apply: anti_symm; eauto using later_exist_2, later_exist_1. Qed.
Lemma later_wand P Q : (P -★ Q) P -★ Q.
Proof. apply wand_intro_r;rewrite -later_sep; eauto using wand_elim_l. Qed.
Lemma later_iff P Q : (P Q) P Q.
Proof. by rewrite /uPred_iff later_and !later_impl. Qed.
Lemma later_sep_affine_r P Q : ( (P Q)) ⊣⊢ ( P Q).
Proof. by rewrite comm later_sep_affine_l comm. Qed.

Global Instance laterN_ne n m : Proper (dist n ==> dist n) (@uPred_laterN M m).
Proof. induction m; simpl. by intros ???. solve_proper. Qed.
Global Instance laterN_proper m :
  Proper ((⊣⊢) ==> (⊣⊢)) (@uPred_laterN M m) := ne_proper _.

Lemma later_laterN n P : ▷^(S n) P ⊣⊢ ▷^n P.
Proof. done. Qed.
Lemma laterN_later n P : ▷^(S n) P ⊣⊢ ▷^n P.
Proof. induction n; simpl; auto. Qed.
Lemma laterN_plus n1 n2 P : ▷^(n1 + n2) P ⊣⊢ ▷^n1 ▷^n2 P.
Proof. induction n1; simpl; auto. Qed.
Lemma laterN_le n1 n2 P : n1 n2 ▷^n1 P ▷^n2 P.
Proof. induction 1; simpl; by rewrite -?later_intro. Qed.

Lemma laterN_mono n P Q : (P Q) ▷^n P ▷^n Q.
Proof. induction n; simpl; auto. Qed.
Lemma laterN_intro n P : P ▷^n P.
Proof. induction n as [|n IH]; simpl; by rewrite -?later_intro. Qed.
Lemma laterN_and n P Q : ▷^n (P Q) ⊣⊢ ▷^n P ▷^n Q.
Proof. induction n as [|n IH]; simpl; rewrite -?later_and; auto. Qed.
Lemma laterN_or n P Q : ▷^n (P Q) ⊣⊢ ▷^n P ▷^n Q.
Proof. induction n as [|n IH]; simpl; rewrite -?later_or; auto. Qed.
Lemma laterN_forall {A} n (Φ : A uPred M) : (▷^n a, Φ a) ⊣⊢ ( a, ▷^n Φ a).
Proof. induction n as [|n IH]; simpl; rewrite -?later_forall; auto. Qed.
Lemma laterN_exist_1 {A} n (Φ : A uPred M) : ( a, ▷^n Φ a) (▷^n a, Φ a).
Proof. induction n as [|n IH]; simpl; rewrite ?later_exist_1; auto. Qed.
Lemma laterN_exist_2 `{Inhabited A} n (Φ : A uPred M) :
  (▷^n a, Φ a) a, ▷^n Φ a.
Proof. induction n as [|n IH]; simpl; rewrite -?later_exist_2; auto. Qed.
Lemma laterN_sep n P Q : ▷^n (P Q) ⊣⊢ ▷^n P ▷^n Q.
Proof. induction n as [|n IH]; simpl; rewrite -?later_sep; auto. Qed.

Global Instance laterN_mono' n : Proper ((⊢) ==> (⊢)) (@uPred_laterN M n).
Proof. intros P Q; apply laterN_mono. Qed.
Global Instance laterN_flip_mono' n :
  Proper (flip (⊢) ==> flip (⊢)) (@uPred_laterN M n).
Proof. intros P Q; apply laterN_mono. Qed.
Lemma laterN_True n : ▷^n True ⊣⊢ True.
Proof. apply (anti_symm (⊢)); auto using laterN_intro. Qed.
Lemma laterN_impl n P Q : ▷^n (P Q) ▷^n P ▷^n Q.
Proof.
  apply impl_intro_l; rewrite -laterN_and; eauto using impl_elim, laterN_mono.
Qed.
Lemma laterN_exist n `{Inhabited A} (Φ : A uPred M) :
  ▷^n ( a, Φ a) ⊣⊢ ( a, ▷^n Φ a).
Proof. apply: anti_symm; eauto using laterN_exist_2, laterN_exist_1. Qed.
Lemma laterN_wand n P Q : ▷^n (P -★ Q) ▷^n P -★ ▷^n Q.
Proof.
  apply wand_intro_r; rewrite -laterN_sep; eauto using wand_elim_l,laterN_mono.
Qed.
Lemma laterN_iff n P Q : ▷^n (P Q) ▷^n P ▷^n Q.
Proof. by rewrite /uPred_iff laterN_and !laterN_impl. Qed.

Lemma ownM_op (a1 a2 : M) :
  uPred_ownM (a1 a2) ⊣⊢ uPred_ownM a1 uPred_ownM a2.
Proof.
  unseal; splitn x y ??; split.
  - intros [z ?]; a1, (a2 z), y, (core y);
    split_and!; [by rewrite (assoc op)| by rewrite cmra_core_r | |].
    × by (core a1); rewrite cmra_core_r.
    × by z.
  - intros (y1&y2&q1&q2&Hx&Hy&[z1 Hy1]&[z2 Hy2]); (z1 z2).
    by rewrite (assoc op _ z1) -(comm op z1) (assoc op z1)
      -(assoc op _ a2) (comm op z1) -Hy1 -Hy2.
Qed.
Lemma ownM_op' (a1 a2 : M) :
  uPred_ownM (a1 a2) ⊣⊢ (uPred_ownM a1 uPred_ownM a2).
Proof.
  unseal; splitn x y ??; split.
  - intros [z ?]; a1, (a2 z), y, ;
    split_and!; [by rewrite (assoc op)| by rewrite right_id | |].
    × by (core a1); rewrite cmra_core_r.
    × split; auto. by z.
  - intros (y1&y2&q1&q2&Hx&Hy&[z1 Hy1]&([z2 Hy2]&Hq2)); (z1 z2).
    by rewrite (assoc op _ z1) -(comm op z1) (assoc op z1)
      -(assoc op _ a2) (comm op z1) -Hy1 -Hy2.
Qed.
Lemma ownM_op'' (a1 a2 : M) :
   uPred_ownM (a1 a2) ⊣⊢ ( uPred_ownM a1 uPred_ownM a2).
Proof.
  unseal; splitn x y ??; split.
  - intros [[z ?] Heqy]. a1, (a2 z), y, ;
    split_and!; [by rewrite (assoc op)| by rewrite right_id | |].
    × rewrite Heqy; split; auto. (core a1); rewrite cmra_core_r //=.
    × split; auto. by z.
  - intros (y1&y2&q1&q2&Hx&Hy&([z1 Hy1]&Hq1)&([z2 Hy2]&Hq2)).
    split.
    × (z1 z2).
        by rewrite (assoc op _ z1) -(comm op z1) (assoc op z1)
           -(assoc op _ a2) (comm op z1) -Hy1 -Hy2.
    × rewrite Hy Hq1 Hq2 right_id //=.
Qed.
Lemma unlimited_ownM_core (a : M) : ( uPred_ownM (core a)) ⊣⊢ uPred_ownM (core a).
Proof.
  splitn x y; split.
  - unseal. intros (([a' Hx]&?)&?); simpl. split; auto.
     (a' x). by rewrite assoc -Hx cmra_core_l.
  - unseal; intros ([a' Hx]&->); simpl. rewrite -(cmra_core_idemp a) Hx.
    split_and!; try (rewrite ?persistent_core; auto; done).
    apply cmra_core_monoN, cmra_includedN_l.
Qed.
Lemma unlimited_ownM (a : M) : Persistent a ( uPred_ownM a) ⊣⊢ uPred_ownM a.
Proof. intros. by rewrite -(persistent_core a) unlimited_ownM_core. Qed.
Lemma ownM_something : True a, uPred_ownM a.
Proof. unseal; splitn x ??. by x; simpl. Qed.
Lemma ownM_something' : Emp a, uPred_ownM a.
Proof. unseal; splitn x ??. by split; auto; x; simpl. Qed.
Lemma ownM_empty : True uPred_ownM .
Proof. unseal; splitn x ??; by x; rewrite left_id. Qed.
Lemma ownM_empty' : Emp uPred_ownM .
Proof. unseal; splitn x ??; by split; auto; x; rewrite left_id. Qed.

Lemma ownMl_op (a1 a2 : M) :
  uPred_ownMl (a1 a2) ⊣⊢ (uPred_ownMl a1 uPred_ownMl a2).
Proof.
  unseal; splitn x y ??; split.
  - intros ?. x, (core x), a1, a2;
    split_and!; simpl; auto.
    by rewrite cmra_core_r.
  - intros (y1&y2&q1&q2&Hx&Hy&Hy1&Hy2).
    simpl in ×. by rewrite Hy Hy1 Hy2.
Qed.
Lemma relevant_ownMl_core (a : M) : ( uPred_ownMl (core a)) ⊣⊢ uPred_ownMl (core a).
Proof.
  splitn x y; split.
  - unseal. intros (Hx&?); simpl. simpl in Hx. rewrite Hx. auto.
  - unseal=>//=. intros Heq. rewrite -?Heq -(cmra_core_idemp a) Heq.
    by rewrite persistent_core.
Qed.
Lemma relevant_ownMl (a : M) : Persistent a ( uPred_ownMl a) ⊣⊢ uPred_ownMl a.
Proof. intros. by rewrite -(persistent_core a) relevant_ownMl_core. Qed.
Lemma ownMl_something : True a, uPred_ownMl a.
Proof. unseal; splitn x y ???. by y; simpl. Qed.
Lemma ownMl_empty : Emp ⊣⊢ uPred_ownMl .
Proof. unseal; splitn x y ??. simpl. split; auto. Qed.

Lemma ownM_valid (a : M) : uPred_ownM a a.
Proof.
  unseal; splitn x y Hv ? [a' ?]; cofe_subst; eauto using cmra_validN_op_l.
Qed.
Lemma ownMl_valid (a : M) : uPred_ownMl a (uPred_ownMl a a).
Proof.
  unseal; splitn x y Hv //= ? Heq.
   x, (core x), y, . rewrite ?cmra_core_r ?right_id. split_and!; auto; by cofe_subst.
Qed.
Lemma valid_intro {A : cmraT} (a : A) : a True a.
Proof. unseal⇒ ?; splitn x y ? ? _ /=; by apply cmra_valid_validN. Qed.
Lemma valid_elim {A : cmraT} (a : A) : ¬ ✓{0} a a False.
Proof. unsealHa; splitn x y ???; apply Ha, cmra_validN_le with n; auto. Qed.
Lemma relevant_valid {A : cmraT} (a : A) : ( a) ⊣⊢ ( a).
Proof.
  unseal. splitn x y ??. simpl; split.
  - intros ((?&?)&?). split; auto. etransitivity; eauto.
  - intros (?&Hempty). split_and!; auto; rewrite Hempty persistent_core //=.
Qed.
Lemma valid_weaken {A : cmraT} (a b : A) : (a b) a.
Proof. unseal; splitn x y _ ?; apply cmra_validN_op_l. Qed.

Lemma ownM_invalid (a : M) : ¬ ✓{0} a uPred_ownM a False.
Proof. by intros; rewrite ownM_valid valid_elim. Qed.
Global Instance ownM_mono : Proper (flip (≼) ==> (⊢)) (@uPred_ownM M).
Proof. intros a b [b' ->]. rewrite ownM_op'. eauto. Qed.

Lemma prod_equivI {A B : cofeT} (x y : A × B) : x y ⊣⊢ x.1 y.1 x.2 y.2.
Proof. by unseal. Qed.
Lemma prod_validI {A B : cmraT} (x : A × B) : x ⊣⊢ x.1 x.2.
Proof. by unseal. Qed.

Lemma later_equivI {A : cofeT} (x y : later A) :
  x y ⊣⊢ (later_car x later_car y).
Proof. by unseal. Qed.

Lemma discrete_valid {A : cmraT} `{!CMRADiscrete A} (a : A) : a ⊣⊢ ( a).
Proof. unseal; splitn x y _ _. by rewrite /= -cmra_discrete_valid_iff. Qed.
Lemma timeless_eq {A : cofeT} (a b : A) : Timeless a a b ⊣⊢ (a b).
Proof.
  unseal⇒ ?. apply (anti_symm (⊢)); splitn x y ??; by apply (timeless_iff n).
Qed.

Lemma option_equivI {A : cofeT} (mx my : option A) :
  mx my ⊣⊢ match mx, my with
             | Some x, Some yx y | None, NoneTrue | _, _False
             end.
Proof.
  uPred.unseal. do 2 split. by destruct 1. by destruct mx, my; try constructor.
Qed.
Lemma option_validI {A : cmraT} (mx : option A) :
   mx ⊣⊢ match mx with Some x x | NoneTrue end.
Proof. uPred.unseal. by destruct mx. Qed.

Lemma timelessP_spec P : TimelessP P n x y, ✓{n} x ✓{n} y P 0 x y P n x y.
Proof.
  split.
  - intros HP n x y ???; induction n as [|n]; auto.
    move: HP; rewrite /TimelessP; unseal⇒ /uPred_in_entails /(_ (S n) x y).
    by destruct 1; auto using cmra_validN_S.
  - moveHP; rewrite /TimelessP; unseal; split⇒ -[|n] x y /=; auto; left.
    apply HP, uPred_closed with n; eauto using cmra_validN_le.
Qed.

Global Instance pure_timeless φ : TimelessP ( φ : uPred M)%I.
Proof. by apply timelessP_spec; unseal ⇒ -[|n] x. Qed.
Global Instance valid_timeless {A : cmraT} `{CMRADiscrete A} (a : A) :
   TimelessP ( a : uPred M)%I.
Proof.
  apply timelessP_spec; unsealn x y /=. by rewrite -!cmra_discrete_valid_iff.
Qed.
Global Instance and_timeless P Q: TimelessP P TimelessP Q TimelessP (P Q).
Proof. by intros ??; rewrite /TimelessP later_and or_and_r; apply and_mono. Qed.
Global Instance or_timeless P Q : TimelessP P TimelessP Q TimelessP (P Q).
Proof.
  intros; rewrite /TimelessP later_or (timelessP _) (timelessP Q); eauto 10.
Qed.
Global Instance impl_timeless P Q : TimelessP Q TimelessP (P Q).
Proof.
  rewrite !timelessP_spec; unsealHP [|n] x y ?? HPQ [|n'] x' ?????; auto.
  apply HP, HPQ, uPred_closed with (S n'); eauto using cmra_validN_le.
Qed.
Global Instance sep_timeless P Q: TimelessP P TimelessP Q TimelessP (P Q).
Proof.
  intros; rewrite /TimelessP later_sep (timelessP P) (timelessP Q).
  apply wand_elim_l', or_elim; apply wand_intro_r; auto;
  first (apply wand_elim_r', or_elim; apply wand_intro_r; auto;
         first rewrite ?(comm _ Q); auto).
  × etransitivity; [|eapply or_intro_r]; unseal.
    splitn x y ?? HP. destruct n; simpl in *; auto.
    naive_solver.
  × etransitivity; [|eapply or_intro_r]; unseal.
    splitn x y ?? HP. destruct n; simpl in *; auto.
    naive_solver.
Qed.
Global Instance wand_timeless P Q : TimelessP Q TimelessP (P -★ Q).
Proof.
  rewrite !timelessP_spec; unsealHP [|n] x y ?? HPQ [|n'] x' y' ????; auto.
  apply HP, HPQ, uPred_closed with (S n');
    eauto 3 using cmra_validN_le, cmra_validN_op_r.
Qed.
Global Instance forall_timeless {A} (Ψ : A uPred M) :
  ( x, TimelessP (Ψ x)) TimelessP ( x, Ψ x).
Proof. by setoid_rewrite timelessP_spec; unseal n x y ??? a; apply . Qed.
Global Instance exist_timeless {A} (Ψ : A uPred M) :
  ( x, TimelessP (Ψ x)) TimelessP ( x, Ψ x).
Proof.
  by setoid_rewrite timelessP_spec; unseal [|n] x y ??;
    [|intros [a ?]; a; apply ].
Qed.
Global Instance eq_timeless {A : cofeT} (a b : A) :
  Timeless a TimelessP (a b : uPred M)%I.
Proof.
  intro; apply timelessP_spec; unsealn x y ???; by apply equiv_dist, timeless.
Qed.
Global Instance ownM_timeless (a : M) : Timeless a TimelessP (uPred_ownM a).
Proof.
  intro; apply timelessP_spec; unsealn x y ???; apply cmra_included_includedN,
    cmra_timeless_included_l; eauto using cmra_validN_le.
Qed.


Lemma atimelessP_spec P : ATimelessP P n x, ✓{n} x P 0 x P n x .
Proof.
  split.
  - intros HP n x ??; induction n as [|n]; auto.
    move: HP; rewrite /ATimelessP; unseal⇒ /uPred_in_entails /(_ (S n) x ).
    destruct 1 as [ HlP | HlF]; auto using cmra_validN_S, ucmra_unit_validN.
    × split; auto. eapply IHn; auto using cmra_validN_S.
    × destruct HlP as (HP&?Haff). auto.
    × simpl in ×. exfalso; auto.
  - moveHP; rewrite /ATimelessP; unseal; split⇒ -[|n] x y /=; auto.
    intros ?? (HP'&Haff). left.
    split; auto. rewrite Haff.
    apply HP, uPred_closed with n; eauto using cmra_validN_le, ucmra_unit_validN.
    rewrite -(dist_le _ _ _ _ Haff); auto.
Qed.


Lemma timeless_atimeless P: TimelessP P ATimelessP P.
Proof.
  rewrite /TimelessP /ATimelessP. intros →. rewrite ?affine_or.
  apply or_mono; eauto using affine_elim.
Qed.

Global Instance const_atimeless φ : ATimelessP ( φ : uPred M)%I.
Proof. apply timeless_atimeless; apply _. Qed.
Global Instance valid_atimeless {A : cmraT} `{CMRADiscrete A} (a : A) :
   ATimelessP ( a : uPred M)%I.
Proof. apply timeless_atimeless; apply _. Qed.
Global Instance and_atimeless P Q: ATimelessP P ATimelessP Q ATimelessP (P Q).
Proof. rewrite /ATimelessP later_and ?affine_and or_and_r; apply and_mono. Qed.
Global Instance or_atimeless P Q : ATimelessP P ATimelessP Q ATimelessP (P Q).
Proof.
  intros; rewrite /ATimelessP later_or ?affine_or (atimelessP _) (atimelessP Q); eauto 10.
Qed.
Global Instance impl_atimeless P Q : ATimelessP Q ATimelessP (P Q).
Proof.
  rewrite !atimelessP_spec; unsealHP [|n] x ? HPQ [|n'] x' ?????; auto.
  apply HP, HPQ, uPred_closed with (S n'); eauto using cmra_validN_le.
Qed.
Global Instance sep_atimeless_1 P Q: TimelessP P TimelessP Q ATimelessP (P Q).
Proof.
  intros; apply timeless_atimeless; apply _.
Qed.
Global Instance sep_atimeless_2 P Q:
  AffineP P AffineP Q ATimelessP P ATimelessP Q ATimelessP (P Q).
Proof.
  intros; rewrite /ATimelessP.
  rewrite {1}(affineP P).
  rewrite {1}(affineP Q).
  rewrite affine_later_distrib.
  rewrite (atimelessP P) (atimelessP Q).
  rewrite sep_or_l. rewrite sep_or_r.
  apply or_elim.
  - apply or_elim.
    × apply or_intro_l'. apply sep_affine_1.
    × apply or_intro_r'. apply sep_elim_l.
  - apply or_intro_r'. rewrite sep_or_r.
    apply or_elim; first apply sep_elim_r.
    unseal. split⇒ -[|n] x y ?? Hfalse /=; auto.
    destruct Hfalse as (?&?&?&?&?&?&?&?). auto.
Qed.
Global Instance wand_atimeless P Q : AffineP P ATimelessP Q ATimelessP (P -★ Q).
Proof.
  rewrite !atimelessP_spec.
  unsealHA HP [|n] x ? HPQ [|n'] x' y' ??? HP'; auto.
  apply affineP in HP'; eauto using cmra_validN_op_r.
  revert HP'. unseal.
  intros (HP'&Haff).
  rewrite Haff ?right_id.
  apply HP; auto.
  specialize (HPQ 0 x' ). rewrite ?left_id in HPQ *=> HPQ.
  eapply HPQ;
    eauto 3 using cmra_validN_le, cmra_validN_op_r, cmra_validN_op_l, cmra_included_l.
  apply uPred_closed with (S n');
    eauto 3 using cmra_validN_le, cmra_validN_op_r, cmra_validN_op_l, cmra_included_l.
  rewrite -Haff. eauto.
Qed.
Global Instance forall_atimeless {A} (Ψ : A uPred M) :
  ( x, ATimelessP (Ψ x)) ATimelessP ( x, Ψ x).
Proof. by setoid_rewrite atimelessP_spec; unseal n x ?? a; apply . Qed.
Global Instance exist_atimeless {A} (Ψ : A uPred M) :
  ( x, ATimelessP (Ψ x)) ATimelessP ( x, Ψ x).
Proof.
  by setoid_rewrite atimelessP_spec; unseal [|n] x ?;
    [|intros [a ?]; a; apply ].
Qed.
Global Instance relevant_atimeless P : ATimelessP P ATimelessP ( P).
Proof.
  intros ?; rewrite /ATimelessP.
  rewrite affine_relevant_later.
  rewrite -relevant_affine atimelessP relevant_or relevant_affine.
  apply or_mono; auto using relevant_elim.
Qed.
Global Instance affine_atimeless P : ATimelessP P ATimelessP ( P).
Proof.
  rewrite /ATimelessP. intros HP.
  by rewrite -affine_affine_later HP affine_affine0.
Qed.
Global Instance eq_atimeless {A : cofeT} (a b : A) :
  Timeless a ATimelessP (a b : uPred M)%I.
Proof. intros. apply timeless_atimeless; apply _. Qed.
Global Instance ownM_atimeless (a : M) : Timeless a ATimelessP (uPred_ownM a).
Proof. intros. apply timeless_atimeless; apply _. Qed.

Global Instance emp_relevant: RelevantP (Emp: uPred M)%I.
Proof.
  rewrite /RelevantP. unseal. splitn x y ??. simpl; intros →.
  rewrite persistent_core; auto.
Qed.
Global Instance pure_relevant φ : RelevantP ( φ : uPred M)%I.
Proof. by rewrite /RelevantP relevant_pure. Qed.
Global Instance relevant_relevant P : RelevantP ( P).
Proof. by intros; apply relevant_intro'. Qed.
Global Instance relevant_affine' P : RelevantP P RelevantP ( P).
Proof. by intros; rewrite /RelevantP; rewrite relevant_affine;
       rewrite {1}(relevantP P).
Qed.
Global Instance and_relevant P Q :
  RelevantP P RelevantP Q RelevantP (P Q).
Proof. by intros; rewrite /RelevantP relevant_and; apply and_mono. Qed.
Global Instance or_relevant P Q :
  RelevantP P RelevantP Q RelevantP (P Q).
Proof. by intros; rewrite /RelevantP relevant_or; apply or_mono. Qed.
Global Instance sep_relevant P Q :
  RelevantP P RelevantP Q RelevantP (P Q).
Proof.
  intros. rewrite /RelevantP {1}(relevantP P) {1}(relevantP Q).
  apply relevant_sep.
Qed.
Global Instance forall_relevant `{Inhabited A} (Ψ : A uPred M) :
  ( x, RelevantP (Ψ x)) RelevantP ( x, Ψ x).
Proof. by intros; rewrite /RelevantP relevant_forall; apply forall_mono. Qed.
Global Instance exist_relevant {A} (Ψ : A uPred M) :
  ( x, RelevantP (Ψ x)) RelevantP ( x, Ψ x).
Proof. by intros; rewrite /RelevantP relevant_exist; apply exist_mono. Qed.
Global Instance eq_relevant {A : cofeT} (a b : A) :
  RelevantP ( (a b) : uPred M)%I.
Proof. by intros; rewrite /RelevantP relevant_eq. Qed.
Global Instance valid_relevant {A : cmraT} (a : A) :
  RelevantP ( a : uPred M)%I.
Proof. by intros; rewrite /RelevantP relevant_valid. Qed.
Global Instance ownM_relevant : Persistent a RelevantP (⧆(@uPred_ownM M a)).
Proof. intros; rewrite /RelevantP. rewrite relevant_affine unlimited_ownM //=. Qed.
Global Instance ownMl_relevant : Persistent a RelevantP (@uPred_ownMl M a).
Proof. intros; rewrite /RelevantP. rewrite relevant_ownMl //=. Qed.
Global Instance default_relevant {A} P (Ψ : A uPred M) (mx : option A) :
  RelevantP P ( x, RelevantP (Ψ x)) RelevantP (default P mx Ψ).
Proof. destruct mx; apply _. Qed.

Lemma relevant_relevant' P `{!RelevantP P} : ( P) ⊣⊢ P.
Proof. apply (anti_symm (⊢)); auto using relevant_elim. Qed.
Lemma relevant_intro P Q `{!RelevantP P} : (P Q) P Q.
Proof. rewrite -(relevant_relevant' P). apply relevant_intro'. Qed.
Lemma relevant_and_sep_l_1 P Q `{!RelevantP P} : (P Q) (P Q).
Proof. by rewrite -(relevant_relevant' P) relevant_and_sep_l_1'. Qed.
Lemma relevant_and_sep_r_1 P Q `{!RelevantP Q} : (P Q) (P Q).
Proof. rewrite -?(comm _ Q). apply relevant_and_sep_l_1; auto. Qed.
Lemma relevant_sep_dup P `{!RelevantP P} : P (P P).
Proof. by rewrite -(relevant_relevant' P) -relevant_sep_dup'_1. Qed.
Lemma relevant_entails_l P Q `{!RelevantP Q} : (P Q) P (Q P).
Proof. by rewrite -(relevant_relevant' Q); apply relevant_entails_l'. Qed.
Lemma relevant_entails_r P Q `{!RelevantP Q} : (P Q) P (P Q).
Proof. by rewrite -(relevant_relevant' Q); apply relevant_entails_r'. Qed.
Lemma relevant_sep' P Q `{!RelevantP P, !RelevantP Q} : (P Q) ⊣⊢ (□(P Q)).
Proof.
  apply (anti_symm (⊢)).
  - rewrite (relevant_relevant'). auto.
  - rewrite relevant_elim. auto.
Qed.

Global Instance affine_affine' P : AffineP ( P).
Proof. rewrite /AffineP ?affine_equiv; auto. Qed.
Global Instance affine_relevant' P : AffineP P AffineP ( P).
Proof. intros. rewrite /AffineP. rewrite -relevant_affine. auto. Qed.
Global Instance emp_affine : AffineP (Emp: uPred M)%I.
Proof. by intros; rewrite /AffineP affine_equiv; auto. Qed.
Global Instance and_affine P Q :
  AffineP P AffineP Q AffineP (P Q).
Proof. by intros; rewrite /AffineP affine_and; apply and_mono. Qed.
Global Instance or_affine P Q :
  AffineP P AffineP Q AffineP (P Q).
Proof. by intros; rewrite /AffineP affine_or; apply or_mono. Qed.
Global Instance sep_affine P Q :
  AffineP P AffineP Q AffineP (P Q).
Proof. by intros; rewrite /AffineP -sep_affine_1; apply sep_mono. Qed.
Global Instance forall_affine `{Inhabited A} (Ψ : A uPred M) :
  ( x, AffineP (Ψ x)) AffineP ( x, Ψ x).
Proof. rewrite /AffineP affine_forall. apply forall_mono. Qed.
Global Instance exist_affine {A} (Ψ : A uPred M) :
  ( x, AffineP (Ψ x)) AffineP ( x, Ψ x).
Proof. by intros; rewrite /AffineP affine_exist; apply exist_mono. Qed.
Global Instance default_affine {A} P (Ψ : A uPred M) (mx : option A) :
  AffineP P ( x, AffineP (Ψ x)) AffineP (default P mx Ψ).
Proof. destruct mx; apply _. Qed.

Lemma affine_affine P `{!AffineP P} : ( P) ⊣⊢ P.
Proof. apply (anti_symm (⊢)); auto using affine_elim. Qed.
Lemma affine_intro P Q `{!AffineP P} : (P Q) P Q.
Proof. rewrite -(affine_affine P). rewrite ?affine_equiv; auto. Qed.
Lemma sep_affine_distrib P Q `{!AffineP P, !AffineP Q}:
  ( P Q) ⊣⊢ (P Q).
Proof. rewrite ?affine_affine. auto. Qed.
Lemma affine_later_distrib' P Q `{!AffineP P, !AffineP Q}: ( (P Q) ( P Q)).
Proof. by rewrite -{1}(affine_affine P) -{1}(affine_affine Q) affine_later_distrib. Qed.
Lemma affine_sep_elim_l' P Q R `{!AffineP Q}: (P R) P Q R.
Proof. rewrite -(affine_affine Q); auto. Qed.
Lemma affine_sep_elim_r' P Q R `{!AffineP P}: (Q R) P Q R.
Proof. rewrite -(affine_affine P); auto. Qed.

End uPred_logic.

Hint Resolve pure_intro.
Hint Resolve or_elim or_intro_l' or_intro_r' : I.
Hint Resolve and_intro and_elim_l' and_elim_r' : I.
Hint Resolve relevant_mono affine_elim : I.
Hint Resolve sep_elim_l' sep_elim_r' sep_mono affine_sep_elim_l' affine_sep_elim_r' : I.
Hint Immediate True_intro False_elim : I.
Hint Immediate iff_refl eq_refl' : I.
End uPred.