carray 3.0.3 → 3.0.5

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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +469 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_descriptor.h +27 -24
  7. data/ext/ca_axis_dispatch.c +14 -26
  8. data/ext/ca_axis_group.c +27 -27
  9. data/ext/ca_binop_dispatch.c +1 -29
  10. data/ext/ca_binop_dispatch.h +0 -1
  11. data/ext/ca_categorical_iterator.c +231 -153
  12. data/ext/ca_compare.c +9 -20
  13. data/ext/ca_composite_dispatch.c +9 -9
  14. data/ext/ca_group_iter.c +16 -17
  15. data/ext/ca_kernel_iterator.c +1240 -2000
  16. data/ext/ca_kernel_iterator.h +502 -519
  17. data/ext/ca_monop_dispatch.c +82 -59
  18. data/ext/ca_monop_dispatch.h +1 -0
  19. data/ext/ca_obj_array.c +152 -45
  20. data/ext/ca_obj_bincmp.c +25 -21
  21. data/ext/ca_obj_binop.c +16 -26
  22. data/ext/ca_obj_bitarray.c +8 -3
  23. data/ext/ca_obj_bitfield.c +63 -49
  24. data/ext/ca_obj_block.c +18 -9
  25. data/ext/ca_obj_byte_swap.c +7 -7
  26. data/ext/ca_obj_const_string.c +188 -22
  27. data/ext/ca_obj_face.c +280 -58
  28. data/ext/ca_obj_face.h +19 -0
  29. data/ext/ca_obj_fake.c +15 -29
  30. data/ext/ca_obj_farray.c +6 -3
  31. data/ext/ca_obj_field.c +30 -8
  32. data/ext/ca_obj_fixlen_string.c +2 -3
  33. data/ext/ca_obj_grid.c +455 -134
  34. data/ext/ca_obj_meld.c +172 -34
  35. data/ext/ca_obj_moncmp.c +23 -8
  36. data/ext/ca_obj_monop.c +113 -67
  37. data/ext/ca_obj_object.c +210 -52
  38. data/ext/ca_obj_record.c +15 -7
  39. data/ext/ca_obj_reduce.c +17 -8
  40. data/ext/ca_obj_refer.c +42 -35
  41. data/ext/ca_obj_remap.c +30 -9
  42. data/ext/ca_obj_repeat.c +9 -10
  43. data/ext/ca_obj_roll.c +31 -10
  44. data/ext/ca_obj_select.c +48 -14
  45. data/ext/ca_obj_select_axis.c +15 -47
  46. data/ext/ca_obj_shift.c +41 -7
  47. data/ext/ca_obj_stack.c +180 -34
  48. data/ext/ca_obj_stride.c +190 -134
  49. data/ext/ca_obj_string.c +80 -4
  50. data/ext/ca_obj_tile.c +32 -11
  51. data/ext/ca_obj_time.c +22 -6
  52. data/ext/ca_obj_timedelta.c +22 -6
  53. data/ext/ca_obj_transpose.c +13 -7
  54. data/ext/ca_obj_triop.c +26 -32
  55. data/ext/ca_obj_window.c +267 -74
  56. data/ext/ca_op_ipower.c +4 -4
  57. data/ext/ca_sort_kernels.h +6 -3
  58. data/ext/ca_sweep_engine.c +2 -2
  59. data/ext/caframe_csv_reader.c +80 -1
  60. data/ext/carray.h +214 -75
  61. data/ext/carray_access.c +200 -99
  62. data/ext/carray_address_basis.c +343 -87
  63. data/ext/carray_attribute.c +113 -6
  64. data/ext/carray_bincount.c +10 -8
  65. data/ext/carray_broadcast.c +13 -8
  66. data/ext/carray_build_flags.h +3 -0
  67. data/ext/carray_call_cfunc.c +108 -0
  68. data/ext/carray_cast.c +375 -96
  69. data/ext/carray_cast_func.rb +90 -20
  70. data/ext/carray_class.c +4 -3
  71. data/ext/carray_config.h +28 -0
  72. data/ext/carray_conversion.c +18 -9
  73. data/ext/carray_copy.c +51 -14
  74. data/ext/carray_core.c +150 -55
  75. data/ext/carray_count.c +56 -20
  76. data/ext/carray_element.c +81 -33
  77. data/ext/carray_factorize.c +138 -84
  78. data/ext/carray_generate.c +199 -52
  79. data/ext/carray_histogram.c +63 -18
  80. data/ext/carray_hold.c +12 -15
  81. data/ext/carray_index_classifier.c +388 -176
  82. data/ext/carray_internal.h +348 -29
  83. data/ext/carray_kernels_bincmp.c +4453 -0
  84. data/ext/carray_kernels_binop.c +11368 -0
  85. data/ext/carray_kernels_init.c +1734 -0
  86. data/ext/carray_kernels_moncmp.c +2104 -0
  87. data/ext/carray_kernels_monop.c +18369 -0
  88. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  89. data/ext/carray_kernels_reduce_boolean.c +406 -0
  90. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  91. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  92. data/ext/carray_kernels_reduce_variance.c +3849 -0
  93. data/ext/carray_kernels_scan.c +4302 -0
  94. data/ext/carray_kernels_search.c +33829 -0
  95. data/ext/carray_kernels_sort.c +10897 -0
  96. data/ext/carray_kernels_triop.c +1864 -0
  97. data/ext/carray_key_windows.c +220 -0
  98. data/ext/carray_lazy.c +170 -111
  99. data/ext/carray_mask.c +337 -94
  100. data/ext/carray_median_percentile.c +82 -28
  101. data/ext/carray_memory_view.c +669 -561
  102. data/ext/carray_operator.c +129 -52
  103. data/ext/carray_order.c +604 -77
  104. data/ext/carray_partition.c +28 -27
  105. data/ext/carray_random.c +85 -31
  106. data/ext/carray_round.c +273 -0
  107. data/ext/carray_scatter.c +87 -45
  108. data/ext/carray_segment.c +3 -3
  109. data/ext/carray_slab.c +123 -37
  110. data/ext/carray_sort.c +259 -51
  111. data/ext/carray_sort_kernel.c +5 -7
  112. data/ext/carray_struct.c +54 -61
  113. data/ext/carray_test.c +26 -5
  114. data/ext/carray_time_text.c +4 -2
  115. data/ext/carray_undef.c +4 -0
  116. data/ext/carray_utils.c +309 -66
  117. data/ext/mk_call_cfunc.rb +19 -2
  118. data/ext/mkkernel.rb +403 -350
  119. data/ext/ruby_carray.c +10 -4
  120. data/ext/version.h +4 -4
  121. data/lib/carray/attribute.rb +139 -22
  122. data/lib/carray/autoload_carray.rb +8 -31
  123. data/lib/carray/autoload_method_extension.rb +14 -5
  124. data/lib/carray/axis_group.rb +255 -106
  125. data/lib/carray/basics.rb +105 -17
  126. data/lib/carray/bincount_nd.rb +12 -2
  127. data/lib/carray/block_iterator.rb +126 -23
  128. data/lib/carray/boolean_reduce.rb +17 -17
  129. data/lib/carray/categorical.rb +195 -59
  130. data/lib/carray/categorical_iterator.rb +132 -29
  131. data/lib/carray/complex.rb +5 -5
  132. data/lib/carray/conditional.rb +67 -18
  133. data/lib/carray/const_string.rb +24 -5
  134. data/lib/carray/construct.rb +15 -11
  135. data/lib/carray/data_type_extension.rb +9 -1
  136. data/lib/carray/distance_limit.rb +77 -0
  137. data/lib/carray/fixlen_string.rb +48 -3
  138. data/lib/carray/frame/column_verbs.rb +71 -0
  139. data/lib/carray/frame/concat.rb +17 -3
  140. data/lib/carray/frame/convert.rb +69 -0
  141. data/lib/carray/frame/csv_parser.rb +111 -17
  142. data/lib/carray/frame/describe.rb +3 -2
  143. data/lib/carray/frame/frame.rb +252 -28
  144. data/lib/carray/frame/group.rb +603 -101
  145. data/lib/carray/frame/io.rb +117 -28
  146. data/lib/carray/frame/join.rb +312 -51
  147. data/lib/carray/frame/records.rb +83 -7
  148. data/lib/carray/frame/reshape.rb +42 -35
  149. data/lib/carray/frame/sort.rb +7 -19
  150. data/lib/carray/frame/verbs.rb +188 -48
  151. data/lib/carray/frame/windows.rb +117 -0
  152. data/lib/carray/frame.rb +2 -0
  153. data/lib/carray/fuse_source.rb +42 -6
  154. data/lib/carray/fusion.rb +40 -16
  155. data/lib/carray/histogram.rb +6 -1
  156. data/lib/carray/inspect.rb +31 -8
  157. data/lib/carray/iterator.rb +85 -7
  158. data/lib/carray/key_windows.rb +620 -0
  159. data/lib/carray/lazy.rb +217 -183
  160. data/lib/carray/mask_gap_fill.rb +63 -57
  161. data/lib/carray/meld_reduce.rb +12 -14
  162. data/lib/carray/methods/bin.rb +26 -18
  163. data/lib/carray/methods/bincount.rb +16 -0
  164. data/lib/carray/methods/bit_string.rb +1 -1
  165. data/lib/carray/methods/choose.rb +16 -3
  166. data/lib/carray/methods/composition.rb +3 -1
  167. data/lib/carray/methods/gather_nd.rb +14 -5
  168. data/lib/carray/methods/insert_block.rb +2 -2
  169. data/lib/carray/methods/is_between.rb +59 -0
  170. data/lib/carray/methods/is_in.rb +61 -37
  171. data/lib/carray/methods/join.rb +1 -6
  172. data/lib/carray/methods/lag.rb +49 -0
  173. data/lib/carray/methods/locate_addr.rb +47 -51
  174. data/lib/carray/methods/mode.rb +1 -1
  175. data/lib/carray/methods/nunique.rb +11 -6
  176. data/lib/carray/methods/resize.rb +3 -3
  177. data/lib/carray/methods/value_counts.rb +4 -11
  178. data/lib/carray/record_order.rb +3 -3
  179. data/lib/carray/rng.rb +3 -0
  180. data/lib/carray/runtime.rb +53 -8
  181. data/lib/carray/segment_iterator.rb +323 -88
  182. data/lib/carray/serialize.rb +45 -14
  183. data/lib/carray/slab_iterator.rb +50 -0
  184. data/lib/carray/stack.rb +15 -12
  185. data/lib/carray/string.rb +113 -5
  186. data/lib/carray/string_operation_extension.rb +21 -6
  187. data/lib/carray/struct.rb +9 -21
  188. data/lib/carray/struct_builder.rb +24 -11
  189. data/lib/carray/time.rb +819 -158
  190. data/lib/carray/window_iterator.rb +122 -16
  191. data/lib/carray.rb +7 -0
  192. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  193. data/yard-stubs/ca_obj_field.rb +4 -0
  194. data/yard-stubs/ca_obj_grid.rb +21 -10
  195. data/yard-stubs/ca_obj_refer.rb +2 -1
  196. data/yard-stubs/ca_obj_roll.rb +2 -2
  197. data/yard-stubs/ca_obj_shift.rb +5 -1
  198. data/yard-stubs/ca_obj_tile.rb +1 -1
  199. data/yard-stubs/ca_obj_transpose.rb +5 -4
  200. data/yard-stubs/ca_obj_window.rb +8 -3
  201. data/yard-stubs/carray_access.rb +6 -1
  202. data/yard-stubs/carray_attribute.rb +3 -6
  203. data/yard-stubs/carray_cast.rb +12 -0
  204. data/yard-stubs/carray_conversion.rb +7 -2
  205. data/yard-stubs/carray_copy.rb +4 -2
  206. data/yard-stubs/carray_count.rb +4 -4
  207. data/yard-stubs/carray_element.rb +17 -10
  208. data/yard-stubs/carray_generate.rb +21 -1
  209. data/yard-stubs/carray_mask.rb +32 -3
  210. data/yard-stubs/carray_math.rb +41 -5
  211. data/yard-stubs/carray_median_percentile.rb +5 -2
  212. data/yard-stubs/carray_memory_view.rb +40 -23
  213. data/yard-stubs/carray_order.rb +129 -22
  214. data/yard-stubs/carray_random.rb +13 -4
  215. data/yard-stubs/carray_scatter.rb +17 -10
  216. data/yard-stubs/carray_slab.rb +9 -2
  217. data/yard-stubs/carray_sort.rb +6 -5
  218. data/yard-stubs/carray_test.rb +5 -3
  219. metadata +25 -3
  220. data/lib/carray/arrow_tensor.rb +0 -401
  221. data/lib/carray/table.rb +0 -108
