From iris.heap_lang Require Import proofmode notation.

Section examples. Context `{!heapGS Σ}.

(** [null] specificities *)
Definition null : loc := Loc 0.

(* [malloc] is just like normal allocation but returns a non-null location *)
Definition malloc : val :=
  λ: "n" "v",
    if: #0 < "n" then
      let: "l" := AllocN "n" "v" in
      if: "l" = #null then AllocN "n" "v" else "l"
    else #(Loc 91).

Lemma malloc_spec (n : Z) (v : val):
  {{{ True }}}
    malloc #n v
  {{{ (l : loc), RET #l; l ↦∗ replicate (Z.to_nat n) v ∗ ⌜l ≠ null⌝ }}}.
Proof.
  iIntros (Φ) "_ ret". wp_lam. wp_pures.
  case_bool_decide.
  - wp_alloc l as "Hl". lia. wp_pures.
    destruct (decide (l = null)) as [-> | nl].
    + wp_if. wp_alloc l' as "Hl'". lia. iApply "ret".
      assert (Z.to_nat n = S (Z.to_nat (n - 1))) as -> by lia.
      rewrite /= !array_cons.
      iModIntro; iSplit; iFrame.
      iDestruct "Hl" as "(? & _)".
      iDestruct "Hl'" as "(? & _)".
      iApply (pointsto_ne with "[$] [$]").
    + rewrite bool_decide_eq_false_2. 2: congruence.
      wp_if. iApply "ret"; auto.
  - wp_if. iApply "ret".
    assert (Z.to_nat n = 0) as -> by lia. rewrite /array /=. auto.
Qed.


(** [Mlist] related to [listof] *)

Fixpoint Mlist (L : list Z) (p : loc) : iProp Σ :=
  match L with
  | nil => ⌜ p = null ⌝
  | x :: L' => ∃ p' : loc, ⌜ p ≠ null ⌝ ∗ p ↦ #x ∗ (p +ₗ 1) ↦ #p' ∗ Mlist L' p'
  end%I.

Fixpoint listof {A} (R : A → val → iProp Σ) (L : list A) (p : loc) : iProp Σ :=
  match L with
  | nil => ⌜ p = null ⌝
  | X :: L' => ∃ p' : loc, ⌜ p ≠ null ⌝ ∗ (∃ x, p ↦ x ∗ R X x) ∗ (p +ₗ 1) ↦ #p' ∗ listof R L' p'
  end%I.

Definition isz (z : Z) : val → iProp Σ := λ v, ⌜v = #z⌝%I.

Lemma Mlistof_listof_Z p L : Mlist L p ⊣⊢ listof isz L p.
Proof.
  induction L as [ | z L ] in p |- *. auto.
  simpl.
  iSplit; iIntros "(%p' & ? & A & ?)"; iExists p';
    rewrite IHL; iFrame; auto.
  iDestruct "A" as "(% & ? & ->)"; auto.
Qed.


(** New list cell, first-order spec *)

Definition cons : val :=
  λ: "v1" "v2",
    let: "p" := malloc #2 #() in
    "p" <- "v1";;
    "p" +ₗ #1 <- "v2";;
    "p".

Lemma cons_correct v1 v2 :
  {{{ True }}} cons v1 v2 {{{ (l : loc), RET #l; ⌜l ≠ null⌝ ∗ l ↦ v1 ∗ (l +ₗ 1) ↦ v2 }}}.
Proof.
  iIntros (φ) "_ ret". wp_lam; wp_pures.
  wp_apply (malloc_spec 2 with "[]") as (l) "(Hl & %)". iPureIntro. lia.
  rewrite !array_cons. iDestruct "Hl" as "(Hl0 & Hl1 & _)".
  wp_store. wp_store. iApply "ret". iFrame. auto.
Qed.


Section about_listof.

Context {A} (R : A → val → iProp Σ).

Lemma cons_listof p' x X L :
  {{{ R X x ∗ listof R L p' }}}
    cons x #p'
  {{{ (p : loc), RET #p; listof R (X :: L) p }}}.
Proof.
  iIntros (φ) "(Hx & Hp') ret".
  wp_apply (cons_correct with "[$]") as (l) "(% & Hl & Hl1)".
  iApply "ret". simpl. iExists p'. iFrame. auto.
Qed.

Definition uncons : val :=
  λ: "p",
    (!"p", !("p" +ₗ #1)).

Lemma uncons_listof p X L :
  {{{ listof R (X :: L) p }}}
    uncons #p
  {{{ (x : val) (p' : loc), RET (x, #p'); R X x ∗ listof R L p' }}}.
Proof.
  iIntros (φ) "Hp ret". simpl.
  iDestruct "Hp" as (p' Hp) "((%x & Hp & Hx) & Hp1 & Hp')".
  wp_lam. wp_load. wp_load. wp_pures. iApply "ret". iFrame. auto.
Qed.

Lemma head_listof p X L :
  {{{ listof R (X :: L) p }}}
    !#p
  {{{ (x : val), RET x; R X x ∗ ∀ Y, R Y x -∗ listof R (Y :: L) p }}}.
Proof.
  iIntros (φ) "Hp ret". simpl listof at 1.
  iDestruct "Hp" as (p' Hp) "((%x & Hp & Hx) & Hp1 & Hp')".
  wp_load. iApply "ret". iFrame. iModIntro.
  iIntros "%Y Hy". simpl. iExists p'. iFrame. auto.
Qed.


Definition miter : val :=
  rec: "miter" "f" "p" :=
    if: "p" = #null then
      #()
    else
      "f" !"p";;
      "miter" "f" !("p" +ₗ #1).

Definition iter_spec_1 (it : val) := ∀ (f : val) l (I : list A → iProp Σ),
  (∀ X x s, {{{ I (X :: s) ∗ R X x }}} f x {{{ RET #(); I s ∗ R X x }}}) →
  ∀ p, {{{ listof R l p ∗ I l }}} it f #p {{{ RET #(); listof R l p ∗ I [] }}}.

Lemma miter_correct_1 : iter_spec_1 miter.
Proof.
  iIntros (f l I Hf p φ) "(Hp & i) ret".
  iInduction l as [ | X l ] "IH" forall (p).
  - iDestruct "Hp" as %->. wp_rec. wp_pures. iApply "ret". auto.
  - simpl listof at 3. iDestruct "Hp" as "(%p' & %Hp' & (%x & Hp & Hx) & Hp1 & Hp')".
    wp_rec. wp_pures. case_bool_decide. congruence. wp_pures. wp_load.
    wp_apply (Hf with "[$i $Hx]") as "(i & Hx)". wp_pures. wp_load.
    wp_apply ("IH" with "Hp' i") as "(Hp' & i)". iApply "ret". by iFrame.
Qed.

Definition iter_spec_2 (it : val) := ∀ (f : val) l (J : list A → list A → iProp Σ),
  (∀ X x k s, {{{ J (X :: s) k ∗ R X x }}} f x {{{ Y, RET #(); J s (k ++ [Y]) ∗ R Y x }}}) →
  ∀ p, {{{ listof R l p ∗ J l [] }}} it f #p {{{ l', RET #(); listof R l' p ∗ J [] l' }}}.

Lemma miter_correct_2 : iter_spec_2 miter.
Proof.
  (* Generalize slightly for the induction *)
  cut (∀ (f : val) l (J : list A → list A → iProp Σ),
          (∀ X x k s, {{{ J (X :: s) k ∗ R X x }}} f x {{{ Y, RET #(); J s (k ++ [Y]) ∗ R Y x }}}) →
          ∀ nnil p, {{{ listof R l p ∗ J l nnil }}} miter f #p {{{ l', RET #(); listof R l' p ∗ J [] (nnil ++ l') }}}).
  { intros H f l J Hf; apply (H f l J Hf nil). }
  iIntros (f l J Hf nnil p φ) "(Hp & j) ret".
  iInduction l as [ | X l ] "IH" forall (p nnil).
  - iDestruct "Hp" as %->. wp_rec. wp_pures. iApply ("ret" $! []). rewrite app_nil_r. auto.
  - simpl listof at 3. iDestruct "Hp" as "(%p' & %Hp' & (%x & Hp & Hx) & Hp1 & Hp')".
    wp_rec. wp_pures. case_bool_decide. congruence. wp_pures. wp_load.
    wp_apply (Hf with "[$j $Hx]") as (Y) "(j & Hx)". wp_pures. wp_load.
    wp_apply ("IH" with "[$Hp'] [$j]") as (l') "(Hp' & j)". iApply "ret".
    rewrite -app_assoc. by iFrame.
Qed.

End about_listof.

End examples.
