Library iris.proofmode.coq_tactics

From iris.algebra Require Export upred.
From iris.algebra Require Import upred_big_op upred_tactics.
From iris.proofmode Require Export environments classes.
From iris.prelude Require Import stringmap hlist.
Import uPred.

Local Notation "Γ !! j" := (env_lookup j Γ).
Local Notation "x ← y ; z" := (match y with Some xz | NoneNone end).
Local Notation "' ( x1 , x2 ) ← y ; z" :=
  (match y with Some (x1,x2)z | NoneNone end).
Local Notation "' ( x1 , x2 , x3 ) ← y ; z" :=
  (match y with Some (x1,x2,x3)z | NoneNone end).

Record envs (M : ucmraT) :=
  Envs { env_relevant : env (uPred M); env_spatial : env (uPred M) }.
Add Printing Constructor envs.
Arguments Envs {_} _ _.
Arguments env_relevant {_} _.
Arguments env_spatial {_} _.

Record envs_wf {M: ucmraT} (Δ : envs M) := {
  env_relevant_valid : env_wf (env_relevant Δ);
  env_spatial_valid : env_wf (env_spatial Δ);
  envs_disjoint i : env_relevant Δ !! i = None env_spatial Δ !! i = None
}.

Coercion of_envs {M} (Δ : envs M) : uPred M :=
  ( envs_wf Δ [★] (map (uPred_relevant) (env_relevant Δ)) [★] env_spatial Δ)%I.
Instance: Params (@of_envs) 1.

Section defs.
Context {M: ucmraT}.

Record envs_Forall2 (R : relation (uPred M)) (Δ1 Δ2 : envs M) : Prop := {
  env_relevant_Forall2 : env_Forall2 R (env_relevant Δ1) (env_relevant Δ2);
  env_spatial_Forall2 : env_Forall2 R (env_spatial Δ1) (env_spatial Δ2)
}.

Definition envs_dom {M} (Δ : envs M) : list string :=
  env_dom (env_relevant Δ) ++ env_dom (env_spatial Δ).

Definition envs_lookup (i : string) (Δ : envs M) : option (bool × uPred M) :=
  let (Γp,Γs) := Δ in
  match env_lookup i Γp with
  | Some PSome (true, P) | NoneP env_lookup i Γs; Some (false, P)
  end.

Definition envs_delete (i : string) (p : bool) (Δ : envs M) : envs M :=
  let (Γp,Γs) := Δ in
  match p with
  | trueEnvs (env_delete i Γp) Γs | falseEnvs Γp (env_delete i Γs)
  end.

Definition envs_lookup_delete (i : string)
    (Δ : envs M) : option (bool × uPred M × envs M) :=
  let (Γp,Γs) := Δ in
  match env_lookup_delete i Γp with
  | Some (P,Γp')Some (true, P, Envs Γp' Γs)
  | None'(P,Γs') env_lookup_delete i Γs; Some (false, P, Envs Γp Γs')
  end.

Definition envs_app (p : bool)
    (Γ : env (uPred M)) (Δ : envs M) : option (envs M) :=
  let (Γp,Γs) := Δ in
  match p with
  | true_ env_app Γ Γs; Γp' env_app Γ Γp; Some (Envs Γp' Γs)
  | false_ env_app Γ Γp; Γs' env_app Γ Γs; Some (Envs Γp Γs')
  end.

Definition envs_simple_replace (i : string) (p : bool) (Γ : env (uPred M))
    (Δ : envs M) : option (envs M) :=
  let (Γp,Γs) := Δ in
  match p with
  | true_ env_app Γ Γs; Γp' env_replace i Γ Γp; Some (Envs Γp' Γs)
  | false_ env_app Γ Γp; Γs' env_replace i Γ Γs; Some (Envs Γp Γs')
  end.

Definition envs_replace (i : string) (p q : bool) (Γ : env (uPred M))
    (Δ : envs M) : option (envs M) :=
  if eqb p q then envs_simple_replace i p Γ Δ
  else envs_app q Γ (envs_delete i p Δ).

Definition envs_split {M}
    (lr : bool) (js : list string) (Δ : envs M) : option (envs M × envs M) :=
  let (Γp,Γs) := Δ in
  '(Γs1,Γs2) env_split js Γs;
  match lr with
  | falseSome (Envs Γp Γs1, Envs Γp Γs2)
  | trueSome (Envs Γp Γs2, Envs Γp Γs1)
  end.

Definition envs_split'
    (lr : bool) (jr: list string) (js : list string) (Δ : envs M) : option (envs M × envs M) :=
  let (Γp,Γs) := Δ in
  '(Γp1,Γp2) env_split jr Γp;
  '(Γs1,Γs2) env_split js Γs;
  match lr with
  | falseSome (Envs Γp1 Γs1, Envs Γp2 Γs2)
  | trueSome (Envs Γp2 Γs2, Envs Γp1 Γs1)
  end.

Definition envs_relevant (Δ : envs M) :=
  if env_spatial Δ is Enil then true else false.

Definition envs_clear_spatial (Δ : envs M) : envs M :=
  Envs (env_relevant Δ) Enil.

Lemma env_spatial_delete_true Δ i:
  env_spatial (envs_delete i true Δ) = env_spatial Δ.
Proof. by destruct Δ as [??]. Qed.

Lemma env_spatial_delete_false Δ i:
  env_spatial (envs_delete i false Δ) = env_delete i (env_spatial Δ).
Proof. by destruct Δ as [??]. Qed.

Lemma env_relevant_delete_false Δ i:
  env_relevant (envs_delete i false Δ) = env_relevant Δ.
Proof. by destruct Δ as [??]. Qed.

Lemma env_relevant_delete_true Δ i:
  env_relevant (envs_delete i true Δ) = env_delete i (env_relevant Δ).
Proof. by destruct Δ as [??]. Qed.

Lemma env_relevant_lookup_true Δ i P:
  envs_lookup i Δ = Some (true, P) env_relevant Δ !! i = Some P.
Proof.
  destruct Δ as [??]. rewrite /envs_lookup. case_match.
  - inversion 1; subst; auto.
  - case_match; congruence.
Qed.

Lemma env_spatial_lookup_false Δ i P:
  envs_lookup i Δ = Some (false, P) env_spatial Δ !! i = Some P.
Proof.
  destruct Δ as [??]. rewrite /envs_lookup. case_match.
  - congruence.
  - case_match; last congruence. inversion 1; subst; auto.
Qed.

End defs.

Section tactics.
Context {M : ucmraT}.
Implicit Types Γ : env (uPred M).
Implicit Types Δ : envs M.
Implicit Types P Q : uPred M.
Lemma of_envs_def Δ :
  of_envs Δ = ( envs_wf Δ
                  [★] (map (uPred_relevant) (env_relevant Δ))
                  [★] env_spatial Δ)%I.
Proof. done. Qed.