data/ext/ca_obj_grid.c CHANGED
@@ -35,9 +35,14 @@ typedef struct {
35
35
  /* INDEX-only. Owned in the non-share case (xfree'd by free_ca_grid),
36
36
  aliased in the share case (parent owns; CA_FLAG_SHARE_INDEX). */
37
37
  ca_size_t *indices;
38
+ /* INDEX-only, NULL when no cell is a hole. holes[i] marks a position
39
+ whose index cell was masked: it selects no parent cell. Its entry in
40
+ indices is 0 and is never range-checked. Owned and aliased as
41
+ indices is. */
42
+ uint8_t *holes;
38
43
  } cag_axis_t;
39
44
 
40
- typedef struct {
45
+ typedef struct _CAGrid {
41
46
  int16_t obj_type;
42
47
  int8_t data_type;
43
48
  int8_t ndim;
@@ -53,6 +58,18 @@ typedef struct {
53
58
  uint8_t nosync;
54
59
  /* -------------*/
55
60
  cag_axis_t *axes; /* [ndim], owned (non-share) or aliased (share) */
61
+ /* A grid with holes (cag_axis_t.holes): set on its mask grid, whose
62
+ holes read as masked. The data grid's holes read the cell at index 0
63
+ of each holed axis, as project and gather_nd do. */
64
+ int8_t holes_read_masked;
65
+ /* A grid with holes over a parent that has no mask: the all-false mask
66
+ its mask grid reads instead, so that showing the holes gives the
67
+ parent no mask. Owned by this grid; NULL otherwise. */
68
+ CArray *mask_standin;
69
+ /* On a mask grid: the grid whose mask it is, which holds the stand-in
70
+ and the parent whose mask replaces it. A Ruby copy of the mask keeps
71
+ that grid alive (rb_ca_mask_array sets masked_array). */
72
+ struct _CAGrid *mask_owner;
56
73
  } CAGrid;
57
74
 
58
75
  static size_t
@@ -70,6 +87,9 @@ ca_grid_dsize (const void *ap)
70
87
  if (ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].indices) {
71
88
  total += ca->axes[k].count * sizeof(ca_size_t);
72
89
  }
90
+ if (ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].holes) {
91
+ total += ca->axes[k].count;
92
+ }
73
93
  }
