(*
 * Dynamic (dense) array.
 *
 * -- Allen
 *)

structure DynArray : 
  sig type 'a array
      type 'a vector

      val all: ('a -> bool) -> 'a array -> bool
      val app: ('a -> unit) -> 'a array -> unit 
      val appi: (int * 'a -> unit) -> 'a array -> unit 
      val array: int * 'a -> 'a array 
      val collate: ('a * 'a -> order) -> 'a array * 'a array -> order
      val copy: {src: 'a array, dst: 'a array, di: int} -> unit 
      val copyVec: {src: 'a vector, dst: 'a array, di: int} -> unit 
      val exists: ('a -> bool) -> 'a array -> bool
      val find: ('a -> bool) -> 'a array -> 'a option
      val findi: (int * 'a -> bool) -> 'a array -> (int * 'a) option
      val foldl: ('a * 'b -> 'b) -> 'b -> 'a array -> 'b 
      val foldli: (int * 'a * 'b -> 'b) -> 'b -> 'a array -> 'b
      val foldr: ('a * 'b -> 'b) -> 'b -> 'a array -> 'b 
      val foldri: (int * 'a * 'b -> 'b) -> 'b -> 'a array -> 'b
      val fromList: 'a list -> 'a array 
      val length: 'a array -> int 
      val maxLen: int 
      val modify: ('a -> 'a) -> 'a array -> unit 
      val modifyi: (int * 'a -> 'a) -> 'a array -> unit 
      val sub: 'a array * int -> 'a 
      val tabulate: int * (int -> 'a) -> 'a array 
      val update: 'a array * int * 'a -> unit 
      val vector: 'a array -> 'a vector

      val fromArray : 'a Array.array * 'a * int -> 'a array
      val baseArray : 'a array -> 'a Array.array
      val checkArray: 'a array * 'a Array.array -> unit
      val clear     : 'a array * int -> unit
      val expandTo  : 'a array * int -> unit
  end =
  struct
     structure A = Array
     structure AS = ArraySlice
     type 'a vector = 'a A.vector 
     datatype 'a array = ARRAY of 'a A.array ref * 'a * int ref

     exception Subscript = General.Subscript
     exception Size      = General.Size
     exception Unimplemented

     infix 9 sub

     val maxLen = A.maxLen

     fun array (n,d) = ARRAY(ref(A.array (n,d)), d, ref 0) 
     fun clear (ARRAY(a,def,cnt),n) = (a := A.array(n,def); cnt := n)
     fun fromArray(a,d,n) = ARRAY(ref a, d, ref n)

     fun baseArray(ARRAY(ref a,_,_)) = a
     fun checkArray(ARRAY(ref a,_,_),a') = if a = a' then () else raise Match

     fun length (ARRAY (ref a,_,ref n)) = n

     fun (ARRAY(ref a, d, _)) sub i = A.sub(a,i) handle _ => d
    
     fun update (ARRAY(r as ref a, d, n), i, e) =
        (A.update(a,i,e); n := Int.max(!n,i+1)) handle _ =>
            let val new_size  = Int.max(i+1,!n*2)
                val new_size  = if new_size < 10 then 10 else new_size
                val new_array = A.array(new_size,d)
            in  A.copy {src = a, dst = new_array, di = 0};
                r := new_array;
                n := i+1;
                A.update(new_array, i, e)
            end

     fun expandTo(arr as ARRAY(_, d, _), N) = update(arr, N-1, d)

     fun tabulate (n, f) = 
         let val array   = A.tabulate(n, f)
             val default = A.sub(array,0)
         in
             ARRAY(ref array, default, ref n)
         end handle _ => raise Size

     fun fromList l =
         let val array   = A.fromList l
             val default = A.sub(array,0)
         in
             ARRAY(ref array, default, ref (List.length l))
         end handle _ => raise Size

     fun slice (ARRAY (ref a, _, ref n)) = AS.slice (a, 0, SOME n)

     fun appi f arr = AS.appi f (slice arr)
     fun app f arr = AS.app f (slice arr)

     fun copy { src, dst, di } =
	 appi (fn (i, x) => update (dst, i + di, x)) src

     fun copyVec { src, dst, di } =
	 Vector.appi (fn (i, x) => update (dst, i + di, x)) src

     fun foldli f init arr = AS.foldli f init (slice arr)
     fun foldri f init arr = AS.foldri f init (slice arr)
     fun foldl f init arr = AS.foldl f init (slice arr)
     fun foldr f init arr = AS.foldr f init (slice arr)
     fun modifyi f arr = AS.modifyi f (slice arr)
     fun modify f arr = AS.modify f (slice arr)
     fun findi p arr = AS.findi p (slice arr)
     fun find p arr = AS.find p (slice arr)
     fun exists p arr = AS.exists p (slice arr)
     fun all p arr = AS.all p (slice arr)
     fun collate c (a1, a2) = AS.collate c (slice a1, slice a2)
     fun vector arr = AS.vector (slice arr)
end
