(* iris/bi/lib/fractional.v *)
From iris.bi Require Export bi
From iris.proofmode Require Import classes classes_make proofmode
From iris.prelude Require Import options

Class Fractional{PROP : bi} (Φ : Qp → PROP) :=
  fractional p q : Φ (p + q)%Qp ⊣⊢ Φ p ∗ Φ q.

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

Global Argumentsfractional {_ _ _} _ _.

Class AsFractional{PROP : bi} (P : PROP) (Φ : Qp → PROP) (q : Qp) := {
  as_fractional : P ⊣⊢ Φ q;
  as_fractional_fractional : Fractional Φ
}.

Global ArgumentsAsFractional {_} _%_I _%_I _%_Qp.

Global HintMode AsFractional - ! - - : typeclass_instances.

Class FrameFractionalQp(qR qP r : Qp) :=
  frame_fractional_qp : qP = (qR + r)%Qp.

Global HintMode FrameFractionalQp ! ! - : typeclass_instances.

Global InstanceFractional_proper :
    Proper (pointwise_relation _ (≡) ==> iff) (@Fractional PROP).
  Proof.
    rewrite /Fractional.
    intros Φ1 Φ2 Hequiv.
    by setoid_rewrite Hequiv.
  Qed.

Local Instancefractional_as_fractional Φ q :
    Fractional Φ → AsFractional (Φ q) Φ q.
  Proof. done. Qed.

Lemma fractional_splitP Φ q1 q2 :
    AsFractional P Φ (q1 + q2) →
    P ⊣⊢ Φ q1 ∗ Φ q2.
  Proof. by move=>-[-> ->]. Qed.

Lemma fractional_halfP Φ q :
    AsFractional P Φ q →
    P ⊣⊢ Φ (q/2)%Qp ∗ Φ (q/2)%Qp.
  Proof. by rewrite -{1}(Qp.div_2 q)=>-[->->]. Qed.

