array.scm 11 KB

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