Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / libguile / arrays.c
1 /* Copyright (C) 1995,1996,1997,1998,2000,2001,2002,2003,2004, 2005, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
17 */
18
19
20 \f
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <string.h>
29
30 #include "libguile/_scm.h"
31 #include "libguile/__scm.h"
32 #include "libguile/eq.h"
33 #include "libguile/chars.h"
34 #include "libguile/eval.h"
35 #include "libguile/fports.h"
36 #include "libguile/smob.h"
37 #include "libguile/feature.h"
38 #include "libguile/root.h"
39 #include "libguile/strings.h"
40 #include "libguile/srfi-13.h"
41 #include "libguile/srfi-4.h"
42 #include "libguile/vectors.h"
43 #include "libguile/bitvectors.h"
44 #include "libguile/bytevectors.h"
45 #include "libguile/list.h"
46 #include "libguile/dynwind.h"
47 #include "libguile/read.h"
48
49 #include "libguile/validate.h"
50 #include "libguile/arrays.h"
51 #include "libguile/array-map.h"
52 #include "libguile/generalized-vectors.h"
53 #include "libguile/generalized-arrays.h"
54 #include "libguile/uniform.h"
55
56
57 scm_t_bits scm_i_tc16_array;
58 #define SCM_SET_ARRAY_CONTIGUOUS_FLAG(x) \
59 (SCM_SET_SMOB_FLAGS ((x), SCM_SMOB_FLAGS (x) | SCM_I_ARRAY_FLAG_CONTIGUOUS))
60 #define SCM_CLR_ARRAY_CONTIGUOUS_FLAG(x) \
61 (SCM_SET_SMOB_FLAGS ((x), SCM_SMOB_FLAGS (x) & ~SCM_I_ARRAY_FLAG_CONTIGUOUS))
62
63
64 SCM_DEFINE (scm_shared_array_root, "shared-array-root", 1, 0, 0,
65 (SCM ra),
66 "Return the root vector of a shared array.")
67 #define FUNC_NAME s_scm_shared_array_root
68 {
69 if (SCM_I_ARRAYP (ra))
70 return SCM_I_ARRAY_V (ra);
71 else if (scm_is_generalized_vector (ra))
72 return ra;
73 scm_wrong_type_arg_msg (FUNC_NAME, SCM_ARG1, ra, "array");
74 }
75 #undef FUNC_NAME
76
77
78 SCM_DEFINE (scm_shared_array_offset, "shared-array-offset", 1, 0, 0,
79 (SCM ra),
80 "Return the root vector index of the first element in the array.")
81 #define FUNC_NAME s_scm_shared_array_offset
82 {
83 scm_t_array_handle handle;
84 SCM res;
85
86 scm_array_get_handle (ra, &handle);
87 res = scm_from_size_t (handle.base);
88 scm_array_handle_release (&handle);
89 return res;
90 }
91 #undef FUNC_NAME
92
93
94 SCM_DEFINE (scm_shared_array_increments, "shared-array-increments", 1, 0, 0,
95 (SCM ra),
96 "For each dimension, return the distance between elements in the root vector.")
97 #define FUNC_NAME s_scm_shared_array_increments
98 {
99 scm_t_array_handle handle;
100 SCM res = SCM_EOL;
101 size_t k;
102 scm_t_array_dim *s;
103
104 scm_array_get_handle (ra, &handle);
105 k = scm_array_handle_rank (&handle);
106 s = scm_array_handle_dims (&handle);
107 while (k--)
108 res = scm_cons (scm_from_ssize_t (s[k].inc), res);
109 scm_array_handle_release (&handle);
110 return res;
111 }
112 #undef FUNC_NAME
113
114 SCM
115 scm_i_make_array (int ndim)
116 {
117 SCM ra;
118 SCM_NEWSMOB(ra, ((scm_t_bits) ndim << 17) + scm_i_tc16_array,
119 scm_gc_malloc ((sizeof (scm_i_t_array) +
120 ndim * sizeof (scm_t_array_dim)),
121 "array"));
122 SCM_I_ARRAY_V (ra) = SCM_BOOL_F;
123 return ra;
124 }
125
126 static char s_bad_spec[] = "Bad scm_array dimension";
127
128
129 /* Increments will still need to be set. */
130
131 static SCM
132 scm_i_shap2ra (SCM args)
133 {
134 scm_t_array_dim *s;
135 SCM ra, spec, sp;
136 int ndim = scm_ilength (args);
137 if (ndim < 0)
138 scm_misc_error (NULL, s_bad_spec, SCM_EOL);
139
140 ra = scm_i_make_array (ndim);
141 SCM_I_ARRAY_BASE (ra) = 0;
142 s = SCM_I_ARRAY_DIMS (ra);
143 for (; !scm_is_null (args); s++, args = SCM_CDR (args))
144 {
145 spec = SCM_CAR (args);
146 if (scm_is_integer (spec))
147 {
148 if (scm_to_long (spec) < 0)
149 scm_misc_error (NULL, s_bad_spec, SCM_EOL);
150 s->lbnd = 0;
151 s->ubnd = scm_to_long (spec) - 1;
152 s->inc = 1;
153 }
154 else
155 {
156 if (!scm_is_pair (spec) || !scm_is_integer (SCM_CAR (spec)))
157 scm_misc_error (NULL, s_bad_spec, SCM_EOL);
158 s->lbnd = scm_to_long (SCM_CAR (spec));
159 sp = SCM_CDR (spec);
160 if (!scm_is_pair (sp)
161 || !scm_is_integer (SCM_CAR (sp))
162 || !scm_is_null (SCM_CDR (sp)))
163 scm_misc_error (NULL, s_bad_spec, SCM_EOL);
164 s->ubnd = scm_to_long (SCM_CAR (sp));
165 s->inc = 1;
166 }
167 }
168 return ra;
169 }
170
171 SCM_DEFINE (scm_make_typed_array, "make-typed-array", 2, 0, 1,
172 (SCM type, SCM fill, SCM bounds),
173 "Create and return an array of type @var{type}.")
174 #define FUNC_NAME s_scm_make_typed_array
175 {
176 size_t k, rlen = 1;
177 scm_t_array_dim *s;
178 SCM ra;
179
180 ra = scm_i_shap2ra (bounds);
181 SCM_SET_ARRAY_CONTIGUOUS_FLAG (ra);
182 s = SCM_I_ARRAY_DIMS (ra);
183 k = SCM_I_ARRAY_NDIM (ra);
184
185 while (k--)
186 {
187 s[k].inc = rlen;
188 SCM_ASSERT_RANGE (1, bounds, s[k].lbnd <= s[k].ubnd + 1);
189 rlen = (s[k].ubnd - s[k].lbnd + 1) * s[k].inc;
190 }
191
192 if (scm_is_eq (fill, SCM_UNSPECIFIED))
193 fill = SCM_UNDEFINED;
194
195 SCM_I_ARRAY_V (ra) =
196 scm_make_generalized_vector (type, scm_from_size_t (rlen), fill);
197
198 if (1 == SCM_I_ARRAY_NDIM (ra) && 0 == SCM_I_ARRAY_BASE (ra))
199 if (s->ubnd < s->lbnd || (0 == s->lbnd && 1 == s->inc))
200 return SCM_I_ARRAY_V (ra);
201 return ra;
202 }
203 #undef FUNC_NAME
204
205 SCM
206 scm_from_contiguous_typed_array (SCM type, SCM bounds, const void *bytes,
207 size_t byte_len)
208 #define FUNC_NAME "scm_from_contiguous_typed_array"
209 {
210 size_t k, rlen = 1;
211 scm_t_array_dim *s;
212 SCM ra;
213 scm_t_array_handle h;
214 void *elts;
215 size_t sz;
216
217 ra = scm_i_shap2ra (bounds);
218 SCM_SET_ARRAY_CONTIGUOUS_FLAG (ra);
219 s = SCM_I_ARRAY_DIMS (ra);
220 k = SCM_I_ARRAY_NDIM (ra);
221
222 while (k--)
223 {
224 s[k].inc = rlen;
225 SCM_ASSERT_RANGE (1, bounds, s[k].lbnd <= s[k].ubnd + 1);
226 rlen = (s[k].ubnd - s[k].lbnd + 1) * s[k].inc;
227 }
228 SCM_I_ARRAY_V (ra) =
229 scm_make_generalized_vector (type, scm_from_size_t (rlen), SCM_UNDEFINED);
230
231
232 scm_array_get_handle (ra, &h);
233 elts = h.writable_elements;
234 sz = scm_array_handle_uniform_element_bit_size (&h);
235 scm_array_handle_release (&h);
236
237 if (sz >= 8 && ((sz % 8) == 0))
238 {
239 if (byte_len % (sz / 8))
240 SCM_MISC_ERROR ("byte length not a multiple of the unit size", SCM_EOL);
241 if (byte_len / (sz / 8) != rlen)
242 SCM_MISC_ERROR ("byte length and dimensions do not match", SCM_EOL);
243 }
244 else if (sz < 8)
245 {
246 /* byte_len ?= ceil (rlen * sz / 8) */
247 if (byte_len != (rlen * sz + 7) / 8)
248 SCM_MISC_ERROR ("byte length and dimensions do not match", SCM_EOL);
249 }
250 else
251 /* an internal guile error, really */
252 SCM_MISC_ERROR ("uniform elements larger than 8 bits must fill whole bytes", SCM_EOL);
253
254 memcpy (elts, bytes, byte_len);
255
256 if (1 == SCM_I_ARRAY_NDIM (ra) && 0 == SCM_I_ARRAY_BASE (ra))
257 if (s->ubnd < s->lbnd || (0 == s->lbnd && 1 == s->inc))
258 return SCM_I_ARRAY_V (ra);
259 return ra;
260 }
261 #undef FUNC_NAME
262
263 SCM
264 scm_from_contiguous_array (SCM bounds, const SCM *elts, size_t len)
265 #define FUNC_NAME "scm_from_contiguous_array"
266 {
267 size_t k, rlen = 1;
268 scm_t_array_dim *s;
269 SCM ra;
270 scm_t_array_handle h;
271
272 ra = scm_i_shap2ra (bounds);
273 SCM_SET_ARRAY_CONTIGUOUS_FLAG (ra);
274 s = SCM_I_ARRAY_DIMS (ra);
275 k = SCM_I_ARRAY_NDIM (ra);
276
277 while (k--)
278 {
279 s[k].inc = rlen;
280 SCM_ASSERT_RANGE (1, bounds, s[k].lbnd <= s[k].ubnd + 1);
281 rlen = (s[k].ubnd - s[k].lbnd + 1) * s[k].inc;
282 }
283 if (rlen != len)
284 SCM_MISC_ERROR ("element length and dimensions do not match", SCM_EOL);
285
286 SCM_I_ARRAY_V (ra) = scm_c_make_vector (rlen, SCM_UNDEFINED);
287 scm_array_get_handle (ra, &h);
288 memcpy (h.writable_elements, elts, rlen * sizeof(SCM));
289 scm_array_handle_release (&h);
290
291 if (1 == SCM_I_ARRAY_NDIM (ra) && 0 == SCM_I_ARRAY_BASE (ra))
292 if (s->ubnd < s->lbnd || (0 == s->lbnd && 1 == s->inc))
293 return SCM_I_ARRAY_V (ra);
294 return ra;
295 }
296 #undef FUNC_NAME
297
298 SCM_DEFINE (scm_make_array, "make-array", 1, 0, 1,
299 (SCM fill, SCM bounds),
300 "Create and return an array.")
301 #define FUNC_NAME s_scm_make_array
302 {
303 return scm_make_typed_array (SCM_BOOL_T, fill, bounds);
304 }
305 #undef FUNC_NAME
306
307 static void
308 scm_i_ra_set_contp (SCM ra)
309 {
310 size_t k = SCM_I_ARRAY_NDIM (ra);
311 if (k)
312 {
313 long inc = SCM_I_ARRAY_DIMS (ra)[k - 1].inc;
314 while (k--)
315 {
316 if (inc != SCM_I_ARRAY_DIMS (ra)[k].inc)
317 {
318 SCM_CLR_ARRAY_CONTIGUOUS_FLAG (ra);
319 return;
320 }
321 inc *= (SCM_I_ARRAY_DIMS (ra)[k].ubnd
322 - SCM_I_ARRAY_DIMS (ra)[k].lbnd + 1);
323 }
324 }
325 SCM_SET_ARRAY_CONTIGUOUS_FLAG (ra);
326 }
327
328
329 SCM_DEFINE (scm_make_shared_array, "make-shared-array", 2, 0, 1,
330 (SCM oldra, SCM mapfunc, SCM dims),
331 "@code{make-shared-array} can be used to create shared subarrays of other\n"
332 "arrays. The @var{mapper} is a function that translates coordinates in\n"
333 "the new array into coordinates in the old array. A @var{mapper} must be\n"
334 "linear, and its range must stay within the bounds of the old array, but\n"
335 "it can be otherwise arbitrary. A simple example:\n"
336 "@lisp\n"
337 "(define fred (make-array #f 8 8))\n"
338 "(define freds-diagonal\n"
339 " (make-shared-array fred (lambda (i) (list i i)) 8))\n"
340 "(array-set! freds-diagonal 'foo 3)\n"
341 "(array-ref fred 3 3) @result{} foo\n"
342 "(define freds-center\n"
343 " (make-shared-array fred (lambda (i j) (list (+ 3 i) (+ 3 j))) 2 2))\n"
344 "(array-ref freds-center 0 0) @result{} foo\n"
345 "@end lisp")
346 #define FUNC_NAME s_scm_make_shared_array
347 {
348 scm_t_array_handle old_handle;
349 SCM ra;
350 SCM inds, indptr;
351 SCM imap;
352 size_t k;
353 ssize_t i;
354 long old_base, old_min, new_min, old_max, new_max;
355 scm_t_array_dim *s;
356
357 SCM_VALIDATE_REST_ARGUMENT (dims);
358 SCM_VALIDATE_PROC (2, mapfunc);
359 ra = scm_i_shap2ra (dims);
360
361 scm_array_get_handle (oldra, &old_handle);
362
363 if (SCM_I_ARRAYP (oldra))
364 {
365 SCM_I_ARRAY_V (ra) = SCM_I_ARRAY_V (oldra);
366 old_base = old_min = old_max = SCM_I_ARRAY_BASE (oldra);
367 s = scm_array_handle_dims (&old_handle);
368 k = scm_array_handle_rank (&old_handle);
369 while (k--)
370 {
371 if (s[k].inc > 0)
372 old_max += (s[k].ubnd - s[k].lbnd) * s[k].inc;
373 else
374 old_min += (s[k].ubnd - s[k].lbnd) * s[k].inc;
375 }
376 }
377 else
378 {
379 SCM_I_ARRAY_V (ra) = oldra;
380 old_base = old_min = 0;
381 old_max = scm_c_generalized_vector_length (oldra) - 1;
382 }
383
384 inds = SCM_EOL;
385 s = SCM_I_ARRAY_DIMS (ra);
386 for (k = 0; k < SCM_I_ARRAY_NDIM (ra); k++)
387 {
388 inds = scm_cons (scm_from_long (s[k].lbnd), inds);
389 if (s[k].ubnd < s[k].lbnd)
390 {
391 if (1 == SCM_I_ARRAY_NDIM (ra))
392 ra = scm_make_generalized_vector (scm_array_type (ra),
393 SCM_INUM0, SCM_UNDEFINED);
394 else
395 SCM_I_ARRAY_V (ra) =
396 scm_make_generalized_vector (scm_array_type (ra),
397 SCM_INUM0, SCM_UNDEFINED);
398 scm_array_handle_release (&old_handle);
399 return ra;
400 }
401 }
402
403 imap = scm_apply_0 (mapfunc, scm_reverse (inds));
404 i = scm_array_handle_pos (&old_handle, imap);
405 SCM_I_ARRAY_BASE (ra) = new_min = new_max = i + old_base;
406 indptr = inds;
407 k = SCM_I_ARRAY_NDIM (ra);
408 while (k--)
409 {
410 if (s[k].ubnd > s[k].lbnd)
411 {
412 SCM_SETCAR (indptr, scm_sum (SCM_CAR (indptr), scm_from_int (1)));
413 imap = scm_apply_0 (mapfunc, scm_reverse (inds));
414 s[k].inc = scm_array_handle_pos (&old_handle, imap) - i;
415 i += s[k].inc;
416 if (s[k].inc > 0)
417 new_max += (s[k].ubnd - s[k].lbnd) * s[k].inc;
418 else
419 new_min += (s[k].ubnd - s[k].lbnd) * s[k].inc;
420 }
421 else
422 s[k].inc = new_max - new_min + 1; /* contiguous by default */
423 indptr = SCM_CDR (indptr);
424 }
425
426 scm_array_handle_release (&old_handle);
427
428 if (old_min > new_min || old_max < new_max)
429 SCM_MISC_ERROR ("mapping out of range", SCM_EOL);
430 if (1 == SCM_I_ARRAY_NDIM (ra) && 0 == SCM_I_ARRAY_BASE (ra))
431 {
432 SCM v = SCM_I_ARRAY_V (ra);
433 size_t length = scm_c_generalized_vector_length (v);
434 if (1 == s->inc && 0 == s->lbnd && length == 1 + s->ubnd)
435 return v;
436 if (s->ubnd < s->lbnd)
437 return scm_make_generalized_vector (scm_array_type (ra), SCM_INUM0,
438 SCM_UNDEFINED);
439 }
440 scm_i_ra_set_contp (ra);
441 return ra;
442 }
443 #undef FUNC_NAME
444
445
446 /* args are RA . DIMS */
447 SCM_DEFINE (scm_transpose_array, "transpose-array", 1, 0, 1,
448 (SCM ra, SCM args),
449 "Return an array sharing contents with @var{array}, but with\n"
450 "dimensions arranged in a different order. There must be one\n"
451 "@var{dim} argument for each dimension of @var{array}.\n"
452 "@var{dim0}, @var{dim1}, @dots{} should be integers between 0\n"
453 "and the rank of the array to be returned. Each integer in that\n"
454 "range must appear at least once in the argument list.\n"
455 "\n"
456 "The values of @var{dim0}, @var{dim1}, @dots{} correspond to\n"
457 "dimensions in the array to be returned, their positions in the\n"
458 "argument list to dimensions of @var{array}. Several @var{dim}s\n"
459 "may have the same value, in which case the returned array will\n"
460 "have smaller rank than @var{array}.\n"
461 "\n"
462 "@lisp\n"
463 "(transpose-array '#2((a b) (c d)) 1 0) @result{} #2((a c) (b d))\n"
464 "(transpose-array '#2((a b) (c d)) 0 0) @result{} #1(a d)\n"
465 "(transpose-array '#3(((a b c) (d e f)) ((1 2 3) (4 5 6))) 1 1 0) @result{}\n"
466 " #2((a 4) (b 5) (c 6))\n"
467 "@end lisp")
468 #define FUNC_NAME s_scm_transpose_array
469 {
470 SCM res, vargs;
471 scm_t_array_dim *s, *r;
472 int ndim, i, k;
473
474 SCM_VALIDATE_REST_ARGUMENT (args);
475 SCM_ASSERT (SCM_HEAP_OBJECT_P (ra), ra, SCM_ARG1, FUNC_NAME);
476
477 if (scm_is_generalized_vector (ra))
478 {
479 /* Make sure that we are called with a single zero as
480 arguments.
481 */
482 if (scm_is_null (args) || !scm_is_null (SCM_CDR (args)))
483 SCM_WRONG_NUM_ARGS ();
484 SCM_VALIDATE_INT_COPY (SCM_ARG2, SCM_CAR (args), i);
485 SCM_ASSERT_RANGE (SCM_ARG2, SCM_CAR (args), i == 0);
486 return ra;
487 }
488
489 if (SCM_I_ARRAYP (ra))
490 {
491 vargs = scm_vector (args);
492 if (SCM_SIMPLE_VECTOR_LENGTH (vargs) != SCM_I_ARRAY_NDIM (ra))
493 SCM_WRONG_NUM_ARGS ();
494 ndim = 0;
495 for (k = 0; k < SCM_I_ARRAY_NDIM (ra); k++)
496 {
497 i = scm_to_signed_integer (SCM_SIMPLE_VECTOR_REF (vargs, k),
498 0, SCM_I_ARRAY_NDIM(ra));
499 if (ndim < i)
500 ndim = i;
501 }
502 ndim++;
503 res = scm_i_make_array (ndim);
504 SCM_I_ARRAY_V (res) = SCM_I_ARRAY_V (ra);
505 SCM_I_ARRAY_BASE (res) = SCM_I_ARRAY_BASE (ra);
506 for (k = ndim; k--;)
507 {
508 SCM_I_ARRAY_DIMS (res)[k].lbnd = 0;
509 SCM_I_ARRAY_DIMS (res)[k].ubnd = -1;
510 }
511 for (k = SCM_I_ARRAY_NDIM (ra); k--;)
512 {
513 i = scm_to_int (SCM_SIMPLE_VECTOR_REF (vargs, k));
514 s = &(SCM_I_ARRAY_DIMS (ra)[k]);
515 r = &(SCM_I_ARRAY_DIMS (res)[i]);
516 if (r->ubnd < r->lbnd)
517 {
518 r->lbnd = s->lbnd;
519 r->ubnd = s->ubnd;
520 r->inc = s->inc;
521 ndim--;
522 }
523 else
524 {
525 if (r->ubnd > s->ubnd)
526 r->ubnd = s->ubnd;
527 if (r->lbnd < s->lbnd)
528 {
529 SCM_I_ARRAY_BASE (res) += (s->lbnd - r->lbnd) * r->inc;
530 r->lbnd = s->lbnd;
531 }
532 r->inc += s->inc;
533 }
534 }
535 if (ndim > 0)
536 SCM_MISC_ERROR ("bad argument list", SCM_EOL);
537 scm_i_ra_set_contp (res);
538 return res;
539 }
540
541 scm_wrong_type_arg_msg (NULL, 0, ra, "array");
542 }
543 #undef FUNC_NAME
544
545 /* attempts to unroll an array into a one-dimensional array.
546 returns the unrolled array or #f if it can't be done. */
547 /* if strict is not SCM_UNDEFINED, return #f if returned array
548 wouldn't have contiguous elements. */
549 SCM_DEFINE (scm_array_contents, "array-contents", 1, 1, 0,
550 (SCM ra, SCM strict),
551 "If @var{array} may be @dfn{unrolled} into a one dimensional shared array\n"
552 "without changing their order (last subscript changing fastest), then\n"
553 "@code{array-contents} returns that shared array, otherwise it returns\n"
554 "@code{#f}. All arrays made by @var{make-array} and\n"
555 "@var{make-uniform-array} may be unrolled, some arrays made by\n"
556 "@var{make-shared-array} may not be.\n\n"
557 "If the optional argument @var{strict} is provided, a shared array will\n"
558 "be returned only if its elements are stored internally contiguous in\n"
559 "memory.")
560 #define FUNC_NAME s_scm_array_contents
561 {
562 SCM sra;
563
564 if (scm_is_generalized_vector (ra))
565 return ra;
566
567 if (SCM_I_ARRAYP (ra))
568 {
569 size_t k, ndim = SCM_I_ARRAY_NDIM (ra), len = 1;
570 if (!SCM_I_ARRAYP (ra) || !SCM_I_ARRAY_CONTP (ra))
571 return SCM_BOOL_F;
572 for (k = 0; k < ndim; k++)
573 len *= SCM_I_ARRAY_DIMS (ra)[k].ubnd - SCM_I_ARRAY_DIMS (ra)[k].lbnd + 1;
574 if (!SCM_UNBNDP (strict) && scm_is_true (strict))
575 {
576 if (ndim && (1 != SCM_I_ARRAY_DIMS (ra)[ndim - 1].inc))
577 return SCM_BOOL_F;
578 if (scm_is_bitvector (SCM_I_ARRAY_V (ra)))
579 {
580 if (len != scm_c_bitvector_length (SCM_I_ARRAY_V (ra)) ||
581 SCM_I_ARRAY_BASE (ra) % SCM_LONG_BIT ||
582 len % SCM_LONG_BIT)
583 return SCM_BOOL_F;
584 }
585 }
586
587 {
588 SCM v = SCM_I_ARRAY_V (ra);
589 size_t length = scm_c_generalized_vector_length (v);
590 if ((len == length) && 0 == SCM_I_ARRAY_BASE (ra) && SCM_I_ARRAY_DIMS (ra)->inc)
591 return v;
592 }
593
594 sra = scm_i_make_array (1);
595 SCM_I_ARRAY_DIMS (sra)->lbnd = 0;
596 SCM_I_ARRAY_DIMS (sra)->ubnd = len - 1;
597 SCM_I_ARRAY_V (sra) = SCM_I_ARRAY_V (ra);
598 SCM_I_ARRAY_BASE (sra) = SCM_I_ARRAY_BASE (ra);
599 SCM_I_ARRAY_DIMS (sra)->inc = (ndim ? SCM_I_ARRAY_DIMS (ra)[ndim - 1].inc : 1);
600 return sra;
601 }
602 else
603 scm_wrong_type_arg_msg (NULL, 0, ra, "array");
604 }
605 #undef FUNC_NAME
606
607
608 static void
609 list_to_array (SCM lst, scm_t_array_handle *handle, ssize_t pos, size_t k)
610 {
611 if (k == scm_array_handle_rank (handle))
612 scm_array_handle_set (handle, pos, lst);
613 else
614 {
615 scm_t_array_dim *dim = scm_array_handle_dims (handle) + k;
616 ssize_t inc = dim->inc;
617 size_t len = 1 + dim->ubnd - dim->lbnd, n;
618 char *errmsg = NULL;
619
620 n = len;
621 while (n > 0 && scm_is_pair (lst))
622 {
623 list_to_array (SCM_CAR (lst), handle, pos, k + 1);
624 pos += inc;
625 lst = SCM_CDR (lst);
626 n -= 1;
627 }
628 if (n != 0)
629 errmsg = "too few elements for array dimension ~a, need ~a";
630 if (!scm_is_null (lst))
631 errmsg = "too many elements for array dimension ~a, want ~a";
632 if (errmsg)
633 scm_misc_error (NULL, errmsg, scm_list_2 (scm_from_ulong (k),
634 scm_from_size_t (len)));
635 }
636 }
637
638
639 SCM_DEFINE (scm_list_to_typed_array, "list->typed-array", 3, 0, 0,
640 (SCM type, SCM shape, SCM lst),
641 "Return an array of the type @var{type}\n"
642 "with elements the same as those of @var{lst}.\n"
643 "\n"
644 "The argument @var{shape} determines the number of dimensions\n"
645 "of the array and their shape. It is either an exact integer,\n"
646 "giving the\n"
647 "number of dimensions directly, or a list whose length\n"
648 "specifies the number of dimensions and each element specified\n"
649 "the lower and optionally the upper bound of the corresponding\n"
650 "dimension.\n"
651 "When the element is list of two elements, these elements\n"
652 "give the lower and upper bounds. When it is an exact\n"
653 "integer, it gives only the lower bound.")
654 #define FUNC_NAME s_scm_list_to_typed_array
655 {
656 SCM row;
657 SCM ra;
658 scm_t_array_handle handle;
659
660 row = lst;
661 if (scm_is_integer (shape))
662 {
663 size_t k = scm_to_size_t (shape);
664 shape = SCM_EOL;
665 while (k-- > 0)
666 {
667 shape = scm_cons (scm_length (row), shape);
668 if (k > 0 && !scm_is_null (row))
669 row = scm_car (row);
670 }
671 }
672 else
673 {
674 SCM shape_spec = shape;
675 shape = SCM_EOL;
676 while (1)
677 {
678 SCM spec = scm_car (shape_spec);
679 if (scm_is_pair (spec))
680 shape = scm_cons (spec, shape);
681 else
682 shape = scm_cons (scm_list_2 (spec,
683 scm_sum (scm_sum (spec,
684 scm_length (row)),
685 scm_from_int (-1))),
686 shape);
687 shape_spec = scm_cdr (shape_spec);
688 if (scm_is_pair (shape_spec))
689 {
690 if (!scm_is_null (row))
691 row = scm_car (row);
692 }
693 else
694 break;
695 }
696 }
697
698 ra = scm_make_typed_array (type, SCM_UNSPECIFIED,
699 scm_reverse_x (shape, SCM_EOL));
700
701 scm_array_get_handle (ra, &handle);
702 list_to_array (lst, &handle, 0, 0);
703 scm_array_handle_release (&handle);
704
705 return ra;
706 }
707 #undef FUNC_NAME
708
709 SCM_DEFINE (scm_list_to_array, "list->array", 2, 0, 0,
710 (SCM ndim, SCM lst),
711 "Return an array with elements the same as those of @var{lst}.")
712 #define FUNC_NAME s_scm_list_to_array
713 {
714 return scm_list_to_typed_array (SCM_BOOL_T, ndim, lst);
715 }
716 #undef FUNC_NAME
717
718 /* Print dimension DIM of ARRAY.
719 */
720
721 static int
722 scm_i_print_array_dimension (scm_t_array_handle *h, int dim, int pos,
723 SCM port, scm_print_state *pstate)
724 {
725 if (dim == h->ndims)
726 scm_iprin1 (scm_array_handle_ref (h, pos), port, pstate);
727 else
728 {
729 ssize_t i;
730 scm_putc_unlocked ('(', port);
731 for (i = h->dims[dim].lbnd; i <= h->dims[dim].ubnd;
732 i++, pos += h->dims[dim].inc)
733 {
734 scm_i_print_array_dimension (h, dim+1, pos, port, pstate);
735 if (i < h->dims[dim].ubnd)
736 scm_putc_unlocked (' ', port);
737 }
738 scm_putc_unlocked (')', port);
739 }
740 return 1;
741 }
742
743 /* Print an array.
744 */
745
746 static int
747 scm_i_print_array (SCM array, SCM port, scm_print_state *pstate)
748 {
749 scm_t_array_handle h;
750 long i;
751 int print_lbnds = 0, zero_size = 0, print_lens = 0;
752
753 scm_array_get_handle (array, &h);
754
755 scm_putc_unlocked ('#', port);
756 if (h.ndims != 1 || h.dims[0].lbnd != 0)
757 scm_intprint (h.ndims, 10, port);
758 if (h.element_type != SCM_ARRAY_ELEMENT_TYPE_SCM)
759 scm_write (scm_array_handle_element_type (&h), port);
760
761 for (i = 0; i < h.ndims; i++)
762 {
763 if (h.dims[i].lbnd != 0)
764 print_lbnds = 1;
765 if (h.dims[i].ubnd - h.dims[i].lbnd + 1 == 0)
766 zero_size = 1;
767 else if (zero_size)
768 print_lens = 1;
769 }
770
771 if (print_lbnds || print_lens)
772 for (i = 0; i < h.ndims; i++)
773 {
774 if (print_lbnds)
775 {
776 scm_putc_unlocked ('@', port);
777 scm_intprint (h.dims[i].lbnd, 10, port);
778 }
779 if (print_lens)
780 {
781 scm_putc_unlocked (':', port);
782 scm_intprint (h.dims[i].ubnd - h.dims[i].lbnd + 1,
783 10, port);
784 }
785 }
786
787 if (h.ndims == 0)
788 {
789 /* Rank zero arrays, which are really just scalars, are printed
790 specially. The consequent way would be to print them as
791
792 #0 OBJ
793
794 where OBJ is the printed representation of the scalar, but we
795 print them instead as
796
797 #0(OBJ)
798
799 to make them look less strange.
800
801 Just printing them as
802
803 OBJ
804
805 would be correct in a way as well, but zero rank arrays are
806 not really the same as Scheme values since they are boxed and
807 can be modified with array-set!, say.
808 */
809 scm_putc_unlocked ('(', port);
810 scm_i_print_array_dimension (&h, 0, 0, port, pstate);
811 scm_putc_unlocked (')', port);
812 return 1;
813 }
814 else
815 return scm_i_print_array_dimension (&h, 0, 0, port, pstate);
816 }
817
818 /* Read an array. This function can also read vectors and uniform
819 vectors. Also, the conflict between '#f' and '#f32' and '#f64' is
820 handled here.
821
822 C is the first character read after the '#'.
823 */
824
825 static int
826 read_decimal_integer (SCM port, int c, ssize_t *resp)
827 {
828 ssize_t sign = 1;
829 ssize_t res = 0;
830 int got_it = 0;
831
832 if (c == '-')
833 {
834 sign = -1;
835 c = scm_getc_unlocked (port);
836 }
837
838 while ('0' <= c && c <= '9')
839 {
840 res = 10*res + c-'0';
841 got_it = 1;
842 c = scm_getc_unlocked (port);
843 }
844
845 if (got_it)
846 *resp = sign * res;
847 return c;
848 }
849
850 SCM
851 scm_i_read_array (SCM port, int c)
852 {
853 ssize_t rank;
854 scm_t_wchar tag_buf[8];
855 int tag_len;
856
857 SCM tag, shape = SCM_BOOL_F, elements;
858
859 /* XXX - shortcut for ordinary vectors. Shouldn't be necessary but
860 the array code can not deal with zero-length dimensions yet, and
861 we want to allow zero-length vectors, of course.
862 */
863 if (c == '(')
864 {
865 scm_ungetc_unlocked (c, port);
866 return scm_vector (scm_read (port));
867 }
868
869 /* Disambiguate between '#f' and uniform floating point vectors.
870 */
871 if (c == 'f')
872 {
873 c = scm_getc_unlocked (port);
874 if (c != '3' && c != '6')
875 {
876 if (c != EOF)
877 scm_ungetc_unlocked (c, port);
878 return SCM_BOOL_F;
879 }
880 rank = 1;
881 tag_buf[0] = 'f';
882 tag_len = 1;
883 goto continue_reading_tag;
884 }
885
886 /* Read rank.
887 */
888 rank = 1;
889 c = read_decimal_integer (port, c, &rank);
890 if (rank < 0)
891 scm_i_input_error (NULL, port, "array rank must be non-negative",
892 SCM_EOL);
893
894 /* Read tag.
895 */
896 tag_len = 0;
897 continue_reading_tag:
898 while (c != EOF && c != '(' && c != '@' && c != ':'
899 && tag_len < sizeof tag_buf / sizeof tag_buf[0])
900 {
901 tag_buf[tag_len++] = c;
902 c = scm_getc_unlocked (port);
903 }
904 if (tag_len == 0)
905 tag = SCM_BOOL_T;
906 else
907 {
908 tag = scm_string_to_symbol (scm_from_utf32_stringn (tag_buf, tag_len));
909 if (tag_len == sizeof tag_buf / sizeof tag_buf[0])
910 scm_i_input_error (NULL, port, "invalid array tag, starting with: ~a",
911 scm_list_1 (tag));
912 }
913
914 /* Read shape.
915 */
916 if (c == '@' || c == ':')
917 {
918 shape = SCM_EOL;
919
920 do
921 {
922 ssize_t lbnd = 0, len = 0;
923 SCM s;
924
925 if (c == '@')
926 {
927 c = scm_getc_unlocked (port);
928 c = read_decimal_integer (port, c, &lbnd);
929 }
930
931 s = scm_from_ssize_t (lbnd);
932
933 if (c == ':')
934 {
935 c = scm_getc_unlocked (port);
936 c = read_decimal_integer (port, c, &len);
937 if (len < 0)
938 scm_i_input_error (NULL, port,
939 "array length must be non-negative",
940 SCM_EOL);
941
942 s = scm_list_2 (s, scm_from_ssize_t (lbnd+len-1));
943 }
944
945 shape = scm_cons (s, shape);
946 } while (c == '@' || c == ':');
947
948 shape = scm_reverse_x (shape, SCM_EOL);
949 }
950
951 /* Read nested lists of elements.
952 */
953 if (c != '(')
954 scm_i_input_error (NULL, port,
955 "missing '(' in vector or array literal",
956 SCM_EOL);
957 scm_ungetc_unlocked (c, port);
958 elements = scm_read (port);
959
960 if (scm_is_false (shape))
961 shape = scm_from_ssize_t (rank);
962 else if (scm_ilength (shape) != rank)
963 scm_i_input_error
964 (NULL, port,
965 "the number of shape specifications must match the array rank",
966 SCM_EOL);
967
968 /* Handle special print syntax of rank zero arrays; see
969 scm_i_print_array for a rationale.
970 */
971 if (rank == 0)
972 {
973 if (!scm_is_pair (elements))
974 scm_i_input_error (NULL, port,
975 "too few elements in array literal, need 1",
976 SCM_EOL);
977 if (!scm_is_null (SCM_CDR (elements)))
978 scm_i_input_error (NULL, port,
979 "too many elements in array literal, want 1",
980 SCM_EOL);
981 elements = SCM_CAR (elements);
982 }
983
984 /* Construct array.
985 */
986 return scm_list_to_typed_array (tag, shape, elements);
987 }
988
989
990 static SCM
991 array_handle_ref (scm_t_array_handle *h, size_t pos)
992 {
993 return scm_c_generalized_vector_ref (SCM_I_ARRAY_V (h->array), pos);
994 }
995
996 static void
997 array_handle_set (scm_t_array_handle *h, size_t pos, SCM val)
998 {
999 scm_c_generalized_vector_set_x (SCM_I_ARRAY_V (h->array), pos, val);
1000 }
1001
1002 /* FIXME: should be handle for vect? maybe not, because of dims */
1003 static void
1004 array_get_handle (SCM array, scm_t_array_handle *h)
1005 {
1006 scm_t_array_handle vh;
1007 scm_array_get_handle (SCM_I_ARRAY_V (array), &vh);
1008 h->element_type = vh.element_type;
1009 h->elements = vh.elements;
1010 h->writable_elements = vh.writable_elements;
1011 scm_array_handle_release (&vh);
1012
1013 h->dims = SCM_I_ARRAY_DIMS (array);
1014 h->ndims = SCM_I_ARRAY_NDIM (array);
1015 h->base = SCM_I_ARRAY_BASE (array);
1016 }
1017
1018 SCM_ARRAY_IMPLEMENTATION (SCM_SMOB_TYPE_BITS (scm_i_tc16_array),
1019 SCM_SMOB_TYPE_MASK,
1020 array_handle_ref, array_handle_set,
1021 array_get_handle)
1022
1023 void
1024 scm_init_arrays ()
1025 {
1026 scm_i_tc16_array = scm_make_smob_type ("array", 0);
1027 scm_set_smob_print (scm_i_tc16_array, scm_i_print_array);
1028 scm_set_smob_equalp (scm_i_tc16_array, scm_array_equal_p);
1029
1030 scm_add_feature ("array");
1031
1032 #include "libguile/arrays.x"
1033
1034 }
1035
1036 /*
1037 Local Variables:
1038 c-file-style: "gnu"
1039 End:
1040 */