(*
    Title:      Standard Basis Library: Vector and Array functor for polymorphic vectors and arrays
    Copyright   David C.J. Matthews 2005

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*)

(* This is almost identical to the VectorOperations functor but works on polymorphic vectors and arrays.
   There may be a way to combine the two. *)

functor PolyVectorOperations(
    type 'a vector
    val length: 'a vector -> word
    val unsafeSub: 'a vector * word -> 'a
    val unsafeSet: 'a vector * word * 'a -> unit (* Array only *)
):
    sig
    val appi : ((int * 'a) -> unit) -> 'a vector -> unit
    val app : ('a -> unit) -> 'a vector -> unit
    val foldli : ((int * 'a * 'b) -> 'b) -> 'b -> 'a vector -> 'b
    val foldri : ((int * 'a * 'b) -> 'b) -> 'b -> 'a vector -> 'b
    val foldl : (('a * 'b) -> 'b) -> 'b -> 'a vector -> 'b
    val foldr : (('a * 'b) -> 'b) -> 'b -> 'a vector -> 'b
    val modifyi : ((int * 'a) -> 'a) -> 'a vector -> unit (* Array only *)
    val modify : ('a -> 'a) -> 'a vector -> unit (* Array only *)
    val findi: (int * 'a -> bool) -> 'a vector -> (int * 'a) option
    val find: ('a -> bool) -> 'a vector -> 'a option
    val exists: ('a -> bool) -> 'a vector -> bool
    val all: ('a -> bool) -> 'a vector -> bool
    val collate: ('a * 'a -> order) -> 'a vector * 'a vector -> order
    end =
struct
        val wordAsInt: word -> int = RunCall.unsafeCast
        
        (* Apply a function to each element in turn *)
        fun appi f vec =
        let
            val len = length vec
            fun doapp j =
                if j >= len then ()
                else (f(wordAsInt j, unsafeSub(vec, j)); doapp(j+0w1))
        in
            doapp 0w0
        end
    
        fun app f vec =
        let     
            val len = length vec
            fun doapp j = 
                if j >= len then ()
                else (f(unsafeSub(vec, j)); doapp(j+0w1))
        in
            doapp 0w0
        end
        
        (* Fold a function over a array. *)
        (* foldl - increasing index *)
        fun foldl f init vec =
        let
            val len = length vec
            fun dofold j acc = 
                if j >= len then acc
                else dofold (j+0w1) (f (unsafeSub(vec, j), acc))
        in
            dofold 0w0 init
        end
    
        fun foldli f init vec =
        let 
            val len = length vec
            fun dofold j acc = 
                if j >= len then acc
                else dofold (j+0w1) (f (wordAsInt j, unsafeSub(vec, j), acc))
        in
            dofold 0w0 init
        end
    
        (* foldr - decreasing index *)
        fun foldr f init vec =
        let
            val len = length vec
            fun dofold j acc = 
                if j = 0w0 then acc
                else dofold (j-0w1) (f (unsafeSub(vec, j-0w1), acc))
        in
            dofold len init
        end
        
        fun foldri f init vec =
        let
            val len = length vec
            fun dofold j acc = 
                if j = 0w0 then acc
                else dofold (j-0w1) (f (wordAsInt(j-0w1), unsafeSub(vec, j-0w1), acc))
        in
            dofold len init
        end
        
        (* Apply a function to each element in turn and update the array with the
           new values. *)
        fun modifyi f vec =
        let                     
            val len = length vec
            fun doupdate j =
                if j >= len then ()
                else (unsafeSet(vec, j, f(wordAsInt j, unsafeSub(vec, j)));
                      doupdate(j+0w1))
        in
            doupdate 0w0
        end
    
        fun modify f vec =
        let
            val len = length vec
            fun doupdate j = 
                if j >= len then ()
                else (unsafeSet(vec, j, f(unsafeSub(vec, j))); doupdate(j+0w1))
        in
            doupdate 0w0
        end

        (* Find a character that matches the predicate. *)
        fun findi pred vec =
        let 
            val len = length vec
            fun dofind j = 
                if j >= len then NONE
                else
                let
                    val v = unsafeSub(vec, j)
                in
                    if pred(wordAsInt j, v)
                    then SOME (wordAsInt j, v)
                    else dofind (j+0w1)
                end
        in
            dofind 0w0
        end
        
        fun find pred vec =
        let
            val len = length vec
            fun dofind j = 
                if j >= len then NONE
                else
                let
                    val v = unsafeSub(vec, j)
                in
                    if pred v
                    then SOME v
                    else dofind (j+0w1)
                end
        in
            dofind 0w0
        end

        fun exists f arr = Option.isSome(find f arr)
        
        fun all pred arr = not (exists (not o pred) arr)

        fun collate cmp (vec1, vec2) =
        let 
            val len1 = length vec1 and len2 = length vec2
            (* Keep comparing items until either we come to the end of one of the arrays or
               we find a mismatch. *)
            fun dotest j =
                if j >= len1 then if len1 = len2 then EQUAL else (* j < len2 *) LESS
                else if j >= len2 then (* But j < len1, so a1 is longer *) GREATER
                else case cmp(unsafeSub(vec1, j), unsafeSub(vec2, j)) of
                    LESS => LESS
                |   GREATER => GREATER
                |   EQUAL => dotest (j+0w1)
        in
            dotest 0w0
        end
end;
