carray 3.0.4 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +438 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_dispatch.c +19 -20
  7. data/ext/ca_axis_group.c +1 -1
  8. data/ext/ca_categorical_iterator.c +34 -27
  9. data/ext/ca_compare.c +4 -4
  10. data/ext/ca_composite_dispatch.c +9 -9
  11. data/ext/ca_kernel_iterator.c +122 -237
  12. data/ext/ca_kernel_iterator.h +522 -337
  13. data/ext/ca_obj_array.c +69 -19
  14. data/ext/ca_obj_bincmp.c +10 -1
  15. data/ext/ca_obj_binop.c +11 -1
  16. data/ext/ca_obj_bitarray.c +5 -1
  17. data/ext/ca_obj_bitfield.c +60 -43
  18. data/ext/ca_obj_byte_swap.c +4 -1
  19. data/ext/ca_obj_const_string.c +15 -3
  20. data/ext/ca_obj_face.c +44 -1
  21. data/ext/ca_obj_fake.c +3 -1
  22. data/ext/ca_obj_field.c +13 -3
  23. data/ext/ca_obj_grid.c +219 -17
  24. data/ext/ca_obj_meld.c +69 -113
  25. data/ext/ca_obj_moncmp.c +9 -1
  26. data/ext/ca_obj_monop.c +86 -12
  27. data/ext/ca_obj_object.c +118 -32
  28. data/ext/ca_obj_record.c +3 -0
  29. data/ext/ca_obj_reduce.c +19 -1
  30. data/ext/ca_obj_refer.c +80 -26
  31. data/ext/ca_obj_remap.c +69 -10
  32. data/ext/ca_obj_roll.c +27 -6
  33. data/ext/ca_obj_select.c +38 -6
  34. data/ext/ca_obj_select_axis.c +1 -0
  35. data/ext/ca_obj_shift.c +32 -7
  36. data/ext/ca_obj_stack.c +311 -111
  37. data/ext/ca_obj_stride.c +159 -64
  38. data/ext/ca_obj_string.c +27 -2
  39. data/ext/ca_obj_tile.c +28 -7
  40. data/ext/ca_obj_transpose.c +8 -4
  41. data/ext/ca_obj_triop.c +12 -1
  42. data/ext/ca_obj_window.c +80 -50
  43. data/ext/ca_op_ipower.c +12 -12
  44. data/ext/ca_sort_kernels.h +6 -3
  45. data/ext/caframe_csv_reader.c +1 -1
  46. data/ext/carray.h +35 -1
  47. data/ext/carray_access.c +43 -67
  48. data/ext/carray_address_basis.c +134 -0
  49. data/ext/carray_attribute.c +32 -0
  50. data/ext/carray_broadcast.c +3 -4
  51. data/ext/carray_build_flags.h +1 -1
  52. data/ext/carray_cast.c +251 -93
  53. data/ext/carray_cast_func.rb +56 -26
  54. data/ext/carray_conversion.c +11 -7
  55. data/ext/carray_core.c +814 -72
  56. data/ext/carray_count.c +6 -0
  57. data/ext/carray_element.c +22 -13
  58. data/ext/carray_factorize.c +20 -20
  59. data/ext/carray_generate.c +8 -2
  60. data/ext/carray_histogram.c +26 -4
  61. data/ext/carray_index_classifier.c +4 -4
  62. data/ext/carray_internal.h +159 -12
  63. data/ext/carray_kernels_bincmp.c +2174 -170
  64. data/ext/carray_kernels_binop.c +25653 -5487
  65. data/ext/carray_kernels_init.c +8 -8
  66. data/ext/carray_kernels_moncmp.c +96 -96
  67. data/ext/carray_kernels_monop.c +12253 -5235
  68. data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
  69. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  70. data/ext/carray_kernels_reduce_cumulative.c +2442 -882
  71. data/ext/carray_kernels_reduce_extreme.c +5658 -1661
  72. data/ext/carray_kernels_reduce_variance.c +2534 -402
  73. data/ext/carray_kernels_scan.c +368 -368
  74. data/ext/carray_kernels_search.c +4551 -604
  75. data/ext/carray_kernels_sort.c +659 -751
  76. data/ext/carray_kernels_triop.c +114 -114
  77. data/ext/carray_key_windows.c +4 -0
  78. data/ext/carray_lazy.c +47 -0
  79. data/ext/carray_loop.c +1 -1
  80. data/ext/carray_mask.c +175 -161
  81. data/ext/carray_median_percentile.c +4 -4
  82. data/ext/carray_memory_view.c +15 -9
  83. data/ext/carray_operator.c +152 -49
  84. data/ext/carray_order.c +230 -10
  85. data/ext/carray_random.c +22 -19
  86. data/ext/carray_scatter.c +24 -7
  87. data/ext/carray_slab.c +128 -29
  88. data/ext/carray_sort.c +224 -16
  89. data/ext/carray_sort_kernel.c +5 -7
  90. data/ext/carray_struct.c +35 -43
  91. data/ext/carray_test.c +74 -19
  92. data/ext/carray_time_text.c +6 -4
  93. data/ext/carray_utils.c +63 -21
  94. data/ext/extconf.rb +11 -0
  95. data/ext/mkkernel.rb +546 -199
  96. data/ext/ruby_carray.c +4 -1
  97. data/ext/version.h +4 -4
  98. data/lib/carray/attribute.rb +42 -5
  99. data/lib/carray/autoload_carray.rb +3 -3
  100. data/lib/carray/autoload_method_extension.rb +5 -3
  101. data/lib/carray/axis_group.rb +13 -7
  102. data/lib/carray/basics.rb +28 -8
  103. data/lib/carray/block_iterator.rb +17 -3
  104. data/lib/carray/categorical.rb +35 -23
  105. data/lib/carray/categorical_iterator.rb +8 -8
  106. data/lib/carray/complex.rb +2 -2
  107. data/lib/carray/conditional.rb +3 -2
  108. data/lib/carray/const_string.rb +10 -0
  109. data/lib/carray/data_type_extension.rb +8 -1
  110. data/lib/carray/fixlen_string.rb +41 -2
  111. data/lib/carray/frame/column_verbs.rb +48 -16
  112. data/lib/carray/frame/concat.rb +17 -3
  113. data/lib/carray/frame/convert.rb +19 -0
  114. data/lib/carray/frame/csv_parser.rb +32 -32
  115. data/lib/carray/frame/describe.rb +4 -2
  116. data/lib/carray/frame/frame.rb +405 -85
  117. data/lib/carray/frame/group.rb +777 -64
  118. data/lib/carray/frame/io.rb +212 -72
  119. data/lib/carray/frame/join.rb +146 -37
  120. data/lib/carray/frame/records.rb +3 -3
  121. data/lib/carray/frame/reshape.rb +89 -11
  122. data/lib/carray/frame/sort.rb +57 -9
  123. data/lib/carray/frame/verbs.rb +384 -49
  124. data/lib/carray/frame/windows.rb +44 -6
  125. data/lib/carray/fuse_source.rb +31 -2
  126. data/lib/carray/inspect.rb +7 -2
  127. data/lib/carray/iterator.rb +32 -0
  128. data/lib/carray/lazy.rb +52 -8
  129. data/lib/carray/mask_gap_fill.rb +37 -17
  130. data/lib/carray/meld_reduce.rb +1 -1
  131. data/lib/carray/methods/choose.rb +3 -2
  132. data/lib/carray/methods/is_in.rb +8 -1
  133. data/lib/carray/methods/meshgrid.rb +3 -1
  134. data/lib/carray/runtime.rb +47 -8
  135. data/lib/carray/segment_iterator.rb +35 -16
  136. data/lib/carray/serialize.rb +53 -19
  137. data/lib/carray/stack.rb +33 -18
  138. data/lib/carray/string.rb +8 -0
  139. data/lib/carray/string_operation_extension.rb +13 -5
  140. data/lib/carray/struct_builder.rb +5 -3
  141. data/lib/carray/time.rb +112 -68
  142. data/lib/carray/window_iterator.rb +16 -4
  143. data/yard-stubs/ca_obj_array.rb +26 -1
  144. data/yard-stubs/ca_obj_transpose.rb +5 -4
  145. data/yard-stubs/carray_attribute.rb +3 -1
  146. data/yard-stubs/carray_cast.rb +10 -5
  147. data/yard-stubs/carray_conversion.rb +13 -3
  148. data/yard-stubs/carray_element.rb +7 -6
  149. data/yard-stubs/carray_mask.rb +28 -4
  150. data/yard-stubs/carray_math.rb +45 -20
  151. data/yard-stubs/carray_memory_view.rb +7 -0
  152. data/yard-stubs/carray_order.rb +100 -4
  153. data/yard-stubs/carray_scatter.rb +7 -5
  154. data/yard-stubs/carray_test.rb +7 -4
  155. metadata +18 -4
