(* iris/bi/embedding.v *)
From iris.algebra Require Import monoid
From iris.bi Require Import interface derived_laws_later big_op
From iris.bi Require Import plainly updates internal_eq
From iris.prelude Require Import options

Local SetPrimitive Projections.

Class Embed(A B : Type) := embed : A → B.

Global Argumentsembed {_ _ _} _%_I : simpl never.

Global Instance: Params (@embed) 3 := {}.

Global TypeclassesOpaque embed.

Global HintMode Embed ! - : typeclass_instances.

Global HintMode Embed - ! : typeclass_instances.

Record BiEmbedMixin(PROP1 PROP2 : bi) `(Embed PROP1 PROP2) := {
  bi_embed_mixin_ne : NonExpansive (embed (A:=PROP1) (B:=PROP2));
  bi_embed_mixin_mono : Proper ((⊢) ==> (⊢)) (embed (A:=PROP1) (B:=PROP2));
  bi_embed_mixin_emp_valid_inj (P : PROP1) :
    (⊢@{PROP2} ⎡P⎤) → ⊢ P;
  bi_embed_mixin_emp_2 : emp ⊢@{PROP2} ⎡emp⎤;
  bi_embed_mixin_impl_2 (P Q : PROP1) :
    (⎡P⎤ → ⎡Q⎤) ⊢@{PROP2} ⎡P → Q⎤;
  bi_embed_mixin_forall_2 A (Φ : A → PROP1) :
    (∀ x, ⎡Φ x⎤) ⊢@{PROP2} ⎡∀ x, Φ x⎤;
  bi_embed_mixin_exist_1 A (Φ : A → PROP1) :
    ⎡∃ x, Φ x⎤ ⊢@{PROP2} ∃ x, ⎡Φ x⎤;
  bi_embed_mixin_sep (P Q : PROP1) :
    ⎡P ∗ Q⎤ ⊣⊢@{PROP2} ⎡P⎤ ∗ ⎡Q⎤;
  bi_embed_mixin_wand_2 (P Q : PROP1) :
    (⎡P⎤ -∗ ⎡Q⎤) ⊢@{PROP2} ⎡P -∗ Q⎤;
  bi_embed_mixin_persistently (P : PROP1) :
    ⎡<pers> P⎤ ⊣⊢@{PROP2} <pers> ⎡P⎤
}.

Class BiEmbed(PROP1 PROP2 : bi) := {
  #[global] bi_embed_embed :: Embed PROP1 PROP2;
  bi_embed_mixin : BiEmbedMixin PROP1 PROP2 bi_embed_embed;
}.

Global HintMode BiEmbed ! - : typeclass_instances.

Global HintMode BiEmbed - ! : typeclass_instances.

Global Argumentsbi_embed_embed : simpl never.

Class BiEmbedEmp(PROP1 PROP2 : bi) `{!BiEmbed PROP1 PROP2} :=
  embed_emp_1 : ⎡ emp : PROP1 ⎤ ⊢ emp.

Global HintMode BiEmbedEmp ! - - : typeclass_instances.

Global HintMode BiEmbedEmp - ! - : typeclass_instances.

Class BiEmbedLater(PROP1 PROP2 : bi) `{!BiEmbed PROP1 PROP2} :=
  embed_later P : ⎡▷ P⎤ ⊣⊢@{PROP2} ▷ ⎡P⎤.

Global HintMode BiEmbedLater ! - - : typeclass_instances.

Global HintMode BiEmbedLater - ! - : typeclass_instances.

Class BiEmbedBUpd(PROP1 PROP2 : bi)
    `{!BiEmbed PROP1 PROP2, !BiBUpd PROP1, !BiBUpd PROP2} :=
  embed_bupd P : ⎡|==> P⎤ ⊣⊢@{PROP2} |==> ⎡P⎤.

Global HintMode BiEmbedBUpd - ! - - - : typeclass_instances.

Global HintMode BiEmbedBUpd ! - - - - : typeclass_instances.

