From iris.heap_lang Require Import proofmode notation.

Section mtree_examples.

(** Telling Iris what algebras to use in the following *)
Context `{!heapGS Σ}.

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.


(** Trees and logical assertions *)

Inductive tree := Leaf | Node (x : Z) (T1 T2 : tree).

Inductive depth : nat -> tree -> Prop :=
  | depth_Leaf : depth 0 Leaf
  | depth_Node n x T1 T2 : depth n T1 -> depth n T2 -> depth (S n) (Node x T1 T2).

Fixpoint Mtree (T : tree) (l : loc) : iProp Σ :=
  match T with
  | Leaf => ⌜ l = null ⌝
  | Node x T1 T2 =>
      ∃ l1 l2 : loc,
        ⌜ l ≠ null ⌝
        ∗ l ↦∗ [(#x); #l1; #l2]
        ∗ Mtree T1 l1 ∗ Mtree T2 l2
  end%I.


(** Building a new record, i.e. allocation + updating fields *)

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

Lemma array_3_split l v0 v1 v2 :
  l ↦∗ [v0; v1; v2] ⊣⊢
    (l +ₗ 0) ↦ v0
  ∗ (l +ₗ 1) ↦ v1
  ∗ (l +ₗ 2) ↦ v2.
Proof.
  rewrite !array_cons array_nil /Loc.add -Z.add_assoc /= Z.add_0_r.
  destruct l.
  iSplit; iIntros "(? & (? & (? & ?)))"; iFrame.
Qed.


(* [copy] program from the lecture *)

Definition tree_copy : val :=
  rec: "copy" "l" :=
    if: "l" = #null then #null else
      new_node
        !("l" +ₗ #0)
        ("copy" !("l" +ₗ #1))
        ("copy" !("l" +ₗ #2)).

Lemma tree_copy_spec : forall T l,
  {{{ Mtree T l }}}
    tree_copy #l
  {{{ (l' : loc), RET #l'; Mtree T l ∗ Mtree T l' }}}.
Proof.
  iIntros (T l Φ) "T post".
  iInduction T as [ | x T1 ? T2 ] "IH" forall (l Φ).
  - wp_rec. simpl.
    iDestruct "T" as %->.
    wp_pures.
    iApply "post". auto.
  - wp_rec.
    simpl.
    iDestruct "T" as (l1 l2) "(%n & l & T1 & T2)".
    wp_pure.
    rewrite bool_decide_eq_false_2. 2: congruence.
    wp_if.
    unfold new_node.
    rewrite array_3_split.
    iDestruct "l" as "(l0 & l1 & l2)".
    wp_load.
    wp_apply ("IH1" with "T2") as (l2') "(T2 & T2')".
    wp_load.
    wp_apply ("IH" with "T1") as (l1') "(T1 & T1')".
    wp_pures.
    wp_load.
    wp_pures.
    wp_apply (malloc_spec 3) as (l') "(l' & %l'n)"; auto.
    wp_pures.
    rewrite array_3_split.
    iDestruct "l'" as "(l'0 & l'1 & l'2)".
    wp_store.
    wp_store.
    wp_store.
    iApply "post". iModIntro.
    iSplitL "l0 l1 l2 T1 T2".
    + iExists l1, l2. iSplit. auto. rewrite array_3_split. iFrame.
    + iExists l1', l2'. iSplit. auto. rewrite array_3_split. iFrame.
Qed.

Definition Mtreecomplete (T : tree) (l : loc) : iProp Σ :=
  ∃ n, Mtree T l ∗ ⌜depth n T⌝.

Lemma complete_tree_copy_spec : forall T l,
  {{{ Mtreecomplete T l }}}
    tree_copy #l
  {{{ (l' : loc), RET #l'; Mtreecomplete T l ∗ Mtreecomplete T l' }}}.
Proof.
  iIntros (T l Φ) "Hl HΦ".
  unfold Mtreecomplete at 1.
  (* Exists *)
  iDestruct "Hl" as (n) "Hl".
  (* Prop *)
  iDestruct "Hl" as "(Hl & %HT)".
  (* using the normal spec of [tree_copy]: *)
  wp_apply (tree_copy_spec with "Hl") as (l') "(Hl & Hl')".
  iApply "HΦ".
  iSplitL "Hl".
  - unfold Mtreecomplete. iExists n. iFrame. iPureIntro. auto.
  - iExists n. iSplit. iFrame. auto.
Qed.

End mtree_examples.