data/ext/ca_obj_stride.c CHANGED
@@ -183,6 +183,9 @@ free_ca_stride (void *ap)
183
183
  {
184
184
  CAStride *ca = (CAStride *) ap;
185
185
  if (ca == NULL) return;
186
+ if ( ca_test_flag(ca, CA_FLAG_FOLDED) ) {
187
+ ca_fold_forget((CArray *) ca);
188
+ }
186
189
  ca_free(ca->mask);
187
190
  if (ca->_pool) {
188
191
  /* Pool path: one xfree covers dim/strides via the pool buffer,
@@ -247,6 +250,10 @@ ca_stride_compose_through (CAStride *leaf, CAStride *parent,
247
250
  for (k = parent->ndim - 1; k >= 0; k--)
248
251
  prod[k] = prod[k + 1] * parent->dim[k];
249
252
 
253
+ /* A parent with a zero-length axis has no cell for the leaf to start at
254
+ (and prod[] below would divide by zero). */
255
+ if (prod[0] == 0) return 0;
256
+
250
257
  /* Compose base offset first. We need the per-dim base position
251
258
  to validate stride composition against parent dim bounds (a small
252
259
  forward stride starting near the end of a parent dim wraps into
@@ -274,11 +281,15 @@ ca_stride_compose_through (CAStride *leaf, CAStride *parent,
274
281
 
275
282
  /* Compose each leaf dim's stride.
276
283
  Validity rule: leaf dim k must advance exactly one parent dim
277
- (not cross multiple parent dims) AND stay within that parent
278
- dim's bounds across leaf's full extent *given the base position*.
279
- Otherwise the leaf's traversal would wrap across parent dim
280
- boundaries, which is a non-strided access pattern that cannot
281
- be folded. */
284
+ (not cross multiple parent dims), and every parent dim must stay
285
+ within its bounds across the leaf's full extent *given the base
286
+ position*. Several leaf dims can advance the same parent dim (a block
287
+ of a reshape: rows step 4 parent columns, columns step 1), so the
288
+ bound is on the sum of their extents, kept in lo[] / hi[]. Otherwise
289
+ the leaf's traversal would wrap across parent dim boundaries, which
290
+ is a non-strided access pattern that cannot be folded. */
291
+ ca_size_t lo[CA_RANK_MAX], hi[CA_RANK_MAX];
292
+ for (m = 0; m < parent->ndim; m++) lo[m] = hi[m] = base_idx[m];
282
293
  for (k = 0; k < leaf->ndim; k++) {
283
294
  if (leaf->strides[k] % parent->bytes != 0) return 0;
284
295
  ca_size_t advance = leaf->strides[k] / parent->bytes;
@@ -298,20 +309,15 @@ ca_stride_compose_through (CAStride *leaf, CAStride *parent,
298
309
  }
299
310
  if (advance != 0) return 0;
300
311
  if (nonzero_count > 1) return 0; /* crosses parent dims */
301
- if (nonzero_count == 1) {
302
- /* Final position in parent dim after the full leaf extent.
303
- For a forward step it must stay strictly below dim; for a
304
- backward step it must stay at or above 0. */
305
- ca_size_t final_pos =
306
- base_idx[nonzero_dim] + (leaf->dim[k] - 1) * nonzero_step;
307
- if (nonzero_step > 0) {
308
- if (final_pos >= parent->dim[nonzero_dim]) return 0;
309
- } else {
310
- if (final_pos < 0) return 0;
311
- }
312
+ if (nonzero_count == 1 && leaf->dim[k] > 1) {
313
+ ca_size_t extent = (leaf->dim[k] - 1) * nonzero_step;
314
+ if (extent > 0) hi[nonzero_dim] += extent;
315
+ else lo[nonzero_dim] += extent;
312
316
  }
313
317
  out_strides[k] = composed;
314
318
  }
319
+ for (m = 0; m < parent->ndim; m++)
320
+ if (lo[m] < 0 || hi[m] >= parent->dim[m]) return 0;
315
321
 
316
322
  /* A leaf cell wider than the remainder of its first parent element
317
323
  (CARefer reinterpreting two float64 as one complex128) covers several
@@ -675,14 +681,20 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
675
681
  }
676
682
  }
677
683
  else {
684
+ /* Peel the axes from the innermost: a % dim[k] is the index on
685
+ axis k and a / dim[k] carries the rest outward. The outermost
686
+ axis takes whatever is left, so ndim-1 divisions per address. */
687
+ ca_size_t *dim = ca->dim;
688
+ int8_t ndim = ca->ndim;
678
689
  for ( i = 0; i < n; i++ ) {
679
- ca_size_t idx[CA_RANK_MAX];
690
+ ca_size_t a = addrs[i];
680
691
  ca_size_t off = ebase;
681
- ca_addr2index((CArray *) ca, addrs[i], idx);
682
- for ( k = 0; k < ca->ndim; k++ ) {
683
- off += idx[k] * esteps[k];
692
+ for ( k = ndim - 1; k > 0; k-- ) {
693
+ ca_size_t q = a / dim[k];
694
+ off += ( a - q * dim[k] ) * esteps[k];
695
+ a = q;
684
696
  }
685
- paddrs[i] = off;
697
+ paddrs[i] = off + a * esteps[0];
686
698
  }
687
699
  }
688
700
  ca_xfer_addrs(root, n, paddrs, data, dir);
@@ -817,9 +829,12 @@ ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
817
829
 
818
830
  /* A reshape over a 1-D cold root (ndim != root->ndim): the request has no
819
831
  box in the root's single axis, but the root cells it touches lie in one
820
- flat span. Ask the root for that span once and walk the request over
821
- it, as xfer_all does for the whole view. A PUT reads the span first so
822
- the cells between the requested ones go back unchanged. */
832
+ flat span. A GET asks the root for that span once and walks the
833
+ request over it, as xfer_all does for the whole view. A PUT hands the
834
+ root the list of the requested cells' addresses instead: sending the
835
+ span back would write the cells between them too, and a root that
836
+ repeats its cells (CATile) would have a stale copy land after the
837
+ written one. */
823
838
  if (aligned && !root->ptr && ca_func[root->obj_type].xfer_stride
824
839
  && ca->bytes == root->bytes && root->ndim == 1
825
840
  && root_base % root->bytes == 0) {
@@ -832,15 +847,33 @@ ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
832
847
  }
833
848
  if ( span_lo >= 0 && span_hi <= root->elements * root->bytes ) {
834
849
  volatile VALUE holder;
835
- ca_size_t rlo = span_lo / root->bytes;
836
- ca_size_t rcount = (span_hi - span_lo) / root->bytes;
837
- ca_size_t rstep = root->bytes;
838
- char *scratch = ALLOCV_N(char, holder, rcount * root->bytes);
839
- ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_GET);
840
- ca_xfer_strided_walk(scratch + (root_base - span_lo), ca->bytes, ndim,
841
- counts, root_stride, d, dir);
842
850
  if ( dir == CA_XFER_PUT ) {
843
- ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_PUT);
851
+ ca_size_t n = 1, m = 0, idx[CA_RANK_MAX];
852
+ ca_size_t *addrs;
853
+ for (k = 0; k < ndim; k++) {
854
+ n *= counts[k];
855
+ idx[k] = 0;
856
+ }
857
+ addrs = ALLOCV_N(ca_size_t, holder, n);
858
+ /* empty: the slot returns on an empty region at its entry */
859
+ while (1) {
860
+ ca_size_t off = root_base;
861
+ for (k = 0; k < ndim; k++) off += idx[k] * root_stride[k];
862
+ addrs[m++] = off / root->bytes;
863
+ k = ndim - 1;
864
+ while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
865
+ if (k < 0) break;
866
+ }
867
+ ca_xfer_addrs(root, n, addrs, d, CA_XFER_PUT);
868
+ }
869
+ else {
870
+ ca_size_t rlo = span_lo / root->bytes;
871
+ ca_size_t rcount = (span_hi - span_lo) / root->bytes;
872
+ ca_size_t rstep = root->bytes;
873
+ char *scratch = ALLOCV_N(char, holder, rcount * root->bytes);
874
+ ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_GET);
875
+ ca_xfer_strided_walk(scratch + (root_base - span_lo), ca->bytes, ndim,
876
+ counts, root_stride, d, dir);
844
877
  }
845
878
  ALLOCV_END(holder);
846
879
  return;
@@ -1114,10 +1147,12 @@ static int ca_layout_is_contiguous (int8_t ndim, const ca_size_t *dim,
1114
1147
  folds onward from itself, so the answer is the one for the root it folds
1115
1148
  to. What is left is the
1116
1149
  boundary views that compute or fetch their contents -- CAObject, the lazy
1117
- per-element transforms, whatever a companion gem installed -- and of those,
1118
- only the ones with an xfer_stride slot can answer a region request at all.
1119
- The rest have nothing better than materialising, so they keep doing it once
1120
- under attach rather than once per transfer. */
1150
+ per-element transforms, whatever a companion gem installed. All of them
1151
+ answer a region request: through their xfer_stride slot, or, when a type
1152
+ leaves it out, through the dispatcher's default, which hands the cells'
1153
+ addresses to xfer_addrs. Attaching one instead would produce all of it
1154
+ and write all of it back, so a cell nobody touched would go through the
1155
+ view's conversion both ways. */
1121
1156
  int
1122
1157
  ca_root_lends_no_memory (void *ap)
1123
1158
  {
@@ -1135,7 +1170,7 @@ ca_root_lends_no_memory (void *ap)
1135
1170
  ca_stride_compose_to_root((CAStride *) root, &below, cs, &base);
1136
1171
  return ca_root_lends_no_memory(below);
1137
1172
  }
1138
- return ca_func[root->obj_type].xfer_stride != NULL;
1173
+ return 1;
1139
1174
  }
1140
1175
 
1141
1176
  /* The same question for the parent of a view that is not itself in the
@@ -1431,8 +1466,10 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1431
1466
  the request is exact) and ca->bytes == root->bytes (no byte
1432
1467
  reinterpret, so the span endpoints are whole root elements). A wider
1433
1468
  lazy root has no single strided box to ask for, so it takes the
1434
- whole-root fallback below -- correct, just not partial. */
1435
- if ( root->ndim == 1 && ca->bytes == root->bytes ) {
1469
+ whole-root fallback below -- correct, just not partial. Reads only:
1470
+ a write goes to the root as the list of its cells below, since
1471
+ sending the span back would write the cells between them too. */
1472
+ if ( dir == CA_XFER_GET && root->ndim == 1 && ca->bytes == root->bytes ) {
1436
1473
  ca_size_t span_lo = composed_base;
1437
1474
  ca_size_t span_hi = composed_base + ca->bytes;
1438
1475
  int8_t k;
@@ -1449,13 +1486,10 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1449
1486
  char *scratch = ALLOCV_N(char, holder, rcount * root->bytes);
1450
1487
  ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_GET);
1451
1488
  ca->ptr = ptr;
1452
- ca_stride_xfer_with_layout(ca, (dir == CA_XFER_PUT) ? 1 : 0,
1489
+ ca_stride_xfer_with_layout(ca, 0,
1453
1490
  scratch + (composed_base - span_lo),
1454
1491
  composed_strides);
1455
1492
  ca->ptr = ptr0;
1456
- if ( dir == CA_XFER_PUT ) {
1457
- ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_PUT);
1458
- }
1459
1493
  ALLOCV_END(holder);
1460
1494
  }
1461
1495
  return;
@@ -1581,6 +1615,51 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1581
1615
  }
