/usr/share/scheme48-1.9/big/array.scm is in scheme48 1.9-5.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 | ; Part of Scheme 48 1.9. See file COPYING for notices and license.
; Authors: Richard Kelsey, Jonathan Rees, Robert Ransom
; (make-array <initial-value> <bound1> ...)
; (array-shape <array>)
; (array-ref <array> <index1> ...)
; (array-set! <array> <value> <index1> ...)
; (make-shared-array <array> <linear-map> <bound1> ...)
; (copy-array <array>)
; (array->vector <array>)
; (array <bounds> . <elements>)
;
; All arrays are zero-based.
;
; The <linear-map> argument to MAKE-SHARED-ARRAY is a linear function
; that maps indices into the shared array into a list of indices into
; the original array. The array returned by MAKE-SHARED-ARRAY shares
; storage with the original array.
;
; (array-ref (make-shared-array a f i1 i2 ... iN) j1 j2 ... jM)
; <==>
; (apply array-ref a (f j1 j2 ... jM))
;
; ARRAY->VECTOR returns a vector containing the elements of an array
; in row-major order.
; An array consists of a vector containing the bounds of the array,
; a vector containing the elements of the array, and a linear map
; expressed as a vector of coefficients and one constant.
; If the map is #(c1 c2 ... cN C0) then the index into the vector of
; elements for (array-ref a i1 i2 ... iN) is
; (+ (* i1 c1) (* i2 c2) ... (* iN cN) C0).
; Interface due to Alan Bawden (except for requiring zero-based arrays)
; Implementation by Richard Kelsey.
(define-record-type array :array
(really-make-array bounds map elements)
array?
(bounds array-bounds) ; vector of array bounds
(map array-map) ; vector of coefficients + one constant
(elements array-elements)) ; vector of actual elements
(define-record-discloser :array
(lambda (array)
(cons 'array (array-shape array))))
(define (array-shape array)
(vector->list (array-bounds array)))
; Calculate the index into an array's element vector that corresponds to
; INDICES. MAP is the array's linear map.
(define (fast-array-index indices map)
(let ((size (- (vector-length map) 1)))
(do ((i 0 (+ i 1))
(j (vector-ref map size)
(+ j (* (vector-ref indices i)
(vector-ref map i)))))
((>= i size) j))))
; The same thing with bounds checking added.
(define (array-index array indices)
(let ((bounds (array-bounds array))
(coefficients (array-map array)))
(let loop ((is indices)
(i 0)
(index (vector-ref coefficients (vector-length bounds))))
(cond ((null? is)
(if (= i (vector-length bounds))
index
(error "wrong number of array indices" array indices)))
((>= i (vector-length bounds))
(error "wrong number of array indices" array indices))
(else
(let ((j (car is)))
(if (and (>= j 0)
(< j (vector-ref bounds i)))
(loop (cdr is)
(+ i 1)
(+ index (* j (vector-ref coefficients i))))
(error "array index out of range" array indices))))))))
(define (array-ref array . indices)
(vector-ref (array-elements array) (array-index array indices)))
(define (array-set! array value . indices)
(vector-set! (array-elements array) (array-index array indices) value))
; This is mostly error checking.
(define (make-array initial bound1 . bounds)
(let* ((all-bounds (cons bound1 bounds))
(bounds (make-vector (length all-bounds)))
(size (do ((bs all-bounds (cdr bs))
(i 0 (+ i 1))
(s 1 (* s (car bs))))
((null? bs) s)
(let ((b (car bs)))
(vector-set! bounds i b)
(if (not (and (integer? b)
(exact? b)
(< 0 b)))
(error "illegal array bounds" all-bounds))))))
(really-make-array bounds
(bounds->map bounds)
(make-vector size initial))))
(define (array bounds . elts)
(let* ((array (apply make-array #f bounds))
(elements (array-elements array))
(size (vector-length elements)))
(if (not (= (length elts) size))
(error "ARRAY got the wrong number of elements" bounds elts))
(do ((i 0 (+ i 1))
(elts elts (cdr elts)))
((null? elts))
(vector-set! elements i (car elts)))
array))
; Determine the linear map that corresponds to a simple array with the
; given bounds.
(define (bounds->map bounds)
(do ((i (- (vector-length bounds) 1) (- i 1))
(s 1 (* s (vector-ref bounds i)))
(l '() (cons s l)))
((< i 0)
(list->vector (reverse (cons 0 (reverse l)))))))
; This is mostly error checking. Two different procedures are used to
; check that the shared array does not extend past the original. The
; full check does a complete check, but, because it must check every corner
; of the shared array, it gets very slow as the number of dimensions
; goes up. The simple check just verifies that all elements of
; the shared array map to elements in the vector of the original.
(define (make-shared-array array linear-map . bounds)
(let ((map (make-shared-array-map array linear-map bounds)))
(if (if (<= (length bounds) maximum-full-bounds-check)
(full-array-bounds-okay? linear-map bounds (array-bounds array))
(simple-array-bounds-okay? map bounds (vector-length
(array-elements array))))
(really-make-array (list->vector bounds)
map
(array-elements array))
(error "shared array out of bounds" array linear-map bounds))))
(define maximum-full-bounds-check 5)
; Check that every corner of the array specified by LINEAR and NEW-BOUNDS
; is within OLD-BOUNDS. This checks every corner of the new array.
(define (full-array-bounds-okay? linear new-bounds old-bounds)
(let ((old-bounds (vector->list old-bounds)))
(let label ((bounds (reverse new-bounds)) (args '()))
(if (null? bounds)
(let loop ((res (apply linear args)) (bounds old-bounds))
(cond ((null? res)
(null? bounds))
((and (not (null? bounds))
(<= 0 (car res))
(< (car res) (car bounds)))
(loop (cdr res) (cdr bounds)))
(else #f)))
(and (label (cdr bounds) (cons 0 args))
(label (cdr bounds) (cons (- (car bounds) 1) args)))))))
; Check that the maximum and minimum possible vector indices possible with
; the given bounds and map would fit in an array of the given size.
(define (simple-array-bounds-okay? map bounds size)
(do ((map (vector->list map) (cdr map))
(bounds bounds (cdr bounds))
(min 0 (if (> 0 (car map))
(+ min (* (car map) (- (car bounds) 1)))
min))
(max 0 (if (< 0 (car map))
(+ max (* (car map) (- (car bounds) 1)))
max)))
((null? bounds)
(and (>= 0 (+ min (car map)))
(< size (+ max (car map)))))))
; Determine the coefficients and constant of the composition of
; LINEAR-MAP and the linear map of ARRAY. BOUNDS is used only to
; determine the rank of LINEAR-MAP's domain.
;
; The coefficients are determined by applying first LINEAR-MAP and then
; ARRAY's map to the vectors (1 0 0 ... 0), (0 1 0 ... 0), ..., (0 ... 0 1).
; Applying them to (0 ... 0) gives the constant of the composition.
(define (make-shared-array-map array linear-map bounds)
(let* ((zero (map (lambda (ignore) 0) bounds))
(do-vector (lambda (v)
(or (apply-map array (apply linear-map v))
(error "bad linear map for shared array"
linear-map array bounds))))
(base (do-vector zero)))
(let loop ((bs bounds) (ces '()) (unit (cons 1 (cdr zero))))
(if (null? bs)
(list->vector (reverse (cons base ces)))
(loop (cdr bs)
(cons (- (do-vector unit) base) ces)
(rotate unit))))))
; Apply ARRAY's linear map to the indices in the list VALUES and
; return the resulting vector index. #F is returned if VALUES is not
; the correct length or if any of its elements are out of range.
(define (apply-map array values)
(let ((map (array-map array))
(bounds (array-bounds array)))
(let loop ((values values)
(i 0)
(index (vector-ref map (vector-length bounds))))
(cond ((null? values)
(if (= i (vector-length bounds))
index
#f))
((>= i (vector-length bounds))
#f)
(else
(let ((j (car values)))
(if (and (>= j 0)
(< j (vector-ref bounds i)))
(loop (cdr values)
(+ i 1)
(+ index (* j (vector-ref map i))))
#f)))))))
; Return LIST with its last element moved to the front.
(define (rotate list)
(let ((l (reverse list)))
(cons (car l) (reverse (cdr l)))))
; Copy an array, shrinking the vector if this is a subarray that does not
; use all of the original array's elements.
(define (copy-array array)
(really-make-array (array-bounds array)
(bounds->map (array-bounds array))
(array->vector array)))
; Make a new vector and copy the elements into it. If ARRAY's map is
; the simple map for its bounds, then the elements are already in the
; appropriate order and we can just copy the element vector.
(define (array->vector array)
(let* ((size (array-element-count array))
(new (make-vector size)))
(if (and (= size (vector-length (array-elements array)))
(equal? (array-map array) (bounds->map (array-bounds array))))
(copy-vector (array-elements array) new)
(copy-elements array new))
new))
(define (array-element-count array)
(let ((bounds (array-bounds array)))
(do ((i 0 (+ i 1))
(s 1 (* s (vector-ref bounds i))))
((>= i (vector-length bounds))
s))))
(define (copy-vector from to)
(do ((i (- (vector-length to) 1) (- i 1)))
((< i 0))
(vector-set! to i (vector-ref from i))))
; Copy the elements of ARRAY into the vector TO. The copying is done one
; row at a time. POSN is a vector containing the index of the row that
; we are currently copying. After the row is copied, POSN is updated so
; that the next row can be copied. A little more cleverness would make
; this faster by replacing the call to FAST-ARRAY-INDEX with some simple
; arithmetic on J.
(define (copy-elements array to)
(let ((bounds (array-bounds array))
(elements (array-elements array))
(map (array-map array)))
(let* ((size (vector-length bounds))
(posn (make-vector size 0))
(step-size (vector-ref bounds (- size 1)))
(delta (vector-ref map (- size 1))))
(let loop ((i 0))
(do ((i2 i (+ i2 1))
(j (fast-array-index posn map) (+ j delta)))
((>= i2 (+ i step-size)))
(vector-set! to i2 (vector-ref elements j)))
(cond ((< (+ i step-size) (vector-length to))
(let loop2 ((k (- size 2)))
(cond ((= (+ (vector-ref posn k) 1) (vector-ref bounds k))
(vector-set! posn k 0)
(loop2 (- k 1)))
(else
(vector-set! posn k (+ 1 (vector-ref posn k))))))
(loop (+ i step-size))))))))
; Testing.
; (define a1 (make-array 0 4 5))
; 0 1 2 3
; 4 5 6 7
; 8 9 10 11
; 12 13 14 15
; 16 17 18 19
; (make-shared-array-map a1 (lambda (x) (list x x)) '(3))
; 0 5 10, #(5 0)
; (make-shared-array-map a1 (lambda (x) (list 2 (- 4 x))) '(3))
; 18 14 10 #(-4 18)
; (make-shared-array-map a1 (lambda (x y) (list (+ x 1) y)) '(2 4))
; 1 2
; 5 6
; 9 10
; 13 14
; #(1 4 1)
|