Lemma envs_lookup_delete_Some Δ Δ' i p P :
  envs_lookup_delete i Δ = Some (p,P,Δ')
   envs_lookup i Δ = Some (p,P) Δ' = envs_delete i p Δ.
Proof.
  rewrite /envs_lookup /envs_delete /envs_lookup_delete.
  destruct Δ as [Γp Γs]; rewrite /= !env_lookup_delete_correct.
  destruct (Γp !! i), (Γs !! i); naive_solver.
Qed.

Lemma envs_lookup_sound Δ i p P :
  envs_lookup i Δ = Some (p,P) Δ □?p P envs_delete i p Δ.
Proof.
  rewrite /envs_lookup /envs_delete /of_envs=>?; apply pure_elim_sep_lHwf.
  destruct Δ as [Γp Γs], (Γp !! i) eqn:?; simplify_eq/=.
  - rewrite (env_lookup_perm Γp) //=.
    ecancel [[★] _; P]%I. ecancel [[★] _]%I.
    rewrite emp_True affine_mono; eauto.
    apply pure_intro.
    destruct Hwf; constructor;
      naive_solver eauto using env_delete_wf, env_delete_fresh.
  - destruct (Γs !! i) eqn:?; simplify_eq/=.
    rewrite (env_lookup_perm Γs) //=.
    ecancel [[★] _; P]%I. ecancel [[★] _]%I.
    rewrite emp_True affine_mono; eauto.
    apply pure_intro.
    destruct Hwf; constructor;
      naive_solver eauto using env_delete_wf, env_delete_fresh.
Qed.

Lemma envs_lookup_delete_different Δ i j p P :
  i j envs_lookup i (envs_delete j p Δ) = envs_lookup i Δ.
Proof.
  intros ?. rewrite /envs_lookup /envs_delete.
  destruct p, Δ; rewrite -(env_lookup_delete_different _ i j) //=.
Qed.

Lemma envs_lookup_delete_lookup Δ Δ' Δ'' i j q P1 P2 R Q :
  envs_wf Δ
  envs_lookup_delete i Δ = Some (false, P1, Δ')
  envs_lookup j Δ' = Some (q, R)
  i j.
Proof.
  rewrite /envs_lookup /envs_lookup_delete /envs_delete /of_envsHwf Hld.
  case (decide (i = j)); auto; intros →.
  destruct Δ as [Γp Γs], (Γp !! j) eqn:Heq; simplify_eq/=.
  - destruct (env_lookup_delete j Γs) as [(P'&?)|] eqn:Hld'; simplify_eq/=; try congruence.
    rewrite env_lookup_delete_Some in Hld' ×.
    × intros (?&?). exfalso.
      eapply envs_disjoint with _ j in Hwf as [|]; simpl in *; congruence.
    × destruct (env_lookup_delete j Γp) as [(P'&?)|] eqn:Hld''; simplify_eq/=; try congruence.
  - destruct Δ' as [Γp' Γs'], (Γp' !! j) eqn:Heq'.
    × destruct (env_lookup_delete j Γp) as [(P'&?)|] eqn:Hld''; simplify_eq/=.
      destruct (env_lookup_delete j Γs) as [(P'&?)|] eqn:Hld'; simplify_eq/=; try congruence.
    × destruct (env_lookup_delete j Γp) as [(P'&?)|] eqn:Hld''; simplify_eq/=.
      destruct (env_lookup_delete j Γs) as [(P'&?)|] eqn:Hld'; simplify_eq/=; try congruence.
      rewrite env_lookup_delete_Some in Hld' ×.
      intros (?&->).
      rewrite env_delete_fresh_eq; eauto.
      destruct Hwf; eauto.
Qed.

Lemma envs_lookup_sound' Δ i p P :
  envs_lookup i Δ = Some (p,P) Δ (P envs_delete i p Δ).
Proof.
  intros. rewrite envs_lookup_sound //. destruct p; by rewrite //= ?relevant_elim.
Qed.

Lemma envs_wf_elim Δ P: (envs_wf Δ (Δ P)) Δ P.
Proof.
  intros HP. rewrite /of_envs. eapply (pure_elim_spare (envs_wf Δ)); eauto.
Qed.

Lemma envs_lookup_relevant_sound Δ i P :
  envs_lookup i Δ = Some (true,P) Δ ( P Δ).
Proof.
  intros. rewrite {1}of_envs_def.
  rewrite -(relevant_relevant' (uPred_pure (envs_wf Δ))).
  rewrite relevant_sep_dup'_1.
  rewrite -assoc. rewrite {2}relevant_elim -of_envs_def.
  rewrite {1}envs_lookup_sound //= (of_envs_def Δ).
  rewrite relevant_elim.
  ecancel [_]%I.
  rewrite {1}(relevant_sep_dup'_1 P).
  ecancel [ P]%I.
  to_front [_]%I.
  rewrite sep_elim_r env_spatial_delete_true.
  ecancel [[★] (env_spatial _)]%I.
  rewrite (env_lookup_perm (env_relevant Δ) i P) //=; last apply env_relevant_lookup_true; auto.
  rewrite env_relevant_delete_true //=.
Qed.
Lemma envs_lookup_relevant_sound' Δ i p P `{RelevantP _ P}:
  envs_lookup i Δ = Some (p,P) Δ ( P Δ).
Proof.
  destruct p; first apply envs_lookup_relevant_sound.
  intros. rewrite {1}of_envs_def.
  rewrite -(relevant_relevant' (uPred_pure (envs_wf Δ))).
  rewrite relevant_sep_dup'_1.
  rewrite -assoc. rewrite {2}relevant_elim -of_envs_def.
  rewrite {1}envs_lookup_sound //= (of_envs_def Δ).
  rewrite relevant_elim.
  ecancel [_]%I.
  rewrite -{1}(relevant_relevant' P).
  rewrite {1}(relevant_sep_dup'_1 P).
  ecancel [ P]%I.
  to_front [_]%I.
  rewrite sep_elim_r. rewrite env_relevant_delete_false.
  ecancel [[★] (map uPred_relevant (env_relevant _))]%I.
  rewrite (env_lookup_perm (env_spatial Δ) i P) //=; last apply env_spatial_lookup_false; auto.
  rewrite env_spatial_delete_false //=.
  rewrite relevant_elim.
  auto.
Qed.

Lemma envs_lookup_split Δ i p P :
  envs_lookup i Δ = Some (p,P) Δ □?p P (□?p P -★ Δ).
Proof.
  rewrite /envs_lookup /of_envs=>?; apply pure_elim_sep_lHwf.
  destruct Δ as [Γp Γs], (Γp !! i) eqn:?; simplify_eq/=.
  - rewrite (env_lookup_perm Γp) //=.
    rewrite pure_equiv // -emp_True left_id.
    cancel [ P]%I. apply wand_intro_l. solve_sep_entails.
  - destruct (Γs !! i) eqn:?; simplify_eq/=.
    rewrite (env_lookup_perm Γs) //=. rewrite pure_equiv // -emp_True left_id.
    cancel [P]. apply wand_intro_l. solve_sep_entails.
Qed.

Lemma envs_lookup_delete_sound Δ Δ' i p P :
  envs_lookup_delete i Δ = Some (p,P,Δ') Δ □?p P Δ'.
Proof. intros [? ->]%envs_lookup_delete_Some. by apply envs_lookup_sound. Qed.
Lemma envs_lookup_delete_sound' Δ Δ' i p P :
  envs_lookup_delete i Δ = Some (p,P,Δ') Δ P Δ'.
Proof. intros [? ->]%envs_lookup_delete_Some. by apply envs_lookup_sound'. Qed.

Lemma envs_app_sound Δ Δ' p Γ :
  envs_app p Γ Δ = Some Δ' Δ ((if p then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ').
Proof.
  rewrite /of_envs /envs_app⇒ ?; apply pure_elim_sep_lHwf.
  destruct Δ as [Γp Γs], p; simplify_eq/=.
  - destruct (env_app Γ Γs) eqn:Happ,
      (env_app Γ Γp) as [Γp'|] eqn:?; simplify_eq/=.
    apply wand_intro_l, sep_intro_Emp_l; [rewrite emp_True; apply affine_mono, pure_intro|].
    + destruct Hwf; constructor; simpl; eauto using env_app_wf.
      intros j. apply (env_app_disjoint _ _ _ j) in Happ.
      naive_solver eauto using env_app_fresh.
    + rewrite (env_app_perm _ _ Γp') //.
      rewrite map_app big_sep_app; solve_sep_entails.
  - destruct (env_app Γ Γp) eqn:Happ,
      (env_app Γ Γs) as [Γs'|] eqn:?; simplify_eq/=.
    apply wand_intro_l, sep_intro_Emp_l; [rewrite emp_True; apply affine_mono, pure_intro|].
    + destruct Hwf; constructor; simpl; eauto using env_app_wf.
      intros j. apply (env_app_disjoint _ _ _ j) in Happ.
      naive_solver eauto using env_app_fresh.
    + rewrite (env_app_perm _ _ Γs') // big_sep_app. solve_sep_entails.
Qed.

Lemma envs_app_sound_affine Δ Δ' p Γ :
  envs_app p Γ Δ = Some Δ' (Δ Δ)
  Δ ⧆((if p then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ').
Proof.
  rewrite /of_envs /envs_app⇒ ? Haff; apply pure_elim_sep_lHwf.
  destruct Δ as [Γp Γs], p; simplify_eq/=.
  - destruct (env_app Γ Γs) eqn:Happ,
      (env_app Γ Γp) as [Γp'|] eqn:?; simplify_eq/=.
    rewrite pure_equiv //= -?emp_True ?left_id in Haff *=>Haff.
    rewrite Haff.
    apply wand_intro_affine_l, sep_intro_Emp_l; [rewrite emp_True; apply affine_mono, pure_intro|].
    + destruct Hwf; constructor; simpl; eauto using env_app_wf.
      intros j. apply (env_app_disjoint _ _ _ j) in Happ.
      naive_solver eauto using env_app_fresh.
    + rewrite (env_app_perm _ _ Γp') //.
      rewrite map_app affine_elim big_sep_app; solve_sep_entails.
  - destruct (env_app Γ Γp) eqn:Happ,
      (env_app Γ Γs) as [Γs'|] eqn:?; simplify_eq/=.
    rewrite pure_equiv //= -?emp_True ?left_id in Haff *=>Haff.
    rewrite Haff.
    apply wand_intro_affine_l, sep_intro_Emp_l; [rewrite emp_True; apply affine_mono, pure_intro|].
    + destruct Hwf; constructor; simpl; eauto using env_app_wf.
      intros j. apply (env_app_disjoint _ _ _ j) in Happ.
      naive_solver eauto using env_app_fresh.
    + rewrite (env_app_perm _ _ Γs') // affine_elim big_sep_app. solve_sep_entails.
Qed.

Lemma envs_simple_replace_sound' Δ Δ' i p Γ :
  envs_simple_replace i p Γ Δ = Some Δ'
  envs_delete i p Δ ((if p then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ')%I.
Proof.
  rewrite /envs_simple_replace /envs_delete /of_envs⇒ ?.
  apply pure_elim_sep_lHwf. destruct Δ as [Γp Γs], p; simplify_eq/=.
  - destruct (env_app Γ Γs) eqn:Happ,
      (env_replace i Γ Γp) as [Γp'|] eqn:?; simplify_eq/=.
    apply wand_intro_l, sep_intro_Emp_l; [rewrite emp_True; apply affine_mono, pure_intro|].
    + destruct Hwf; constructor; simpl; eauto using env_replace_wf.
      intros j. apply (env_app_disjoint _ _ _ j) in Happ.
      destruct (decide (i = j)); try naive_solver eauto using env_replace_fresh.
    + rewrite (env_replace_perm _ _ Γp') //.
      rewrite map_app big_sep_app; solve_sep_entails.
  - destruct (env_app Γ Γp) eqn:Happ,
      (env_replace i Γ Γs) as [Γs'|] eqn:?; simplify_eq/=.
    apply wand_intro_l, sep_intro_Emp_l; [rewrite emp_True; apply affine_mono, pure_intro|].
    + destruct Hwf; constructor; simpl; eauto using env_replace_wf.
      intros j. apply (env_app_disjoint _ _ _ j) in Happ.
      destruct (decide (i = j)); try naive_solver eauto using env_replace_fresh.
    + rewrite (env_replace_perm _ _ Γs') // big_sep_app. solve_sep_entails.
Qed.

Lemma envs_simple_replace_sound'_affine Δ Δ' i p Γ :
  envs_simple_replace i p Γ Δ = Some Δ'
  (envs_delete i p Δ envs_delete i p Δ)
  envs_delete i p Δ ((if p then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ')%I.
Proof.
  intros Hrep Haff. rewrite Haff. move: Hrep.
  rewrite /envs_simple_replace /envs_delete /of_envs⇒ ?.
  rewrite -sep_affine_3.
  apply pure_elim_sep_lHwf. destruct Δ as [Γp Γs], p; simplify_eq/=.
  - destruct (env_app Γ Γs) eqn:Happ,
      (env_replace i Γ Γp) as [Γp'|] eqn:Heqo; simplify_eq/=.
    apply wand_intro_affine_l, sep_intro_Emp_l; [rewrite emp_True; apply affine_mono, pure_intro|].
    + destruct Hwf; constructor; simpl; eauto using env_replace_wf.
      intros j. apply (env_app_disjoint _ _ _ j) in Happ.
      destruct (decide (i = j)); try naive_solver eauto using env_replace_fresh.
    + rewrite (env_replace_perm _ _ Γp') //.
      rewrite map_app big_sep_app. rewrite ?affine_elim. solve_sep_entails.
  - destruct (env_app Γ Γp) eqn:Happ,
      (env_replace i Γ Γs) as [Γs'|] eqn:?; simplify_eq/=.
    apply wand_intro_affine_l, sep_intro_Emp_l; [rewrite emp_True; apply affine_mono, pure_intro|].
    + destruct Hwf; constructor; simpl; eauto using env_replace_wf.
      intros j. apply (env_app_disjoint _ _ _ j) in Happ.
      destruct (decide (i = j)); try naive_solver eauto using env_replace_fresh.
    + rewrite (env_replace_perm _ _ Γs') // big_sep_app. rewrite affine_elim. solve_sep_entails.
Qed.

Lemma envs_simple_replace_sound Δ Δ' i p P Γ :
  envs_lookup i Δ = Some (p,P) envs_simple_replace i p Γ Δ = Some Δ'
  Δ ((if p then P else P)
       ((if p then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ'))%I.
Proof. intros. by rewrite envs_lookup_sound// envs_simple_replace_sound'//. Qed.

Lemma envs_simple_replace_sound_affine Δ Δ' i p P Γ :
  envs_lookup i Δ = Some (p,P) envs_simple_replace i p Γ Δ = Some Δ'
  (envs_delete i p Δ envs_delete i p Δ)
  Δ ((if p then P else P)
       ⧆((if p then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ'))%I.
Proof. intros. rewrite envs_lookup_sound // envs_simple_replace_sound'_affine//.
       destruct p; by rewrite //=.
Qed.

Lemma envs_replace_sound' Δ Δ' i p q Γ :
  envs_replace i p q Γ Δ = Some Δ'
  envs_delete i p Δ ((if q then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ')%I.
Proof.
  rewrite /envs_replace; destruct (eqb _ _) eqn:Hpq.
  - apply eqb_prop in Hpq as →. apply envs_simple_replace_sound'.
  - apply envs_app_sound.
Qed.

Lemma envs_replace_sound'_affine Δ Δ' i p q Γ :
  envs_replace i p q Γ Δ = Some Δ'
  (envs_delete i p Δ envs_delete i p Δ)
  envs_delete i p Δ ((if q then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ')%I.
Proof.
  rewrite /envs_replace; destruct (eqb _ _) eqn:Hpq.
  - apply eqb_prop in Hpq as →. apply envs_simple_replace_sound'_affine.
  - apply envs_app_sound_affine.
Qed.

Lemma envs_replace_sound Δ Δ' i p q P Γ :
  envs_lookup i Δ = Some (p,P) envs_replace i p q Γ Δ = Some Δ'
  Δ ((if p then P else P)
       ((if q then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ'))%I.
Proof. intros. by rewrite envs_lookup_sound// envs_replace_sound'//. Qed.

Lemma envs_replace_sound_affine Δ Δ' i p q P Γ :
  envs_lookup i Δ = Some (p,P) envs_replace i p q Γ Δ = Some Δ'
  (envs_delete i p Δ envs_delete i p Δ)
  Δ ((if p then P else P)
       ⧆((if q then [★] (map uPred_relevant Γ) else [★] Γ) -★ Δ'))%I.
Proof.
  intros. rewrite envs_lookup_sound// envs_replace_sound'_affine//.
  destruct p; rewrite //=.
Qed.

Lemma map_relevant_dup Ps:
  [★] (map uPred_relevant Ps)
      (([★](map uPred_relevant Ps)) ([★](map uPred_relevant Ps): uPred M))%I.
Proof.
  induction Ps as [| P Ps].
  - rewrite ?right_id //=.
  - rewrite //= {1}(relevant_sep_dup'_1 P) {1}IHPs. solve_sep_entails.
Qed.

Lemma map_relevant_relevant Ps :
  [★] (map uPred_relevant Ps) □([★](map uPred_relevant Ps): uPred M).
Proof.
  induction Ps as [| P Ps].
  - rewrite ?right_id //=. by rewrite {1}relevant_relevant'.
  - simpl. rewrite {1}IHPs. rewrite -{1}(relevant_relevant' (P)). rewrite sep_relevant.
    eapply relevant_mono, sep_mono; auto using relevant_elim.
Qed.

Lemma envs_split_sound Δ lr js Δ1 Δ2 :
  envs_split lr js Δ = Some (Δ1,Δ2) Δ Δ1 Δ2.
Proof.
  rewrite /envs_split /of_envs⇒ ?; apply pure_elim_sep_lHwf.
  destruct Δ as [Γp Γs], (env_split js _) as [[Γs1 Γs2]|] eqn:?; simplify_eq/=.
  rewrite (env_split_perm Γs) // big_sep_app map_relevant_dup.
  destruct lr; simplify_eq/=;
  cancel [[★] (map uPred_relevant Γp); [★] (map uPred_relevant Γp); [★] Γs1; [★] Γs2]%I;
    destruct Hwf; apply sep_intro_Emp_l; rewrite emp_True; apply affine_mono, pure_intro; constructor;
      naive_solver eauto using env_split_wf_1, env_split_wf_2,
      env_split_fresh_1, env_split_fresh_2.
Qed.

Lemma envs_split'_sound Δ lr jr js Δ1 Δ2 :
  envs_split' lr jr js Δ = Some (Δ1,Δ2) Δ (Δ1 Δ2).
Proof.
  rewrite /envs_split' /of_envs⇒ ?; apply pure_elim_sep_lHwf.
  destruct Δ as [Γp Γs].
  destruct (env_split jr _) as [[Γr1 Γr2]|] eqn:?; simplify_eq /=;
  destruct (env_split js _) as [[Γs1 Γs2]|] eqn:?; simplify_eq /=.
  rewrite (env_split_perm Γp) // map_app big_sep_app.
  rewrite (env_split_perm Γs) // big_sep_app.
  destruct lr; simplify_eq/=;
  cancel [[★] (map uPred_relevant Γr1); [★] (map uPred_relevant Γr2); [★] Γs1; [★] Γs2]%I;
    destruct Hwf; apply sep_intro_Emp_l; rewrite emp_True; apply affine_mono, pure_intro;
    constructor;
      naive_solver eauto using env_split_wf_1, env_split_wf_2,
      env_split_fresh_1, env_split_fresh_2.
Qed.

Lemma envs_clear_spatial_sound Δ : Δ envs_clear_spatial Δ [★] env_spatial Δ.
Proof.
  rewrite /of_envs /envs_clear_spatial /=; apply pure_elim_sep_lHwf.
  rewrite right_id -assoc; apply sep_intro_Emp_l;
  [rewrite emp_True; apply affine_mono, pure_intro|done].
  destruct Hwf; constructor; simpl; auto using Enil_wf.
Qed.

Lemma env_fold_wand Γ Q : env_fold uPred_wand Q Γ ⊣⊢ ([★] Γ -★ Q).
Proof.
  revert Q; induction Γ as [|Γ IH i P]=> Q /=; [by rewrite wand_Emp|].
  by rewrite IH wand_curry (comm uPred_sep).
Qed.

Lemma envs_relevant_relevant Δ : envs_relevant Δ = true RelevantP Δ.
Proof.
  intros; destruct Δ as [? []]; simplify_eq/=.
  rewrite /RelevantP of_envs_def. simpl.
  rewrite ?right_id. rewrite {1}map_relevant_relevant.
  rewrite -{1}(relevant_relevant' (_)).
  apply relevant_sep.
Qed.
Hint Immediate envs_relevant_relevant : typeclass_instances.

Global Instance envs_Forall2_refl (R : relation (uPred M)) :
  Reflexive R Reflexive (envs_Forall2 R).
Proof. by constructor. Qed.
Global Instance envs_Forall2_sym (R : relation (uPred M)) :
  Symmetric R Symmetric (envs_Forall2 R).
Proof. intros ??? [??]; by constructor. Qed.
Global Instance envs_Forall2_trans (R : relation (uPred M)) :
  Transitive R Transitive (envs_Forall2 R).
Proof. intros ??? [??] [??] [??]; constructor; etrans; eauto. Qed.
Global Instance envs_Forall2_antisymm (R R' : relation (uPred M)) :
  AntiSymm R R' AntiSymm (envs_Forall2 R) (envs_Forall2 R').
Proof. intros ??? [??] [??]; constructor; by eapply (anti_symm _). Qed.
Lemma envs_Forall2_impl (R R' : relation (uPred M)) Δ1 Δ2 :
  envs_Forall2 R Δ1 Δ2 ( P Q, R P Q R' P Q) envs_Forall2 R' Δ1 Δ2.
Proof. intros [??] ?; constructor; eauto using env_Forall2_impl. Qed.

Global Instance of_envs_mono : Proper (envs_Forall2 (⊢) ==> (⊢)) (@of_envs M).
Proof.
  intros [Γp1 Γs1] [Γp2 Γs2] [Hp Hs]; unfold of_envs; simpl in ×.
  apply pure_elim_sep_lHwf. apply sep_intro_Emp_l.
  - destruct Hwf; rewrite emp_True; apply affine_mono, pure_intro; constructor;
      naive_solver eauto using env_Forall2_wf, env_Forall2_fresh.
  - f_equiv; last repeat f_equiv; auto .
    f_equiv. clear -Hp. induction Hp; first econstructor.
    econstructor; auto.
    apply relevant_mono; auto.
Qed.
Global Instance of_envs_proper : Proper (envs_Forall2 (⊣⊢) ==> (⊣⊢)) (@of_envs M).
Proof.
  intros Δ1 Δ2 ?; apply (anti_symm (⊢)); apply of_envs_mono;
    eapply envs_Forall2_impl; [| |symmetry|]; eauto using equiv_entails.
Qed.
Global Instance Envs_mono (R : relation (uPred M)) :
  Proper (env_Forall2 R ==> env_Forall2 R ==> envs_Forall2 R) (@Envs M).
Proof. by constructor. Qed.

Adequacy

Lemma tac_adequate P : (Envs Enil Enil P) Emp P.
Proof.
  intros <-. rewrite /of_envs /= !right_id.
  rewrite emp_True; apply affine_mono, pure_intro; repeat constructor.
Qed.

Basic rules


Lemma tac_assumption_empty : Envs Enil Enil (Emp: uPred M).
Proof. by rewrite of_envs_def //= sep_elim_r ?right_id. Qed.
Lemma tac_assumption_rel i P : Envs (Esnoc Enil i P) Enil P.
Proof. rewrite of_envs_def //= ?right_id. apply sep_elim_r. Qed.
Lemma tac_assumption_rel' i P : Envs (Esnoc Enil i P) Enil P.
Proof. rewrite of_envs_def //= ?right_id. rewrite relevant_elim. apply sep_elim_r. Qed.
Lemma tac_assumption_rel_affine i P : Envs (Esnoc Enil i (P)) Enil P.
Proof. rewrite of_envs_def //= ?left_id ?right_id. transitivity (P)%I;
         last apply affine_elim. rewrite relevant_elim. apply sep_elim_r.
Qed.
Lemma tac_assumption_spatial i P : Envs Enil (Esnoc Enil i P) P.
Proof. rewrite of_envs_def //= ?left_id ?right_id. apply sep_elim_r. Qed.
Lemma tac_assumption_spatial_affine i P : Envs Enil (Esnoc Enil i (P)) P.
Proof. rewrite of_envs_def //= ?left_id ?right_id. transitivity (P)%I;
         last apply affine_elim. apply sep_elim_r.
Qed.

Lemma tac_assumption Δ Δ' i p P Q :
  envs_lookup_delete i Δ = Some (p,P,Δ')
  FromAssumption p P Q
  (Δ' Δ')
  Δ Q.
Proof.
  intros ?? Haff; rewrite envs_lookup_delete_sound //; eauto.
  by rewrite Haff sep_elim_l.
Qed.

Lemma tac_rename Δ Δ' i j p P Q :
  envs_lookup i Δ = Some (p,P)
  envs_simple_replace i p (Esnoc Enil j P) Δ = Some Δ'
  (Δ' Q) Δ Q.
Proof.
  intros. rewrite envs_simple_replace_sound //.
  destruct p; simpl; by rewrite right_id wand_elim_r.
Qed.

Lemma tac_affine_intro Δ Q :
  (Δ Δ) (Δ Q) Δ Q.
Proof. intros Haff ?. rewrite Haff. by apply affine_mono. Qed.

Lemma tac_clear Δ Δ' i p P Q :
  envs_lookup_delete i Δ = Some (p,P,Δ') AffineP P (Δ' Q) Δ Q.
Proof.
  intros. rewrite envs_lookup_delete_sound //. destruct p;
  rewrite -(affine_affine P) //= ?relevant_affine sep_elim_r //=.
Qed.

False

Lemma tac_ex_falso Δ Q : (Δ False) Δ Q.
Proof. by rewrite -(False_elim Q). Qed.

Pure

Lemma tac_pure_intro Δ Q (φ : Prop) :
  FromPure Q φ
  (Δ Δ)
  φ Δ Q.
Proof.
  intros ? Haff ?. rewrite -(from_pure Q) //.
  rewrite Haff emp_True.
  apply affine_mono, True_intro.
Qed.

Lemma tac_pure Δ Δ' i p P φ Q :
  envs_lookup_delete i Δ = Some (p, P, Δ') IntoPure P φ
  (φ Δ' Q) Δ Q.
Proof.
  intros ?? HQ. rewrite envs_lookup_delete_sound' //; simpl.
  rewrite (into_pure P). by apply pure_elim_sep_l.
Qed.

Lemma tac_pure_revert Δ φ Q : (Δ φ Q) (φ Δ Q).
Proof. intros ?. by rewrite pure_equiv // left_id. Qed.

Later

Class IntoLaterEnv (Γ1 Γ2 : env (uPred M)) :=
  into_later_env : env_Forall2 IntoLater Γ1 Γ2.
Class IntoLaterEnvs (Δ1 Δ2 : envs M) := {
  into_later_relevant: IntoLaterEnv (env_relevant Δ1) (env_relevant Δ2);
  into_later_spatial: IntoLaterEnv (env_spatial Δ1) (env_spatial Δ2)
}.

Global Instance into_later_env_nil : IntoLaterEnv Enil Enil.
Proof. constructor. Qed.
Global Instance into_later_env_snoc Γ1 Γ2 i P Q :
  IntoLaterEnv Γ1 Γ2 IntoLater P Q
  IntoLaterEnv (Esnoc Γ1 i P) (Esnoc Γ2 i Q).
Proof. by constructor. Qed.

Global Instance into_later_envs Γp1 Γp2 Γs1 Γs2 :
  IntoLaterEnv Γp1 Γp2 IntoLaterEnv Γs1 Γs2
  IntoLaterEnvs (Envs Γp1 Γs1) (Envs Γp2 Γs2).
Proof. by split. Qed.

Lemma into_later_env_sound Δ1 Δ2 : IntoLaterEnvs Δ1 Δ2 Δ1 Δ2.
Proof.
  intros [Hp Hs]; rewrite /of_envs /= !later_sep.
  repeat apply sep_mono; try apply affine_mono; try apply relevant_mono.
  - rewrite -later_intro; apply affine_mono, pure_mono; destruct 1; constructor;
      naive_solver eauto using env_Forall2_wf, env_Forall2_fresh.
  - induction Hp; rewrite /= ?later_sep; auto using sep_mono, later_intro.
    apply sep_mono; auto.
    rewrite -(relevant_elim (uPred_later _)) -relevant_relevant_later.
    apply relevant_mono; auto.
  - induction Hs; rewrite /= ?later_sep; auto using sep_mono, later_intro.
Qed.

Lemma tac_next Δ Δ' Q Q' :
  IntoLaterEnvs Δ Δ' FromLater Q Q' (Δ' Q') Δ Q.
Proof. intros ?? HQ. by rewrite -(from_later Q) into_later_env_sound HQ. Qed.

Lemma tac_löb Δ Δ' i Q:
  envs_relevant Δ = true
  (Δ Δ)
  envs_app true (Esnoc Enil i (Q)%I) Δ = Some Δ'
  (Δ' Q) Δ Q.
Proof.
  intros ? Haff ? HQ.
  rewrite -(relevant_elim Q).
  rewrite -(unlimited_löb_1 ( Q)%I).
  rewrite -(relevant_relevant' Δ).
  rewrite -relevant_affine.
  apply relevant_mono.
  rewrite Haff.
  apply affine_mono.
  apply wand_intro_l.
  rewrite envs_app_sound //; simpl.
  rewrite ?right_id.
  rewrite relevant_affine.
  rewrite wand_elim_r //=.
Qed.

Lemma tac_timeless Δ Δ' i P Q :
  TimelessElim Q
  envs_lookup i Δ = Some (false, P)%I TimelessP P
  envs_simple_replace i false (Esnoc Enil i P) Δ = Some Δ'
  (Δ' Q) Δ Q.
Proof.
  intros ???? HQ. rewrite envs_simple_replace_sound //; simpl.
  by rewrite right_id HQ timeless_elim.
Qed.

Lemma tac_atimeless Δ Δ' i P Q :
  ATimelessElim Q
  envs_lookup i Δ = Some (false, P)%I ATimelessP P
  envs_simple_replace i false (Esnoc Enil i (P)%I) Δ = Some Δ'
  (Δ' Q) Δ Q.
Proof.
  intros ???? HQ. rewrite envs_simple_replace_sound //; simpl.
  by rewrite right_id HQ atimeless_elim.
Qed.

Always

Lemma tac_relevant_intro Δ Q : envs_relevant Δ = true (Δ Q) Δ Q.
Proof. intros. by apply: relevant_intro. Qed.
Lemma tac_relevant Δ Δ' i p P P' Q :
  envs_lookup i Δ = Some (p, P)%I IntoRelevantP P P'
  envs_replace i p true (Esnoc Enil i P') Δ = Some Δ'
  (Δ' Q) Δ Q.
Proof.
  intros ??? <-. rewrite envs_replace_sound //; simpl. destruct p.
  - by rewrite right_id (into_relevantP P) relevant_relevant' wand_elim_r.
  - by rewrite right_id (into_relevantP P) wand_elim_r.
Qed.

Implication and wand

Lemma tac_impl_intro Δ Δ' i P Q :
  envs_relevant Δ = true
  envs_app false (Esnoc Enil i P) Δ = Some Δ'
  (Δ' Q) Δ P Q.
Proof.
  intros ?? HQ. rewrite (relevantP Δ) envs_app_sound //; simpl.
  by rewrite ?right_id relevant_wand_impl_1 relevant_elim HQ.
Qed.
Lemma tac_impl_intro_relevant Δ Δ' i P P' Q :
  IntoRelevantP P P'
  envs_app true (Esnoc Enil i P') Δ = Some Δ'
  (Δ' Q) Δ P Q.
Proof.
  intros ?? HQ. rewrite envs_app_sound //; simpl. apply impl_intro_l.
  by rewrite right_id {1}(into_relevantP P) relevant_and_sep_l_1 wand_elim_r.
Qed.
Lemma tac_impl_intro_pure Δ P φ Q : IntoPure P φ (φ Δ Q) Δ P Q.
Proof.
  intros. apply impl_intro_l; rewrite (into_pure P).
  by rewrite affine_elim; apply pure_elim_l.
Qed.

Lemma tac_wand_intro Δ Δ' i P Q :
  envs_app false (Esnoc Enil i P) Δ = Some Δ' (Δ' Q) Δ P -★ Q.
Proof.
  intros ? HQ. rewrite envs_app_sound //; simpl. by rewrite right_id HQ.
Qed.
Lemma tac_wand_intro_relevant Δ Δ' i P P' Q :
  IntoRelevantP P P'
  envs_app true (Esnoc Enil i P') Δ = Some Δ'
  (Δ' Q) Δ P -★ Q.
Proof.
  intros. rewrite envs_app_sound //; simpl.
  rewrite right_id. by apply wand_mono.
Qed.
Lemma tac_wand_intro_pure Δ P φ Q : IntoPure P φ (φ Δ Q) Δ P -★ Q.
Proof.
  intros. by apply wand_intro_l; rewrite (into_pure P); apply pure_elim_sep_l.
Qed.

Lemma tac_specialize Δ Δ' Δ'' i p j q P1 P2 R Q :
  envs_lookup_delete i Δ = Some (p, P1, Δ')
  envs_lookup j (if p then Δ else Δ') = Some (q, R)
  IntoWand R P1 P2
  match p with
  | trueenvs_simple_replace j q (Esnoc Enil j P2) Δ
  | falseenvs_replace j q false (Esnoc Enil j P2) Δ'
             
  end = Some Δ''
  (Δ'' Q) Δ Q.
Proof.
  intros [? ->]%envs_lookup_delete_Some ??? <-. destruct p.
  - rewrite envs_lookup_relevant_sound // envs_simple_replace_sound //; simpl.
    destruct q.
    + by rewrite assoc (into_wand R) relevant_wand_1 wand_elim_r right_id wand_elim_r.
    + by rewrite assoc (into_wand R) relevant_elim wand_elim_r right_id wand_elim_r.
  - rewrite envs_lookup_sound //; simpl.
    rewrite envs_lookup_sound // (envs_replace_sound' _ Δ'') //; simpl.
    destruct q.
    + by rewrite //= right_id assoc (into_wand R) relevant_elim wand_elim_r wand_elim_r.
    + by rewrite //= assoc (into_wand R) wand_elim_r right_id wand_elim_r.
Qed.

Lemma tac_specialize_relevant Δ Δ' Δ'' i p j q P1 P2 R Q :
  envs_lookup_delete i Δ = Some (p, P1, Δ')
  envs_lookup j (if p then Δ else Δ') = Some (q, R)
  IntoWand R P1 P2
  envs_simple_replace j q (Esnoc Enil j P2) Δ = Some Δ''
  match p with
    | trueTrue
    | falseAffineP R RelevantP P2
  end
  
  (Δ'' Q) Δ Q.
Proof.
  intros Hld ??? HR <-. apply envs_wf_elimHwf.
  destruct p.
  - apply envs_lookup_delete_Some in Hld as [? ->].
    rewrite envs_lookup_relevant_sound // envs_simple_replace_sound //; simpl.
    destruct q.
    + by rewrite assoc (into_wand R) relevant_wand_1 wand_elim_r right_id wand_elim_r.
    + by rewrite assoc (into_wand R) relevant_elim wand_elim_r right_id wand_elim_r.
  - assert (i j).
    { eapply envs_lookup_delete_lookup; eauto. }
    apply envs_lookup_delete_Some in Hld as [? ->].
    rewrite (envs_lookup_sound _ j) //; last (erewrite <-envs_lookup_delete_different; eauto).
    rewrite envs_lookup_split //; last (rewrite envs_lookup_delete_different; eauto).
    simpl. destruct HR as [? ?].
    assert (□?q P2 ⊣⊢ P2) as HP2.
     { destruct q; auto. by rewrite relevant_relevant'. }
     trans (P1 (□?q P2) (P1 -★ envs_delete j q Δ))%I.
     + rewrite assoc -(comm _ P1) assoc. apply sep_mono; last reflexivity.
       trans ((P1 □?q R) □?q P2)%I.
       × rewrite HP2. apply relevant_entails_r'.
         rewrite (into_wand R). rewrite relevant_relevant'.
         by rewrite relevant_if_elim wand_elim_r.
       × rewrite relevant_if_elim affine_sep_elim_l' //.
     + rewrite assoc (comm _ P1) -assoc wand_elim_r.
        rewrite envs_simple_replace_sound' //=; destruct q; by rewrite //= right_id wand_elim_r.
Qed.
Class IntoAssert (P : uPred M) (Q : uPred M) (R : uPred M) :=
  into_assert : R (P -★ Q) Q.
Global Arguments into_assert _ _ _ {_}.
Lemma into_assert_default P Q : IntoAssert P Q P.
Proof. by rewrite /IntoAssert wand_elim_r. Qed.

Lemma tac_specialize_assert Δ Δ' Δ1 Δ2' j q lr js R P1 P2 P1' Q :
  envs_lookup_delete j Δ = Some (q, R, Δ')
  IntoWand R P1 P2 IntoAssert P1 Q P1'
  ('(Δ1,Δ2) envs_split lr js Δ';
    Δ2' envs_app (envs_relevant Δ1 && q) (Esnoc Enil j P2) Δ2;
    
    Some (Δ1,Δ2')) = Some (Δ1,Δ2')
  (Δ1 P1) (Δ2' Q) Δ Q.
Proof.
  intros [? ->]%envs_lookup_delete_Some ??? HP1 HQ.
  destruct (envs_split _ _ _) as [[? Δ2]|] eqn:?; simplify_eq/=;
    destruct (envs_app _ _ _) eqn:?; simplify_eq/=.
  rewrite envs_lookup_sound // envs_split_sound //.
  rewrite (envs_app_sound Δ2) //; simpl.
  destruct q; rewrite //=; destruct (envs_relevant Δ1) eqn:?; simplify_eq/=.
  - rewrite right_id (into_wand R) (relevantP Δ1) HP1.
    by rewrite assoc relevant_sep wand_elim_l wand_elim_r.
  - by rewrite right_id (into_wand R) relevant_elim assoc HP1 wand_elim_l wand_elim_r.
  - by rewrite right_id (into_wand R) assoc HP1 wand_elim_l wand_elim_r.
  - by rewrite right_id (into_wand R) assoc HP1 wand_elim_l wand_elim_r.
Qed.



Lemma tac_specialize_pure Δ Δ' j q R P1 P2 φ Q :
  envs_lookup j Δ = Some (q, R)
  IntoWand R P1 P2 FromPure P1 φ
  envs_simple_replace j q (Esnoc Enil j P2) Δ = Some Δ'
  φ (Δ' Q) Δ Q.
Proof.
  intros. rewrite envs_simple_replace_sound //; simpl.
  destruct q; rewrite //=;
  by rewrite right_id (into_wand R) -(from_pure P1) // wand_Emp wand_elim_r.
Qed.


Lemma tac_specialize_relevant_domain Δ Δ' Δ'' j q P1 P2 R Q :
  envs_lookup_delete j Δ = Some (q, R, Δ')
  IntoWand R P1 P2
  envs_simple_replace j q (Esnoc Enil j P2) Δ = Some Δ''
  (Δ' P1) RelevantP P1
  (Δ'' Q) Δ Q.
Proof.
  intros [? ->]%envs_lookup_delete_Some ?? HP1 ? <-.
  - rewrite envs_lookup_sound //.
    rewrite -(idemp uPred_and (envs_delete _ _ _)).
    rewrite {1}HP1 (relevantP P1) relevant_and_sep_l_1 assoc.
    rewrite envs_simple_replace_sound' //; simpl.
    destruct q; rewrite //=.
    × rewrite right_id (into_wand R) relevant_wand_1 wand_elim_l.
      by rewrite wand_elim_r.
    × rewrite right_id (into_wand R) relevant_elim wand_elim_l.
      by rewrite wand_elim_r.
Qed.

Lemma tac_specialize_relevant_range Δ Δ' Δ'' j q P1 P2 R Q :
  envs_lookup_delete j Δ = Some (q, R, Δ')
  IntoWand R P1 P2
  envs_simple_replace j q (Esnoc Enil j P2) Δ = Some Δ''
  (Δ' P1) AffineP R RelevantP P2
  (Δ'' Q) Δ Q.
Proof.
  intros [? ->]%envs_lookup_delete_Some ?? HP1 ?? <-.
  - rewrite envs_lookup_sound //=.
    trans (P2 □?q R envs_delete j q Δ)%I.
    × apply relevant_entails_l'.
      rewrite (into_wand R) HP1.
      destruct q; rewrite //=; first rewrite relevant_elim; rewrite wand_elim_l; eauto.
    × rewrite envs_simple_replace_sound' //=.
      rewrite assoc. rewrite (comm _ ( P2)%I).
      destruct q; rewrite //= right_id.
      ** rewrite relevant_elim affine_sep_elim_r'; eauto. apply wand_elim_r.
      ** rewrite affine_sep_elim_r'; eauto. by rewrite relevant_elim wand_elim_r.
Qed.


Lemma tac_revert Δ Δ' i p P Q :
  envs_lookup_delete i Δ = Some (p,P,Δ')
  (Δ' if p then P -★ Q else P -★ Q) Δ Q.
Proof.
  intros ? HQ. rewrite envs_lookup_delete_sound //; simpl. destruct p.
  - by rewrite HQ wand_elim_r.
  - by rewrite HQ wand_elim_r.
Qed.

Lemma tac_revert_spatial Δ Q :
  (envs_clear_spatial Δ env_fold uPred_wand Q (env_spatial Δ)) Δ Q.
Proof.
  intros . by rewrite envs_clear_spatial_sound env_fold_wand wand_elim_l.
Qed.

Lemma tac_assert Δ Δ1 Δ2 Δ2' lr js j P Q R :
  IntoAssert P Q R
  envs_split lr js Δ = Some (Δ1,Δ2)
  envs_app (envs_relevant Δ1) (Esnoc Enil j P) Δ2 = Some Δ2'
  (Δ1 P) (Δ2' Q) Δ Q.
Proof.
  intros ??? HP HQ. rewrite envs_split_sound //.
  destruct (envs_relevant Δ1) eqn:?.
  - rewrite (relevantP Δ1) HP envs_app_sound //; simpl.
    by rewrite right_id wand_elim_r.
  - rewrite HP envs_app_sound //; simpl. by rewrite right_id wand_elim_r.
Qed.

Lemma tac_assert_relevant Δ Δ' j P Q :
  RelevantP P
  envs_app true (Esnoc Enil j P) Δ = Some Δ'
  (Δ P) (Δ' Q) Δ Q.
Proof.
  intros ?? HP ?.
  rewrite -(idemp uPred_and Δ) {1}HP envs_app_sound //; simpl.
  by rewrite right_id {1}(relevantP P) relevant_and_sep_l_1 wand_elim_r.
Qed.


Lemma tac_pose_proof_affine Δ Δ' j P Q :
  (Emp P)
  envs_app true (Esnoc Enil j (P)%I) Δ = Some Δ'
  (Δ' Q) Δ Q.
Proof.
  intros HP ? <-. rewrite envs_app_sound //; simpl.
  assert (Emp P) as <-.
  { by apply affine_intro; first apply _. }
  rewrite right_id -emp_relevant.
  rewrite wand_Emp //=.
Qed.


Lemma tac_pose_proof_hyp_spare Δ Δ' Δ'' i p j P Q :
  envs_lookup_delete i Δ = Some (p, P, Δ')
  envs_app p (Esnoc Enil j P) (if p then Δ else Δ') = Some Δ''
  (Δ'' Q) Δ Q.
Proof.
  intros [? ->]%envs_lookup_delete_Some ? <-. destruct p.
  - rewrite envs_lookup_relevant_sound // envs_app_sound //; simpl.
    by rewrite right_id wand_elim_r.
  - rewrite envs_lookup_sound // envs_app_sound //; simpl.
    by rewrite right_id wand_elim_r.
Qed.

Lemma tac_pose_proof_hyp Δ Δ' Δ'' i p j P Q :
  envs_lookup_delete i Δ = Some (p, P, Δ')
  envs_app p (Esnoc Enil j P) Δ' = Some Δ''
  (Δ'' Q) Δ Q.
Proof.
  intros [? ->]%envs_lookup_delete_Some ? <-. destruct p.
  - rewrite envs_lookup_sound // envs_app_sound //; simpl.
    by rewrite right_id wand_elim_r.
  - rewrite envs_lookup_sound // envs_app_sound //; simpl.
    by rewrite right_id wand_elim_r.
Qed.

Lemma tac_apply Δ Δ' i p R P1 P2 :
  envs_lookup_delete i Δ = Some (p, R, Δ') IntoWand R P1 P2
  (Δ' P1) Δ P2.
Proof.
  intros ?? HP1. rewrite envs_lookup_delete_sound' //.
  by rewrite (into_wand R) HP1 wand_elim_l.
Qed.

Rewriting

Lemma tac_rewrite Δ i p Pxy (lr : bool) Q :
  envs_lookup i Δ = Some (p, Pxy)
   {A : cofeT} (x y : A) (Φ : A uPred M),
    (Pxy x y)
    (Q ⊣⊢ Φ (if lr then y else x))
    ( n, Proper (dist n ==> dist n) Φ)
    (Δ Φ (if lr then x else y)) Δ Q.
Proof.
  intros ? A x y ? HPxy → ?; apply eq_rewrite; auto.
  rewrite {1}envs_lookup_sound' //.
  destruct lr.
  × eapply sep_elim_l_equiv; auto.
  × eapply sep_elim_l_equiv. rewrite HPxy. auto using eq_sym.
Qed.

Lemma tac_rewrite_in Δ i Pxy j q P (lr : bool) Q :
  envs_lookup i Δ = Some (true, Pxy)
  envs_lookup j Δ = Some (q, P)%I
   {A : cofeT} Δ' x y (Φ : A uPred M),
    (Pxy (x y)%I)
    (P ⊣⊢ Φ (if lr then y else x))
    ( n, Proper (dist n ==> dist n) Φ)
    envs_simple_replace j q (Esnoc Enil j (Φ (if lr then x else y))) Δ = Some Δ'
    (Δ' Q) Δ Q.
Proof.
  intros ?? A Δ' x y Φ HPxy HP ?? <-.
  rewrite (envs_lookup_relevant_sound _ i) //.
  rewrite comm HPxy.
  rewrite (envs_simple_replace_sound _ _ j q P) //; simpl; destruct q.
  - rewrite HP right_id -assoc; apply wand_elim_r'. destruct lr.
    + apply (eq_rewrite x y (λ y, Φ y -★ Δ')%I); eauto with I; first solve_proper.
      × rewrite (relevant_elim (⧆(x y))%I). rewrite comm. eapply sep_elim_l_equiv;
          auto using affine_elim.
      × rewrite relevant_affine. apply sep_elim_l.
    + apply (eq_rewrite y x (λ y, Φ y -★ Δ')%I); eauto using eq_sym with I; first solve_proper.
      × rewrite (relevant_elim (⧆(x y))%I). rewrite comm eq_sym. eapply sep_elim_l_equiv;
          eauto using affine_elim, eq_sym with I.
      × rewrite relevant_affine. apply sep_elim_l.
  - rewrite HP right_id -assoc; apply wand_elim_r'. destruct lr.
    + apply (eq_rewrite x y (λ y, Φ y -★ Δ')%I); eauto with I; first solve_proper.
      × rewrite (relevant_elim (⧆(x y))%I). rewrite comm. eapply sep_elim_l_equiv;
          auto using affine_elim.
      × rewrite relevant_affine. apply sep_elim_l.
    + apply (eq_rewrite y x (λ y, Φ y -★ Δ')%I); eauto using eq_sym with I; first solve_proper.
      × rewrite (relevant_elim (⧆(x y))%I). rewrite comm eq_sym. eapply sep_elim_l_equiv;
          eauto using affine_elim, eq_sym with I.
      × rewrite relevant_affine. apply sep_elim_l.
Qed.

Conjunction splitting

Lemma tac_and_split Δ P Q1 Q2 : FromAnd P Q1 Q2 (Δ Q1) (Δ Q2) Δ P.
Proof. intros. rewrite -(from_and P). by apply and_intro. Qed.

Separating conjunction splitting

Lemma tac_sep_split Δ Δ1 Δ2 lr js P Q1 Q2 :
  FromSep P Q1 Q2
  envs_split lr js Δ = Some (Δ1,Δ2)
  (Δ1 Q1) (Δ2 Q2) Δ P.
Proof.
  intros. rewrite envs_split_sound // -(from_sep P). by apply sep_mono.
Qed.

Lemma tac_sep_split' Δ Δ1 Δ2 lr jr js P Q1 Q2 :
  FromSep P Q1 Q2
  envs_split' lr jr js Δ = Some (Δ1,Δ2)
  (Δ1 Q1) (Δ2 Q2) Δ P.
Proof.
  intros. rewrite envs_split'_sound // -(from_sep P). by apply sep_mono.
Qed.

Combining

Lemma tac_combine Δ1 Δ2 Δ3 Δ4 i1 p P1 i2 q P2 j P Q :
  envs_lookup_delete i1 Δ1 = Some (p,P1,Δ2)
  envs_lookup_delete i2 (if p then Δ1 else Δ2) = Some (q,P2,Δ3)
  FromSep P P1 P2
  envs_app (p && q) (Esnoc Enil j P)
    (if q then (if p then Δ1 else Δ2) else Δ3) = Some Δ4
  (Δ4 Q) Δ1 Q.
Proof.
  intros [? ->]%envs_lookup_delete_Some [? ->]%envs_lookup_delete_Some ?? <-.
  destruct p.
  - rewrite envs_lookup_relevant_sound //. destruct q.
    + rewrite envs_lookup_relevant_sound // envs_app_sound //; simpl.
      by rewrite right_id assoc relevant_sep (from_sep P) wand_elim_r.
    + rewrite envs_lookup_sound // envs_app_sound //; simpl.
      by rewrite right_id assoc relevant_elim (from_sep P) wand_elim_r.
  - rewrite envs_lookup_sound //; simpl. destruct q.
    + rewrite envs_lookup_relevant_sound // envs_app_sound //; simpl.
      by rewrite right_id assoc relevant_elim (from_sep P) wand_elim_r.
    + rewrite envs_lookup_sound // envs_app_sound //; simpl.
      by rewrite right_id assoc (from_sep P) wand_elim_r.
Qed.

Lemma tac_sep_destruct Δ Δ' i p j1 j2 P P1 P2 Q :
  envs_lookup i Δ = Some (p, P) IntoSep p P P1 P2
  envs_simple_replace i p (Esnoc (Esnoc Enil j1 P1) j2 P2) Δ = Some Δ'
  (Δ' Q) Δ Q.
Proof.
  intros. rewrite envs_simple_replace_sound //; simpl. destruct p; rewrite //=.
  - by rewrite (into_sep true P) right_id (comm uPred_sep ( P1)%I) wand_elim_r.
  - by rewrite (into_sep false P) right_id (comm uPred_sep P1) wand_elim_r.
Qed.

Framing

Lemma tac_frame Δ Δ' i p R P Q :
  envs_lookup_delete i Δ = Some (p, R, Δ') Frame R P Q
  ((if p then Δ else Δ') Q) Δ P.
Proof.
  intros [? ->]%envs_lookup_delete_Some ? HQ. destruct p.
  - by rewrite envs_lookup_relevant_sound // relevant_elim -(frame R P) HQ.
  - rewrite envs_lookup_sound //; simpl. by rewrite -(frame R P) HQ.
Qed.

Disjunction

Lemma tac_or_l Δ P Q1 Q2 : FromOr P Q1 Q2 (Δ Q1) Δ P.
Proof. intros. rewrite -(from_or P). by apply or_intro_l'. Qed.
Lemma tac_or_r Δ P Q1 Q2 : FromOr P Q1 Q2 (Δ Q2) Δ P.
Proof. intros. rewrite -(from_or P). by apply or_intro_r'. Qed.

Lemma tac_or_destruct Δ Δ1 Δ2 i p j1 j2 P P1 P2 Q :
  envs_lookup i Δ = Some (p, P) IntoOr P P1 P2
  envs_simple_replace i p (Esnoc Enil j1 P1) Δ = Some Δ1
  envs_simple_replace i p (Esnoc Enil j2 P2) Δ = Some Δ2
  (Δ1 Q) (Δ2 Q) Δ Q.
Proof.
  intros ???? HP1 HP2. rewrite envs_lookup_sound //. destruct p.
  - rewrite (into_or P) //= relevant_or sep_or_r; apply or_elim. simpl.
    + rewrite (envs_simple_replace_sound' _ Δ1) //.
      by rewrite /= right_id wand_elim_r.
    + rewrite (envs_simple_replace_sound' _ Δ2) //.
      by rewrite /= right_id wand_elim_r.
  - rewrite (into_or P) //= sep_or_r; apply or_elim.
    + rewrite (envs_simple_replace_sound' _ Δ1) //.
      by rewrite /= right_id wand_elim_r.
    + rewrite (envs_simple_replace_sound' _ Δ2) //.
      by rewrite /= right_id wand_elim_r.
Qed.

Forall

Lemma tac_forall_intro {A} Δ (Φ : A uPred M) : ( a, Δ Φ a) Δ a, Φ a.
Proof. apply forall_intro. Qed.

Class ForallSpecialize {As} (xs : hlist As)
  (P : uPred M) (Φ : himpl As (uPred M)) := forall_specialize : P Φ xs.
Arguments forall_specialize {_} _ _ _ {_}.

Global Instance forall_specialize_nil P : ForallSpecialize hnil P P | 100.
Proof. done. Qed.
Global Instance forall_specialize_cons A As x xs Φ (Ψ : A himpl As (uPred M)) :
  ( x, ForallSpecialize xs (Φ x) (Ψ x))
  ForallSpecialize (hcons x xs) ( x : A, Φ x) Ψ.
Proof. rewrite /ForallSpecialize /= ⇒ <-. by rewrite (forall_elim x). Qed.

Lemma tac_forall_specialize {As} Δ Δ' i p P (Φ : himpl As (uPred M)) Q xs :
  envs_lookup i Δ = Some (p, P) ForallSpecialize xs P Φ
  envs_simple_replace i p (Esnoc Enil i (Φ xs)) Δ = Some Δ'
  (Δ' Q) Δ Q.
Proof.
  intros. rewrite envs_simple_replace_sound //; simpl.
  destruct p; by rewrite //= right_id (forall_specialize _ P) wand_elim_r.
Qed.

Lemma tac_forall_revert {A} Δ (Φ : A uPred M) :
  (Δ a, Φ a) a, Δ Φ a.
Proof. intros a. by rewrite (forall_elim a). Qed.

Existential

Lemma tac_exist {A} Δ P (Φ : A uPred M) :
  FromExist P Φ ( a, Δ Φ a) Δ P.
Proof. intros ? [a ?]. rewrite -(from_exist P). eauto using exist_intro'. Qed.

Lemma tac_exist_destruct {A} Δ i p j P (Φ : A uPred M) Q :
  envs_lookup i Δ = Some (p, P) IntoExist P Φ
  ( a, Δ',
    envs_simple_replace i p (Esnoc Enil j (Φ a)) Δ = Some Δ' (Δ' Q))
  Δ Q.
Proof.
  intros ?? . rewrite envs_lookup_sound //. destruct p; rewrite //=.
  - rewrite (into_exist P) relevant_exist sep_exist_r.
    apply exist_elima; destruct ( a) as (Δ'&?&?).
    rewrite envs_simple_replace_sound' //; simpl.
    by rewrite right_id wand_elim_r.
  - rewrite (into_exist P) sep_exist_r.
    apply exist_elima; destruct ( a) as (Δ'&?&?).
    rewrite envs_simple_replace_sound' //; simpl.
    by rewrite right_id wand_elim_r.
Qed.
End tactics.