1582
1616
  }
1583
1617
 
1618
+ /* Whether a layout of `ndim` axes (dims, byte strides, byte base) addresses
1619
+ each cell of a packed array of the same number of cells exactly once:
1620
+ ordered by the size of their strides, the axes longer than one must be
1621
+ packed (the smallest stride is `bytes`, each next one the previous times
1622
+ its axis length), and the lowest address must be 0. Equal counts alone
1623
+ do not say this: a zero or repeated stride covers fewer cells. */
1624
+ static int
1625
+ ca_stride_layout_is_permutation (int8_t ndim, const ca_size_t *dim,
1626
+ const ca_size_t *strides, ca_size_t base,
1627
+ ca_size_t bytes)
1628
+ {
1629
+ int8_t order[CA_RANK_MAX];
1630
+ int8_t n = 0, i, j;
1631
+ ca_size_t lowest = base, expect = bytes;
1632
+
1633
+ for (i=0; i<ndim; i++) {
1634
+ if ( dim[i] <= 1 ) {
1635
+ continue;
1636
+ }
1637
+ if ( strides[i] < 0 ) {
1638
+ lowest += (dim[i] - 1) * strides[i];
1639
+ }
1640
+ for (j=n; j>0; j--) {
1641
+ ca_size_t a = strides[order[j-1]], b = strides[i];
1642
+ if ( (a < 0 ? -a : a) <= (b < 0 ? -b : b) ) {
1643
+ break;
1644
+ }
1645
+ order[j] = order[j-1];
1646
+ }
1647
+ order[j] = i;
1648
+ n++;
1649
+ }
1650
+ if ( lowest != 0 ) {
1651
+ return 0;
1652
+ }
1653
+ for (i=0; i<n; i++) {
1654
+ ca_size_t s = strides[order[i]];
1655
+ if ( (s < 0 ? -s : s) != expect ) {
1656
+ return 0;
1657
+ }
1658
+ expect *= dim[order[i]];
1659
+ }
1660
+ return 1;
1661
+ }
1662
+
1584
1663
  static void