Class BiEmbedFUpd(PROP1 PROP2 : bi)
    `{!BiEmbed PROP1 PROP2, !BiFUpd PROP1, !BiFUpd PROP2} :=
  embed_fupd E1 E2 P : ⎡|={E1,E2}=> P⎤ ⊣⊢@{PROP2} |={E1,E2}=> ⎡P⎤.

Global HintMode BiEmbedFUpd - ! - - - : typeclass_instances.

Global HintMode BiEmbedFUpd ! - - - - : typeclass_instances.

Class BiEmbedSbi(PROP1 PROP2 : bi)
    `{!BiEmbed PROP1 PROP2, !Sbi PROP1, !Sbi PROP2} := {
  embed_si_emp_valid (P : PROP1) :
    <si_emp_valid> ⎡ P ⎤ ⊣⊢@{siPropI} <si_emp_valid> P;
  embed_si_pure_1 Pi : ⎡ <si_pure> Pi ⎤ ⊢@{PROP2} <si_pure> Pi;
}.

Global HintMode BiEmbedSbi - ! - - - : typeclass_instances.

Global HintMode BiEmbedSbi ! - - - - : typeclass_instances.

Local Notationembed := (embed (A:=bi_car PROP1) (B:=bi_car PROP2)).

Local Notation"⎡ P ⎤" := (embed P) : bi_scope.
  Implicit Types P : PROP1.

Global Instanceembed_ne : NonExpansive embed.
  Proof. eapply bi_embed_mixin_ne, bi_embed_mixin. Qed.

Global Instanceembed_mono : Proper ((⊢) ==> (⊢)) embed.
  Proof. eapply bi_embed_mixin_mono, bi_embed_mixin. Qed.

Lemma embed_emp_valid_injP : (⊢@{PROP2} ⎡P⎤) → ⊢ P.
  Proof. eapply bi_embed_mixin_emp_valid_inj, bi_embed_mixin. Qed.

Lemma embed_emp_2: emp ⊢ ⎡emp⎤.
  Proof. eapply bi_embed_mixin_emp_2, bi_embed_mixin. Qed.

Lemma embed_impl_2P Q : (⎡P⎤ → ⎡Q⎤) ⊢ ⎡P → Q⎤.
  Proof. eapply bi_embed_mixin_impl_2, bi_embed_mixin. Qed.

Lemma embed_forall_2A (Φ : A → PROP1) : (∀ x, ⎡Φ x⎤) ⊢ ⎡∀ x, Φ x⎤.
  Proof. eapply bi_embed_mixin_forall_2, bi_embed_mixin. Qed.

Lemma embed_exist_1A (Φ : A → PROP1) : ⎡∃ x, Φ x⎤ ⊢ ∃ x, ⎡Φ x⎤.
  Proof. eapply bi_embed_mixin_exist_1, bi_embed_mixin. Qed.

Lemma embed_sepP Q : ⎡P ∗ Q⎤ ⊣⊢ ⎡P⎤ ∗ ⎡Q⎤.
  Proof. eapply bi_embed_mixin_sep, bi_embed_mixin. Qed.

Lemma embed_wand_2P Q : (⎡P⎤ -∗ ⎡Q⎤) ⊢ ⎡P -∗ Q⎤.
  Proof. eapply bi_embed_mixin_wand_2, bi_embed_mixin. Qed.

Lemma embed_persistentlyP : ⎡<pers> P⎤ ⊣⊢ <pers> ⎡P⎤.
  Proof. eapply bi_embed_mixin_persistently, bi_embed_mixin. Qed.

Local Notationembed := (embed (A:=bi_car PROP1) (B:=bi_car PROP2)).

Local Notation"⎡ P ⎤" := (embed P) : bi_scope.
  Implicit Types P Q R : PROP1.

Global Instanceembed_proper : Proper ((≡) ==> (≡)) embed.
  Proof. apply (ne_proper _). Qed.

Global Instanceembed_flip_mono : Proper (flip (⊢) ==> flip (⊢)) embed.
  Proof. solve_proper. Qed.

