(* Array2 -- as of 1995-09-12, 1997-03-12, 1998-04-07 *)

(* Representation of an m * n array: an m-vector of n-arrays (rows),
   and the dimensions (m, n), m = number of rows, n = number of columns. *)

(* The implementation below is pretty naive, using the Array and Vector 
   operations and doing more bounds checks than strictly necessary.  *)

type 'a array = ('a Array.array Vector.vector * int * int) ref 

datatype traversal = RowMajor | ColMajor

type 'a region = { base : 'a array, row : int, col : int, 
		   nrows : int option, ncols : int option}

fun fromList [] = ref (Vector.fromList [], 0, 0)
  | fromList (xs1 :: xsr) =
    let val row1 = Array.fromList xs1
	val rowr = List.map Array.fromList xsr
	val cols = Array.length row1
	val vec = 
	    if List.all (fn r => Array.length r = cols) rowr then
		Vector.fromList (row1 :: rowr)
	    else
		raise Size
    in 
	ref (vec, Vector.length vec, cols)
    end

fun array (m, n, x) = 
    ref (Vector.tabulate(m, fn _ => Array.array(n, x)), m, n);

fun tabulate RowMajor (m, n, f) = 
    ref (Vector.tabulate(m, fn i => Array.tabulate(n, fn j => f(i,j))), m, n)
  | tabulate ColMajor (m, n, f) = 
    if m > 0 andalso n > 0 then 
	let val f00 = f(0, 0)
	    val arr = Vector.tabulate(m, fn i => Array.array(n, f00))
	    (* Column 0: do not apply f to (0,0) again: *)
	    val _ = VectorSlice.appi (fn (r, a) => Array.update(a, 0, f(r, 0))) 
		                (VectorSlice.slice(arr, 1, NONE));
	    (* Remaining columns: loop, updating all rows: *)
	    fun loop c = 
		if c < n then  
		    (Vector.appi (fn (r, a) => Array.update(a, c, f(r, c))) 
		                 arr;
		     loop (c+1))
		else ()
	in loop 1; ref (arr, m, n) end
    else
	tabulate RowMajor (m, n, f)

fun dimensions (ref (a,m,n)) = (m,n);

fun nRows (ref (a,m,n)) = m;

fun nCols (ref (a,m,n)) = n;

fun sub(ref (a,m,n), i, j) = Array.sub(Vector.sub(a, i), j);

fun update(ref (a,m,n), i, j, x) = Array.update(Vector.sub(a, i), j, x);

fun row (ref (a, _, _), i) = 
    Array.vector(Vector.sub(a, i));

fun column (ref (a, m, n), j) = 
    if j<0 orelse j>=n then raise Subscript
    else Vector.tabulate(m, fn k => Array.sub(Vector.sub(a, k), j))

fun fold RowMajor f b (ref (a, _, _)) = 
    Vector.foldl (fn (xs, res) => Array.foldl f res xs) b a
  | fold ColMajor f b (ref (a, m, n)) = 
    let fun rows j i b = 
	    if i >= m then b 
	    else rows j (i+1) (f(Array.sub(Vector.sub(a, i), j), b))
	fun cols j b =
	    if j >= n then b 
	    else cols (j+1) (rows j 0 b)
    in cols 0 b end

fun stop len i NONE = 
    if i<0 orelse i>len then raise Subscript
    else len
  | stop len i (SOME n) = 
    if i<0 orelse n<0 orelse i+n>len then raise Subscript
    else i+n;

fun foldi RowMajor f b { base = ref (a, m, n), row, col, nrows, ncols } = 
    VectorSlice.foldli 
           (fn (i, xs, res) => 
	       ArraySlice.foldli 
	       (fn (j,x,res) => f(i,j,x,res)) res (ArraySlice.slice(xs,col,ncols)))
	   b (VectorSlice.slice(a, row, nrows))
  | foldi ColMajor f b { base = ref (a, m, n), row, col, nrows, ncols } = 
    let val stoprow = stop m row nrows
	val stopcol = stop n col ncols
	fun rows j i b = 
	    if i >= stoprow then b 
	    else rows j (i+1) (f(i, j, Array.sub(Vector.sub(a, i), j), b))
	fun cols j b =
	    if j >= stopcol then b 
	    else cols (j+1) (rows j row b)
    in cols col b end

fun app RowMajor f (ref (a, _, _)) = 
    Vector.app (Array.app f) a
  | app ColMajor f arr  =
    fold ColMajor (fn (a, _) => f a) () arr

fun appi RowMajor f { base = ref (a, _, _), row, col, nrows, ncols } =
    VectorSlice.appi 
          (fn (i, xs) => ArraySlice.appi 
	   (fn (j, x) => f(i, j, x)) (ArraySlice.slice(xs, col, ncols)))
	  (VectorSlice.slice(a, row, nrows))
  | appi ColMajor f reg =
    foldi ColMajor (fn (i, j, a, _) => f (i, j, a)) () reg

fun modify RowMajor f (ref (a, _, _)) = 
    Vector.app (Array.modify f) a
  | modify ColMajor f arr =
    foldi ColMajor (fn (i, j, a, _) => update(arr, i, j, f a)) ()
          {base=arr, row=0, col=0, nrows=NONE, ncols=NONE}

fun modifyi RowMajor f { base = ref (a, _, _), row, col, nrows, ncols } =
    VectorSlice.appi 
          (fn (i, xs) => ArraySlice.modifyi (fn (j, x) => f(i, j, x)) 
	                               (ArraySlice.slice(xs, col, ncols)))
	  (VectorSlice.slice(a, row, nrows))
  | modifyi ColMajor f (reg as {base, ...}) =
    foldi ColMajor (fn (i, j, a, _) => update(base, i, j, f (i, j, a))) () reg

fun copy { src = { base = ref (sa, sm, sn), row = src_row, col = src_col, 
		   nrows, ncols }, 
	   dst = ref (da, dm, dn), dst_row, dst_col } =
    let val stoprow = stop sm src_row nrows
	fun bottomUp from_row to_row = 
	    if from_row < src_row then ()
	    else
		(ArraySlice.copy { src = ArraySlice.slice
				    (Vector.sub(sa, from_row), src_col, ncols),
				   dst = Vector.sub(da, to_row),
				   di = dst_col };
		 bottomUp (from_row-1) (to_row-1))
	fun topDown from_row to_row = 
	    if from_row >= stoprow then ()
	    else
		(ArraySlice.copy { src = ArraySlice.slice
				    (Vector.sub(sa, from_row), src_col, ncols),
				   dst = Vector.sub(da, to_row),
				   di = dst_col };
		 topDown (from_row+1) (to_row+1))
    in
	if src_row <= dst_row then (* top dst overlaps with bot src; 
				      copy bottom-up *)
	    bottomUp (stoprow-1) (stoprow-1+dst_row-src_row)
	else (* bot dst overlaps with top src; copy top-down *)
	    topDown src_row dst_row
    end

	