1585
1664
  ca_stride_func_fill_data (void *ap, void *ptr)
1586
1665
  {
@@ -1616,7 +1695,9 @@ ca_stride_func_fill_data (void *ap, void *ptr)
1616
1695
  into scratch only to overwrite every byte with *ptr -- wasted
1617
1696
  work. Delegate to root.fill_data instead.
1618
1697
  Safety gates:
1619
- (1) elements match -- our view covers every root cell;
1698
+ (1) elements match and the composed layout addresses each root cell
1699
+ exactly once -- our view covers every root cell (a zero or
1700
+ repeated stride has as many cells but covers fewer);
1620
1701
  (2) bytes match -- our cell width equals root's cell width.
1621
1702
  Without (2), a type-reinterpreting CAStride (CAField over a
1622
1703
  complex entity for `.real`/`.imag`) would corrupt root: root
@@ -1628,7 +1709,9 @@ ca_stride_func_fill_data (void *ap, void *ptr)
1628
1709
  if ( ca_func[root->obj_type].attach != ca_stride_func.attach &&
1629
1710
  ca_func[root->obj_type].attach != ca_window_func.attach &&
1630
1711
  ca->elements == root->elements &&
1631
- ca->bytes == root->bytes ) {
1712
+ ca->bytes == root->bytes &&
1713
+ ca_stride_layout_is_permutation(ca->ndim, ca->dim, composed_strides,
1714
+ composed_base, bytes) ) {
1632
1715
  ca_func[root->obj_type].fill_data(root, ptr);
1633
1716
  return;
1634
1717
  }
@@ -1686,19 +1769,19 @@ ca_stride_func_fill_data (void *ap, void *ptr)
1686
1769
  }
