123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
- ; (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 the 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 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)
|