From iris.heap_lang Require Import lang proofmode notation.
Set Default Goal Selector "!".

Section hw6.

Context `{!heapGS Σ}.

Fixpoint isList (l : val) (xs : list val) : iProp Σ :=
  match xs with
  | [] => ⌜l = NONEV⌝  (* NONEV = null pointer *)
  | x :: xs => ∃ (hd : loc) l', ⌜l = SOMEV (#hd)⌝ ∗
                                hd ↦ (x, l') ∗ isList l' xs
  end.

(* exercise: inc_spec (2 stars) *)
Definition inc : val :=
  rec: "inc" "l" :=
    match: "l" with
      NONE => #()
    | SOME "hd" => (* hd ↦ (h, t) *)
        let: "h" := Fst (! "hd") in
        let: "t" := Snd (! "hd") in
        "hd" <- ("h" + #1, "t");;
        "inc" "t"
    end.

Lemma inc_spec (l : val) (xs : list Z) :
  {{{ isList l ((λ x : Z, #x) <$> xs) }}}
    inc l
  {{{ RET #(); isList l ((λ x, #(x + 1)) <$> xs)}}}.
Proof.
  (**
    The proof proceeds by structural induction in [xs]. As [l] changes in each
    iteration, we must universally quantify over it to strengthen the induction
    hypothesis.
  *)
  generalize dependent l.
  induction xs as [|x xs' IH].
  - (* Base Case: xs = [] *)
    iIntros (l) "%Φ -> HΦ".
    simpl.
    wp_rec.
    wp_match.
    by iApply "HΦ".
  - (* Induction step: xs = x :: xs' *)
    (* exercise *)
    iIntros (l) "%Φ H HΦ".
    simpl.
Admitted.

(* exercise: append_spec (2 stars) *)
Definition append : val :=
  rec: "append" "l1" "l2" :=
    match: "l1" with
      NONE => "l2"
    | SOME "hd" =>
        let: "x" := Fst (!"hd") in
        let: "l1'" := Snd (!"hd") in
        let: "r" := "append" "l1'" "l2" in
        "hd" <- ("x", "r");;
        SOME "hd"
    end.

Lemma append_spec (l1 l2 : val) (xs ys : list val) :
  {{{ isList l1 xs ∗ isList l2 ys }}}
    append l1 l2
  {{{ l, RET l; isList l (xs ++ ys) }}}.
Proof.
  revert ys l1 l2.
  induction xs as [| x xs' IH]; simpl.
  (* exercise *)
Admitted.

(* exercise: sum_list_coq (2 stars)
   Write a function sum_list_coq that takes a list Z (i.e., a list of numbers)
   and returns its sum. Make *)
Fixpoint sum_list_coq (l : list Z) : Z (* := ... *)
. Admitted.

Example sum_list_coq_ex_1 : sum_list_coq [] = 0.
(* Proof. simpl. reflexivity. Qed. *) Admitted.

Example sum_list_coq_ex_2 : sum_list_coq [1; 2; 3]%Z = 6.
(* Proof. simpl. reflexivity. Qed. *) Admitted.

(* exercise: sum_list_spec (3 stars)
   Here is a HeapLang function that sums a list: *)
Definition sum_list : val :=
  rec: "sum_list" "l" :=
    match: "l" with
      NONE => #0
    | SOME "hd" =>
        let: "h" := Fst (!"hd") in
        let: "t" := Snd (!"hd") in
        "h" + "sum_list" "t"
    end.

(* State and prove a specification for sum_list saying that sum_list computes
   the same thing as sum_list_coq on its argument. *)

(* grad exercise *)
(* Write a HeapLang function that implements "repeat", the Coq library function
   that takes a value x and a number n and returns a list with n x's (e.g.,
   repeat 3 5 = [3; 3; 3; 3; 3]). Write and prove a spec showing that your function
   correctly implements repeat.
   Hint: When the number is greater than 0, you will need to use ref to allocate a
   new list cell. *)
(* Definition repeat_list : val := ...*)

(* Lemma repeat_list_spec:
  {{{ ... }}}
  repeat_list x #n
  {{{ ... }}}. *)

End hw6.
