File: Array2.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 (277 lines) | stat: -rw-r--r-- 10,550 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
(*
    Title:      Standard Basis Library: Array2 structure.
    Author:     David Matthews
    Copyright   David Matthews 2000, 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
*)

structure Array2 : ARRAY2 =
struct
    (* There are lots of possible implementations with advantages and
       disadvantages according to the requirements.  I'm choosing a very
       simple implementation in terms of arrays of arrays. *)
    (* This is implemented as a vector of rows i.e. Vector.sub(v, 0)
       returns the first row, Vector.sub(v, 1) the second. *)
    (* It's a bit messy though.  In order for this to be an eqtype for
       any 'a it needs to be treated specially by the compiler so we
       have to inherit a type that has been created specially for the
       purpose. *)
    type 'a array = 'a Bootstrap.array
    type 'a implementation = 'a Array.array Vector.vector
    fun toArray(impl: 'a implementation): 'a array = RunCall.unsafeCast impl
    fun fromArray(a: 'a array): 'a implementation = RunCall.unsafeCast a

    type 'a region =
    {
        base : 'a array,
        row : int,
        col : int,
        nrows : int option,
        ncols : int option
    }

    datatype traversal = RowMajor | ColMajor

    fun array(r, c, init) =
        toArray(Vector.tabulate(r, fn _ => Array.array(c, init)))

    fun fromList l =
    let
        (* Check that all the lists have the same length. *)
        fun checkLen(l, NONE) = SOME(List.length l)
          | checkLen(l, SOME i) =
                if List.length l <> i
                then raise Size
                else SOME i
        val _ = List.foldl checkLen NONE l
    in
        (* Build the arrays. *)
        toArray(Vector.fromList(List.map (fn ll => Array.fromList ll) l))
    end

    fun tabulate RowMajor (r, c, f) =
        toArray(Vector.tabulate(r, fn r' => Array.tabulate(c, fn c' => f(r', c'))))
    |   tabulate ColMajor (r, c, f) =
        let
            (* First tabulate into column-major vectors. *)
            val vecs =
                Vector.tabulate(c,
                    fn c' => Vector.tabulate(r, fn r' => f(r', c')))
        in
            (* Convert this to row-major arrays. *)
            tabulate RowMajor (r, c,
                fn (r', c') => Vector.sub(Vector.sub(vecs, c'), r'))
        end

    (* Internal functions: These are used where we have already checked
       that the indexes are in range.  Actually, at the moment these
       repeat the checking anyway. *)
    fun uncheckedSub(a, i, j) = Array.sub(Vector.sub(fromArray a, i), j)
    and uncheckedUpdate(arr, i, j, a) = Array.update(Vector.sub(fromArray arr, i), j, a)

    fun sub(a, i, j) = Array.sub(Vector.sub(fromArray a, i), j)

    fun update (arr, i, j, a) = Array.update(Vector.sub(fromArray arr, i), j, a)

    fun nRows a = Vector.length(fromArray a)

    (* This next is wrong in the case where nRows = 0. It'll do
       for the moment. *)
    fun nCols a = Array.length(Vector.sub(fromArray a, 0))

    fun dimensions a = (nRows a, nCols a)

    fun row(a, i) = Array.vector(Vector.sub(fromArray a, i))

    fun column(a, j) = Vector.tabulate(nRows a, fn i => sub(a, i, j))

    (* Internal function.  Check that the region is valid and get
       the actual lengths. *)
    fun getRegion {base, row, col, nrows, ncols} =
    let
        val (lRows, lCols) = dimensions base
        val nrows' =
            case nrows of
                NONE =>
                    if row < 0 orelse row > lRows
                    then raise Subscript
                    else lRows - row
            |   SOME r =>
                    if r < 0 orelse row < 0 orelse r+row > lRows
                    then raise Subscript
                    else r
        val ncols' =
            case ncols of
                NONE =>
                    if col < 0 orelse col > lCols
                    then raise Subscript
                    else lCols - col
            |   SOME c =>
                    if c < 0 orelse col < 0 orelse c+col > lCols
                    then raise Subscript
                    else c
    in
        (nrows', ncols')
    end

    fun copy {src as {base, row, col, ...}, dst, dst_row, dst_col} =
    let
        (* Check the region and get the lengths. *)
        val (nrows, ncols) = getRegion src
        val (dRows, dCols) = dimensions dst

        fun copyIncrementing(r, c) =
            if r = nrows then ()
            else if c = ncols then copyIncrementing(r+1, 0)
            else
               (
                uncheckedUpdate(dst, dst_row+r, dst_col+c,
                    uncheckedSub(base, row+r, col+c));
                copyIncrementing(r, c+1)
               )

        fun copyDecrementing(r, c) =
            if r < 0 then ()
            else if c < 0 then copyDecrementing(r-1, ncols-1)
            else
               (
                uncheckedUpdate(dst, dst_row+r, dst_col+c,
                    uncheckedSub(base, row+r, col+c));
                copyDecrementing(r, c-1)
               )
    in
        (* Check the destination *)
        if dst_row < 0 orelse dst_col < 0 orelse
           dst_row+nrows > dRows orelse dst_col+ncols > dCols
        then raise Subscript
        else (* We have to be careful if dst = src and the regions
                overlap.  Rather than treat the overlapped case
                specially we simply choose incrementing or decrementing
                copies depending on the indexes. *)
            if dst_row < row orelse (dst_row = row andalso dst_col < col)
        then copyIncrementing(0, 0)
        else copyDecrementing(nrows-1, ncols-1)
    end

    fun appi tr f (reg as {base, row, col, ...}) =
    let
        val (nrows, ncols) = getRegion reg
        fun appRowMajor (r, c) =
            if r = nrows then ()
            else if c = ncols then appRowMajor(r+1, 0)
            else
              (
               f(r+row, c+col, uncheckedSub(base, r+row, c+col));
               appRowMajor(r, c+1)
              )
        fun appColMajor (r, c) =
            if c = ncols then ()
            else if r = nrows then appColMajor(0, c+1)
            else
              (
               f(r+row, c+col, uncheckedSub(base, r+row, c+col));
               appColMajor(r+1, c)
              )
    in
        case tr of
            RowMajor => appRowMajor(0, 0)
        |   ColMajor => appColMajor(0, 0)
    end

    fun app tr f arr =
        appi tr (f o #3) {base=arr, row=0, col=0, nrows=NONE, ncols=NONE}

    (* Just define modify in terms of app. *)
    fun modifyi tr f (reg as {base, ...}) =
        appi tr (fn(i, j, a) => uncheckedUpdate(base, i, j, f(i, j, a))) reg
    fun modify tr f arr =
        modifyi tr (f o #3) {base=arr, row=0, col=0, nrows=NONE, ncols=NONE}

    (* Fold is fairly similar to app. *)
    fun foldi tr f init (reg as {base, row, col, ...}) =
    let
        val (nrows, ncols) = getRegion reg
        fun foldRowMajor (r, c, i) =
            if r = nrows then i
            else if c = ncols then foldRowMajor(r+1, 0, i)
            else
               foldRowMajor(r, c+1,
                    f(r+row, c+col, uncheckedSub(base, r+row, c+col), i))

        fun foldColMajor (r, c, i) =
            if c = ncols then i
            else if r = nrows then foldColMajor(0, c+1, i)
            else
               foldColMajor(r+1, c,
                    f(r+row, c+col, uncheckedSub(base, r+row, c+col), i))
    in
        case tr of
            RowMajor => foldRowMajor(0, 0, init)
        |   ColMajor => foldColMajor(0, 0, init)
    end

    fun fold tr f init arr = 
        foldi tr (fn (_,_,a,b) => f (a,b)) init 
                {base=arr, row=0, col=0, nrows=NONE, ncols=NONE}

    local
        (* Install the pretty printer for arrays *)
        (* We may have to do this outside the structure if we
           have opaque signature matching. *)
        fun 'a pretty(depth: FixedInt.int)
                  (printElem: 'a * FixedInt.int -> PolyML.pretty)
                  (x: 'a array): PolyML.pretty =
            let
                open PolyML
                val (nrows, ncols) = dimensions x

                fun put_elem (w, index, l, d) =
                    if d = 0 then PrettyString "..." :: l
                    else if d < 0 then l
                    else printElem (w, d-1) ::
                            (if index <> ncols-1 then PrettyString "," :: PrettyBreak(1, 0) :: l else l)
                    
                fun putRowElements (row, col, tail, depth) =
                    if col < 0
                    then tail
                    else putRowElements(row, col-1, put_elem(sub(x, row, col), col, tail, depth), depth+1)

                (* TODO: This formats everything as a single block.  We really want
                   each row to be formatted as a block with consistent breaks. *)
                fun putRow(r, d, l) =
                    if r < 0 then l
                    else if d < 0 then putRow(r-1, d+1, l)
                    else if d = 0 then putRow(r-1, d+1, PrettyString "..." :: l)
                    else
                    let
                        val rowTail =
                            if r <> nrows-1 then PrettyString "," :: PrettyBreak(1, 0) :: l else l
                        val rowPrint =
                            PrettyString "[" :: 
                                putRowElements(r, ncols-1, PrettyString "]" :: rowTail, d - FixedInt.fromInt ncols + 1)
                    in
                        putRow(r-1, d+1, rowPrint)
                    end
            in
                PrettyBlock(3, false, [],
                    PrettyString "fromList[" ::
                    (if depth <= 0 then [PrettyString "...]"]
                     else putRow(nrows-1, depth - FixedInt.fromInt nrows + 1, [PrettyString "]"])
                    ))
            end
    in
        val () = PolyML.addPrettyPrinter pretty
    end
end;