1
(* Animation of sorting algorithms. *)
5
(* The status of a sorting process *)
9
| Pause of (unit -> status)
12
(* Information on a given sorting process *)
14
type graphic_context =
15
{ array : int array; (* Data to sort *)
16
x0 : int; (* X coordinate, lower left corner *)
17
y0 : int; (* Y coordinate, lower left corner *)
18
width : int; (* Width in pixels *)
19
height : int; (* Height in pixels *)
20
nelts : int; (* Number of elements in the array *)
21
maxval : int; (* Max value in the array + 1 *)
22
rad : int; (* Dimension of the rectangles *)
23
foreground : int; (* The color of the pen *)
24
background : int; (* The color of the background *)
25
mutable action : status (* What to do next *)
29
(* Array assignment and exchange with screen update *)
32
fill_rect (gc.x0 + (gc.width * i) / gc.nelts)
33
(gc.y0 + (gc.height * v) / gc.maxval)
38
set_color gc.background; draw gc i gc.array.(i);
39
set_color gc.foreground; draw gc i v;
44
let val_i = gc.array.(i) in
45
assign gc i gc.array.(j);
49
(* Construction of a graphic context *)
51
let initialize name funct array maxval x y w h fg bg =
52
let (_, label_height) = text_size name in
53
let rad = (w - 2) / (Array.length array) - 1 in
55
{ array = Array.copy array;
56
x0 = x + 1; (* Leave one pixel left for Y axis *)
57
y0 = y + 1; (* Leave one pixel below for X axis *)
58
width = w - 2; (* 1 pixel left, 1 pixel right *)
59
height = h - 1 - label_height - rad;
60
nelts = Array.length array;
65
action = Finished } in
66
moveto (gc.x0 - 1) (gc.y0 + gc.height);
67
set_color gc.background;
68
fill_rect (gc.x0) (gc.y0 + 1) (gc.width - 1) (gc.height - 1);
69
set_color gc.foreground;
70
lineto (gc.x0 - 1) (gc.y0 - 1);
71
lineto (gc.x0 + gc.width) (gc.y0 - 1);
72
moveto (gc.x0 - 1) (gc.y0 + gc.height);
74
for i = 0 to Array.length array - 1 do
77
gc.action <- funct gc;
81
(* Main animation function *)
83
let skip_key () = let _ = read_key () in ();;
85
let report_finished gc =
86
set_color gc.foreground;
87
moveto (gc.x0 + gc.width / 2) (gc.y0 + gc.height / 3);
90
let display functs nelts maxval =
91
let a = Array.make nelts 0 in
92
for i = 0 to nelts - 1 do
93
a.(i) <- Random.int maxval
95
let q = Queue.create () in
96
for i = 0 to Array.length functs - 1 do
97
let (name, funct, x, y, w, h, fg, bg) = functs.(i) in
98
Queue.add (initialize name funct a maxval x y w h fg bg) q
100
let delay_of_char c = (int_of_char c - 48) * 500 in
101
let delay = ref (delay_of_char '\080') in
104
let gc = Queue.take q in
105
begin match gc.action with
106
| Finished -> report_finished gc
109
for i = 0 to !delay do () done;
112
if key_pressed () then begin
113
match read_key () with
114
| 'q' | 'Q' -> raise Exit
115
| '0' .. '9' as c -> delay := delay_of_char c
120
| Queue.Empty -> skip_key ()
123
(* The sorting functions.
124
These functions are written in some kind of continuation-passing style
125
so that whenever they are about to perform a comparison, they stop
126
and return a function that does the remainder of the sort. *)
131
let ordered = ref true in
133
if i + 1 >= Array.length gc.array then
142
if gc.array.(i + 1) < gc.array.(i) then begin
143
exchange gc i (i + 1);
152
let insertion_sort gc =
154
if i >= Array.length gc.array then Finished else
155
let val_i = gc.array.(i) in
162
if val_i >= gc.array.(j - 1) then begin
166
assign gc j gc.array.(j - 1);
175
let selection_sort gc =
177
if i + 1 >= Array.length gc.array then Finished else
180
if j >= Array.length gc.array then begin
185
if gc.array.(j) < gc.array.(!min) then min := j;
194
let rec quick lo hi cont =
195
if lo >= hi then cont () else begin
196
let p = gc.array.(hi) in
200
quick lo (i - 1) (fun () -> quick (i + 1) hi cont)
203
if j <= lo then begin
204
if i < j then exchange gc i j;
208
if p <= gc.array.(j) then
211
if i < j then exchange gc i j;
215
if i >= hi then loop3 i j else
217
if gc.array.(i) <= p then
225
in quick 0 (Array.length gc.array - 1) (fun () -> Finished)
232
let father n = (n - 1) / 2
233
and left_son n = n + n + 1
234
and right_son n = n + n + 2 in
236
let rec from_bottom i last cont =
237
if i == 0 then cont () else begin
239
let ls = left_son j and rs = right_son j in
242
if gc.array.(j) < gc.array.(ls) then exchange gc j ls;
243
from_bottom j last cont)
246
if gc.array.(j) < gc.array.(ls) then
248
if gc.array.(rs) <= gc.array.(ls) then
252
from_bottom j last cont)
255
if gc.array.(j) < gc.array.(rs) then
257
if gc.array.(rs) <= gc.array.(ls) then
261
from_bottom j last cont)
263
from_bottom j last cont))
266
let rec from_top i last cont =
267
let ls = left_son i and rs = right_son i in
270
else if rs > last then
272
if gc.array.(ls) <= gc.array.(i) then
276
from_top ls last cont
280
if gc.array.(ls) <= gc.array.(i) then
282
if gc.array.(rs) <= gc.array.(i) then
286
if gc.array.(rs) <= gc.array.(ls) then begin
287
exchange gc i ls; from_top ls last cont
289
exchange gc i rs; from_top rs last cont
293
if gc.array.(rs) <= gc.array.(ls) then begin
294
exchange gc i ls; from_top ls last cont
296
exchange gc i rs; from_top rs last cont
299
let rec loop1 last cont =
300
if last >= Array.length gc.array
302
else from_bottom last last (fun () -> loop1 (last + 1) cont) in
304
let rec loop2 last cont =
305
if last < 0 then cont () else begin
306
exchange gc 0 (last + 1);
307
from_top 0 last (fun () -> loop2 (last - 1) cont)
310
loop1 1 (fun () -> loop2 (Array.length gc.array - 2) (fun () -> Finished))
316
let rec merge i l1 l2 cont =
321
assign gc i v2; merge (i + 1) l1 r2 cont
323
assign gc i v1; merge (i + 1) r1 l2 cont
324
| (v1 :: r1, v2 :: r2) ->
327
then begin assign gc i v1; merge (i + 1) r1 l2 cont end
328
else begin assign gc i v2; merge (i + 1) l1 r2 cont end) in
329
let rec msort start len cont =
330
if len < 2 then cont () else begin
332
msort start m (fun () ->
333
msort (start + m) (len - m) (fun () ->
335
(Array.to_list (Array.sub gc.array start m))
336
(Array.to_list (Array.sub gc.array (start + m) (len - m)))
339
msort 0 (Array.length gc.array) (fun () -> Finished)
346
moveto 0 0; draw_string "Press a key to start...";
348
while not (key_pressed ()) do incr seed done;
352
let prompt = "0: fastest ... 9: slowest, press 'q' to quit" in
353
moveto 0 0; draw_string prompt;
354
let (_, h) = text_size prompt in
355
let sx = size_x () / 2 and sy = (size_y () - h) / 3 in
356
let grey c = c + (c lsl 8) + (c lsl 16) in
358
"Bubble", bubble_sort, 0, h, sx, sy, cyan, (grey 0x70);
359
"Insertion", insertion_sort, 0, h + sy, sx, sy, blue, (grey 0xA0);
360
"Selection", selection_sort, 0, h + 2 * sy, sx, sy, green, (grey 0x80);
361
"Quicksort", quick_sort, sx, h, sx, sy, magenta, (grey 0xD0);
362
"Heapsort", heap_sort, sx, h + sy, sx, sy, black, (grey 0xE0);
363
"Mergesort", merge_sort, sx, h + 2 * sy, sx, sy, red, (grey 0xF0) |]
368
if !Sys.interactive then () else begin animate (); exit 0 end;;