74
94
  return total;
75
95
  }
@@ -160,13 +180,15 @@ ca_grid_check_protos (CArray *parent, int8_t ndim, const cag_axis_t *protos)
160
180
  if (protos[k].kind == CAG_AXIS_STRIDE) {
161
181
  ca_size_t first = protos[k].start;
162
182
  ca_size_t last = first + (protos[k].count - 1) * protos[k].step;
163
- CA_CHECK_INDEX(first, parent->dim[k]);
164
- if (protos[k].count > 1) CA_CHECK_INDEX(last, parent->dim[k]);
183
+ ca_index_in_range_at(first, parent->dim[k], k);
184
+ if (protos[k].count > 1) ca_index_in_range_at(last, parent->dim[k], k);
165
185
  }
166
186
  else {
167
187
  for (j = 0; j < protos[k].count; j++) {
168
- ca_size_t v = protos[k].indices[j];
169
- CA_CHECK_INDEX(v, parent->dim[k]);
188
+ if ( protos[k].holes && protos[k].holes[j] ) {
189
+ continue;
190
+ }
191
+ ca_index_in_range_at(protos[k].indices[j], parent->dim[k], k);
170
192
  }
171
193
  }
172
194
  }
@@ -207,6 +229,9 @@ ca_grid_setup (CAGrid *ca, CArray *parent, int8_t ndim,
207
229
  ca->parent = parent;
208
230
  ca->attach = 0;
209
231
  ca->nosync = 0;
232
+ ca->holes_read_masked = 0;
233
+ ca->mask_standin = NULL;
234
+ ca->mask_owner = NULL;
210
235
 
211
236
  for (k = 0; k < ndim; k++) ca->dim[k] = protos[k].count;
212
237
 
@@ -217,7 +242,9 @@ ca_grid_setup (CAGrid *ca, CArray *parent, int8_t ndim,
217
242
  ca_set_flag(ca, CA_FLAG_SHARE_INDEX);
218
243
  ca->axes = protos;
219
244
  } else {
220
- ca->axes = ALLOC_N(cag_axis_t, ndim);
245
+ /* Zeroed: an axis's indices or holes that cannot be allocated raises
246
+ with the later axes not yet copied, and free_ca_grid reads them. */
247
+ ca->axes = ZALLOC_N(cag_axis_t, ndim);
221
248
  for (k = 0; k < ndim; k++) {
222
249
  ca->axes[k].kind = protos[k].kind;
223
250
  ca->axes[k].count = protos[k].count;
@@ -225,42 +252,126 @@ ca_grid_setup (CAGrid *ca, CArray *parent, int8_t ndim,
225
252
  ca->axes[k].start = protos[k].start;
226
253
  ca->axes[k].step = protos[k].step;
227
254
  ca->axes[k].indices = NULL;
255
+ ca->axes[k].holes = NULL;
228
256
  } else {
229
257
  ca->axes[k].start = 0;
230
258
  ca->axes[k].step = 0;
231
259
  ca->axes[k].indices = ALLOC_N(ca_size_t,
232
260
  protos[k].count > 0 ? protos[k].count : 1);
261
+ ca->axes[k].holes = NULL;
233
262
  ca_size_t j;
263
+ if ( protos[k].holes ) {
264
+ for (j = 0; j < protos[k].count && ! protos[k].holes[j]; j++) ;
265
+ if ( j < protos[k].count ) {
266
+ ca->axes[k].holes = ALLOC_N(uint8_t, protos[k].count);
267
+ memcpy(ca->axes[k].holes, protos[k].holes, protos[k].count);
268
+ }
269
+ }
234
270
  for (j = 0; j < protos[k].count; j++) {
235
271
  ca_size_t v = protos[k].indices[j];
272
+ if ( ca->axes[k].holes && ca->axes[k].holes[j] ) {
273
+ ca->axes[k].indices[j] = 0;
274
+ continue;
275
+ }
236
276
  ca->axes[k].indices[j] = ( v < 0 ) ? v + parent->dim[k] : v;
237
277
  }
238
278
  }
239
279
  }
240
280
  }
241
281
 
242
- if ( ca_has_mask(parent) ) {
243
- ca_create_mask(ca);
244
- }
282
+ return 0;
283
+ }
245
284
 
285
+ /* Whether any position of the grid is a hole (cag_axis_t.holes). */
286
+ static int
287
+ cag_has_holes (const CAGrid *ca)
288
+ {
289
+ int8_t k;
290
+ for (k = 0; k < ca->ndim; k++) {
291
+ if ( ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].holes ) {
292
+ return 1;
293
+ }
294
+ }
246
295
  return 0;
247
296
  }
248
297
 
249
- CAGrid *
250
- ca_grid_new (CArray *parent, int8_t ndim, cag_axis_t *protos)
298
+ int
299
+ ca_grid_has_holes (void *ap)
300
+ {
301
+ return cag_has_holes((const CAGrid *) ap);
302
+ }
303
+
304
+ /* A grid with holes has a mask from the start: its holes read as masked
305
+ whatever the parent holds. A mask grid (holes_read_masked) takes none.
306
+ A view of a value array or a mask array can never carry a mask, so it
307
+ can not show a hole. */
308
+ static VALUE
309
+ ca_grid_inherit_mask (VALUE view)
310
+ {
311
+ CAGrid *ca = (CAGrid *) view;
312
+ if ( cag_has_holes(ca) && ! ca->holes_read_masked ) {
313
+ if ( ca_is_value_array(ca) || ca_is_mask_array(ca) ) {
314
+ rb_raise(rb_eArgError,
315
+ "a masked index cell reads as masked, which a view of the %s "
316
+ "array can not hold", ca_is_value_array(ca) ? "value" : "mask");
317
+ }
318
+ ca_create_mask((CArray *) ca);
319
+ return Qnil;
320
+ }
321
+ return ca_view_inherit_mask(view);
322
+ }
323
+
324
+ typedef struct {
325
+ CAGrid *ca;
326
+ CArray *parent;
327
+ int8_t ndim;
328
+ cag_axis_t *protos;
329
+ } ca_grid_new_arg;
330
+
331
+ static VALUE
332
+ ca_grid_new_setup (VALUE varg)
333
+ {
334
+ ca_grid_new_arg *a = (ca_grid_new_arg *) varg;
335
+ ca_grid_setup(a->ca, a->parent, a->ndim, a->protos, 0);
336
+ return Qnil;
337
+ }
338
+
339
+ /* holes_read_masked is the copied grid's: a copy of a mask grid is one.
340
+ The setup allocates the axes after the struct and its pool: when that
341
+ fails, the struct goes too. */
342
+ static CAGrid *
343
+ ca_grid_new_as (CArray *parent, int8_t ndim, cag_axis_t *protos,
344
+ int8_t holes_read_masked, CAGrid *mask_owner)
251
345
  {
252
346
  CAGrid *ca;
347
+ ca_grid_new_arg a;
253
348
  ca_grid_check_protos(parent, ndim, protos);
349
+ /* check: ca_grid_check_protos above */
254
350
  ca = (CAGrid *) ca_array_alloc(CA_OBJ_GRID, ndim);
255
- ca_grid_setup(ca, parent, ndim, protos, 0);
351
+ a.ca = ca;
352
+ a.parent = parent;
353
+ a.ndim = ndim;
354
+ a.protos = protos;
355
+ ca_fill_or_free((CArray *) ca, ca_grid_new_setup, (VALUE) &a);
356
+ ca->holes_read_masked = holes_read_masked;
357
+ ca->mask_owner = mask_owner;
358
+ ca_fill_or_free((CArray *) ca, ca_grid_inherit_mask, (VALUE) ca);
256
359
  return ca;
257
360
  }