1687
1770
  case 2: {
1688
1771
  int16_t v; memcpy(&v, ptr, 2);
1689
- int16_t *d = (int16_t *) dst;
1772
+ ca_u_int16_t *d = (ca_u_int16_t *) dst;
1690
1773
  for (i = 0; i < inner_count; i++) d[i] = v;
1691
1774
  break;
1692
1775
  }
1693
1776
  case 4: {
1694
1777
  int32_t v; memcpy(&v, ptr, 4);
1695
- int32_t *d = (int32_t *) dst;
1778
+ ca_u_int32_t *d = (ca_u_int32_t *) dst;
1696
1779
  for (i = 0; i < inner_count; i++) d[i] = v;
1697
1780
  break;
1698
1781
  }
1699
1782
  case 8: {
1700
1783
  int64_t v; memcpy(&v, ptr, 8);
1701
- int64_t *d = (int64_t *) dst;
1784
+ ca_u_int64_t *d = (ca_u_int64_t *) dst;
1702
1785
  for (i = 0; i < inner_count; i++) d[i] = v;
1703
1786
  break;
1704
1787
  }
@@ -2090,6 +2173,13 @@ rb_cs_initialize_copy (VALUE self, VALUE other)
2090
2173
  ca_stride_setup(ca, cs->obj_type, cs->parent, cs->data_type, cs->bytes,
2091
2174
  cs->ndim, cs->dim, cs->strides, cs->base_offset);
2092
2175
  rb_ca_set_parent(self, rb_ca_parent(other));
2176
+ if ( ca_test_flag(cs, CA_FLAG_FOLDED) ) {
2177
+ /* the copy has the same @parent, so it answers to the same source */
2178
+ CArray *src = ca_fold_source((CArray *) cs);
2179
+ if ( src ) {
2180
+ ca_fold_note_source((CArray *) ca, src);
2181
+ }
2182
+ }
2093
2183
  return self;
2094
2184
  }
2095
2185
 
@@ -2164,11 +2254,11 @@ rb_ca_as_strided (int argc, VALUE *argv, VALUE self)
2164
2254
  ca_check_rank_fits(len, NULL);
2165
2255
  ndim = (int8_t) len;
2166
2256
  for (i = 0; i < len; i++) {
2167
- shape[i] = NUM2SIZE(RARRAY_AREF(rshape, i));
2168
- strides[i] = NUM2SIZE(RARRAY_AREF(rstrides, i));
2257
+ shape[i] = ca_integer_arg(RARRAY_AREF(rshape, i), "a length", NULL);
2258
+ strides[i] = ca_integer_arg(RARRAY_AREF(rstrides, i), "a stride", NULL);
2169
2259
  }
