File: BoolArray.sml

package info (click to toggle)
polyml 5.2.1-1.1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd, wheezy
  • size: 19,692 kB
  • ctags: 17,567
  • sloc: cpp: 37,221; sh: 9,591; asm: 4,120; ansic: 428; makefile: 203; ml: 191; awk: 91; sed: 10
file content (565 lines) | stat: -rw-r--r-- 17,125 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
(*
    Title:      Standard Basis Library: BoolArray and BoolVector Structures
    Author:     David Matthews
    Copyright   David Matthews 1999, 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
*)

(* G&R status: BoolVector and BoolArray done.  TODO: Add sliced versions. *)
local
	open RuntimeCalls; (* for POLY_SYS and EXC numbers *)
	open LibrarySupport

	datatype address = Address of word(* Abstract *)
	
	(* TODO: Use a single word for vectors of size <= 31 (30 on Sparc). *)
	datatype vector = Vector of int*address
	and array = Array of int*address
	
	val System_lock: address -> unit   = RunCall.run_call1 POLY_SYS_lockseg;
	val System_loadb: address*word->word = RunCall.run_call2 POLY_SYS_load_byte;
	val System_setb: address * word * word -> unit   = RunCall.run_call3 POLY_SYS_assign_byte;
	val System_move_bytes:
		address*word*address*word*word->unit = RunCall.run_call5 POLY_SYS_move_bytes

	val wordSize : word = LibrarySupport.wordSize;

	(* Casts between int and word. *)
	val intAsWord: int -> word = RunCall.unsafeCast
	and wordAsInt: word -> int = RunCall.unsafeCast

	val bitsPerWord = wordAsInt wordSize * 8

	(* Limit the size to the maximum value of Word.word.  This is actually
	   a long integer but since we always do the arithmetic as integers
	   it will be fine. *)
	val maxLen = IntInf.pow(2, Word.wordSize) - 1

	local
		val System_alloc  = RunCall.run_call3 POLY_SYS_alloc_store
		val F_mutable_bytes : int = 65;
	in
		(* All the arrays and vectors are initially created containing zeros
		   and then initialised. If the length is zero a one-word object
		   is created.  In the case of vectors this will remain all zeros
		   and will be locked so that two zero-sized vectors will be equal. *)
		fun alloc bits : address =
			let
				val words : word =
					if bits = 0
					then 0w1 (* Zero-sized objects are not allowed. *)
					else if bits < 0 orelse bits > maxLen
					then raise General.Size
					else intAsWord(Int.quot((bits + (bitsPerWord - 1)), bitsPerWord))
			in
				System_alloc(words, F_mutable_bytes, 0)
			end
	end

	val andb = Word.andb and orb = Word.orb and notb = Word.notb
	and << = Word.<< and >> = Word.>>;
	
	infix 9 sub
	infix 7 andb
	infix 6 orb
	infix 5 << >>


 	(* Create a vector/array from a list.  Used as the basis of
	   Array.fromList and Vector.fromList. *)
	fun fromList' (l : bool list) =
		let
			val length = List.length l
			(* Make a array initialised to zero. *)
			val vec = alloc length;
			
			(* Accumulate the list elements into bytes and store
			   them in the vector. *)
			fun init (byteno, acc, bit, []) =
				if bit = 0w1 then () else System_setb(vec, byteno, acc)
			  | init (byteno, acc, bit, a :: b) =
			 	let
					val byte = if a then bit orb acc else acc
				in
					if bit = 0wx80
					then
						(
						System_setb(vec, byteno, acc);
						init(byteno+0w1, 0w0, 0w1, b)
						)
					else init(byteno, byte, bit << 0w1, b)
				end
		in
			init(0w0, 0w0, 0w1, l);
			(length, vec)
		end

    fun tabulate' (length: int, f : int->bool) =
	let
		val vec =
			if length >= 0 then alloc length else raise General.Size;

		(* Accumulate the bits into bytes and store into the array. *)
		fun init i byteNo bit acc =
		if i < length
		then
		let
			val byte = if f i then bit orb acc else acc
		in
			if bit = 0wx80
			then ( System_setb(vec, byteNo, byte) ; init (i+1) (byteNo+0w1) 0w1 0w0 )
			else init (i+1) byteNo (bit << 0w1) byte
		end
		else if acc = 0w0
		then ()
		else (* Put in the last byte. *)
			System_setb(vec, byteNo, acc)
	in
		init 0 0w0 0w1 0w0;
		(length, vec)
	end

	(* Internal function which subscripts the vector assuming that
	   the index has already been checked for validity. *)
	fun uncheckedSub (v: address, i: int): bool =
		let
			val (byteOffset, bitOffset) = IntInf.quotRem(i, 8)
			val byte = System_loadb(v, intAsWord byteOffset);
			val mask = 0w1 << intAsWord bitOffset
		in
			byte andb mask <> 0w0
		end

	(* Internal function.  Checks a slice for validity and returns the
	   effective length if it is valid otherwise raises Subscript. *)
	fun vec_slice_length(Vector(len, vec): vector, i: int, NONE) =
			if i >= 0 andalso i <= len
			then len-i (* Length is rest of vector. *)
			else raise General.Subscript
	 |  vec_slice_length(Vector(len, vec): vector, i: int, SOME l) =
			if i >= 0 andalso l >= 0 andalso i+l <= len
			then l (* Length is as given. *)
			else raise General.Subscript

	(* Move a set of bits from one vector of bytes to another.  The bits
	   may not be on the same byte alignment.  Does not examine the
	   destination so if dest_off is not byte aligned any bits required in
	   the first byte must be passed in as src_in.  Returns any bits which
	   do not exactly fit into a byte.  *)
	(* TODO: This only handles the case where the source starts at the beginning
	   of the vector.  It is easy to modify it for the case where the source
	   offset is a multiple of 8 but more difficult to handle the other cases. *)
	fun move_bits(src: address, dest: address, dest_off, len, last_bits) =
	let
		val dest_byte = intAsWord(Int.quot(dest_off, 8)) (* Byte offset *)
		val dest_bit = intAsWord dest_off - dest_byte*0w8 (* Bit offset *)

		fun do_move last byte len : word =
			if len >= 8
			then let
				(* Get the next byte and shift it up *)
				val newbyte = last orb (System_loadb(src, byte) << dest_bit)
			in
				(* Store the low-order 8 bits into the destination. *)
				System_setb(dest, dest_byte+byte, newbyte);
				(* Shift the accumulator down by 8 bits and get ready for
				   the next byte. *)
				do_move (newbyte >> 0w8) (byte+0w1) (len-8)
			end
			else if len <= 0
			then last
			else (* 0 < len < 8 *)
			let
				(* Get the next byte and shift it up *)
				val nextsrc = System_loadb(src, byte);
				val newbyte: word = last orb (nextsrc << dest_bit)
				(* This assumes that any extra bits of the source are
				   zero. *)
			in
				if len + Word.toInt dest_bit >= 8
				then
					(
					(* Store the low-order 8 bits into the destination. *)
					System_setb(dest, dest_byte+byte, newbyte);
					(* Shift the accumulator down by 8 bits and get ready for
					   the next byte. *)
					do_move (newbyte >> 0w8) (byte+0w1) (len-8)
					)
				else newbyte
			end
	in
		(* TODO: If dest_bit is zero we can simply move the bytes.  If len
		   is not a multiple of 8 we may have to return the low-order bits. *)
		do_move last_bits 0w0 len
	end