258
361
 
362
+ CAGrid *
363
+ ca_grid_new (CArray *parent, int8_t ndim, cag_axis_t *protos)
364
+ {
365
+ return ca_grid_new_as(parent, ndim, protos, 0, NULL);
366
+ }
367
+
259
368
  CAGrid *
260
369
  ca_grid_new_share (CArray *parent, int8_t ndim, cag_axis_t *protos)
261
370
  {
371
+ /* check: the mask grid copies its own grid's checked axes */
262
372
  CAGrid *ca = (CAGrid *) ca_array_alloc(CA_OBJ_GRID, ndim);
263
373
  ca_grid_setup(ca, parent, ndim, protos, 1);
374
+ ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
264
375
  return ca;
265
376
  }
266
377
 
@@ -270,12 +381,16 @@ free_ca_grid (void *ap)
270
381
  CAGrid *ca = (CAGrid *) ap;
271
382
  if ( ca != NULL ) {
272
383
  ca_free(ca->mask);
273
- if ( ! (ca->flags & CA_FLAG_SHARE_INDEX) ) {
384
+ ca_free(ca->mask_standin);
385
+ if ( ! (ca->flags & CA_FLAG_SHARE_INDEX) && ca->axes ) {
274
386
  int8_t k;
275
387
  for (k = 0; k < ca->ndim; k++) {
276
388
  if ( ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].indices ) {
277
389
  xfree(ca->axes[k].indices);
278
390
  }
391
+ if ( ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].holes ) {
392
+ xfree(ca->axes[k].holes);
393
+ }
279
394
  }
280
395
  xfree(ca->axes);
281
396
  }
@@ -301,7 +416,8 @@ static void *
301
416
  ca_grid_func_clone (void *ap)
302
417
  {
303
418
  CAGrid *ca = (CAGrid *) ap;
304
- return ca_grid_new(ca->parent, ca->ndim, ca->axes);
419
+ return ca_grid_new_as(ca->parent, ca->ndim, ca->axes, ca->holes_read_masked,
420
+ ca->mask_owner);
305
421
  }
306
422
 
307
423
  /* Per-axis parent-index lookup. STRIDE = one multiply, INDEX = one
@@ -315,6 +431,124 @@ cag_parent_index_for (const cag_axis_t *ax, ca_size_t i)
315
431
  return ax->indices[i];
316
432
  }
317
433
 
434
+ /* A mask grid over its owner's stand-in (ca_mask_standin_current): what
435
+ it reads from now on. */
436
+ static void
437
+ cag_mask_current (CAGrid *m)
438
+ {
439
+ CAGrid *o = m->mask_owner;
440
+ if ( o != NULL ) {
441
+ m->parent = ca_mask_standin_current((CArray *) m, m->parent, o->parent,
442
+ o->mask_standin);
443
+ }
444
+ }
445
+
446
+ /* After a write of n cells of `data` to a mask grid: a true that went
447
+ into the stand-in goes on to the parent's mask. */
448
+ static void
449
+ cag_mask_settle_after (CAGrid *m, const void *data, ca_size_t n)
450
+ {
451
+ CAGrid *o = m->mask_owner;
452
+ if ( o != NULL && m->parent == o->mask_standin && ca_mask_any_true(data, n) ) {
453
+ ca_mask_standin_settle(o->parent, o->mask_standin);
454
+ }
455
+ }
456
+
457
+ /* What the GET of a hole writes when it reads no parent cell: true in a
458
+ mask grid, an empty cell otherwise (a parent with no cell to read). */
459
+ static void
460
+ cag_hole_cells (const CAGrid *ca, char *data, ca_size_t n)
461
+ {
462
+ ca_size_t i;
463
+ if ( ca->holes_read_masked ) {
464
+ memset(data, 1, (size_t) n);
465
+ }
466
+ else if ( ca->data_type == CA_OBJECT ) {
467
+ for (i = 0; i < n; i++) ((VALUE *) data)[i] = INT2FIX(0);
468
+ }
469
+ else {
470
+ memset(data, 0, (size_t) (n * ca->bytes));
471
+ }
472
+ }
473
+
474
+ /* The parent address of the view cell at idx. A hole takes index 0 on its
475
+ holed axes; the return says whether the cell is one. */
476
+ static int
477
+ cag_parent_addr (const CAGrid *ca, const ca_size_t *idx, ca_size_t *paddr)
478
+ {
479
+ ca_size_t pa = 0;
480
+ int hole = 0;
481
+ int8_t k;
482
+ for (k = 0; k < ca->ndim; k++) {
483
+ const cag_axis_t *ax = &ca->axes[k];
484
+ if ( ax->kind == CAG_AXIS_INDEX && ax->holes && ax->holes[idx[k]] ) {
485
+ hole = 1;
486
+ }
487
+ pa = pa * ca->parent->dim[k] + cag_parent_index_for(ax, idx[k]);
488
+ }
489
+ *paddr = pa;
490
+ return hole;
491
+ }
492
+
493
+ /* The transfer of a grid with holes, for the view cells at addrs (NULL:
494
+ the whole view in order). The parent is asked for one batch of
495
+ addresses. A GET reads every cell, a hole reading the cell at index 0
496
+ of its holed axes (in a mask grid it is then set true); a PUT leaves
497
+ the holes out. */
498
+ static void
499
+ ca_grid_holed_xfer (CAGrid *ca, ca_size_t n, const ca_size_t *addrs,
500
+ char *data, int dir)
501
+ {
502
+ CArray *parent;
503
+ ca_size_t bytes = ca->bytes;
504
+ volatile VALUE h_addr = 0, h_hole = 0, h_data = 0;
505
+ ca_size_t *paddrs;
506
+ uint8_t *hole;
507
+ ca_size_t i, m, idx[CA_RANK_MAX];
508
+
509
+ if ( n <= 0 ) {
510
+ return;
511
+ }
512
+ cag_mask_current(ca);
513
+ parent = ca->parent;
514
+ paddrs = ALLOCV_N(ca_size_t, h_addr, n);
515
+ hole = ALLOCV_N(uint8_t, h_hole, n);
516
+ for (i = 0; i < n; i++) {
517
+ ca_addr2index((CArray *) ca, addrs ? addrs[i] : i, idx);
518
+ hole[i] = (uint8_t) cag_parent_addr(ca, idx, &paddrs[i]);
519
+ }
520
+
521
+ if ( dir == CA_XFER_GET ) {
522
+ if ( parent->elements == 0 ) {
523
+ cag_hole_cells(ca, data, n); /* every cell is a hole */
524
+ }
525
+ else {
526
+ ca_xfer_addrs(parent, n, paddrs, data, CA_XFER_GET);
527
+ if ( ca->holes_read_masked ) {
528
+ for (i = 0; i < n; i++) {
529
+ if ( hole[i] ) cag_hole_cells(ca, data + i * bytes, 1);
530
+ }
531
+ }
532
+ }
533
+ }
534
+ else {
535
+ char *pdata = ALLOCV_N(char, h_data, n * bytes);
536
+ for (i = 0, m = 0; i < n; i++) {
537
+ if ( hole[i] ) continue;
538
+ paddrs[m] = paddrs[i];
539
+ memcpy(pdata + m * bytes, data + i * bytes, (size_t) bytes);
540
+ m++;
541
+ }
542
+ if ( m > 0 ) {
543
+ ca_xfer_addrs(parent, m, paddrs, pdata, CA_XFER_PUT);
544
+ cag_mask_settle_after(ca, pdata, m);
545
+ }
546
+ ALLOCV_END(h_data);
547
+ }
548
+ ALLOCV_END(h_hole);
549
+ ALLOCV_END(h_addr);
550
+ }
551
+
318
552
  /* per-cell logic lives in ca_grid_func_xfer_index; forward-declared for
319
553
  the region-delivery paths below. */