2170
2260
  if (! NIL_P(roffset)) {
2171
- base_offset = NUM2SIZE(roffset);
2261
+ base_offset = ca_integer_arg(roffset, "offset", NULL);
2172
2262
  }
2173
2263
 
2174
2264
  VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
@@ -2215,12 +2305,12 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2215
2305
  RARRAY_LEN(wary), (int) ndim);
2216
2306
  }
2217
2307
  for (i = 0; i < ndim; i++) {
2218
- window[i] = NUM2SIZE(RARRAY_AREF(wary, i));
2308
+ window[i] = ca_integer_arg(RARRAY_AREF(wary, i), "a window size", NULL);
2219
2309
  }
2220
2310
  }
2221
2311
  else if (nargs == ndim) {
2222
2312
  for (i = 0; i < ndim; i++) {
2223
- window[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2313
+ window[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a window size", NULL);
2224
2314
  }
2225
2315
  }
2226
2316
  else {
@@ -2239,11 +2329,11 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2239
2329
  RARRAY_LEN(rstep), (int) ndim);
2240
2330
  }
2241
2331
  for (i = 0; i < ndim; i++) {
2242
- step[i] = NUM2SIZE(RARRAY_AREF(rstep, i));
2332
+ step[i] = ca_integer_arg(RARRAY_AREF(rstep, i), "a step", NULL);
2243
2333
  }
2244
2334
  }
2245
2335
  else {
2246
- ca_size_t s = NUM2SIZE(rstep);
2336
+ ca_size_t s = ca_integer_arg(rstep, "step", NULL);
2247
2337
  for (i = 0; i < ndim; i++) step[i] = s;
2248
2338
  }
2249
2339
 
@@ -2326,13 +2416,13 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2326
2416
  }
2327
2417
  nspatial = (int8_t) RARRAY_LEN(wary);
2328
2418
  for (i = 0; i < nspatial; i++) {
2329
- window[i] = NUM2SIZE(RARRAY_AREF(wary, i));
2419
+ window[i] = ca_integer_arg(RARRAY_AREF(wary, i), "a window size", NULL);
2330
2420
  }
2331
2421
  }
2332
2422
  else if (nargs >= 1 && nargs <= ndim) {
2333
2423
  nspatial = (int8_t) nargs;
2334
2424
  for (i = 0; i < nspatial; i++) {
2335
- window[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2425
+ window[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a window size", NULL);
2336
2426
  }
2337
2427
  }
2338
2428
  else {
@@ -2355,11 +2445,11 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2355
2445
  RARRAY_LEN(rstep), (int) nspatial);
2356
2446
  }
2357
2447
  for (i = 0; i < nspatial; i++) {
2358
- step[i] = NUM2SIZE(RARRAY_AREF(rstep, i));
2448
+ step[i] = ca_integer_arg(RARRAY_AREF(rstep, i), "a step", NULL);
2359
2449
  }
2360
2450
  }
2361
2451
  else {
2362
- ca_size_t s = NUM2SIZE(rstep);
2452
+ ca_size_t s = ca_integer_arg(rstep, "step", NULL);
2363
2453
  for (i = 0; i < nspatial; i++) step[i] = s;
2364
2454
  }
2365
2455
 
@@ -2441,12 +2531,12 @@ rb_ca_block_view (int argc, VALUE *argv, VALUE self)
2441
2531
  RARRAY_LEN(bary), (int) ndim);
2442
2532
  }
2443
2533
  for (i = 0; i < ndim; i++) {
2444
- block[i] = NUM2SIZE(RARRAY_AREF(bary, i));
2534
+ block[i] = ca_integer_arg(RARRAY_AREF(bary, i), "a block size", NULL);
2445
2535
  }
2446
2536
  }
