File: BoolArray.sml

package info (click to toggle)
polyml 5.7.1-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 40,616 kB
  • sloc: cpp: 44,142; ansic: 26,963; sh: 22,002; asm: 13,486; makefile: 602; exp: 525; python: 253; awk: 91
file content (517 lines) | stat: -rw-r--r-- 20,087 bytes parent folder | download | duplicates (4)
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
(*
    Title:      Standard Basis Library: BoolArray and BoolVector Structures
    Author:     David Matthews
    Copyright   David Matthews 1999, 2005, 2016

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.
    
    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
*)
local
    open LibrarySupport

    (* TODO: Use a single word for vectors of size <= number of bits in a word. *)
    (* We use int here for the length rather than word because the number of bits
       could be more than the maximum value of Word.word. *)
    datatype vector = Vector of int * Bootstrap.byteVector (* This has a byte-wise equality. *)
    and array = Array of int * Bootstrap.byteArray (* This has pointer equality. *)

    val wordSize : word = LibrarySupport.wordSize

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

    val bitsPerWord = wordSize * 0w8

    (* Limit the size to Array.maxLen to avoid arithmetic overflow. *)
    val maxLen = Array.maxLen

    local
        val F_mutable_bytes = 0wx41
    in
        (* Allocate memory for a vector or an array. *)
        fun alloc (bits: int) =
        let
            val words : word =
                if bits < 0 orelse bits > maxLen
                then raise General.Size
                else (Word.fromInt bits + (bitsPerWord - 0w1)) div bitsPerWord
            val vec = RunCall.allocateByteMemory(words, F_mutable_bytes)
            val bytes = words * wordSize
            fun fill n =
                if n = bytes
                then ()
                else (RunCall.storeByte(vec, n, 0w0); fill(n+0w1))
            (* We will only set the bits that we actually use.  Unused bytes will be uninitialised.
               The equality function we're using tests all the bytes so we need to initialise them. *)
        in
            if bytes = 0w0 then () else fill(bytes - wordSize);
            vec
        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 RunCall.storeByte(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
                    (
                        RunCall.storeByte(vec, byteno, byte);
                        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 ( RunCall.storeByte(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. *)
            RunCall.storeByte(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, i: int): bool =
        let
            val iW = Word.fromInt i
            val byte = RunCall.loadByte(v, iW >> 0w3)
            val mask = 0w1 << (iW andb 0w7)
        in
            byte andb mask <> 0w0
        end

    (* 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: Bootstrap.byteVector, dest: Bootstrap.byteVector, 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 (RunCall.loadByteFromImmutable(src, byte) << dest_bit)
            in
                (* Store the low-order 8 bits into the destination. *)
                RunCall.storeByte(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 = RunCall.loadByteFromImmutable(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. *)
                    RunCall.storeByte(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 (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
            RunCall.clearMutableBit vec;
            Vector(length, vec)
        end
    
        fun tabulate (length: int, f : int->elem): vector =
        let
            val (length, vec) = tabulate' (length, f)
        in
            RunCall.clearMutableBit vec;
            Vector(length, vec)
        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
                        RunCall.storeByte(new_vec, b, res);
                        copy (b+0w1) (l-8)
                    end
            in
                copy 0w0 len;
                RunCall.clearMutableBit 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 = RunCall.loadByteFromImmutable(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
                        RunCall.storeByte(new_vec, b, res);
                        copy (b+0w1) (l-8)
                    end
            in
                copy 0w0 len;
                RunCall.clearMutableBit 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 RunCall.storeByte(new_vec, intAsWord(Int.quot(dest_off, 8)), bits)
            in
                copy_list l 0 0w0;
                RunCall.clearMutableBit 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(depth: FixedInt.int) _ (x: vector) =
                let
                    open PolyML
                    val last = length x - 1
                    fun put_elem (index, w, (l, d)) =
                        if d = 0 then ([PrettyString "...]"], d+1)
                        else if d < 0 then ([], d+1)
                        else
                        (
                        PrettyString(if w then "true" else "false") ::
                            (if index <> last then PrettyString "," :: PrettyBreak(1, 0) :: l else l),
                        d+1
                        )
                in
                    PrettyBlock(3, false, [],
                        PrettyString "fromList[" ::
                        (if depth <= 0 then [PrettyString "...]"]
                         else #1 (foldri put_elem ([PrettyString "]"], depth - FixedInt.fromInt last) x) )
                   )
                end
        in
            val () = PolyML.addPrettyPrinter 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 iW = Word.fromInt i
            val byteOffsetW = iW >> 0w3
            val byte = RunCall.loadByte(v, byteOffsetW);
            val mask = 0w1 << (iW andb 0w7)
            val newByte =
                if new then byte orb mask
                else byte andb (notb mask)
        in
            RunCall.storeByte(v, byteOffsetW, newByte)
        end

        fun array (len, ini) =
        let
            (* Create the uninitialised array. *)
            val vec = alloc len
            (* Set the bytes to all zeros or all ones.  Generally this will set
               more bits than we need but that doesn't matter. *)
            val initByte = if ini then 0wxff else 0wx00
            val bytes = (Word.fromInt len + 0w7) >> 0w3
            (* TODO: This should be set by a built-in. *)
            fun setBytes b =
                if b >= bytes then ()
                else (RunCall.storeByte(vec, b, initByte); setBytes (b+0w1))
            val () = setBytes 0w0
        in
            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=Array (slen, s), dst=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=Vector(slen, s), dst=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(depth: FixedInt.int) _ (x: array) =
                let
                    open PolyML
                    val last = length x - 1
                    fun put_elem (index, w, (l, d)) =
                        if d = 0 then ([PrettyString "...]"], d+1)
                        else if d < 0 then ([], d+1)
                        else
                        (
                        PrettyString(if w then "true" else "false") ::
                            (if index <> last then PrettyString "," :: PrettyBreak(1, 0) :: l else l),
                        d+1
                        )
                in
                    PrettyBlock(3, false, [],
                        PrettyString "fromList[" ::
                        (if depth <= 0 then [PrettyString "...]"]
                         else #1 (foldri put_elem ([PrettyString "]"], depth - FixedInt.fromInt last) x) )
                   )
                end
        in
            val () = PolyML.addPrettyPrinter pretty
        end
    end
end;