320
554
  static void ca_grid_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir);
@@ -327,10 +561,28 @@ ca_grid_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
327
561
  CAGrid *ca = (CAGrid *) ap;
328
562
  ca_size_t idx0[CA_RANK_MAX];
329
563
  int8_t i;
564
+ int hole = 0;
330
565
  for (i = 0; i < ca->ndim; i++) {
331
- idx0[i] = cag_parent_index_for(&ca->axes[i], idx[i]);
566
+ const cag_axis_t *ax = &ca->axes[i];
567
+ idx0[i] = cag_parent_index_for(ax, idx[i]);
568
+ if ( ax->kind == CAG_AXIS_INDEX && ax->holes && ax->holes[idx[i]] ) {
569
+ hole = 1;
570
+ }
571
+ }
572
+ if ( hole ) {
573
+ if ( dir == CA_XFER_PUT ) {
574
+ return;
575
+ }
576
+ if ( ca->holes_read_masked || ca->parent->elements == 0 ) {
577
+ cag_hole_cells(ca, (char *) data, 1);
578
+ return;
579
+ }
332
580
  }
581
+ cag_mask_current(ca);
333
582
  ca_xfer_index(ca->parent, idx0, data, dir);
583
+ if ( dir == CA_XFER_PUT ) {
584
+ cag_mask_settle_after(ca, data, 1);
585
+ }
334
586
  }
335
587
 
336
588
  /* Batched address gather/scatter. Per-axis index remap
@@ -354,6 +606,11 @@ ca_grid_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
354
606
  int8_t k;
355
607
  volatile VALUE holder;
356
608
 
609
+ if ( cag_has_holes(ca) ) {
610
+ ca_grid_holed_xfer(ca, n, addrs, (char *) data, dir);
611
+ return;
612
+ }
613
+
357
614
  /* Opportunistic fast path. ca_resolve_attached_root_via_identity lifts
358
615
  parent->ptr through an identity CAStride compose-fold (handles a
359
616
  flatten[idx].reshape(*) chain etc.). */
@@ -409,8 +666,9 @@ ca_grid_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
409
666
  int8_t k;
410
667
 