2447
2537
  else if (nargs == ndim) {
2448
2538
  for (i = 0; i < ndim; i++) {
2449
- block[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2539
+ block[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a block size", NULL);
2450
2540
  }
2451
2541
  }
2452
2542
  else {
@@ -2571,7 +2661,12 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2571
2661
  if (! seen[i]) imap[k++] = i;
2572
2662
  }
2573
2663
 
2574
- return rb_ca_trans_new(self, imap);
2664
+ {
2665
+ /* A reordering of the axes, as transpose is: a Face stays its Face. */
2666
+ volatile VALUE obj = rb_ca_trans_new(self, imap);
2667
+ CA_WRAPPER_LIFT(obj, self, parent);
2668
+ return obj;
2669
+ }
2575
2670
  }
2576
2671
 
2577
2672
  /* flip(*axes) -- reverses the listed axes by negating their strides; with no
@@ -2740,14 +2835,14 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2740
2835
 
2741
2836
  /* offset from positional or keyword (not both) */
2742
2837
  if (nargs == 0) {
2743
- if (! NIL_P(roffset)) offset = NUM2SIZE(roffset);
2838
+ if (! NIL_P(roffset)) offset = ca_integer_arg(roffset, "offset", NULL);
2744
2839
  }
2745
2840
  else if (nargs == 1) {
2746
2841
  if (! NIL_P(roffset)) {
2747
2842
  rb_raise(rb_eArgError,
2748
2843
  "diagonal: give offset positionally OR as keyword, not both");
2749
2844
  }
2750
- offset = NUM2SIZE(RARRAY_AREF(rposary, 0));
2845
+ offset = ca_integer_arg(RARRAY_AREF(rposary, 0), "offset", NULL);
2751
2846
  }
2752
2847
  else {
2753
2848
  rb_raise(rb_eArgError,
data/ext/ca_obj_string.c CHANGED
@@ -216,7 +216,11 @@ ca_string_refuse_cell (VALUE v, const char *where)
216
216
  "UNDEF masks a cell)", where, rb_obj_classname(v));
217
217
  }
218
218
 
219
- /* Every cell of `parent` that is not masked is a String. */
219
+ /* Every cell of `parent` that is not masked is a String. Under a masked
220
+ cell of a parent that can be written, a value that is not a String (the
221
+ 0 an object array starts with) becomes an empty String, so that the cell
222
+ is still a String once it is unmasked. A view is written through its
223
+ value array, which leaves the mask as it is. */
220
224
  static void
221
225
  ca_string_check_cells (VALUE parent_val, CArray *parent)
222
226
  {
@@ -226,8 +230,14 @@ ca_string_check_cells (VALUE parent_val, CArray *parent)
226
230
  VALUE *p = (VALUE *) parent->ptr;
227
231
  boolean8_t *m = ( parent->mask && parent->mask->ptr )
228
232
  ? (boolean8_t *) parent->mask->ptr : NULL;
233
+ int writable = ! ca_is_readonly(parent);
229
234
  for (i = 0; i < parent->elements; i++) {
230
- if ( m && m[i] ) continue;
235
+ if ( m && m[i] ) {
236
+ if ( writable && ! RB_TYPE_P(p[i], T_STRING) ) {
237
+ p[i] = rb_utf8_str_new("", 0);
238
+ }
239
+ continue;
240
+ }
231
241
  if ( ! RB_TYPE_P(p[i], T_STRING) ) {
232
242
  snprintf(where, sizeof(where), "cell %" PRId64, (int64_t) i);
233
243
  ca_string_refuse_cell(p[i], where);
@@ -238,6 +248,7 @@ ca_string_check_cells (VALUE parent_val, CArray *parent)
238
248
  volatile VALUE list =
239
249
  rb_funcall(rb_funcall(parent_val, rb_intern("flatten"), 0),
240
250
  rb_intern("to_a"), 0);
251
+ volatile VALUE values = Qnil, raw = Qnil;
241
252
  long n = RARRAY_LEN(list), j;
242
253
  for (j = 0; j < n; j++) {
243
254
  VALUE v = rb_ary_entry(list, j);
@@ -247,6 +258,20 @@ ca_string_check_cells (VALUE parent_val, CArray *parent)
247
258
  ca_string_refuse_cell(v, where);
248
259
  }
249
260
  }
261
+ if ( ! ca_is_readonly(parent) ) {
262
+ for (j = 0; j < n; j++) {
263
+ if ( rb_ary_entry(list, j) != CA_UNDEF ) continue;
264
+ if ( NIL_P(values) ) {
265
+ values = rb_funcall(rb_funcall(parent_val, rb_intern("value"), 0),
266
+ rb_intern("flatten"), 0);
267
+ raw = rb_funcall(values, rb_intern("to_a"), 0);
268
+ }
269
+ if ( ! RB_TYPE_P(rb_ary_entry(raw, j), T_STRING) ) {
270
+ rb_funcall(values, rb_intern("[]="), 2, LONG2NUM(j),
271
+ rb_utf8_str_new("", 0));
272
+ }
273
+ }
274
+ }
250
275
  }
251
276
  }
252
277
 
data/ext/ca_obj_tile.c CHANGED
@@ -108,6 +108,8 @@ ca_tile_setup (CATile *ca, CArray *parent, ca_size_t *reps)
108
108
  ca->parent = parent;
109
109
  ca->attach = 0;
110
110
  ca->nosync = 0;
111
+ ca->reps = NULL; /* set before either allocation can raise */
112
+ ca->dim = NULL;
111
113
  ca->reps = ALLOC_N(ca_size_t, ndim);
112
114
  ca->dim = ALLOC_N(ca_size_t, ndim);
113
115
 
@@ -120,15 +122,33 @@ ca_tile_setup (CATile *ca, CArray *parent, ca_size_t *reps)
120
122
  return 0;
121
123
  }
122
124
 
125
+ typedef struct {
126
+ CATile *ca;
127
+ CArray *parent;
128
+ ca_size_t *reps;
129
+ } ca_tile_new_arg;
130
+
131
+ static VALUE
132
+ ca_tile_new_setup (VALUE varg)
133
+ {
134
+ ca_tile_new_arg *a = (ca_tile_new_arg *) varg;
135
+ ca_tile_setup(a->ca, a->parent, a->reps);
136
+ return Qnil;
137
+ }
138
+
139
+ /* The setup allocates reps and dim after the struct: when either fails,
140
+ the struct goes too. */
123
141
  CATile *
124
142
  ca_tile_new (CArray *parent, ca_size_t *reps)