Global Instanceembed_entails_inj : Inj (⊢) (⊢) embed.
  Proof.
    move=> P Q /bi.entails_wand. rewrite embed_wand_2.
    by move=> /embed_emp_valid_inj /bi.wand_entails.
  Qed.

Global Instanceembed_inj : Inj (≡) (≡) embed.
  Proof.
    intros P Q EQ. apply bi.equiv_entails, conj; apply (inj embed); rewrite EQ //.
  Qed.

Lemma embed_emp_valid(P : PROP1) : (⊢ ⎡P⎤) ↔ (⊢ P).
  Proof.
    rewrite /bi_emp_valid. split=> HP.
    - by apply embed_emp_valid_inj.
    - by rewrite embed_emp_2 HP.
  Qed.

Lemma embed_emp`{!BiEmbedEmp PROP1 PROP2} : ⎡ emp ⎤ ⊣⊢ emp.
  Proof. apply (anti_symm _); eauto using embed_emp_1, embed_emp_2. Qed.

Lemma embed_forallA (Φ : A → PROP1) : ⎡∀ x, Φ x⎤ ⊣⊢ ∀ x, ⎡Φ x⎤.
  Proof.
    apply bi.equiv_entails; split; [|apply embed_forall_2].
    apply bi.forall_intro=>?. by rewrite bi.forall_elim.
  Qed.

Lemma embed_existA (Φ : A → PROP1) : ⎡∃ x, Φ x⎤ ⊣⊢ ∃ x, ⎡Φ x⎤.
  Proof.
    apply bi.equiv_entails; split; [apply embed_exist_1|].
    apply bi.exist_elim=>?. by rewrite -bi.exist_intro.
  Qed.

Lemma embed_andP Q : ⎡P ∧ Q⎤ ⊣⊢ ⎡P⎤ ∧ ⎡Q⎤.
  Proof. rewrite !bi.and_alt embed_forall. by f_equiv=>-[]. Qed.

Lemma embed_orP Q : ⎡P ∨ Q⎤ ⊣⊢ ⎡P⎤ ∨ ⎡Q⎤.
  Proof. rewrite !bi.or_alt embed_exist. by f_equiv=>-[]. Qed.

Lemma embed_implP Q : ⎡P → Q⎤ ⊣⊢ (⎡P⎤ → ⎡Q⎤).
  Proof.
    apply bi.equiv_entails; split; [|apply embed_impl_2].
    apply bi.impl_intro_l. by rewrite -embed_and bi.impl_elim_r.
  Qed.

Lemma embed_wandP Q : ⎡P -∗ Q⎤ ⊣⊢ (⎡P⎤ -∗ ⎡Q⎤).
  Proof.
    apply bi.equiv_entails; split; [|apply embed_wand_2].
    apply bi.wand_intro_l. by rewrite -embed_sep bi.wand_elim_r.
  Qed.

Lemma embed_pureφ : ⎡⌜φ⌝⎤ ⊣⊢ ⌜φ⌝.
  Proof.
    rewrite (@bi.pure_alt PROP1) (@bi.pure_alt PROP2) embed_exist.
    do 2 f_equiv. apply bi.equiv_entails. split; [apply bi.True_intro|].
    rewrite -(_ : (emp → emp : PROP1) ⊢ True) ?embed_impl;
      last apply bi.True_intro.
    apply bi.impl_intro_l. by rewrite right_id.
  Qed.

Lemma embed_iffP Q : ⎡P ↔ Q⎤ ⊣⊢ (⎡P⎤ ↔ ⎡Q⎤).
  Proof. by rewrite embed_and !embed_impl. Qed.

Lemma embed_wand_iffP Q : ⎡P ∗-∗ Q⎤ ⊣⊢ (⎡P⎤ ∗-∗ ⎡Q⎤).
  Proof. by rewrite embed_and !embed_wand. Qed.

Lemma embed_affinely_2P : <affine> ⎡P⎤ ⊢ ⎡<affine> P⎤.
  Proof. by rewrite embed_and -embed_emp_2. Qed.

Lemma embed_affinely`{!BiEmbedEmp PROP1 PROP2} P : ⎡<affine> P⎤ ⊣⊢ <affine> ⎡P⎤.
  Proof. by rewrite /bi_intuitionistically embed_and embed_emp. Qed.