in
	structure BoolVector: MONO_VECTOR =
	struct
		type vector = vector
		type elem = bool
		val maxLen = maxLen
		
		fun length(Vector(l, _)) = l
		
		fun op sub (vec as Vector(l, v), i: int): bool =
			if i < 0 orelse i >= l then raise General.Subscript
			else uncheckedSub(v, i)
	
	 	(* Create a vector from a list.  Must lock the vector before
		   returning it. *)
	    fun fromList (l : elem list) : vector =
		let
			val (length, vec) = fromList' l
		in
		    System_lock vec;
		    Vector(length, vec)
		end
	
	    fun tabulate (length: int, f : int->elem): vector =
		let
			val (length, vec) = tabulate' (length, f)
		in
		    System_lock vec;
		    Vector(length, vec)
		end
		
		fun extract(slice as (Vector(_, vec), i: int, _)) =
		let
			(* Check the slice for validity and get the length *)
			val len = vec_slice_length slice
		in
			(* TODO: We may be able to handle special cases where the
			   source and destination are aligned on the same bit offset.
			   For the moment just take the simple approach. *)
			tabulate(len, fn j => uncheckedSub(vec, j+i))
		end
	
(*		fun map f (Vector(len, vec)) =
			let
				val new_vec = alloc len (* Destination vector. *)
				fun mapbyte b i acc max =
					if i = max then acc
					else if f ((b andb i) <> 0w0)
					then mapbyte b (i<<0w1) (acc orb i) max
					else mapbyte b (i<<0w1) acc max
				fun copy b l =
					if l <= 0 then ()
					else let
						val byte = System_loadb(vec, b)
						val res =
							(* Map each byte to get the result.  Must not
							   apply the function beyond the last bit. *)
							if l >= 8 then mapbyte byte 0w1 0w0 0wx100
							else mapbyte byte 0w1 0w0 (0w1 << Word.fromInt l)
					in
						System_setb(new_vec, b, res);
						copy (b+0w1) (l-8)
					end
			in
				copy 0w0 len;
				System_lock new_vec;
				Vector(len, new_vec)
			end*)

		fun mapi f (Vector(len, vec)) =
			let
				val new_vec = alloc len (* Destination vector. *)
				fun mapbyte b i acc max l =
					if i = max then acc
					else if f (len-l, ((b andb i) <> 0w0))
					then mapbyte b (i<<0w1) (acc orb i) max (l-1)
					else mapbyte b (i<<0w1) acc max (l-1)
				fun copy b l =
					if l <= 0 then ()
					else let
						val byte = System_loadb(vec, b)
						val res =
							(* Map each byte to get the result.  Must not
							   apply the function beyond the last bit. *)
							if l >= 8 then mapbyte byte 0w1 0w0 0wx100 l
							else mapbyte byte 0w1 0w0 (0w1 << Word.fromInt l) l
					in
						System_setb(new_vec, b, res);
						copy (b+0w1) (l-8)
					end
			in
				copy 0w0 len;
				System_lock new_vec;
				Vector(len, new_vec)
			end

		(* To save duplicating almost the same code just define map in terms of mapi. *)
		fun map f v = mapi (fn (_, x) => f x) v

		(* Return a copy of the vector with a particular entry replaced *)
		fun update (v as Vector(len, _), i, c) =
			if i < 0 orelse i >= len
			then raise Subscript
			else mapi (fn (j, s) => if j = i then c else s) v
	
		fun concat l =
		let
			(* Calculate the total length *)
			fun total [] i = i
			  | total (Vector(len, _)::t) i = total t (i+len)
		
			val total_len = total l 0
		in
			let
				(* Allocate a new vector. *)
				val new_vec = alloc total_len
				(* Copy all the source vectors into the destination. *)
				fun copy_list (Vector(src_len, src_vec)::t) dest_off bits =
				  	let
						val next = move_bits(src_vec, new_vec,
											 dest_off, src_len, bits)
					in
						copy_list t (dest_off+src_len) next
					end
				 |  copy_list [] dest_off bits =
				 	(* At the end of the lists store any extra in the last byte. *)
					if bits = 0w0 then ()
					else System_setb(new_vec, intAsWord(Int.quot(dest_off, 8)), bits)
			in
				copy_list l 0 0w0;
				System_lock new_vec;
				Vector(total_len, new_vec)
			end
		end

		(* Create the other functions. *)
		structure VectorOps =
			VectorOperations(
				struct
					type vector = vector and elem = elem
					fun length(Vector(l, _)) = intAsWord l
					fun unsafeSub (Vector(_, v), i) = uncheckedSub(v, wordAsInt i)
					fun unsafeSet _ = raise Fail "Should not be called"
				end);
	
		open VectorOps;


		local
			(* Install the pretty printer for BoolVector.vector *)
			fun pretty(put: string->unit, beg: int*bool->unit,
					   brk: int*int->unit, nd: unit->unit) (depth: int) _ x =
				let
					val last = length x - 1
					fun put_elem (index, w, d) =
						if d = 0 then (put "..."; d-1)
						else if d < 0 then d-1
						else
						(
						put(if w then "true" else "false");
						if index <> last then (put ","; brk(1, 0)) else ();
						d-1
						)
				in
					beg(3, false);
					put "fromList[";
					if depth <= 0 then put "..."
					else (foldli put_elem depth x; ());
					put "]";
					nd()
				end
		in
			val () = PolyML.install_pp pretty
		end
	
	end

	structure BoolArray: MONO_ARRAY =
	struct
		type array = array
		type elem = bool
		type vector = vector
		val maxLen = maxLen;

	 	fun length(Array(l, _)) = l
		
		(* Internal function for updating a bit assuming the bounds
		   checks have already been done. *)
		fun uncheckedUpdate(v, i, new): unit =
		let
			val (byteOffset, bitOffset) = IntInf.quotRem(i, 8)
			val byteOffsetW = intAsWord byteOffset
			val byte = System_loadb(v, byteOffsetW);
			val mask = 0w1 << intAsWord bitOffset
			val newByte =
				if new then byte orb mask
				else byte andb (notb mask)
		in
			System_setb(v, byteOffsetW, newByte)
		end

		fun array (len, ini) =
		let
			(* Create the array with zeros initially. *)
			val vec = alloc len
			(* If we need to set this to true we set all the bytes to 0xff.
			   This could mean that we have bits set which don't correspond
			   to values in the range and so two arrays with the same
			   abstract values could have different representations.  That's
			   safe for arrays because equality is pointer equality but
			   wouldn't be safe for immutables because structure equality
			   could give the wrong answer. *)
			fun setTrue i b =
				if len <= i then ()
				else (System_setb(vec, b, 0wxff); setTrue (i+8) (b+0w1))
		in
			if ini then setTrue 0 0w0 else ();
			Array(len, vec)
		end
	
		fun op sub (Array(l, v), i: int): elem =
			if i < 0 orelse i >= l then raise General.Subscript
			else uncheckedSub(v, i)

		(* Exported update function. *)
	    fun update (Array (l, v), i: int, new) : unit =
			if i < 0 orelse i >= l
			then raise General.Subscript
			else uncheckedUpdate(v, i, new)

	 	(* Create an array from a list. *)
	    fun fromList (l : elem list) : array = Array(fromList' l)

	    fun tabulate (length: int , f : int->elem): array =
			Array(tabulate'(length, f))

		fun vector(Array(len, vec)): vector =
			(* TODO: We may be able to handle special cases where the
			   source and destination are aligned on the same bit offset.
			   For the moment just take the simple approach. *)
			BoolVector.tabulate(len, fn j => uncheckedSub(vec, j))

		(* Copy one array into another. The arrays could be the same but in that case di must be zero. *)
		fun copy {src as Array (slen, s), dst as Array (dlen, d), di: int} =
			if di < 0 orelse di+slen > dlen
			then raise General.Subscript
			else (* TODO: Handle multiple bits where possible by using
			   move_bits or a variant. *)
			let
			fun copyBits n =
					if n >= slen then ()
					else
						(uncheckedUpdate(d, di+n, uncheckedSub(s, n));
						 copyBits(n+1))
			in
				copyBits 0
			end

(*		fun copy {src as Array (slen, s), dst as Array (dlen, d), di: int} =
			let
			in
				if di < 0 orelse di+slen > dlen
				then raise General.Subscript
				else if si < di
				then (* Moving up - Start from the end *)
				(* TODO: Handle multiple bits where possible by using
				   move_bits or a variant. *)
				let
					fun copyBits n =
						if n < 0 then ()
						else
							(uncheckedUpdate(d, di+n, uncheckedSub(s, si+n));
							 copyBits(n-1))
				in
					copyBits (slen-1)
				end
				else (* Moving down. *)
				let
					fun copyBits n =
						if n >= slice_len then ()
						else
							(uncheckedUpdate(d, di+n, uncheckedSub(s, si+n));
							 copyBits(n+1))
				in
					copyBits 0
				end
			end
*)	
		(* Copy a vector into an array. *)
		fun copyVec {src as Vector(slen, s), dst as Array (dlen, d), di: int} =
			let
				fun copyBits n =
					if n >= slen then ()
					else
						(uncheckedUpdate(d, di+n, uncheckedSub(s, n));
						 copyBits(n+1))
			in
				if di < 0 orelse di+slen > dlen
				then raise General.Subscript
				else copyBits 0
			end

		(* Create the other functions. *)
		structure VectorOps =
			VectorOperations(
				struct
					type vector = array and elem = elem
					fun length(Array(l, _)) = intAsWord l
					fun unsafeSub (Array(_, v), i) = uncheckedSub(v, wordAsInt i)
					fun unsafeSet (Array(_, v), i, new) = uncheckedUpdate(v, wordAsInt i, new)
				end);
	
		open VectorOps;
	
		local
			(* Install the pretty printer for BoolArray.array *)
			(* We may have to do this outside the structure if we
			   have opaque signature matching. *)
			fun pretty(put: string->unit, beg: int*bool->unit,
					   brk: int*int->unit, nd: unit->unit) (depth: int) _ x =
				let
					val last = length x - 1
					fun put_elem (index, w, d) =
						if d = 0 then (put "..."; d-1)
						else if d < 0 then d-1
						else
						(
						put(if w then "true" else "false");
						if index <> last then (put ","; brk(1, 0)) else ();
						d-1
						)
				in
					beg(3, false);
					put "fromList[";
					if depth <= 0 then put "..."
					else (foldli put_elem depth x; ());
					put "]";
					nd()
				end
		in
			val () = PolyML.install_pp pretty
		end
	end
end;

(* Equality for both BoolVector.vector and BoolArray.array will be
   inherited from word.  This will give pointer equality for them
   which is correct for array but not for vector.  We need to
   install a special equality function for BoolVector. *)
local
open BoolVector
in
fun it (a: vector, b: vector): bool =
	length a = length b andalso
	foldli (fn (i, v, res) => res andalso sub(b, i) = v) true a
end;
RunCall.addOverload it "=";