125
143
  {
126
- CATile *ca;
144
+ ca_tile_new_arg a;
127
145
  ca_tile_setup(NULL, parent, reps);
128
- ca = ALLOC(CATile);
129
- ca_tile_setup(ca, parent, reps);
130
- ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
131
- return ca;
146
+ a.ca = ALLOC(CATile);
147
+ a.parent = parent;
148
+ a.reps = reps;
149
+ ca_fill_or_free((CArray *) a.ca, ca_tile_new_setup, (VALUE) &a);
150
+ ca_fill_or_free((CArray *) a.ca, ca_view_inherit_mask, (VALUE) a.ca);
151
+ return a.ca;
132
152
  }
133
153
 
134
154
  static void
@@ -548,7 +568,7 @@ rb_ca_tile (int argc, VALUE *argv, VALUE self)
548
568
  len, ca->ndim);
549
569
  }
550
570
  for ( i = 0; i < ca->ndim; i++ ) {
551
- reps[i] = NUM2SIZE(rb_ary_entry(ary, i));
571
+ reps[i] = ca_integer_arg(rb_ary_entry(ary, i), "a repeat count", NULL);
552
572
  }
553
573
  }
554
574
  else {
@@ -559,7 +579,7 @@ rb_ca_tile (int argc, VALUE *argv, VALUE self)
559
579
  ca->ndim, argc);
560
580
  }
561
581
  for ( i = 0; i < ca->ndim; i++ ) {
562
- reps[i] = NUM2SIZE(argv[i]);
582
+ reps[i] = ca_integer_arg(argv[i], "a repeat count", NULL);
563
583
  }
564
584
  }
565
585
 
@@ -623,6 +643,7 @@ Init_ca_obj_tile (void)
623
643
  &catile_data_type,
624
644
  rb_cCATileMask,
625
645
  &catile_mask_data_type, &ca_tile_func, sizeof(ca_tile_func));
646
+ ca_register_cell_moving_view(CA_OBJ_TILE);
626
647
  rb_define_const(rb_cObject, "CA_OBJ_TILE", INT2NUM(CA_OBJ_TILE));
627
648
 
628
649
  rb_define_method(rb_cCArray, "tile", rb_ca_tile, -1);
@@ -42,8 +42,8 @@ ca_trans_setup (CAStride *ca, CArray *parent, ca_size_t *imap)
42
42
  "specified %i-th dimension number out of range", i);
43
43
  }
44
44
  if (map[idim] != -1) {
45
- rb_raise(rb_eArgError,
46
- "specified %i-th dimension number is duplicated", i);
45
+ rb_raise(rb_eArgError, "%s: axis %" PRId64 " is given twice",
46
+ ca_calling_method_name(), (int64_t) idim);
47
47
  }
48
48
  map[idim] = i;
49
49
  newdim[i] = dim0[idim];
@@ -120,10 +120,14 @@ rb_ca_trans (int argc, VALUE *argv, VALUE self)
120
120
  for (i = 0; i < ca->ndim; i++) imap[i] = ca->ndim - i - 1;
121
121
  }
122
122
  else if (argc == ca->ndim) {
123
- for (i = 0; i < ca->ndim; i++) imap[i] = NUM2SIZE(argv[i]);
123
+ /* Each axis read as every axis argument is: negative from the end. */
124
+ for (i = 0; i < ca->ndim; i++) {
125
+ imap[i] = ca_normalize_axis_arg(argv[i], ca->ndim, NULL);
126
+ }
124
127
  }
125
128
  else {
126
- rb_raise(rb_eArgError, "# of arguments should be equal to ndim");
129
+ rb_raise(rb_eArgError, "%s: %d axes given for ndim %d",
130
+ ca_calling_method_name(), argc, (int) ca->ndim);
127
131
  }
128
132
 
129
133
  obj = rb_ca_trans_new(self, imap);
data/ext/ca_obj_triop.c CHANGED
@@ -267,9 +267,11 @@ pull_operand (CArray *op, int is_scalar, const ca_lazy_req_t *req,
267
267
 
268
268
  /* Evaluate the cells a request names into `data` (packed, n cells). */
269
269
  static void
270
- ca_triop_eval (CATriOp *to, const ca_lazy_req_t *req, void *data)
270
+ ca_triop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
271
271
  {
272
+ CATriOp *to = (CATriOp *) node;
272
273
  ca_size_t slab_n = req->n;
274
+ (void) dir;
273
275
  void *scratch2, *scratch3;
274
276
  ca_size_t op2_step, op3_step;
275
277
 
@@ -316,6 +318,12 @@ ca_triop_eval (CATriOp *to, const ca_lazy_req_t *req, void *data)
316
318
  ca_lazy_arena_release(scratch2);
317
319
  }
318
320
 
321
+ static void
322
+ ca_triop_eval (CATriOp *to, const ca_lazy_req_t *req, void *data)
323
+ {
324
+ ca_lazy_eval_recorded(to, 3, to->operands, 0, NULL, ca_triop_eval_cells, req, data, CA_XFER_GET);
325
+ }
326
+
319
327
  static void
320
328
  ca_triop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
321
329
  ca_size_t *strides, void *data, int dir)
@@ -542,6 +550,9 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
542
550
  r3 = ca_lazy_settle_scalar(r3, r3_scalar);
543
551
  }
544
552
 
553
+ ca_refuse_without_common_type(r1, r2);
554
+ ca_refuse_without_common_type(r1, r3);
555
+ ca_refuse_without_common_type(r2, r3);
545
556
  TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
546
557
  TypedData_Get_Struct(r2, CArray, &carray_data_type, op2);
547
558
  TypedData_Get_Struct(r3, CArray, &carray_data_type, op3);