Lemma embed_absorbinglyP : ⎡<absorb> P⎤ ⊣⊢ <absorb> ⎡P⎤.
  Proof. by rewrite embed_sep embed_pure. Qed.

Lemma embed_intuitionistically_2P : □ ⎡P⎤ ⊢ ⎡□ P⎤.
  Proof. by rewrite /bi_intuitionistically -embed_affinely_2 embed_persistently. Qed.

Lemma embed_intuitionistically`{!BiEmbedEmp PROP1 PROP2} P : ⎡□ P⎤ ⊣⊢ □ ⎡P⎤.
  Proof. by rewrite /bi_intuitionistically embed_affinely embed_persistently. Qed.

Lemma embed_persistently_ifP b : ⎡<pers>?b P⎤ ⊣⊢ <pers>?b ⎡P⎤.
  Proof. destruct b; simpl; auto using embed_persistently. Qed.

Lemma embed_affinely_if_2P b : <affine>?b ⎡P⎤ ⊢ ⎡<affine>?b P⎤.
  Proof. destruct b; simpl; auto using embed_affinely_2. Qed.

Lemma embed_affinely_if`{!BiEmbedEmp PROP1 PROP2} P b :
    ⎡<affine>?b P⎤ ⊣⊢ <affine>?b ⎡P⎤.
  Proof. destruct b; simpl; auto using embed_affinely. Qed.

Lemma embed_absorbingly_ifb P : ⎡<absorb>?b P⎤ ⊣⊢ <absorb>?b ⎡P⎤.
  Proof. destruct b; simpl; auto using embed_absorbingly. Qed.

Lemma embed_intuitionistically_if_2P b : □?b ⎡P⎤ ⊢ ⎡□?b P⎤.
  Proof. destruct b; simpl; auto using embed_intuitionistically_2. Qed.

Lemma embed_intuitionistically_if`{!BiEmbedEmp PROP1 PROP2} P b :
    ⎡□?b P⎤ ⊣⊢ □?b ⎡P⎤.
  Proof. destruct b; simpl; auto using embed_intuitionistically. Qed.

Global Instanceembed_persistent P : Persistent P → Persistent ⎡P⎤.
  Proof. intros ?. by rewrite /Persistent -embed_persistently -persistent. Qed.

Global Instanceembed_affine `{!BiEmbedEmp PROP1 PROP2} P : Affine P → Affine ⎡P⎤.
  Proof. intros ?. by rewrite /Affine (affine P) embed_emp. Qed.

Global Instanceembed_absorbing P : Absorbing P → Absorbing ⎡P⎤.
  Proof. intros ?. by rewrite /Absorbing -embed_absorbingly absorbing. Qed.

Global Instanceembed_and_homomorphism :
    MonoidHomomorphism bi_and bi_and (≡) embed.
  Proof.
    by split; [split|]; try apply _;
      [setoid_rewrite embed_and|rewrite embed_pure].
  Qed.

Global Instanceembed_or_homomorphism :
    MonoidHomomorphism bi_or bi_or (≡) embed.
  Proof.
    by split; [split|]; try apply _;
      [setoid_rewrite embed_or|rewrite embed_pure].
  Qed.

Global Instanceembed_sep_entails_homomorphism :
    MonoidHomomorphism bi_sep bi_sep (flip (⊢)) embed.
  Proof.
    split; [split|]; simpl; try apply _;
      [by setoid_rewrite embed_sep|by rewrite embed_emp_2].
  Qed.

Lemma embed_big_sepL_2{A} (Φ : nat → A → PROP1) l :
    ([∗ list] k↦x ∈ l, ⎡Φ k x⎤) ⊢ ⎡[∗ list] k↦x ∈ l, Φ k x⎤.
  Proof. apply (big_opL_commute (R:=flip (⊢)) _). Qed.

