//
// Course: BU CAS CS 520
// Instructor: Hongwei Xi (hwxi AT cs DOT bu DOT edu)
//

(* ****** ****** *)

dataview array_v (a:t@ype, int, addr) =
  | {l:addr} array_v_nil (a, 0, l) of ()
  | {n:nat} {l:addr}
    array_v_cons (a, n+1, l) of (a @ l, array_v (a, n, l+sizeof a))
(*
    // you may also define it this way:
    array_v_cons (a, n+1, l) of (MUL (n, sizeof a, ofs), array_v (a, n, l), a @ l + ofs)
*)

fun{a:t@ype}
  getfst {n:pos} {l:addr}
    (pf: !array_v (a, n, l) | p: ptr l): a = let
  prval array_v_cons (pf1, pf2) = pf // pf1: a @ l, pf2: ...
  val x = !p
  prval () = pf := array_v_cons {a} (pf1, pf2)
in
  x
end // end of [getfst]

(*
fun{a:t@ype} // O(i)-time
  getelt {n,i:nat | i < n} {l:addr}
    (pf: !array_v (a, n, l) | p: ptr l, i: int i): a =
  if i = 0 then getfst (pf | p)
  else let
    prval array_v_cons (pf1, pf2) = pf // pf1: a @ l, pf2: ...
    val x = getelt {n-1,i-1} {l+sizeof a} (pf2 | p+sizeof<a>, i-1)
    prval () = pf := array_v_cons {a} (pf1, pf2)    
  in
    x
  end // end of [if]
*)

extern
prfun split {a:t@ype}
  {n,i:nat | i <= n}
  {l:addr} {ofs:int} (
    pf_mul: MUL (i, sizeof a, ofs)
  , pf_arr: array_v (a, n, l)
  , i: size_t i
) : (
  array_v (a, i, l), array_v (a, n-i, l+ofs)
)

extern
prfun unsplit {a:t@ype}
  {i,ni:nat}
  {l:addr} {ofs:int} (
    pf_mul: MUL (i, sizeof a, ofs)
  , pf1_arr: array_v (a, i, l)
  , pf2_arr: array_v (a, ni, l+ofs)
) : (
  array_v (a, i+ni, l)
)

extern
prfun takeout {a:t@ype}
  {n,i:nat} {l:addr} {ofs:int} (
    pf_mul: MUL (i, sizeof a, ofs)
  , pf_arr: array_v (a, n, l)
  ) : (a @ l+ofs, a @ l+ofs -<lin> array_v (a, n, l))

(*
fun{a:t@ype} // O(1)-time
  getelt {n,i:nat | i < n} {l:addr}
    (pf: !array_v (a, n, l) | p: ptr l, i: size_t i): a = let
  val (pf_mul | ofs) = mul2_size1_size1 (i, sizeof<a>)
  prval (pf1, pf2) = split {a} (pf_mul, pf, i)
  val x = getfst (pf2 | p + ofs)
  prval () = pf := unsplit {a} (pf_mul, pf1, pf2)
in
  x
end // end of [getelt]
*)

fun{a:t@ype} // O(1)-time
  getelt {n,i:nat | i < n} {l:addr}
    (pf: !array_v (a, n, l) | p: ptr l, i: size_t i): a = let
  val (pf_mul | ofs) = mul2_size1_size1 (i, sizeof<a>)
  prval (pf_at, fpf) = takeout {a} (pf_mul, pf)
  val x = !(p+ofs)
  prval () = pf := fpf (pf_at)
in
  x
end // end of [getelt]

(* ****** ****** *)

(* end of [array.dats] *)