411
668
  for (k = 0; k < g->ndim; k++) {
412
- if (g->axes[k].kind == CAG_AXIS_INDEX && g->axes[k].count != 1) {
413
- return 0; /* multi-INDEX axis -> true gather, decline */
669
+ if (g->axes[k].kind == CAG_AXIS_INDEX
670
+ && (g->axes[k].count != 1 || g->axes[k].holes)) {
671
+ return 0; /* multi-INDEX axis -> true gather, decline; a hole is no stride */
414
672
  }
415
673
  }
416
674
 
@@ -505,8 +763,9 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
505
763
  /* The request is over the view's addresses, so a transposed / flat request
506
764
  is legal and must not be composed axis-by-axis; see
507
765
  ca_xfer_stride_request_is_axis_box (carray.h). */
508
- if ( ! ca_xfer_stride_request_is_axis_box(g, starts, counts, strides) ) {
509
- aligned = 0;
766
+ if ( cag_has_holes(g)
767
+ || ! ca_xfer_stride_request_is_axis_box(g, starts, counts, strides) ) {
768
+ aligned = 0; /* a grid with holes goes cell by cell (xfer_index) */
510
769
  }
511
770
  else {
512
771
  for (k = 0; k < ndim; k++) {
@@ -519,6 +778,7 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
519
778
  ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
520
779
  for (k = 0; k < ndim; k++) base += starts[k] * gnative[k];
521
780
  for (k = 0; k < ndim; k++) idx[k] = 0;
781
+ /* empty: the slot returns on an empty region at its entry */
522
782
  while (1) {
523
783
  ca_size_t goff = base, gidx[CA_RANK_MAX];
524
784
  for (k = 0; k < ndim; k++) goff += idx[k] * strides[k];
@@ -607,6 +867,7 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
607
867
  ca_xfer_stride/ca_xfer_addrs. */
608
868
  for (k = 0; k < ndim; k++) o[k] = 0;
609
869
 
870
+ /* empty: the slot returns on an empty region at its entry */
610
871
  while (1) {
611
872
  ca_size_t pbase = 0, doff = 0;
612
873
  for (k = 0; k < inner; k++) {
@@ -659,14 +920,22 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
659
920
  The predicate asks what the parent is, not whether it is attached now,
660
921
  so attach, sync and detach all take the same branch. */
661
922
 
923
+ /* A grid with holes fills and writes back its buffer through its own
924
+ transfers too: the descriptor engine has no hole. */
925
+ static int
926
+ cag_owns_buffer (CAGrid *ca)
927
+ {
928
+ return cag_has_holes(ca) || ca_parent_lends_no_memory(ca->parent);
929
+ }
930
+
662
931
  static void
663
932
  ca_grid_func_allocate (void *ap)
664
933
  {
665
934
  CAGrid *ca = (CAGrid *) ap;
666
- if ( ! ca_parent_lends_no_memory(ca->parent) ) {
935
+ if ( ! cag_owns_buffer(ca) ) {
667
936
  ca_attach(ca->parent);
668
937
  }
669
- ca->ptr = xmalloc(ca_length(ca));
938
+ ca->ptr = ca_alloc_cells((CArray *) ca);
670
939
  }
671
940
 
672
941
  static void
@@ -676,7 +945,7 @@ ca_grid_func_attach (void *ap)
676
945
  ca_axis_desc_t desc[CA_RANK_MAX];
677
946
  ca_size_t pdims[CA_RANK_MAX];
678
947
 
679
- if ( ca_parent_lends_no_memory(ca->parent) ) {
948
+ if ( cag_owns_buffer(ca) ) {
680
949
  ca_axis_view_attach_owned(ca);
681
950
  return;
682
951
  }
@@ -694,7 +963,7 @@ ca_grid_func_sync (void *ap)
694
963
  ca_axis_desc_t desc[CA_RANK_MAX];
695
964
  ca_size_t pdims[CA_RANK_MAX];
696
965
 
697
- if ( ca_parent_lends_no_memory(ca->parent) ) {
966
+ if ( cag_owns_buffer(ca) ) {
698
967
  ca_axis_view_sync_owned(ca);
699
968
  return;
700
969
  }
@@ -711,7 +980,7 @@ ca_grid_func_detach (void *ap)
711
980
  CAGrid *ca = (CAGrid *) ap;
712
981
  xfree(ca->ptr);
713
982
  ca->ptr = NULL;
714
- if ( ! ca_parent_lends_no_memory(ca->parent) ) {
983
+ if ( ! cag_owns_buffer(ca) ) {
715
984
  ca_detach(ca->parent);
716
985
  }
717
986
  }
@@ -736,9 +1005,41 @@ ca_grid_func_run_fast_path (void *ap, char *data, int dir)
736
1005
  static void
737
1006
  ca_grid_func_xfer_all (void *ap, void *data, int dir)
738
1007
  {
1008
+ CAGrid *ca = (CAGrid *) ap;
1009
+ if ( cag_has_holes(ca) ) {
1010
+ ca_grid_holed_xfer(ca, ca->elements, NULL, (char *) data, dir);
1011
+ return;
1012
+ }
739
1013
  ca_axis_view_xfer_all(ap, ca_grid_func_run_fast_path, 1, data, dir);
740
1014
  }
741
1015
 
1016
+ /* The fill of a grid with holes: the parent cells of the positions that
1017
+ are not holes, as one address batch. */
1018
+ static void
1019
+ ca_grid_holed_fill (CAGrid *ca, void *ptr)
1020
+ {
1021
+ volatile VALUE holder = 0;
1022
+ ca_size_t *paddrs;
1023
+ ca_size_t i, m = 0, pa, idx[CA_RANK_MAX];
1024
+
1025
+ if ( ca->elements <= 0 ) {
1026
+ return;
1027
+ }
1028
+ cag_mask_current(ca);
1029
+ paddrs = ALLOCV_N(ca_size_t, holder, ca->elements);
1030
+ for (i = 0; i < ca->elements; i++) {
1031
+ ca_addr2index((CArray *) ca, i, idx);
1032
+ if ( ! cag_parent_addr(ca, idx, &pa) ) {
1033
+ paddrs[m++] = pa;
1034
+ }
1035
+ }
1036
+ if ( m > 0 ) {
1037
+ ca_fill_addrs(ca->parent, m, paddrs, ptr);
1038
+ cag_mask_settle_after(ca, ptr, 1);
1039
+ }
1040
+ ALLOCV_END(holder);
1041
+ }
1042
+
742
1043
  static void
743
1044
  ca_grid_func_fill_data (void *ap, void *ptr)
744
1045
  {
@@ -746,6 +1047,11 @@ ca_grid_func_fill_data (void *ap, void *ptr)
746
1047
  ca_axis_desc_t desc[CA_RANK_MAX];
747
1048
  ca_size_t pdims[CA_RANK_MAX];
748
1049
 
1050
+ if ( cag_has_holes(ca) ) {
1051
+ ca_grid_holed_fill(ca, ptr);
1052
+ return;
1053
+ }
1054
+
749
1055
  ca_grid_describe_axes(ca, desc, pdims);
750
1056
 
751
1057
  /* Writing the selected cells used to go through a whole-parent attach and
@@ -765,15 +1071,32 @@ ca_grid_func_fill_data (void *ap, void *ptr)
765
1071
  ca_sync_detach(ca->parent);
766
1072
  }
767
1073
 
1074
+ /* The mask is a grid over the parent's mask with the same axes, holes
1075
+ included; there the holes read as masked. */
768
1076
  static void
769
1077
  ca_grid_func_create_mask (void *ap)
770
1078
  {
771
1079
  CAGrid *ca = (CAGrid *) ap;
1080
+ CArray *mparent;
1081
+ int holed = cag_has_holes(ca);
1082
+
1083
+ /* A grid with holes shows them without giving a parent that has no mask
1084
+ one: its mask grid reads an all-false stand-in until a true is written
1085
+ outside the holes (ca_mask_standin_new). */
1086
+ if ( holed && ! ca_has_mask(ca->parent) ) {
1087
+ if ( ! ca->mask_standin ) {
1088
+ ca->mask_standin = ca_mask_standin_new(ca->parent);
1089
+ }
1090
+ mparent = ca->mask_standin;
1091
+ }
1092
+ else {
1093
+ ca_create_mask(ca->parent);
1094
+ mparent = ca->parent->mask;
1095
+ }
772
1096
 
773
- ca_create_mask(ca->parent);
774
-
775
- ca->mask = (CArray *) ca_grid_new_share(ca->parent->mask,
776
- ca->ndim, ca->axes);
1097
+ ca->mask = (CArray *) ca_grid_new_share(mparent, ca->ndim, ca->axes);
1098
+ ((CAGrid *) ca->mask)->holes_read_masked = (int8_t) holed;
1099
+ ((CAGrid *) ca->mask)->mask_owner = holed ? ca : NULL;
777
1100
  }
778
1101
 
779
1102
  ca_operation_function_t ca_grid_func = {
@@ -810,41 +1133,6 @@ rb_ca_grid_new (VALUE cary, int8_t ndim, cag_axis_t *protos)
810
1133
  return obj;
811
1134
  }
812
1135
 
813
- /* Detect a plain Ruby Range (Integer first/last, exclude_end?, step=1
814
- implicit) and convert it to STRIDE (start, count, step=1).
815
- Returns 1 on success (fills *out_start, *out_count), 0 on fallback
816
- (caller should materialise to INDEX). Negative endpoints are
817
- normalised against dim_size like everywhere else in CArray. */
818
- static int
819
- cag_range_to_stride (VALUE range, ca_size_t dim_size,
820
- ca_size_t *out_start, ca_size_t *out_count)
821
- {
822
- VALUE rb_first, rb_last;
823
- ca_size_t first, last, count;
824
- int exclude_end;
825
-
826
- if ( ! rb_obj_is_kind_of(range, rb_cRange) ) return 0;
827
- rb_first = rb_funcall(range, rb_intern("first"), 0);
828
- rb_last = rb_funcall(range, rb_intern("last"), 0);
829
- if ( ! FIXNUM_P(rb_first) || ! FIXNUM_P(rb_last) ) return 0;
830
- exclude_end = RTEST(rb_funcall(range, rb_intern("exclude_end?"), 0));
831
-
832
- first = NUM2SIZE(rb_first);
833
- last = NUM2SIZE(rb_last);
834
- if (first < 0) first += dim_size;
835
- if (last < 0) last += dim_size;
836
- if (exclude_end) last -= 1;
837
-
838
- if (first < 0 || first >= dim_size) return 0;
839
- if (last < first - 1) return 0; /* empty range */
840
- if (last >= dim_size) return 0;
841
- count = last - first + 1;
842
-
843
- *out_start = first;
844
- *out_count = count;
845
- return 1;
846
- }
847
-
848
1136
  /* The INDEX axes of rb_ca_grid, read with their index arrays attached,
849
1137
  and the view built from them. */
850
1138
 
@@ -859,90 +1147,96 @@ static VALUE
859
1147
  ca_grid_build_body (VALUE arg)
860
1148
  {
861
1149
  ca_grid_build_ctx_t *c = (ca_grid_build_ctx_t *) arg;
862
- volatile VALUE holders[CA_RANK_MAX];
863
- volatile VALUE obj;
864
1150
  int8_t i;
865
1151
 
866
1152
  for (i = 0; i < c->ca->ndim; i++) {
867
1153
  CArray *ci = c->ci[i];
868
- holders[i] = Qfalse;
869
1154
  if ( ! ci ) {
870
1155
  continue;
871
1156
  }
872
- c->protos[i].kind = CAG_AXIS_INDEX;
1157
+ c->protos[i].kind = CAG_AXIS_INDEX;
1158
+ c->protos[i].count = ci->elements;
1159
+ c->protos[i].indices = (ca_size_t *) ci->ptr;
1160
+ c->protos[i].holes = NULL;
873
1161
  if ( ca_is_any_masked(ci) ) {
874
- /* mask-filtered snapshot: skip masked cells. setup copies the
875
- indices into its own buffer, so the snapshot only has to live
876
- until ca_grid_new returns. */
877
- ca_size_t gsize = ci->elements - ca_count_masked(ci);
878
- ca_size_t *src = (ca_size_t *) ci->ptr;
879
- boolean8_t *m = (boolean8_t *) ci->mask->ptr;
880
- ca_size_t *tmp = ALLOCV_N(ca_size_t, holders[i], gsize > 0 ? gsize : 1);
881
- ca_size_t j, n = 0;
882
- for (j = 0; j < ci->elements; j++) {
883
- if ( ! m[j] ) tmp[n++] = src[j];
884
- }
885
- c->protos[i].count = gsize;
886
- c->protos[i].indices = tmp;
887
- } else {
888
- c->protos[i].count = ci->elements;
889
- c->protos[i].indices = (ca_size_t *) ci->ptr;
1162
+ /* A masked index cell selects nothing: its position stays, as a hole
1163
+ (masked on read, skipped on write). setup copies the mask into
1164
+ its own buffer. */
1165
+ c->protos[i].holes = (uint8_t *) ci->mask->ptr;
890
1166
  }
891
1167
  c->protos[i].start = 0;
892
1168
  c->protos[i].step = 0;
893
1169
  }
894
1170
 
895
- obj = rb_ca_grid_new(c->self, c->ca->ndim, c->protos);
1171
+ return rb_ca_grid_new(c->self, c->ca->ndim, c->protos);
1172
+ }
896
1173
 
897
- for (i = 0; i < c->ca->ndim; i++) {
898
- if ( c->ci[i] && holders[i] != Qfalse ) {
899
- ALLOCV_END(holders[i]);
1174
+ /* A scalar key (an Integer or a Float) picks one cell along its axis, and that axis is
1175
+ not in the result -- as it is not in a block reference. The view is
1176
+ built with the axis at length 1 and the axis then dropped. */
1177
+ static VALUE
1178
+ ca_grid_drop_scalar_axes (VALUE obj, int argc, VALUE *argv)
1179
+ {
1180
+ VALUE idx[CA_RANK_MAX];
1181
+ int i, any = 0;
1182
+ for (i = 0; i < argc; i++) {
1183
+ if ( ca_index_key_is_scalar(argv[i]) ) {
1184
+ idx[i] = INT2FIX(0);
1185
+ any = 1;
1186
+ }
1187
+ else {
1188
+ idx[i] = Qnil;
900
1189
  }
901
1190
  }
902
- return obj;
1191
+ if ( ! any ) {
1192
+ return obj;
1193
+ }
1194
+ return rb_ca_fetch2(obj, argc, idx);
903
1195
  }
904
1196
 
1197
+ /* ca[k0, k1, ...] with a CArray among the keys. An integer CArray picks
1198
+ the cells it lists along its axis, a boolean one the cells it marks (it
1199
+ must be as long as the axis). Every other key is read as the indexer
1200
+ reads it (ca_index_axis_key): an Integer picks one cell and drops the
1201
+ axis, a Range / ArithmeticSequence / Array form a run, nil the axis. */
905
1202
  VALUE
906
1203
  rb_ca_grid (int argc, VALUE *argv, VALUE self)
907
1204
  {
908
- volatile VALUE obj, ridx, rval;
1205
+ volatile VALUE obj, rval;
909
1206
  volatile VALUE list = rb_ary_new();
910
1207
  CArray *ca;
911
1208
  CArray *ci[CA_RANK_MAX]; /* CAWrap holders for INDEX axes; NULL for STRIDE */
912
1209
  cag_axis_t protos[CA_RANK_MAX];
1210
+ VALUE expanded[CA_RANK_MAX];
1211
+ long n;
913
1212
  ca_size_t i;
914
1213
 
915
- /* Dispatch hook: route mixed AP + single boolean to CASelectAxis
916
- (slab-copy optimised path). Falls through to per-cell CAGrid gather
917
- when not eligible (integer index, multiple INDIRECT axes, indirect
918
- not on axis 0). Inline argv[0] type check avoids the function call
919
- when axis 0 is nil/Integer (most patterns with INDIRECT on inner axes). */
920
- extern int ca_csa_dispatch_bypass;
921
- if ( ! ca_csa_dispatch_bypass && argc > 0 && TYPE(argv[0]) == T_DATA ) {
922
- if ( rb_ca_select_axis_eligible_p(argc, argv, self) ) {
923
- return rb_ca_select_axis(argc, argv, self);
924
- }
925
- }
926
-
927
1214
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
928
1215
 
929
- ridx = rb_ary_new4(argc, argv);
1216
+ n = ca_index_expand_rubber(ca->ndim, argc, argv, expanded);
1217
+ if ( n >= 0 ) {
1218
+ argc = (int) n;
1219
+ argv = expanded;
1220
+ }
930
1221
 
931
- if ( RARRAY_LEN(ridx) > ca->ndim ) {
1222
+ if ( argc > ca->ndim ) {
932
1223
  rb_raise(rb_eArgError, "# of arguments doesn't equal to the ndim");
933
1224
  }
934
- else if ( RARRAY_LEN(ridx) < ca->ndim ) {
1225
+ else if ( argc < ca->ndim ) {
935
1226
  volatile VALUE ref;
936
1227
  CArray *cv;
937
1228
  ca_size_t rdim[CA_RANK_MAX];
938
- ca_size_t rndim = RARRAY_LEN(ridx);
1229
+ ca_size_t rndim = argc;
939
1230
  ca_size_t j = 0, k;
940
1231
  for (i=0; i<rndim; i++) {
941
- rval = rb_ary_entry(ridx, i);
1232
+ rval = argv[i];
942
1233
  if ( rb_obj_is_carray(rval) ) {
943
1234
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
944
1235
  rdim[i] = 1;
945
1236
  for (k=0; k<cv->ndim; k++) {
1237
+ if ( j >= ca->ndim ) {
1238
+ rb_raise(rb_eArgError, "invalid total ndim of args");
1239
+ }
946
1240
  rdim[i] *= ca->dim[j];
947
1241
  j += 1;
948
1242
  }
@@ -956,45 +1250,69 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
956
1250
  rb_raise(rb_eArgError, "invalid total ndim of args");
957
1251
  }
958
1252
  ref = rb_ca_refer_new(self, ca->data_type, rndim, rdim, ca->bytes, 0);
959
- return rb_ca_grid(argc, argv, ref);
1253
+ /* An index array that spans several axes picks along the one axis
1254
+ they are joined into, so it is read flat there. */
1255
+ {
1256
+ VALUE flat[CA_RANK_MAX];
1257
+ for (i=0; i<rndim; i++) {
1258
+ flat[i] = rb_obj_is_carray(argv[i]) ? rb_ca_flatten(argv[i]) : argv[i];
1259
+ }
1260
+ return rb_ca_grid(argc, flat, ref);
1261
+ }
1262
+ }
1263
+
1264
+ /* Dispatch hook: route a boolean on axis 0 with plain keys elsewhere to
1265
+ CASelectAxis (slab-copy optimised path). Falls through to CAGrid when
1266
+ not eligible. The inline argv[0] type check avoids the call when axis
1267
+ 0 is nil / Integer (most patterns with the CArray on an inner axis). */
1268
+ extern int ca_csa_dispatch_bypass;
1269
+ if ( ! ca_csa_dispatch_bypass && argc > 0 && TYPE(argv[0]) == T_DATA ) {
1270
+ if ( rb_ca_select_axis_eligible_p(argc, argv, self) ) {
1271
+ return ca_grid_drop_scalar_axes(rb_ca_select_axis(argc, argv, self),
1272
+ argc, argv);
1273
+ }
960
1274
  }
961
1275
 
962
- /* Build per-axis protos:
963
- nil -> STRIDE(0, parent.dim, 1), no CArray
964
- Range -> STRIDE if plain integer Range, else fall through to INDEX
965
- CArray -> INDEX with indices = (ca_size_t*)ca_wrap_readonly(...)->ptr */
966
1276
  for (i = 0; i < ca->ndim; i++) {
967
1277
  ca_size_t dim_size = ca->dim[i];
968
- rval = rb_ary_entry(ridx, i);
1278
+ rval = argv[i];
969
1279
  ci[i] = NULL;
970
- if ( NIL_P(rval) ) {
1280
+ if ( ! rb_obj_is_carray(rval) ) {
1281
+ ca_size_t start, count, step;
1282
+ ca_index_axis_key(rval, dim_size, (int) i, &start, &count, &step);
971
1283
  protos[i].kind = CAG_AXIS_STRIDE;
972
- protos[i].count = dim_size;
973
- protos[i].start = 0;
974
- protos[i].step = 1;
1284
+ protos[i].count = count;
1285
+ protos[i].start = start;
1286
+ protos[i].step = step;
975
1287
  protos[i].indices = NULL;
1288
+ protos[i].holes = NULL;
976
1289
  continue;
977
1290
  }
978
- if ( rb_obj_is_kind_of(rval, rb_cRange) ) {
979
- ca_size_t start, count;
980
- if ( cag_range_to_stride(rval, dim_size, &start, &count) ) {
981
- protos[i].kind = CAG_AXIS_STRIDE;
982
- protos[i].count = count;
983
- protos[i].start = start;
984
- protos[i].step = 1;
985
- protos[i].indices = NULL;
986
- continue;
1291
+ {
1292
+ /* An index array picks cells along its one axis. One of more axes
1293
+ states a shape the axis does not have. */
1294
+ CArray *cs;
1295
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cs);
1296
+ if ( cs->ndim > 1 ) {
1297
+ volatile VALUE sshape = rb_ary_new();
1298
+ int k;
1299
+ for (k = 0; k < cs->ndim; k++) rb_ary_push(sshape, SIZE2NUM(cs->dim[k]));
1300
+ rb_raise(rb_eArgError,
1301
+ "%s: index array of shape %"PRIsVALUE" on axis %d (an index "
1302
+ "array picks along one axis; use .flatten)",
1303
+ ca_calling_method_name(), sshape, (int) i);
987
1304
  }
988
- /* fallback: materialise to integer array */
989
- rval = rb_funcall(rb_mKernel, rb_intern("CA_SIZE"), 1, rval);
990
1305
  }
991
- else if ( rb_obj_is_carray(rval) ) {
992
- if ( rb_ca_is_boolean_type(rval) ) {
993
- rval = rb_ca_where(rval);
1306
+ if ( rb_ca_is_boolean_type(rval) ) {
1307
+ CArray *cb;
1308
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cb);
1309
+ if ( cb->elements != dim_size ) {
1310
+ rb_raise(rb_eArgError,
1311
+ "mismatch of # of elements ( %lld <=> %lld ) in reference "
1312
+ "by selection on axis %d",
1313
+ (long long) cb->elements, (long long) dim_size, (int) i);
994
1314
  }
995
- }
996
- else if ( TYPE(rval) == T_ARRAY ) {
997
- rb_raise(rb_eTypeError, "grid: an axis is an Integer, a Range, nil or a CArray of indices (got Array)");
1315
+ rval = rb_ca_where(rval);
998
1316
  }
999
1317
 
1000
1318
  /* INDEX axis: converted here, read once the window below opens */
@@ -1020,7 +1338,7 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
1020
1338
  (VALUE) &c);
1021
1339
  }
1022
1340
 
1023
- return obj;
1341
+ return ca_grid_drop_scalar_axes(obj, argc, argv);
1024
1342
  }
1025
1343
 
1026
1344
  static VALUE
@@ -1044,6 +1362,9 @@ rb_ca_grid_initialize_copy (VALUE self, VALUE other)
1044
1362
  }
1045
1363
  /* Copy the axes: the source may be collected before the copy. */
1046
1364
  ca_grid_setup(ca, cs->parent, cs->ndim, cs->axes, 0);
1365
+ ca->holes_read_masked = cs->holes_read_masked;
1366
+ ca->mask_owner = cs->mask_owner;
1367
+ ca_grid_inherit_mask((VALUE) ca);
1047
1368
 
1048
1369
  return self;
1049
1370
  }