Lemma embed_big_sepM_2`{Countable K} {A} (Φ : K → A → PROP1) (m : gmap K A) :
    ([∗ map] k↦x ∈ m, ⎡Φ k x⎤) ⊢ ⎡[∗ map] k↦x ∈ m, Φ k x⎤.
  Proof. apply (big_opM_commute (R:=flip (⊢)) _). Qed.

Lemma embed_big_sepS_2`{Countable A} (Φ : A → PROP1) (X : gset A) :
    ([∗ set] y ∈ X, ⎡Φ y⎤) ⊢ ⎡[∗ set] y ∈ X, Φ y⎤.
  Proof. apply (big_opS_commute (R:=flip (⊢)) _). Qed.

Lemma embed_big_sepMS_2`{Countable A} (Φ : A → PROP1) (X : gmultiset A) :
    ([∗ mset] y ∈ X, ⎡Φ y⎤) ⊢ ⎡[∗ mset] y ∈ X, Φ y⎤.
  Proof. apply (big_opMS_commute (R:=flip (⊢)) _). Qed.

Global Instanceembed_sep_homomorphism :
      MonoidHomomorphism bi_sep bi_sep (≡) embed.
    Proof.
      by split; [split|]; try apply _;
        [setoid_rewrite embed_sep|rewrite embed_emp].
    Qed.

Lemma embed_big_sepL{A} (Φ : nat → A → PROP1) l :
      ⎡[∗ list] k↦x ∈ l, Φ k x⎤ ⊣⊢ [∗ list] k↦x ∈ l, ⎡Φ k x⎤.
    Proof. apply (big_opL_commute _). Qed.

Lemma embed_big_sepM`{Countable K} {A} (Φ : K → A → PROP1) (m : gmap K A) :
      ⎡[∗ map] k↦x ∈ m, Φ k x⎤ ⊣⊢ [∗ map] k↦x ∈ m, ⎡Φ k x⎤.
    Proof. apply (big_opM_commute _). Qed.

Lemma embed_big_sepS`{Countable A} (Φ : A → PROP1) (X : gset A) :
      ⎡[∗ set] y ∈ X, Φ y⎤ ⊣⊢ [∗ set] y ∈ X, ⎡Φ y⎤.
    Proof. apply (big_opS_commute _). Qed.

Lemma embed_big_sepMS`{Countable A} (Φ : A → PROP1) (X : gmultiset A) :
      ⎡[∗ mset] y ∈ X, Φ y⎤ ⊣⊢ [∗ mset] y ∈ X, ⎡Φ y⎤.
    Proof. apply (big_opMS_commute _). Qed.

Lemma embed_laterNn P : ⎡▷^n P⎤ ⊣⊢ ▷^n ⎡P⎤.
    Proof. induction n=>//=. rewrite embed_later. by f_equiv. Qed.

Lemma embed_except_0P : ⎡◇ P⎤ ⊣⊢ ◇ ⎡P⎤.
    Proof. by rewrite embed_or embed_later embed_pure. Qed.

Global Instanceembed_timeless P : Timeless P → Timeless ⎡P⎤.
    Proof.
      intros ?. by rewrite /Timeless -embed_except_0 -embed_later timeless.
    Qed.

Lemma embed_si_purePi : ⎡<si_pure> Pi⎤ ⊣⊢ <si_pure> Pi.
    Proof.
      apply (anti_symm _); [apply embed_si_pure_1|].
      rewrite -(si_pure_si_emp_valid_elim ⎡ _ ⎤).
      by rewrite embed_si_emp_valid si_emp_valid_si_pure.
    Qed.