Lemma fractional_mergeP1 P2 Φ q1 q2 `{!Fractional Φ} :
    AsFractional P1 Φ q1 → AsFractional P2 Φ q2 →
    P1 ∗ P2 ⊣⊢ Φ (q1 + q2)%Qp.
  Proof. move=>-[-> _] [-> _]. rewrite fractional //. Qed.

Global Instancepersistent_fractional (P : PROP) :
    Persistent P → TCOr (Affine P) (Absorbing P) → Fractional (λ _, P).
  Proof. intros ?? q q'. apply: bi.persistent_sep_dup. Qed.

Global Instancefractional_sep Φ Ψ :
    Fractional Φ → Fractional Ψ → Fractional (λ q, Φ q ∗ Ψ q)%I.
  Proof.
    intros ?? q q'. rewrite !fractional -!assoc. f_equiv.
    rewrite !assoc. f_equiv. by rewrite comm.
  Qed.

Global Instancefractional_embed `{!BiEmbed PROP PROP'} Φ :
    Fractional Φ → Fractional (λ q, ⎡ Φ q ⎤ : PROP')%I.
  Proof. intros ???. by rewrite fractional embed_sep. Qed.

Global Instanceas_fractional_embed `{!BiEmbed PROP PROP'} P Φ q :
    AsFractional P Φ q → AsFractional (⎡ P ⎤) (λ q, ⎡ Φ q ⎤)%I q.
  Proof. intros [??]; split; [by f_equiv|apply _]. Qed.

Global Instancefractional_big_sepL {A} (l : list A) Ψ :
    (∀ k x, Fractional (Ψ k x)) →
    Fractional (PROP:=PROP) (λ q, [∗ list] k↦x ∈ l, Ψ k x q)%I.
  Proof. intros ? q q'. rewrite -big_sepL_sep. by setoid_rewrite fractional. Qed.

Global Instancefractional_big_sepL2 {A B} (l1 : list A) (l2 : list B) Ψ :
    (∀ k x1 x2, Fractional (Ψ k x1 x2)) →
    Fractional (PROP:=PROP) (λ q, [∗ list] k↦x1; x2 ∈ l1; l2, Ψ k x1 x2 q)%I.
  Proof. intros ? q q'. rewrite -big_sepL2_sep. by setoid_rewrite fractional. Qed.

Global Instancefractional_big_sepM `{Countable K} {A} (m : gmap K A) Ψ :
    (∀ k x, Fractional (Ψ k x)) →
    Fractional (PROP:=PROP) (λ q, [∗ map] k↦x ∈ m, Ψ k x q)%I.
  Proof. intros ? q q'. rewrite -big_sepM_sep. by setoid_rewrite fractional. Qed.

Global Instancefractional_big_sepS `{Countable A} (X : gset A) Ψ :
    (∀ x, Fractional (Ψ x)) →
    Fractional (PROP:=PROP) (λ q, [∗ set] x ∈ X, Ψ x q)%I.
  Proof. intros ? q q'. rewrite -big_sepS_sep. by setoid_rewrite fractional. Qed.

Global Instancefractional_big_sepMS `{Countable A} (X : gmultiset A) Ψ :
    (∀ x, Fractional (Ψ x)) →
    Fractional (PROP:=PROP) (λ q, [∗ mset] x ∈ X, Ψ x q)%I.
  Proof. intros ? q q'. rewrite -big_sepMS_sep. by setoid_rewrite fractional. Qed.

Global Instancefrom_sep_fractional P Φ q1 q2 :
    AsFractional P Φ (q1 + q2) → FromSep P (Φ q1) (Φ q2).
  Proof. rewrite /FromSep=>-[-> ->] //. Qed.

Global Instancecombine_sep_as_fractional P1 P2 Φ q1 q2 :
    AsFractional P1 Φ q1 → AsFractional P2 Φ q2 →
    CombineSepAs P1 P2 (Φ (q1 + q2)%Qp) | 50.

Global Instancefrom_sep_fractional_half P Φ q :
    AsFractional P Φ q → FromSep P (Φ (q / 2)%Qp) (Φ (q / 2)%Qp) | 10.
  Proof. rewrite /FromSep -{1}(Qp.div_2 q). intros [-> <-]. rewrite Qp.div_2 //. Qed.

Global Instancecombine_sep_as_fractional_half P Φ q :
    AsFractional P Φ (q/2) → CombineSepAs P P (Φ q) | 50.

Global Instanceinto_sep_fractional P Φ q1 q2 :
    AsFractional P Φ (q1 + q2) → IntoSep P (Φ q1) (Φ q2).
  Proof. intros [??]. rewrite /IntoSep [P]fractional_split //. Qed.

Global Instanceinto_sep_fractional_half P Φ q :
    AsFractional P Φ q → IntoSep P (Φ (q / 2)%Qp) (Φ (q / 2)%Qp) | 100.
  Proof. intros [??]. rewrite /IntoSep [P]fractional_half //. Qed.

Global Instanceframe_fractional_qp_add_l q q' : FrameFractionalQp q (q + q') q'.
  Proof. by rewrite /FrameFractionalQp. Qed.

Global Instanceframe_fractional_qp_add_r q q' : FrameFractionalQp q' (q + q') q.
  Proof. by rewrite /FrameFractionalQp Qp.add_comm. Qed.

Global Instanceframe_fractional_qp_half q : FrameFractionalQp (q/2) q (q/2).
  Proof. by rewrite /FrameFractionalQp Qp.div_2. Qed.

Lemma frame_fractionalΦ p R P qR qP r :
    AsFractional R Φ qR →
    AsFractional P Φ qP →
    FrameFractionalQp qR qP r →
    Frame p R P (Φ r).
  Proof.
    rewrite /Frame /FrameFractionalQp=> -[-> _] [-> ?] ->.
    by rewrite bi.intuitionistically_if_elim fractional.
  Qed.

Definition internal_fractional{PROP : bi} (Φ : Qp → PROP) : PROP :=
  tc_opaque (□ ∀ p q, Φ (p + q)%Qp ∗-∗ Φ p ∗ Φ q)%I.

Global Instance: Params (@internal_fractional) 1 := {}.

Global Instanceinternal_fractional_ne n :
    Proper (pointwise_relation _ (dist n) ==> dist n) (@internal_fractional PROP).
  Proof. solve_proper. Qed.

Global Instanceinternal_fractional_proper :
    Proper (pointwise_relation _ (≡) ==> (≡)) (@internal_fractional PROP).
  Proof. solve_proper. Qed.

Global Instanceinternal_fractional_affine Φ : Affine (internal_fractional Φ).
  Proof. rewrite /internal_fractional /=. apply _. Qed.

Global Instanceinternal_fractional_persistent Φ :
    Persistent (internal_fractional Φ).
  Proof. rewrite /internal_fractional /=. apply _. Qed.

Lemma fractional_internal_fractionalΦ :
    Fractional Φ → ⊢ internal_fractional Φ.
  Proof.
    intros. iIntros "!>" (q1 q2).
    rewrite [Φ (q1 + q2)%Qp]fractional //=; auto.
  Qed.

Lemma internal_fractional_iffΦ Ψ:
    □ (∀ q, Φ q ∗-∗ Ψ q) ⊢ internal_fractional Φ -∗ internal_fractional Ψ.
  Proof.
    iIntros "#Hiff #HΦdup !>" (p q).
    iSplit.
    - iIntros "H".
      iDestruct ("Hiff" with "H") as "HΦ".
      iDestruct ("HΦdup" with "HΦ") as "[H1 ?]".
      iSplitL "H1"; iApply "Hiff"; iFrame.
    - iIntros "[H1 H2]".
      iDestruct ("Hiff" with "H1") as "HΦ1".
      iDestruct ("Hiff" with "H2") as "HΦ2".
      iApply "Hiff".
      iApply "HΦdup".
      iFrame.
  Qed.