array.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; (make-array <initial-value> <bound1> ...)
  3. ; (array-shape <array>)
  4. ; (array-ref <array> <index1> ...)
  5. ; (array-set! <array> <value> <index1> ...)
  6. ; (make-shared-array <array> <linear-map> <bound1> ...)
  7. ; (copy-array <array>)
  8. ; (array->vector <array>)
  9. ; (array <bounds> . <elements>)
  10. ;
  11. ; All arrays are zero based.
  12. ;
  13. ; The <linear-map> argument to MAKE-SHARED-ARRAY is a linear function
  14. ; that maps indices into the shared array into a list of indices into
  15. ; the original array. The array returned by MAKE-SHARED-ARRAY shares
  16. ; storage with the original array.
  17. ;
  18. ; (array-ref (make-shared-array a f i1 i2 ... iN) j1 j2 ... jM)
  19. ; <==>
  20. ; (apply array-ref a (f j1 j2 ... jM))
  21. ;
  22. ; ARRAY->VECTOR returns a vector containing the elements of an array
  23. ; in row-major order.
  24. ; An array consists of a vector containing the bounds of the array,
  25. ; a vector containing the elements of the array, and a linear map
  26. ; expressed as a vector of coefficients and one constant.
  27. ; If the map is #(c1 c2 ... cN C0) then the index into the vector of
  28. ; elements for (array-ref a i1 i2 ... iN) is
  29. ; (+ (* i1 c1) (* i2 c2) ... (* iN cN) C0).
  30. ; Interface due to Alan Bawden (except for requiring zero-based arrays)
  31. ; Implementation by Richard Kelsey.
  32. (define-record-type array :array
  33. (really-make-array bounds map elements)
  34. array?
  35. (bounds array-bounds) ; vector of array bounds
  36. (map array-map) ; vector of coefficients + one constant
  37. (elements array-elements)) ; vector of actual elements
  38. (define-record-discloser :array
  39. (lambda (array)
  40. (cons 'array (array-shape array))))
  41. (define (array-shape array)
  42. (vector->list (array-bounds array)))
  43. ; Calculate the index into an array's element vector that corresponds to
  44. ; INDICES. MAP is the array's linear map.
  45. (define (fast-array-index indices map)
  46. (let ((size (- (vector-length map) 1)))
  47. (do ((i 0 (+ i 1))
  48. (j (vector-ref map size)
  49. (+ j (* (vector-ref indices i)
  50. (vector-ref map i)))))
  51. ((>= i size) j))))
  52. ; The same thing with bounds checking added.
  53. (define (array-index array indices)
  54. (let ((bounds (array-bounds array))
  55. (coefficients (array-map array)))
  56. (let loop ((is indices)
  57. (i 0)
  58. (index (vector-ref coefficients (vector-length bounds))))
  59. (cond ((null? is)
  60. (if (= i (vector-length bounds))
  61. index
  62. (error "wrong number of array indices" array indices)))
  63. ((>= i (vector-length bounds))
  64. (error "wrong number of array indices" array indices))
  65. (else
  66. (let ((j (car is)))
  67. (if (and (>= j 0)
  68. (< j (vector-ref bounds i)))
  69. (loop (cdr is)
  70. (+ i 1)
  71. (+ index (* j (vector-ref coefficients i))))
  72. (error "array index out of range" array indices))))))))
  73. (define (array-ref array . indices)
  74. (vector-ref (array-elements array) (array-index array indices)))
  75. (define (array-set! array value . indices)
  76. (vector-set! (array-elements array) (array-index array indices) value))
  77. ; This is mostly error checking.
  78. (define (make-array initial bound1 . bounds)
  79. (let* ((all-bounds (cons bound1 bounds))
  80. (bounds (make-vector (length all-bounds)))
  81. (size (do ((bs all-bounds (cdr bs))
  82. (i 0 (+ i 1))
  83. (s 1 (* s (car bs))))
  84. ((null? bs) s)
  85. (let ((b (car bs)))
  86. (vector-set! bounds i b)
  87. (if (not (and (integer? b)
  88. (exact? b)
  89. (< 0 b)))
  90. (error "illegal array bounds" all-bounds))))))
  91. (really-make-array bounds
  92. (bounds->map bounds)
  93. (make-vector size initial))))
  94. (define (array bounds . elts)
  95. (let* ((array (apply make-array #f bounds))
  96. (elements (array-elements array))
  97. (size (vector-length elements)))
  98. (if (not (= (length elts) size))
  99. (error "ARRAY got the wrong number of elements" bounds elts))
  100. (do ((i 0 (+ i 1))
  101. (elts elts (cdr elts)))
  102. ((null? elts))
  103. (vector-set! elements i (car elts)))
  104. array))
  105. ; Determine the linear map that corresponds to a simple array with the
  106. ; given bounds.
  107. (define (bounds->map bounds)
  108. (do ((i (- (vector-length bounds) 1) (- i 1))
  109. (s 1 (* s (vector-ref bounds i)))
  110. (l '() (cons s l)))
  111. ((< i 0)
  112. (list->vector (reverse (cons 0 (reverse l)))))))
  113. ; This is mostly error checking. Two different procedures are used to
  114. ; check that the shared array does not extend past the original. The
  115. ; full check does a complete check, but, because it must check every corner
  116. ; of the shared array, it gets very slow as the number of dimensions
  117. ; goes up. The simple check just verifies that the all elements of
  118. ; the shared array map to elements in the vector of the original.
  119. (define (make-shared-array array linear-map . bounds)
  120. (let ((map (make-shared-array-map array linear-map bounds)))
  121. (if (if (<= (length bounds) maximum-full-bounds-check)
  122. (full-array-bounds-okay? linear-map bounds (array-bounds array))
  123. (simple-array-bounds-okay? map bounds (vector-length
  124. (array-elements array))))
  125. (really-make-array (list->vector bounds)
  126. map
  127. (array-elements array))
  128. (error "shared array out of bounds" array linear-map bounds))))
  129. (define maximum-full-bounds-check 5)
  130. ; Check that every corner of the array specified by LINEAR and NEW-BOUNDS
  131. ; is within OLD-BOUNDS. This checks every corner of the new array.
  132. (define (full-array-bounds-okay? linear new-bounds old-bounds)
  133. (let ((old-bounds (vector->list old-bounds)))
  134. (let label ((bounds (reverse new-bounds)) (args '()))
  135. (if (null? bounds)
  136. (let loop ((res (apply linear args)) (bounds old-bounds))
  137. (cond ((null? res)
  138. (null? bounds))
  139. ((and (not (null? bounds))
  140. (<= 0 (car res))
  141. (< (car res) (car bounds)))
  142. (loop (cdr res) (cdr bounds)))
  143. (else #f)))
  144. (and (label (cdr bounds) (cons 0 args))
  145. (label (cdr bounds) (cons (- (car bounds) 1) args)))))))
  146. ; Check that the maximum and minimum possible vector indices possible with
  147. ; the given bounds and map would fit in an array of the given size.
  148. (define (simple-array-bounds-okay? map bounds size)
  149. (do ((map (vector->list map) (cdr map))
  150. (bounds bounds (cdr bounds))
  151. (min 0 (if (> 0 (car map))
  152. (+ min (* (car map) (- (car bounds) 1)))
  153. min))
  154. (max 0 (if (< 0 (car map))
  155. (+ max (* (car map) (- (car bounds) 1)))
  156. max)))
  157. ((null? bounds)
  158. (and (>= 0 (+ min (car map)))
  159. (< size (+ max (car map)))))))
  160. ; Determine the coefficients and constant of the composition of
  161. ; LINEAR-MAP and the linear map of ARRAY. BOUNDS is used only to
  162. ; determine the rank of LINEAR-MAP's domain.
  163. ;
  164. ; The coefficients are determined by applying first LINEAR-MAP and then
  165. ; ARRAY's map to the vectors (1 0 0 ... 0), (0 1 0 ... 0), ..., (0 ... 0 1).
  166. ; Applying them to (0 ... 0) gives the constant of the composition.
  167. (define (make-shared-array-map array linear-map bounds)
  168. (let* ((zero (map (lambda (ignore) 0) bounds))
  169. (do-vector (lambda (v)
  170. (or (apply-map array (apply linear-map v))
  171. (error "bad linear map for shared array"
  172. linear-map array bounds))))
  173. (base (do-vector zero)))
  174. (let loop ((bs bounds) (ces '()) (unit (cons 1 (cdr zero))))
  175. (if (null? bs)
  176. (list->vector (reverse (cons base ces)))
  177. (loop (cdr bs)
  178. (cons (- (do-vector unit) base) ces)
  179. (rotate unit))))))
  180. ; Apply ARRAY's linear map to the indices in the list VALUES and
  181. ; return the resulting vector index. #F is returned if VALUES is not
  182. ; the correct length or if any of its elements are out of range.
  183. (define (apply-map array values)
  184. (let ((map (array-map array))
  185. (bounds (array-bounds array)))
  186. (let loop ((values values)
  187. (i 0)
  188. (index (vector-ref map (vector-length bounds))))
  189. (cond ((null? values)
  190. (if (= i (vector-length bounds))
  191. index
  192. #f))
  193. ((>= i (vector-length bounds))
  194. #f)
  195. (else
  196. (let ((j (car values)))
  197. (if (and (>= j 0)
  198. (< j (vector-ref bounds i)))
  199. (loop (cdr values)
  200. (+ i 1)
  201. (+ index (* j (vector-ref map i))))
  202. #f)))))))
  203. ; Return LIST with its last element moved to the front.
  204. (define (rotate list)
  205. (let ((l (reverse list)))
  206. (cons (car l) (reverse (cdr l)))))
  207. ; Copy an array, shrinking the vector if this is subarray that does not
  208. ; use all of the original array's elements.
  209. (define (copy-array array)
  210. (really-make-array (array-bounds array)
  211. (bounds->map (array-bounds array))
  212. (array->vector array)))
  213. ; Make a new vector and copy the elements into it. If ARRAY's map is
  214. ; the simple map for its bounds, then the elements are already in the
  215. ; appropriate order and we can just copy the element vector.
  216. (define (array->vector array)
  217. (let* ((size (array-element-count array))
  218. (new (make-vector size)))
  219. (if (and (= size (vector-length (array-elements array)))
  220. (equal? (array-map array) (bounds->map (array-bounds array))))
  221. (copy-vector (array-elements array) new)
  222. (copy-elements array new))
  223. new))
  224. (define (array-element-count array)
  225. (let ((bounds (array-bounds array)))
  226. (do ((i 0 (+ i 1))
  227. (s 1 (* s (vector-ref bounds i))))
  228. ((>= i (vector-length bounds))
  229. s))))
  230. (define (copy-vector from to)
  231. (do ((i (- (vector-length to) 1) (- i 1)))
  232. ((< i 0))
  233. (vector-set! to i (vector-ref from i))))
  234. ; Copy the elements of ARRAY into the vector TO. The copying is done one
  235. ; row at a time. POSN is a vector containing the index of the row that
  236. ; we are currently copying. After the row is copied, POSN is updated so
  237. ; that the next row can be copied. A little more cleverness would make
  238. ; this faster by replacing the call to FAST-ARRAY-INDEX with some simple
  239. ; arithmetic on J.
  240. (define (copy-elements array to)
  241. (let ((bounds (array-bounds array))
  242. (elements (array-elements array))
  243. (map (array-map array)))
  244. (let* ((size (vector-length bounds))
  245. (posn (make-vector size 0))
  246. (step-size (vector-ref bounds (- size 1)))
  247. (delta (vector-ref map (- size 1))))
  248. (let loop ((i 0))
  249. (do ((i2 i (+ i2 1))
  250. (j (fast-array-index posn map) (+ j delta)))
  251. ((>= i2 (+ i step-size)))
  252. (vector-set! to i2 (vector-ref elements j)))
  253. (cond ((< (+ i step-size) (vector-length to))
  254. (let loop2 ((k (- size 2)))
  255. (cond ((= (+ (vector-ref posn k) 1) (vector-ref bounds k))
  256. (vector-set! posn k 0)
  257. (loop2 (- k 1)))
  258. (else
  259. (vector-set! posn k (+ 1 (vector-ref posn k))))))
  260. (loop (+ i step-size))))))))
  261. ; Testing.
  262. ; (define a1 (make-array 0 4 5))
  263. ; 0 1 2 3
  264. ; 4 5 6 7
  265. ; 8 9 10 11
  266. ; 12 13 14 15
  267. ; 16 17 18 19
  268. ; (make-shared-array-map a1 (lambda (x) (list x x)) '(3))
  269. ; 0 5 10, #(5 0)
  270. ; (make-shared-array-map a1 (lambda (x) (list 2 (- 4 x))) '(3))
  271. ; 18 14 10 #(-4 18)
  272. ; (make-shared-array-map a1 (lambda (x y) (list (+ x 1) y)) '(2 4))
  273. ; 1 2
  274. ; 5 6
  275. ; 9 10
  276. ; 13 14
  277. ; #(1 4 1)