(* -*- mode: sml -*-
* $Id: lists.smlnj,v 1.2 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
val sub = Array.sub
val update = Array.update
fun for (start, stop, f) =
let
fun loop i =
if i > stop
then ()
else (f i; loop (i + 1))
in
loop start
end
fun failwith s = raise Fail s
structure Deque:
sig
type 'a t
exception Empty
val make: int * 'a -> 'a t
val iota: int -> int t
val is_empty: 'a t -> bool
val equal: ''a t * ''a t -> bool
val length: 'a t -> int
val nth: 'a t * int -> 'a
val push_front: 'a * 'a t -> unit
val push_back: 'a t * 'a -> unit
val take_front: 'a t -> 'a
val take_back: 'a t -> 'a
val copy: 'a t -> 'a t
val reverse: 'a t -> 'a t
end =
struct
type 'a t = {size: int ref,
first: int ref,
last: int ref,
field: 'a array ref,
fill: 'a}
local
fun make sel (d: 'a t) = sel d
in
fun fill z = make #fill z
end
local
fun make sel (d: 'a t) = !(sel d)
in
fun field z = make #field z
fun first z = make #first z
fun last z = make #last z
fun size z = make #size z
end
exception Empty
fun make (n, dummy) =
let
val n = Int.max (n, 0)
val nplus = Int.max (1, n)
in
{size = ref nplus,
first = ref (Int.quot (nplus, 2)),
last = ref (Int.quot (nplus, 2) - 1),
field = ref (Array.array (nplus, dummy)),
fill = dummy}
end
fun iota i =
let
val i = Int.max (0, i)
val iplus = Int.max (1, i)
in
{size = ref iplus,
first = ref 0,
last = ref (i - 1),
field = ref (Array.tabulate (iplus, fn n => n + 1)),
fill = i}
end
fun length buf = last buf - first buf + 1
fun is_empty buf = last buf < first buf
fun array_eq (arr1, off1, arr2, off2, i) =
let
fun loop (off1, off2, i) =
case i of
0 => true
| n =>
sub (arr1, off1) = sub (arr2, off2)
andalso loop (off1 + 1, off2 + 1, n - 1)
in loop (off1, off2, i)
end
fun equal (buf1, buf2) =
let
val len = length buf1
in
len = length buf2
andalso array_eq (field buf1, first buf1,
field buf2, first buf2,
len)
end
fun nth (buf, n) =
if n < 0 orelse n >= length buf
then failwith "nth"
else sub (field buf, first buf + n)
fun double_shift buf =
let
val new_size = size buf * 2
val len = length buf
val new_first = Int.quot (new_size - len, 2)
val new_field = Array.array (new_size, fill buf)
val _ = Array.copy {src = field buf,
si = first buf,
dst = new_field,
di = new_first,
len = SOME len}
in
#size buf := new_size;
#field buf := new_field;
#first buf := new_first;
#last buf := new_first + len - 1
end
fun push_front (elem, buf) =
let
val _ = if first buf = 0 then double_shift buf else ()
val new_first = first buf - 1
in
update (field buf, new_first, elem);
#first buf := new_first
end
fun push_back (buf, elem) =
let
val _ = if last buf = size buf - 1 then double_shift buf else ()
val new_last = last buf + 1
in
update (field buf, new_last, elem);
#last buf := new_last
end
fun take_front buf =
if is_empty buf
then raise Empty
else
let
val old_first = first buf
in
#first buf := old_first + 1;
sub (field buf, old_first)
end
fun take_back buf =
if is_empty buf
then raise Empty
else
let
val old_last = last buf
in
#last buf := old_last - 1;
sub (field buf, old_last)
end
fun copy buf =
let
val len = length buf
val new_buf = make (len, fill buf)
val _ = Array.copy {src = field buf,
si = first buf,
dst = field new_buf,
di = 0,
len = SOME len}
in
#first new_buf := 0;
#last new_buf := len - 1;
new_buf
end
fun reverse buf =
let
val len = length buf
val fst = first buf
val fld = field buf
val new_buf = make (len, fill buf)
val new_fld = field new_buf
val _ =
for (0, len - 1, fn i =>
update (new_fld, len - i - 1, sub (fld, fst + i)))
in
#first new_buf := 0;
#last new_buf := len - 1;
new_buf
end
end
open Deque
fun empty () = iota 0
val size = 10000
fun test_lists () =
let
val d1 = iota size
val d2 = copy d1
val d3 = empty ()
val _ = for (1, length d2, fn _ => push_back (d3, take_front d2))
val _ = for (1, length d3, fn _ => push_back (d2, take_back d3))
val d1 = reverse d1
val _ = if size <> nth (d1, 0) then failwith "First test failed" else ()
val _ = if length d1 <> length d2 then failwith "Second test failed" else ()
val _ = if not (equal (d1, d2)) then failwith "Third test failed" else ()
in
length d1
end
fun main (name, args) =
let
val n =
case Int.fromString (hd (args @ ["1"])) of
NONE => 1
| SOME n => n
val result = ref 0
val _ = for (1, n, fn _ => result := test_lists ())
in
print (concat [Int.toString (!result), "\n"]);
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("lists", Test.main);