Lemma embed_internal_inj`{!Sbi PROP3} P Q : ⎡P⎤ ≡ ⎡Q⎤ ⊢@{PROP3} P ≡ Q.
    Proof.
      intros. rewrite -(si_pure_internal_eq ⎡ _ ⎤%I) -(si_pure_internal_eq P).
      f_equiv. rewrite !prop_ext_si_emp_valid.
      rewrite /bi_wand_iff !si_emp_valid_and.
      by rewrite -!embed_wand !embed_si_emp_valid.
    Qed.

Lemma embed_internal_eq(A : ofe) (x y : A) : ⎡x ≡ y⎤ ⊣⊢@{PROP2} x ≡ y.
    Proof. apply embed_si_pure. Qed.

Lemma embed_plainlyP : ⎡■ P⎤ ⊣⊢ ■ ⎡P⎤.
    Proof. by rewrite /plainly embed_si_pure embed_si_emp_valid. Qed.

Lemma embed_plainly_ifp P : ⎡■?p P⎤ ⊣⊢ ■?p ⎡P⎤.
    Proof. destruct p; simpl; auto using embed_plainly. Qed.

Lemma embed_plain(P : PROP1) : Plain P → Plain (PROP:=PROP2) ⎡P⎤.
    Proof. intros ?. by rewrite /Plain {1}(plain P) embed_plainly. Qed.

Global HintExtern 4 (Plain _) =>
  notypeclasses refine (embed_plain _ _) : typeclass_instances.

Local Instanceembed_embed : Embed PROP1 PROP3 := λ P, ⎡ ⎡ P ⎤ ⎤%I.

Lemma embed_embedding_mixin: BiEmbedMixin PROP1 PROP3 embed_embed.
  Proof.
    split; unfold embed, embed_embed.
    - solve_proper.
    - solve_proper.
    - intros P. by rewrite !embed_emp_valid.
    - by rewrite -!embed_emp_2.
    - intros P Q. by rewrite -!embed_impl.
    - intros A Φ. by rewrite -!embed_forall.
    - intros A Φ. by rewrite -!embed_exist.
    - intros P Q. by rewrite -!embed_sep.
    - intros P Q. by rewrite -!embed_wand.
    - intros P. by rewrite -!embed_persistently.
  Qed.

Local Instanceembed_bi_embed : BiEmbed PROP1 PROP3 :=
    {| bi_embed_mixin := embed_embedding_mixin |}.

Lemma embed_embed_alt(P : PROP1) : ⎡ P ⎤ ⊣⊢@{PROP3} ⎡ ⎡ P ⎤ ⎤.
  Proof. done. Qed.

Lemma embed_embed_emp:
    BiEmbedEmp PROP1 PROP2 → BiEmbedEmp PROP2 PROP3 →
    BiEmbedEmp PROP1 PROP3.
  Proof. rewrite /BiEmbedEmp !embed_embed_alt. by intros -> ->. Qed.

Lemma embed_embed_later:
    BiEmbedLater PROP1 PROP2 → BiEmbedLater PROP2 PROP3 →
    BiEmbedLater PROP1 PROP3.
  Proof. intros ?? P. by rewrite !embed_embed_alt !embed_later. Qed.

Lemma embed_embed_sbi`{!Sbi PROP1, !Sbi PROP2, !Sbi PROP3} :
    BiEmbedSbi PROP1 PROP2 → BiEmbedSbi PROP2 PROP3 →
    BiEmbedSbi PROP1 PROP3.
  Proof.
    intros ??; split.
    - intros P. by rewrite embed_embed_alt !embed_si_emp_valid.
    - intros Pi. by rewrite embed_embed_alt !embed_si_pure.
  Qed.

Lemma embed_embed_bupd`{!BiBUpd PROP1, !BiBUpd PROP2, !BiBUpd PROP3} :
    BiEmbedBUpd PROP1 PROP2 → BiEmbedBUpd PROP2 PROP3 →
    BiEmbedBUpd PROP1 PROP3.
  Proof. intros ?? P. by rewrite !embed_embed_alt !embed_bupd. Qed.

Lemma embed_embed_fupd`{!BiFUpd PROP1, !BiFUpd PROP2, !BiFUpd PROP3} :
    BiEmbedFUpd PROP1 PROP2 → BiEmbedFUpd PROP2 PROP3 →
    BiEmbedFUpd PROP1 PROP3.
  Proof. intros ?? E1 E2 P. by rewrite !embed_embed_alt !embed_fupd. Qed.