carray 3.0.1 → 3.0.3

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_mask.c CHANGED
@@ -185,16 +185,67 @@ ca_mask_word_count (const boolean8_t *m, ca_size_t n)
185
185
 
186
186
  enum { CA_MASK_SCAN_ANY, CA_MASK_SCAN_ALL, CA_MASK_SCAN_COUNT };
187
187
 
188
+ /* The block walk runs under ca_lazy_arena_protect: the transfer from a
189
+ virtual mask can raise, and the arena must be left however it ends. */
190
+ typedef struct {
191
+ boolean8_t *scratch; /* first: released by ca_lazy_arena_release_held */
192
+ CArray *mask;
193
+ int op;
194
+ ca_size_t rows;
195
+ ca_size_t inner;
196
+ ca_size_t *native;
197
+ ca_size_t result;
198
+ } ca_mask_scan_t;
199
+
200
+ static VALUE
201
+ ca_mask_scan_virtual_body (VALUE arg)
202
+ {
203
+ ca_mask_scan_t *sc = (ca_mask_scan_t *) arg;
204
+ CArray *mask = sc->mask;
205
+ int8_t ndim = mask->ndim;
206
+ ca_size_t outer = mask->dim[0];
207
+ ca_size_t off;
208
+ int8_t k;
209
+
210
+ sc->scratch = (boolean8_t *) ca_lazy_arena_acquire(sc->rows * sc->inner);
211
+
212
+ for (off = 0; off < outer; off += sc->rows) {
213
+ ca_size_t nrow = (outer - off < sc->rows) ? (outer - off) : sc->rows;
214
+ ca_size_t n = nrow * sc->inner;
215
+ ca_size_t starts[CA_RANK_MAX] = {0};
216
+ ca_size_t counts[CA_RANK_MAX];
217
+
218
+ starts[0] = off;
219
+ counts[0] = nrow;
220
+ for (k = 1; k < ndim; k++) {
221
+ counts[k] = mask->dim[k];
222
+ }
223
+
224
+ ca_xfer_stride(mask, starts, counts, sc->native, sc->scratch, CA_XFER_GET);
225
+
226
+ if ( sc->op == CA_MASK_SCAN_ANY ) {
227
+ if ( ca_mask_word_any(sc->scratch, n) ) { sc->result = 1; break; }
228
+ }
229
+ else if ( sc->op == CA_MASK_SCAN_ALL ) {
230
+ if ( ! ca_mask_word_all_set(sc->scratch, n) ) { sc->result = 0; break; }
231
+ }
232
+ else { /* CA_MASK_SCAN_COUNT — no early exit */
233
+ sc->result += ca_mask_word_count(sc->scratch, n);
234
+ }
235
+ }
236
+ return Qnil;
237
+ }
238
+
188
239
  static ca_size_t
189
240
  ca_mask_scan_virtual (CArray *mask, int op)
190
241
  {
191
- int8_t ndim = mask->ndim;
192
- ca_size_t outer = mask->dim[0];
193
- ca_size_t inner = 1;
194
- ca_size_t native[CA_RANK_MAX];
195
- ca_size_t rows, off, result = 0;
196
- boolean8_t *scratch;
197
- int8_t k;
242
+ int8_t ndim = mask->ndim;
243
+ ca_size_t outer = mask->dim[0];
244
+ ca_size_t inner = 1;
245
+ ca_size_t native[CA_RANK_MAX];
246
+ ca_size_t rows;
247
+ ca_mask_scan_t sc;
248
+ int8_t k;
198
249
 
199
250
  for (k = 1; k < ndim; k++) {
200
251
  inner *= mask->dim[k];
@@ -212,41 +263,18 @@ ca_mask_scan_virtual (CArray *mask, int op)
212
263
  if ( rows < 1 ) rows = 1;
213
264
  if ( rows > outer ) rows = outer;
214
265
 
215
- if ( op == CA_MASK_SCAN_ALL ) {
216
- result = 1; /* vacuously all-set until a non-set cell is found */
217
- }
218
-
219
- ca_lazy_arena_enter();
220
- scratch = (boolean8_t *) ca_lazy_arena_acquire(rows * inner);
221
-
222
- for (off = 0; off < outer; off += rows) {
223
- ca_size_t nrow = (outer - off < rows) ? (outer - off) : rows;
224
- ca_size_t n = nrow * inner;
225
- ca_size_t starts[CA_RANK_MAX] = {0};
226
- ca_size_t counts[CA_RANK_MAX];
227
-
228
- starts[0] = off;
229
- counts[0] = nrow;
230
- for (k = 1; k < ndim; k++) {
231
- counts[k] = mask->dim[k];
232
- }
233
-
234
- ca_xfer_stride(mask, starts, counts, native, scratch, CA_XFER_GET);
235
-
236
- if ( op == CA_MASK_SCAN_ANY ) {
237
- if ( ca_mask_word_any(scratch, n) ) { result = 1; break; }
238
- }
239
- else if ( op == CA_MASK_SCAN_ALL ) {
240
- if ( ! ca_mask_word_all_set(scratch, n) ) { result = 0; break; }
241
- }
242
- else { /* CA_MASK_SCAN_COUNT — no early exit */
243
- result += ca_mask_word_count(scratch, n);
244
- }
245
- }
266
+ sc.scratch = NULL;
267
+ sc.mask = mask;
268
+ sc.op = op;
269
+ sc.rows = rows;
270
+ sc.inner = inner;
271
+ sc.native = native;
272
+ /* ALL is vacuously all-set until a non-set cell is found */
273
+ sc.result = ( op == CA_MASK_SCAN_ALL ) ? 1 : 0;
246
274
 
247
- ca_lazy_arena_release(scratch);
248
- ca_lazy_arena_exit();
249
- return result;
275
+ ca_lazy_arena_protect(ca_mask_scan_virtual_body, ca_lazy_arena_release_held,
276
+ (VALUE) &sc);
277
+ return sc.result;
250
278
  }
251
279
 
252
280
  boolean8_t *
@@ -323,7 +351,7 @@ ca_is_any_masked (void *ap)
323
351
  non-zero word (the typical "all zero, no mask actually set" hot
324
352
  pattern). An O(n) scan cache is not viable here: the mask is
325
353
  dynamic and a view chain can introduce a mask mid-chain. */
326
- ca_attach(ca->mask);
354
+ ca_attach(ca->mask); /* window: nothing raises inside */
327
355
  flag = ca_mask_word_any((boolean8_t *) ca->mask->ptr, ca->elements);
328
356
  ca_detach(ca->mask);
329
357
  }
@@ -350,7 +378,7 @@ ca_is_all_masked (void *ap)
350
378
  /* cheap mask: O(1) alias attach + word-level all_set with early exit.
351
379
  The word check uses (w == 0x0101...01ULL) under the {0,1}
352
380
  invariant; returns on the first non-fully-set word. */
353
- ca_attach(ca->mask);
381
+ ca_attach(ca->mask); /* window: nothing raises inside */
354
382
  flag = ca_mask_word_all_set((boolean8_t *) ca->mask->ptr, ca->elements);
355
383
  ca_detach(ca->mask);
356
384
  }
@@ -363,6 +391,19 @@ ca_is_all_masked (void *ap)
363
391
  return flag;
364
392
  }
365
393
 
394
+ /* The create_mask slot of a view whose cells map one-to-one onto its
395
+ parent's in address order (a cast, a byte swap, a Face over its storage):
396
+ the mask is the parent's mask seen in the view's shape. */
397
+
398
+ void
399
+ ca_view_func_create_mask (void *ap)
400
+ {
401
+ CAView *ca = (CAView *) ap;
402
+ ca_create_mask(ca->parent);
403
+ ca->mask = (CArray *) ca_refer_new(ca->parent->mask,
404
+ CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
405
+ }
406
+
366
407
  /* create mask array if array has mask but has not mask array */
367
408
 
368
409
  void
@@ -374,30 +415,161 @@ ca_update_mask (void *ap)
374
415
  }
375
416
  }
376
417
 
418
+ static VALUE
419
+ ca_create_mask_attach (VALUE arg)
420
+ {
421
+ ca_attach((CArray *) arg); /* window: released by the view's detach */
422
+ return Qnil;
423
+ }
424
+
425
+ CArray *
426
+ ca_multi_parent_mask (CArray *p, CArray **standin)
427
+ {
428
+ *standin = NULL;
429
+ if ( ! ca_has_mask(p) ) {
430
+ CArray *z = carray_new(CA_BOOLEAN, p->ndim, p->dim, 0, NULL);
431
+ MEMZERO(z->ptr, boolean8_t, z->elements);
432
+ if ( ca_is_readonly(p) ) {
433
+ ca_set_flag(z, CA_FLAG_READ_ONLY);
434
+ }
435
+ *standin = z;
436
+ return z;
437
+ }
438
+ ca_create_mask(p);
439
+ return p->mask;
440
+ }
441
+
442
+ /* A value array or a mask array: a parent that can never be given a mask,
443
+ whose stand-in therefore stays in place for good. */
444
+ static int
445
+ ca_multi_parent_mask_unmaskable (CArray *p)
446
+ {
447
+ return ca_is_value_array(p) || ca_is_mask_array(p);
448
+ }
449
+
450
+ void
451
+ ca_multi_parent_mask_refresh (CArray *mask, CArray **parents,
452
+ CArray **standins, int32_t n, int for_write)
453
+ {
454
+ CAMultiParent *mp = (CAMultiParent *) mask;
455
+ int32_t k;
456
+ /* Not inside an attach window: its buffer and its detach are those of
457
+ the parents it was opened with. The attach and allocate slots run
458
+ before the buffer is published, and replace the stand-ins then. */
459
+ if ( standins == NULL || ( CAVIEW(mask)->attach > 0 && mask->ptr != NULL ) ) {
460
+ return;
461
+ }
462
+ /* A replaced stand-in stays allocated until the view is freed: a copy
463
+ of the mask (dup) may still point at it. */
464
+ for ( k = 0; k < n; k++ ) {
465
+ CArray *z = standins[k];
466
+ if ( z == NULL || mp->parents[k] != z
467
+ || ca_test_flag(z, CA_FLAG_READ_ONLY)
468
+ || ca_multi_parent_mask_unmaskable(parents[k]) ) {
469
+ continue;
470
+ }
471
+ if ( ! ( for_write || ca_has_mask(parents[k]) ) ) {
472
+ continue;
473
+ }
474
+ ca_create_mask(parents[k]);
475
+ mp->parents[k] = parents[k]->mask;
476
+ if ( k == 0 ) {
477
+ CAVIEW(mask)->parent = parents[k]->mask;
478
+ }
479
+ }
480
+ }
481
+
482
+ void
483
+ ca_multi_parent_mask_check (CArray *mask, CArray **parents,
484
+ CArray **standins, int32_t n)
485
+ {
486
+ CAMultiParent *mp = (CAMultiParent *) mask;
487
+ int32_t k;
488
+ if ( standins == NULL ) {
489
+ return;
490
+ }
491
+ for ( k = 0; k < n; k++ ) {
492
+ CArray *z = standins[k];
493
+ ca_size_t i;
494
+ if ( z == NULL || mp->parents[k] != z
495
+ || ! ca_multi_parent_mask_unmaskable(parents[k]) ) {
496
+ continue;
497
+ }
498
+ for ( i = 0; i < z->elements && ! ((boolean8_t *) z->ptr)[i]; i++ ) ;
499
+ if ( i == z->elements ) {
500
+ continue;
501
+ }
502
+ MEMZERO(z->ptr, boolean8_t, z->elements);
503
+ rb_raise(rb_eTypeError, "can not create mask array for the %s array",
504
+ ca_is_value_array(parents[k]) ? "value" : "mask");
505
+ }
506
+ }
507
+
508
+ void
509
+ ca_multi_parent_mask_settle (CArray *mask, CArray **parents,
510
+ CArray **standins, int32_t n)
511
+ {
512
+ CAMultiParent *mp = (CAMultiParent *) mask;
513
+ int32_t k;
514
+ if ( standins == NULL ) {
515
+ return;
516
+ }
517
+ ca_multi_parent_mask_check(mask, parents, standins, n);
518
+ for ( k = 0; k < n; k++ ) {
519
+ CArray *z = standins[k];
520
+ ca_size_t i;
521
+ if ( z == NULL || mp->parents[k] != z
522
+ || ca_test_flag(z, CA_FLAG_READ_ONLY)
523
+ || ca_multi_parent_mask_unmaskable(parents[k]) ) {
524
+ continue;
525
+ }
526
+ for ( i = 0; i < z->elements && ! ((boolean8_t *) z->ptr)[i]; i++ ) ;
527
+ if ( i == z->elements ) {
528
+ continue;
529
+ }
530
+ ca_create_mask(parents[k]);
531
+ ca_xfer_all(parents[k]->mask, z->ptr, CA_XFER_PUT);
532
+ MEMZERO(z->ptr, boolean8_t, z->elements);
533
+ }
534
+ }
535
+
377
536
  void
378
537
  ca_create_mask (void *ap)
379
538
  {
380
539
  CArray *ca = (CArray *) ap;
381
540
 
541
+ if ( ca->mask ) {
542
+ return;
543
+ }
544
+
382
545
  if ( ca_is_value_array(ca) ) {
383
- rb_raise(rb_eRuntimeError,
546
+ rb_raise(rb_eTypeError,
384
547
  "can not create mask array for the value array");
385
548
  }
386
549
 
387
550
  if ( ca_is_mask_array(ca) ) {
388
- rb_raise(rb_eRuntimeError,
551
+ rb_raise(rb_eTypeError,
389
552
  "can not create mask array for the mask array");
390
553
  }
391
554
 
392
- if ( ! ca->mask ) {
393
- ca_func[ca->obj_type].create_mask(ca);
394
- ca_set_flag(ca->mask, CA_FLAG_MASK_ARRAY); /* set array as mask array */
395
- if ( ca_is_view(ca) ) {
396
- if ( CAVIEW(ca)->attach ) {
397
- ca_attach(ca->mask);
398
- if ( ca_is_view(ca->mask) ) {
399
- CAVIEW(ca->mask)->attach = CAVIEW(ca)->attach;
400
- }
555
+ ca_func[ca->obj_type].create_mask(ca);
556
+ ca_set_flag(ca->mask, CA_FLAG_MASK_ARRAY); /* set array as mask array */
557
+ if ( ca_is_view(ca) ) {
558
+ if ( CAVIEW(ca)->attach ) {
559
+ /* The new mask joins its view's level. Its attach reads the
560
+ parent's mask, which can raise (a Ruby-backed parent); the mask
561
+ then comes off again, since the view's sync and detach would
562
+ find it attached to nothing. */
563
+ int tag = 0;
564
+ rb_protect(ca_create_mask_attach, (VALUE) ca->mask, &tag);
565
+ if ( tag ) {
566
+ CArray *mask = ca->mask;
567
+ ca->mask = NULL;
568
+ ca_free(mask);
569
+ rb_jump_tag(tag);
570
+ }
571
+ if ( ca_is_view(ca->mask) ) {
572
+ CAVIEW(ca->mask)->attach = CAVIEW(ca)->attach;
401
573
  }
402
574
  }
403
575
  }
@@ -415,6 +587,20 @@ ca_clear_mask (void *ap)
415
587
  }
416
588
  }
417
589
 
590
+ typedef struct {
591
+ CArray *ca;
592
+ CArray *mask;
593
+ } ca_setup_mask_t;
594
+
595
+ /* Pushing the cells into ca's mask can raise (a CAObject's mask hooks). */
596
+ static VALUE
597
+ ca_setup_mask_body (VALUE arg)
598
+ {
599
+ ca_setup_mask_t *c = (ca_setup_mask_t *) arg;
600
+ ca_sync_data(c->ca->mask, c->mask->ptr);
601
+ return Qnil;
602
+ }
603
+
418
604
  void
419
605
  ca_setup_mask (void *ap, CArray *mask)
420
606
  {
@@ -423,12 +609,13 @@ ca_setup_mask (void *ap, CArray *mask)
423
609
  ca_update_mask(ca);
424
610
 
425
611
  if ( mask ) {
612
+ ca_setup_mask_t c;
426
613
  if ( ! ca->mask ) {
427
614
  ca_create_mask(ca);
428
615
  }
429
- ca_attach(mask);
430
- ca_sync_data(ca->mask, mask->ptr);
431
- ca_detach(mask);
616
+ c.ca = ca;
617
+ c.mask = mask;
618
+ ca_attach_window(1, &c.mask, "r", ca_setup_mask_body, (VALUE) &c);
432
619
  }
433
620
  else if ( ca->mask ) {
434
621
  boolean8_t zero = 0;
@@ -443,12 +630,59 @@ ca_setup_mask (void *ap, CArray *mask)
443
630
 
444
631
  */
445
632
 
446
- void
447
- ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
633
+ /* The overlay reads each source's mask into the destination's. A
634
+ source's mask can be made or read by calling Ruby (a CAObject's mask
635
+ hooks) and raise, so every mask is made first and the lot are read
636
+ inside one attach window.
637
+
638
+ A source whose cells live in the destination's root has its mask read
639
+ into a snapshot before the destination's mask is made, cleared or
640
+ written: the destination's mask may be that same memory, and the word
641
+ OR would otherwise read bytes it has already set. */
642
+ typedef struct {
643
+ CArray **list; /* [0] the destination mask, then the attached sources' */
644
+ boolean8_t **snap; /* per source: its mask read in advance, or NULL */
645
+ CArray **att; /* per source: its attached mask when not snapped */
646
+ char *scalar; /* per source: the source is a scalar */
647
+ int k; /* entries in list */
648
+ int ns; /* sources */
649
+ ca_size_t elements;
650
+ } ca_mask_overlay_t;
651
+
652
+ static VALUE
653
+ ca_mask_overlay_body (VALUE arg)
654
+ {
655
+ ca_mask_overlay_t *o = (ca_mask_overlay_t *) arg;
656
+ boolean8_t *ma = (boolean8_t *) o->list[0]->ptr;
657
+ int i;
658
+ for (i=0; i<o->ns; i++) {
659
+ boolean8_t *ms = o->snap[i] ? o->snap[i] : (boolean8_t *) o->att[i]->ptr;
660
+ /* scalar masked source -> broadcast to all (memset); array source
661
+ -> pure word OR (ca_mask_word_or). A defensive `if (*ms)` is
662
+ unnecessary: under the {0,1} invariant, `*ma |= *ms` equals
663
+ `if (*ms) *ma = 1`. Post-condition: the mask byte stays in {0,1}. */
664
+ if ( o->scalar[i] ) {
665
+ if ( *ms ) {
666
+ memset(ma, 1, o->elements);
667
+ }
668
+ }
669
+ else {
670
+ ca_mask_word_or(ma, ms, o->elements);
671
+ }
672
+ }
673
+ return Qnil;
674
+ }
675
+
676
+ /* overwrite: the destination's mask is cleared before the sources are
677
+ OR'd in (and left alone when it has none and no source has a mask). */
678
+ static void
679
+ ca_copy_mask_overlay_core (CArray *ca, ca_size_t elements, int n,
680
+ CArray **slist, int overwrite)
448
681
  {
449
- CArray *ca = (CArray *) ap;
450
682
  CArray *cs;
451
- boolean8_t *ma, *ms;
683
+ ca_mask_overlay_t o;
684
+ volatile VALUE *holders;
685
+ char *modes;
452
686
  int i, some_has_mask = 0;
453
687
 
454
688
  for (i=0; i<n; i++) {
@@ -458,81 +692,115 @@ ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
458
692
  }
459
693
  }
460
694
 
461
- if ( some_has_mask ) {
695
+ if ( ! some_has_mask ) {
696
+ if ( overwrite ) {
697
+ ca_update_mask(ca);
698
+ if ( ca->mask ) {
699
+ boolean8_t zero = 0;
700
+ ca_fill(ca->mask, &zero);
701
+ }
702
+ }
703
+ return;
704
+ }
462
705
 
463
- ca_update_mask(ca);
464
- if ( ! ca->mask ) {
465
- ca_create_mask(ca);
706
+ if ( elements > ca->elements ) {
707
+ elements = ca->elements;
708
+ }
709
+
710
+ o.list = ALLOCA_N(CArray *, n + 1);
711
+ o.snap = ALLOCA_N(boolean8_t *, n);
712
+ o.att = ALLOCA_N(CArray *, n);
713
+ o.scalar = ALLOCA_N(char, n);
714
+ holders = ALLOCA_N(VALUE, n);
715
+ modes = ALLOCA_N(char, n + 2);
716
+ o.elements = elements;
717
+ o.ns = 0;
718
+ for (i=0; i<n; i++) {
719
+ holders[i] = 0;
720
+ }
721
+ ca_update_mask(ca);
722
+
723
+ /* Every source mask is made, and those in the destination's root are
724
+ read, before the destination's mask is touched. */
725
+ for (i=0; i<n; i++) {
726
+ cs = slist[i];
727
+ if ( ! cs ) {
728
+ continue;
729
+ }
730
+ ca_update_mask(cs);
731
+ if ( ! cs->mask ) {
732
+ continue;
733
+ }
734
+ o.scalar[o.ns] = (char) ca_is_scalar(cs);
735
+ o.att[o.ns] = NULL;
736
+ o.snap[o.ns] = NULL;
737
+ if ( ca_shares_root(cs, ca) || ca_shares_root(cs->mask, ca) ||
738
+ ( ca->mask && ca_shares_root(cs->mask, ca->mask) ) ) {
739
+ o.snap[o.ns] = ALLOCV_N(boolean8_t, holders[o.ns], cs->mask->elements);
740
+ ca_xfer_all(cs->mask, (char *) o.snap[o.ns], CA_XFER_GET);
741
+ }
742
+ else {
743
+ o.att[o.ns] = cs->mask;
466
744
  }
745
+ o.ns++;
746
+ }
467
747
 
468
- if ( elements > ca->elements ) {
469
- elements = ca->elements;
748
+ ca_create_mask(ca);
749
+ if ( overwrite ) {
750
+ boolean8_t zero = 0;
751
+ ca_fill(ca->mask, &zero);
752
+ }
753
+
754
+ o.k = 0;
755
+ o.list[o.k] = ca->mask;
756
+ modes[o.k++] = 'w';
757
+ for (i=0; i<o.ns; i++) {
758
+ if ( o.att[i] ) {
759
+ o.list[o.k] = o.att[i];
760
+ modes[o.k++] = 'r';
470
761
  }
762
+ }
763
+ modes[o.k] = '\0';
764
+ ca_attach_window(o.k, o.list, modes, ca_mask_overlay_body, (VALUE) &o);
471
765
 
472
- ca_attach(ca->mask);
473
- for (i=0; i<n; i++) {
474
- cs = slist[i];
475
- if ( ! cs ) {
476
- continue;
477
- }
478
- ca_update_mask(cs);
479
- if ( ! cs->mask ) {
480
- continue;
481
- }
482
- ca_attach(cs->mask);
483
- ma = (boolean8_t *) ca->mask->ptr;
484
- ms = (boolean8_t *) cs->mask->ptr;
485
- /* scalar masked source -> broadcast to all (memset); array source
486
- -> pure word OR (ca_mask_word_or). A defensive `if (*ms)` is
487
- unnecessary: under the {0,1} invariant, `*ma |= *ms` equals
488
- `if (*ms) *ma = 1`. Post-condition: the mask byte stays in {0,1}. */
489
- if ( ca_is_scalar(cs) ) {
490
- if ( *ms ) {
491
- memset(ma, 1, elements);
492
- }
493
- }
494
- else {
495
- ca_mask_word_or(ma, ms, elements);
496
- }
497
- ca_detach(cs->mask);
766
+ for (i=0; i<n; i++) {
767
+ if ( holders[i] ) {
768
+ ALLOCV_END(holders[i]);
498
769
  }
499
- ca_sync(ca->mask);
500
- ca_detach(ca->mask);
501
770
  }
502
771
  }
503
772
 
773
+ void
774
+ ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
775
+ {
776
+ ca_copy_mask_overlay_core((CArray *) ap, elements, n, slist, 0);
777
+ }
778
+
504
779
  void
505
780
  ca_copy_mask_overlay (void *ap, ca_size_t elements, int n, ...)
506
781
  {
507
782
  CArray *ca = (CArray *) ap;
508
783
  CArray **slist;
784
+ volatile VALUE holder;
509
785
  va_list args;
510
786
  int i;
511
787
 
512
- slist = xmalloc(sizeof(CArray *)*n);
788
+ slist = ALLOCV_N(CArray *, holder, n);
513
789
  va_start(args, n);
514
790
  for (i=0; i<n; i++) {
515
791
  slist[i] = va_arg(args, CArray *);
516
792
  }
517
793
  va_end(args);
518
794
 
519
- ca_copy_mask_overlay_n(ca, elements, n, slist);
795
+ ca_copy_mask_overlay_core(ca, elements, n, slist, 0);
520
796
 
521
- xfree(slist);
797
+ ALLOCV_END(holder);
522
798
  }
523
799
 
524
800
  void
525
801
  ca_copy_mask_overwrite_n (void *ap, ca_size_t elements, int n, CArray **slist)
526
802
  {
527
- CArray *ca = (CArray *) ap;
528
-
529
- ca_update_mask(ca);
530
- if ( ca->mask ) {
531
- boolean8_t zero = 0;
532
- ca_fill(ca->mask, &zero);
533
- }
534
-
535
- ca_copy_mask_overlay_n(ca, elements, n, slist);
803
+ ca_copy_mask_overlay_core((CArray *) ap, elements, n, slist, 1);
536
804
  }
537
805
 
538
806
  void
@@ -540,25 +808,20 @@ ca_copy_mask_overwrite (void *ap, ca_size_t elements, int n, ...)
540
808
  {
541
809
  CArray *ca = (CArray *) ap;
542
810
  CArray **slist;
811
+ volatile VALUE holder;
543
812
  va_list args;
544
813
  int i;
545
814
 
546
- slist = xmalloc(sizeof(CArray*)*n);
815
+ slist = ALLOCV_N(CArray *, holder, n);
547
816
  va_start(args, n);
548
817
  for (i=0; i<n; i++) {
549
818
  slist[i] = va_arg(args, CArray*);
550
819
  }
551
820
  va_end(args);
552
821
 
553
- ca_update_mask(ca);
554
- if ( ca->mask ) {
555
- boolean8_t zero = 0;
556
- ca_fill(ca->mask, &zero);
557
- }
822
+ ca_copy_mask_overlay_core(ca, elements, n, slist, 1);
558
823
 
559
- ca_copy_mask_overlay_n(ca, elements, n, slist);
560
-
561
- xfree(slist);
824
+ ALLOCV_END(holder);
562
825
  }
563
826
 
564
827
  void
@@ -582,7 +845,7 @@ ca_count_masked (void *ap)
582
845
  if ( ca->mask ) {
583
846
  if ( ca_attach_is_alias(ca->mask) ) {
584
847
  /* cheap mask: O(1) alias attach + word-bulk count. */
585
- ca_attach(ca->mask);
848
+ ca_attach(ca->mask); /* window: nothing raises inside */
586
849
  m = (boolean8_t *) ca->mask->ptr;
587
850
  count = ca_mask_word_count(m, ca->elements);
588
851
  ca_detach(ca->mask);
@@ -644,7 +907,7 @@ ca_unmask (void *ap, char *fill_value)
644
907
  ca_fill(ca->mask, &zero);
645
908
  }
646
909
  else {
647
- ca_attach(ca);
910
+ ca_attach(ca); /* window: nothing raises inside */
648
911
 
649
912
  switch ( ca->bytes ) {
650
913
  case 1: proc_fill_bang_width(uint8_t); break;
@@ -654,38 +917,56 @@ ca_unmask (void *ap, char *fill_value)
654
917
  default: proc_fill_bang_bytes(); break; /* cmplx128 (16) + fixlen */
655
918
  }
656
919
 
657
- ca_sync(ca);
658
- ca_detach(ca);
920
+ ca_sync_detach(ca);
659
921
  }
660
922
  }
661
923
  }
662
924
 
663
- CArray *
664
- ca_unmask_copy (void *ap, char *fill_value)
665
- {
666
- CArray *ca = (CArray *) ap;
925
+ typedef struct {
926
+ CArray *ca;
667
927
  CArray *co;
928
+ char *fill_value;
929
+ } ca_unmask_copy_args_t;
930
+
931
+ static VALUE
932
+ ca_unmask_copy_fill (VALUE arg)
933
+ {
934
+ ca_unmask_copy_args_t *a = (ca_unmask_copy_args_t *) arg;
935
+ CArray *ca = a->ca, *co = a->co;
668
936
  char *q;
669
937
  boolean8_t *m;
670
938
  ca_size_t i;
671
939
 
672
- co = ca_template(ca);
673
940
  ca_copy_data(ca, co->ptr);
674
941
 
675
- if ( fill_value && ca_has_mask(ca) ) {
676
- ca_attach(ca);
942
+ /* Only the mask is needed now that the values are in co, so read the
943
+ mask alone rather than producing the view's cells a second time. */
944
+ if ( a->fill_value && ca_has_mask(ca) ) {
945
+ volatile VALUE holder = 0;
946
+ ca_update_mask(ca);
947
+ m = ALLOCV_N(boolean8_t, holder, ca->elements);
948
+ ca_copy_data(ca->mask, (char *) m);
677
949
  q = co->ptr;
678
- m = (boolean8_t *) ca->mask->ptr;
679
950
  for (i=0; i<ca->elements; i++) {
680
- if ( *m ) {
681
- memcpy(q, fill_value, ca->bytes);
951
+ if ( m[i] ) {
952
+ memcpy(q, a->fill_value, ca->bytes);
682
953
  }
683
- m++; q+=co->bytes;
954
+ q+=co->bytes;
684
955
  }
685
- ca_detach(ca);
956
+ ALLOCV_END(holder);
686
957
  }
958
+ return Qnil;
959
+ }
687
960
 
688
- return co;
961
+ CArray *
962
+ ca_unmask_copy (void *ap, char *fill_value)
963
+ {
964
+ ca_unmask_copy_args_t a;
965
+ a.ca = (CArray *) ap;
966
+ a.co = ca_template(a.ca);
967
+ a.fill_value = fill_value;
968
+ ca_fill_or_free(a.co, ca_unmask_copy_fill, (VALUE) &a);
969
+ return a.co;
689
970
  }
690
971
 
691
972
  void
@@ -694,16 +975,12 @@ ca_invert_mask (void *ap)
694
975
  CArray *ca = (CArray *) ap;
695
976
  boolean8_t *m;
696
977
 
697
- ca_update_mask(ca);
698
-
699
- if ( ! ca->mask ) {
700
- ca_create_mask(ca);
701
- }
978
+ ca_create_mask(ca);
702
979
 
703
- ca_attach(ca->mask);
980
+ ca_attach(ca->mask); /* window: nothing raises inside */
704
981
  m = (boolean8_t *) ca->mask->ptr;
705
982
  ca_mask_word_not(m, m, ca->elements); /* in-place 1 - m, LSB-XOR word loop */
706
- ca_detach(ca->mask);
983
+ ca_sync_detach(ca->mask);
707
984
 
708
985
  return;
709
986
  }
@@ -712,9 +989,10 @@ boolean8_t *
712
989
  ca_allocate_mask_iterator_n (int n, CArray **slist)
713
990
  {
714
991
  boolean8_t *m, *mp, *ms;
715
- CArray *cs;
992
+ CArray *cs, **masks;
993
+ char *scalar;
716
994
  ca_size_t j, elements = -1;
717
- int i, some_has_mask = 0;
995
+ int i, k = 0, some_has_mask = 0;
718
996
 
719
997
  for (i=0; i<n; i++) {
720
998
  if ( slist[i] ) {
@@ -728,7 +1006,7 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
728
1006
  elements = slist[i]->elements;
729
1007
  }
730
1008
  else if ( ! ca_is_scalar(slist[i]) ) {
731
- rb_raise(rb_eRuntimeError,
1009
+ rb_raise(rb_eArgError,
732
1010
  "# of elements is different among the given arrays");
733
1011
  }
734
1012
  }
@@ -743,13 +1021,18 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
743
1021
  (degenerate call); guard so the alloc/memset never see a negative size
744
1022
  (which would wrap to a huge bound). Also silences GCC's VRP warnings. */
745
1023
  if ( elements < 0 ) elements = 0;
746
- m = xmalloc(sizeof(boolean8_t)*elements);
747
- memset(m, 0, elements);
748
1024
 
749
1025
  if ( ! some_has_mask ) {
1026
+ m = xmalloc(sizeof(boolean8_t)*elements);
1027
+ memset(m, 0, elements);
750
1028
  return m;
751
1029
  }
752
1030
 
1031
+ /* The sources' masks, made and attached before the result is allocated:
1032
+ making or reading one can call Ruby (a CAObject's mask hooks) and
1033
+ raise, and then nothing is left attached or allocated. */
1034
+ masks = ALLOCA_N(CArray *, n);
1035
+ scalar = ALLOCA_N(char, n);
753
1036
  for (i=0; i<n; i++) {
754
1037
  cs = slist[i];
755
1038
  if ( ! cs ) {
@@ -759,10 +1042,17 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
759
1042
  if ( ! cs->mask ) {
760
1043
  continue;
761
1044
  }
762
- ca_attach(cs->mask);
763
- ms = (boolean8_t *) cs->mask->ptr;
1045
+ masks[k] = cs->mask;
1046
+ scalar[k++] = (char) ca_is_scalar(cs);
1047
+ }
1048
+ ca_attach_all(masks, k); /* window: nothing raises inside */
1049
+
1050
+ m = xmalloc(sizeof(boolean8_t)*elements);
1051
+ memset(m, 0, elements);
1052
+ for (i=0; i<k; i++) {
1053
+ ms = (boolean8_t *) masks[i]->ptr;
764
1054
  mp = m;
765
- if ( ca_is_scalar(cs) ) {
1055
+ if ( scalar[i] ) {
766
1056
  if ( *ms ) {
767
1057
  for (j=0; j<elements; j++) {
768
1058
  *mp = 1;
@@ -773,7 +1063,9 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
773
1063
  else {
774
1064
  ca_mask_word_or(mp, ms, elements); /* m[j] |= ms[j], word-bulk OR */
775
1065
  }
776
- ca_detach(cs->mask);
1066
+ }
1067
+ while ( k > 0 ) {
1068
+ ca_detach(masks[--k]);
777
1069
  }
778
1070
  return m;
779
1071
  }
@@ -783,10 +1075,11 @@ ca_allocate_mask_iterator (int n, ...)
783
1075
  {
784
1076
  boolean8_t *m;
785
1077
  CArray **slist;
1078
+ volatile VALUE holder;
786
1079
  va_list args;
787
1080
  int i;
788
1081
 
789
- slist = xmalloc(sizeof(CArray *)*n);
1082
+ slist = ALLOCV_N(CArray *, holder, n);
790
1083
  va_start(args, n);
791
1084
  for (i=0; i<n; i++) {
792
1085
  slist[i] = va_arg(args, CArray *);
@@ -795,7 +1088,7 @@ ca_allocate_mask_iterator (int n, ...)
795
1088
 
796
1089
  m = ca_allocate_mask_iterator_n(n, slist);
797
1090
 
798
- xfree(slist);
1091
+ ALLOCV_END(holder);
799
1092
 
800
1093
  return m;
801
1094
  }
@@ -949,12 +1242,15 @@ rb_ca_mask_array (VALUE self)
949
1242
 
950
1243
  ca_update_mask(ca);
951
1244
  if ( ca->mask ) {
1245
+ /* The mask is reached from its array, not through a parent link, so
1246
+ ca_is_readonly on the mask never sees the array's read-only state.
1247
+ Pass it on here, where the mask is handed out. */
1248
+ if ( ca_is_readonly(ca) ) {
1249
+ ca_set_flag(ca->mask, CA_FLAG_READ_ONLY);
1250
+ }
952
1251
  obj = TypedData_Wrap_Struct(ca_mask_class[ca->obj_type],
953
- ca_mask_typeddata[ca->obj_type], ca->mask);
1252
+ ca_mask_typeddata[ca->obj_type], ca->mask);
954
1253
  rb_ivar_set(obj, rb_intern("masked_array"), self);
955
- if ( OBJ_FROZEN(self) ) {
956
- rb_ca_freeze(obj);
957
- }
958
1254
  return obj;
959
1255
  }
960
1256
  else {
@@ -981,30 +1277,54 @@ rb_ca_set_mask (VALUE self, VALUE rval)
981
1277
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
982
1278
 
983
1279
  if ( ca_is_value_array(ca) ) {
984
- rb_raise(rb_eRuntimeError,
1280
+ rb_raise(rb_eTypeError,
985
1281
  "can not create mask for the value array");
986
1282
  }
987
1283
 
988
1284
  if ( ca_is_mask_array(ca) ) {
989
- rb_raise(rb_eRuntimeError,
1285
+ rb_raise(rb_eTypeError,
990
1286
  "can not create mask for the mask array");
991
1287
  }
992
1288
 
993
- ca_update_mask(ca);
994
- if ( ! ca->mask ) {
995
- ca_create_mask(ca);
1289
+ if ( TYPE(rmask) == T_ARRAY ) {
1290
+ /* Flattened as a store into a boolean array of this shape flattens
1291
+ it, and then taken as a 1-D boolean array (UNDEF becomes a masked
1292
+ cell, as in any boolean array made from it). */
1293
+ CArray shape = *ca;
1294
+ shape.data_type = CA_BOOLEAN;
1295
+ shape.bytes = ca_sizeof[CA_BOOLEAN];
1296
+ rmask = rb_ary_flatten_for_elements(rmask, ca->elements, &shape);
1297
+ if ( NIL_P(rmask) ) {
1298
+ rb_raise(rb_eArgError, "failed to guess data size of given array");
1299
+ }
1300
+ rmask = rb_ca_wrap_readonly(rmask, INT2NUM(CA_BOOLEAN));
996
1301
  }
997
1302
 
998
1303
  if ( rb_obj_is_carray(rmask) ) {
1304
+ /* The array owns the shape, as in a store: the new mask agrees with
1305
+ it, is 1-D or squeezes to it, or broadcasts along size-1 axes.
1306
+ Checked before the mask exists, so a refused one leaves none. */
1307
+ ca_broadcast_to_destination(self, &rmask);
999
1308
  TypedData_Get_Struct(rmask, CArray, &carray_data_type, cv);
1309
+ /* A new mask taken from self's own root, or from self's mask, is
1310
+ read from a copy: making and writing self's mask would change it
1311
+ before it is read. */
1312
+ ca_update_mask(ca);
1313
+ if ( ca_shares_root(cv, ca) ||
1314
+ ( ca->mask && ca_shares_root(cv, ca->mask) ) ) {
1315
+ rmask = rb_ca_copy(rmask);
1316
+ TypedData_Get_Struct(rmask, CArray, &carray_data_type, cv);
1317
+ }
1000
1318
  if ( ! ca_is_boolean_type(cv) ) {
1001
- cv = ca_wrap_readonly(rval, CA_BOOLEAN);
1319
+ cv = ca_wrap_readonly(rmask, CA_BOOLEAN);
1002
1320
  }
1321
+ ca_create_mask(ca);
1003
1322
  ca_setup_mask(ca, cv);
1004
1323
  ca_copy_mask_overlay(ca, ca->elements, 1, cv);
1005
1324
  return rval;
1006
1325
  }
1007
1326
  else {
1327
+ ca_create_mask(ca);
1008
1328
  return rb_ca_store_all(rb_ca_mask_array(self), rmask);
1009
1329
  }
1010
1330
  }
@@ -1020,39 +1340,36 @@ with <code>self</code>. The returned array has 1 for the masked elements and
1020
1340
  VALUE
1021
1341
  rb_ca_is_masked (VALUE self)
1022
1342
  {
1023
- volatile VALUE mask;
1024
- CArray *ca, *cm, *co;
1343
+ volatile VALUE out;
1344
+ CArray *ca, *co;
1025
1345
  boolean8_t zero = 0;
1026
- boolean8_t *m, *p;
1346
+ boolean8_t *p;
1027
1347
  ca_size_t i;
1028
1348
 
1029
1349
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1030
1350
 
1031
1351
  if ( ca_is_scalar(ca) ) {
1032
- co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1352
+ co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1033
1353
  }
1034
1354
  else {
1035
- co = carray_new(CA_BOOLEAN, ca->ndim, ca->dim, ca->bytes, NULL);
1355
+ co = carray_new(CA_BOOLEAN, ca->ndim, ca->dim, ca->bytes, NULL);
1036
1356
  }
1357
+ out = ca_wrap_struct(co); /* owned by Ruby before anything can raise */
1037
1358
 
1038
1359
  ca_update_mask(ca);
1039
1360
  if ( ! ca->mask ) {
1040
1361
  ca_fill(co, &zero);
1041
1362
  }
1042
1363
  else {
1043
- mask = rb_ca_mask_array(self);
1044
- TypedData_Get_Struct(mask, CArray, &carray_data_type, cm);
1045
- ca_attach(cm);
1046
- m = (boolean8_t *) cm->ptr;
1364
+ /* Copied straight into the result, then normalised in place. */
1365
+ ca_copy_data(ca->mask, co->ptr);
1047
1366
  p = (boolean8_t *) co->ptr;
1048
1367
  for (i=0; i<ca->elements; i++) {
1049
- *p = ( *m ) ? 1 : 0;
1050
- m++; p++;
1368
+ p[i] = ( p[i] ) ? 1 : 0;
1051
1369
  }
1052
- ca_detach(cm);
1053
1370
  }
1054
1371
 
1055
- return ca_wrap_struct(co);
1372
+ return out;
1056
1373
  }
1057
1374
 
1058
1375
  /* @overload is_not_masked
@@ -1066,39 +1383,52 @@ The returned array has 0 for the masked elements and
1066
1383
  VALUE
1067
1384
  rb_ca_is_not_masked (VALUE self)
1068
1385
  {
1069
- volatile VALUE mask;
1070
- CArray *ca, *cm, *co;
1386
+ volatile VALUE out;
1387
+ CArray *ca, *co;
1071
1388
  boolean8_t one = 1;
1072
- boolean8_t *m, *p;
1389
+ boolean8_t *p;
1073
1390
  ca_size_t i;
1074
1391
 
1075
1392
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1076
1393
 
1077
1394
  if ( ca_is_scalar(ca) ) {
1078
- co = (CArray *) cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1395
+ co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1079
1396
  }
1080
1397
  else {
1081
- co = carray_new(CA_BOOLEAN, ca->ndim, ca->dim, ca->bytes, NULL);
1398
+ co = carray_new(CA_BOOLEAN, ca->ndim, ca->dim, ca->bytes, NULL);
1082
1399
  }
1400
+ out = ca_wrap_struct(co); /* owned by Ruby before anything can raise */
1083
1401
 
1084
1402
  ca_update_mask(ca);
1085
1403
  if ( ! ca->mask ) {
1086
1404
  ca_fill(co, &one);
1087
1405
  }
1088
1406
  else {
1089
- mask = rb_ca_mask_array(self);
1090
- TypedData_Get_Struct(mask, CArray, &carray_data_type, cm);
1091
- ca_attach(cm);
1092
- m = (boolean8_t *) cm->ptr;
1407
+ /* Copied straight into the result, then normalised in place. */
1408
+ ca_copy_data(ca->mask, co->ptr);
1093
1409
  p = (boolean8_t *) co->ptr;
1094
1410
  for (i=0; i<ca->elements; i++) {
1095
- *p = ( *m ) ? 0 : 1;
1096
- m++; p++;
1411
+ p[i] = ( p[i] ) ? 0 : 1;
1097
1412
  }
1098
- ca_detach(cm);
1099
1413
  }
1100
1414
 
1101
- return ca_wrap_struct(co);
1415
+ return out;
1416
+ }
1417
+
1418
+ VALUE
1419
+ rb_ca_scan_mask_count (VALUE self, VALUE axis)
1420
+ {
1421
+ VALUE present = rb_funcall(self, rb_intern("is_not_masked"), 0);
1422
+ VALUE count;
1423
+ if ( NIL_P(axis) ) {
1424
+ count = rb_funcall(present, rb_intern("cumsum"), 0);
1425
+ }
1426
+ else {
1427
+ VALUE kw = rb_hash_new();
1428
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis);
1429
+ count = rb_funcallv_kw(present, rb_intern("cumsum"), 1, &kw, RB_PASS_KEYWORDS);
1430
+ }
1431
+ return rb_funcall(count, rb_intern("to_type"), 1, INT2NUM(CA_INT64));
1102
1432
  }
1103
1433
 
1104
1434
  /* @overload unmask (fill_value = nil)
@@ -1124,19 +1454,20 @@ rb_ca_unmask_method (int argc, VALUE *argv, VALUE self)
1124
1454
  rfval = argv[0];
1125
1455
  }
1126
1456
 
1457
+ /* Filling with UNDEF leaves the cells undefined, as storing UNDEF does. */
1458
+ if ( rfval == CA_UNDEF ) {
1459
+ return self;
1460
+ }
1461
+
1127
1462
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1128
1463
 
1129
1464
  if ( rfval != CA_UNSPECIFIED ) {
1130
- /* Face has surface != storage; the fill value is cast in the storage data_type. */
1131
- int8_t conv_type = ca->data_type;
1132
- if ( ca_is_face(ca) ) {
1133
- CArray *root = ca;
1134
- while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
1135
- if (root) conv_type = root->data_type;
1136
- }
1137
- rcs = rb_cscalar_new_with_value(conv_type, ca->bytes, rfval);
1465
+ /* The fill is a value of the array (a struct, an instant for a Face):
1466
+ rb_ca_obj2ptr writes it as one cell of the storage. */
1467
+ rcs = rb_cscalar_new(ca_strip_face(ca)->data_type, ca->bytes, NULL);
1138
1468
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
1139
1469
  fval = cv->ptr;
1470
+ rb_ca_obj2ptr(self, rfval, fval);
1140
1471
  }
1141
1472
 
1142
1473
  ca_unmask(ca, fval);
@@ -1181,19 +1512,21 @@ rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
1181
1512
  rfval = argv[0];
1182
1513
  }
1183
1514
 
1515
+ /* Filling with UNDEF leaves the cells undefined, as storing UNDEF does:
1516
+ the copy keeps the mask. */
1517
+ if ( rfval == CA_UNDEF ) {
1518
+ return rb_ca_copy(self);
1519
+ }
1520
+
1184
1521
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1185
1522
 
1186
1523
  if ( rfval != CA_UNSPECIFIED ) {
1187
- /* Face has surface != storage; the fill value is cast in the storage data_type. */
1188
- int8_t conv_type = ca->data_type;
1189
- if ( ca_is_face(ca) ) {
1190
- CArray *root = ca;
1191
- while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
1192
- if (root) conv_type = root->data_type;
1193
- }
1194
- rcs = rb_cscalar_new_with_value(conv_type, ca->bytes, rfval);
1524
+ /* The fill is a value of the array (a struct, an instant for a Face):
1525
+ rb_ca_obj2ptr writes it as one cell of the storage. */
1526
+ rcs = rb_cscalar_new(ca_strip_face(ca)->data_type, ca->bytes, NULL);
1195
1527
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
1196
1528
  fval = cv->ptr;
1529
+ rb_ca_obj2ptr(self, rfval, fval);
1197
1530
  }
1198
1531
 
1199
1532
  co = ca_unmask_copy(ca, fval);
@@ -1228,6 +1561,7 @@ VALUE
1228
1561
  rb_ca_invert_mask (VALUE self)
1229
1562
  {
1230
1563
  CArray *ca;
1564
+ rb_ca_modify(self);
1231
1565
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1232
1566
  ca_invert_mask(ca);
1233
1567
  return self;
@@ -1259,24 +1593,53 @@ rb_ca_s_guard_undef(int argc, VALUE *argv, VALUE klass)
1259
1593
 
1260
1594
  /* mask_eq(v) — return form of "mask cells equal to v".
1261
1595
  Equivalent to: obj = copy; obj[:eq, v] = UNDEF; obj */
1596
+ /* Masks a copy of self at the cells the store key `argv` selects. A
1597
+ read-only Face (CAConstString, CACategorical) copies read-only, so the
1598
+ mask goes on a copy of its storage and the Face is put back; a predicate
1599
+ key (`:eq, v`) is evaluated on the Face, where it compares values rather
1600
+ than storage. */
1262
1601
  static VALUE
1263
- rb_ca_mask_eq (VALUE self, VALUE v)
1602
+ ca_mask_copy_where (VALUE self, int argc, VALUE *argv)
1264
1603
  {
1265
- volatile VALUE obj = rb_ca_copy(self);
1266
- rb_funcall(obj, rb_intern("[]="), 3,
1267
- ID2SYM(rb_intern("eq")), v, CA_UNDEF);
1604
+ CArray *ca;
1605
+ volatile VALUE obj;
1606
+ VALUE *all_args = ALLOCA_N(VALUE, argc + 1);
1607
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1608
+ if ( ca_is_face(ca) && ca_is_readonly(ca) ) {
1609
+ obj = rb_ca_copy(rb_ca_strip_face_value(self));
1610
+ if ( argc >= 1 && SYMBOL_P(argv[0]) ) {
1611
+ all_args[0] = rb_funcallv(self, SYM2ID(argv[0]), argc - 1, argv + 1);
1612
+ all_args[1] = CA_UNDEF;
1613
+ rb_funcallv(obj, rb_intern("[]="), 2, all_args);
1614
+ }
1615
+ else {
1616
+ memcpy(all_args, argv, argc * sizeof(VALUE));
1617
+ all_args[argc] = CA_UNDEF;
1618
+ rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
1619
+ }
1620
+ return rb_funcall(obj, rb_intern("face_lift"), 1, self);
1621
+ }
1622
+ obj = rb_ca_copy(self);
1623
+ memcpy(all_args, argv, argc * sizeof(VALUE));
1624
+ all_args[argc] = CA_UNDEF;
1625
+ rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
1268
1626
  return obj;
1269
1627
  }
1270
1628
 
1629
+ static VALUE
1630
+ rb_ca_mask_eq (VALUE self, VALUE v)
1631
+ {
1632
+ VALUE args[2] = { ID2SYM(rb_intern("eq")), v };
1633
+ return ca_mask_copy_where(self, 2, args);
1634
+ }
1635
+
1271
1636
  /* mask_invalid — return form of "mask NaN/Inf cells".
1272
1637
  Equivalent to: obj = copy; obj[:is_invalid] = UNDEF; obj */
1273
1638
  static VALUE
1274
1639
  rb_ca_mask_invalid (VALUE self)
1275
1640
  {
1276
- volatile VALUE obj = rb_ca_copy(self);
1277
- rb_funcall(obj, rb_intern("[]="), 2,
1278
- ID2SYM(rb_intern("is_invalid")), CA_UNDEF);
1279
- return obj;
1641
+ VALUE args[1] = { ID2SYM(rb_intern("is_invalid")) };
1642
+ return ca_mask_copy_where(self, 1, args);
1280
1643
  }
1281
1644
 
1282
1645
  /* mask_where(*args) — generic return form predicate masking.
@@ -1285,17 +1648,10 @@ rb_ca_mask_invalid (VALUE self)
1285
1648
  static VALUE
1286
1649
  rb_ca_mask_where (int argc, VALUE *argv, VALUE self)
1287
1650
  {
1288
- volatile VALUE obj;
1289
- VALUE *all_args;
1290
1651
  if (argc == 0) {
1291
1652
  rb_raise(rb_eArgError, "mask_where requires at least 1 argument");
1292
1653
  }
1293
- obj = rb_ca_copy(self);
1294
- all_args = ALLOCA_N(VALUE, argc + 1);
1295
- memcpy(all_args, argv, argc * sizeof(VALUE));
1296
- all_args[argc] = CA_UNDEF;
1297
- rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
1298
- return obj;
1654
+ return ca_mask_copy_where(self, argc, argv);
1299
1655
  }
1300
1656
 
1301
1657
  /* @overload inherit_mask (*others):
@@ -1309,6 +1665,7 @@ static VALUE
1309
1665
  rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1310
1666
  {
1311
1667
  CArray **slist;
1668
+ volatile VALUE holder;
1312
1669
  CArray *ca, *cs;
1313
1670
  int i;
1314
1671
 
@@ -1316,7 +1673,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1316
1673
 
1317
1674
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1318
1675
 
1319
- slist = xmalloc(sizeof(CArray *)*argc);
1676
+ slist = ALLOCV_N(CArray *, holder, argc);
1320
1677
  for (i=0; i<argc; i++) {
1321
1678
  if ( rb_obj_is_carray(argv[i]) ) {
1322
1679
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
@@ -1328,7 +1685,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1328
1685
  }
1329
1686
  ca_copy_mask_overlay_n(ca, ca->elements, argc, slist);
1330
1687
 
1331
- xfree(slist);
1688
+ ALLOCV_END(holder);
1332
1689
 
1333
1690
  return self;
1334
1691
  }
@@ -1348,6 +1705,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1348
1705
  {
1349
1706
  VALUE other;
1350
1707
  CArray **slist;
1708
+ volatile VALUE holder;
1351
1709
  CArray *ca, *cs;
1352
1710
  int i;
1353
1711
  va_list rothers;
@@ -1357,7 +1715,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1357
1715
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1358
1716
 
1359
1717
  va_start(rothers, n);
1360
- slist = xmalloc(sizeof(CArray *)*n);
1718
+ slist = ALLOCV_N(CArray *, holder, n);
1361
1719
  for (i=0; i<n; i++) {
1362
1720
  other = va_arg(rothers, VALUE);
1363
1721
  if ( rb_obj_is_carray(other) ) {
@@ -1372,7 +1730,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1372
1730
 
1373
1731
  ca_copy_mask_overlay_n(ca, ca->elements, n, slist);
1374
1732
 
1375
- xfree(slist);
1733
+ ALLOCV_END(holder);
1376
1734
 
1377
1735
  return self;
1378
1736
  }
@@ -1388,6 +1746,7 @@ static VALUE
1388
1746
  rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1389
1747
  {
1390
1748
  CArray **slist;
1749
+ volatile VALUE holder;
1391
1750
  CArray *ca, *cs;
1392
1751
  int i;
1393
1752
 
@@ -1395,7 +1754,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1395
1754
 
1396
1755
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1397
1756
 
1398
- slist = xmalloc(sizeof(CArray *)*argc);
1757
+ slist = ALLOCV_N(CArray *, holder, argc);
1399
1758
  for (i=0; i<argc; i++) {
1400
1759
  if ( rb_obj_is_carray(argv[i]) ) {
1401
1760
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
@@ -1407,7 +1766,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1407
1766
  }
1408
1767
  ca_copy_mask_overwrite_n(ca, ca->elements, argc, slist);
1409
1768
 
1410
- xfree(slist);
1769
+ ALLOCV_END(holder);
1411
1770
 
1412
1771
  return self;
1413
1772
  }
@@ -1427,6 +1786,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1427
1786
  {
1428
1787
  VALUE other;
1429
1788
  CArray **slist;
1789
+ volatile VALUE holder;
1430
1790
  CArray *ca, *cs;
1431
1791
  int i;
1432
1792
  va_list rothers;
@@ -1436,7 +1796,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1436
1796
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1437
1797
 
1438
1798
  va_start(rothers, n);
1439
- slist = xmalloc(sizeof(CArray *)*n);
1799
+ slist = ALLOCV_N(CArray *, holder, n);
1440
1800
  for (i=0; i<n; i++) {
1441
1801
  other = va_arg(rothers, VALUE);
1442
1802
  if ( rb_obj_is_carray(other) ) {
@@ -1451,34 +1811,34 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1451
1811
 
1452
1812
  ca_copy_mask_overwrite_n(ca, ca->elements, n, slist);
1453
1813
 
1454
- xfree(slist);
1814
+ ALLOCV_END(holder);
1455
1815
 
1456
1816
  return self;
1457
1817
  }
1458
1818
 
1459
- /* Helper: build a fill-valued int64 CArray whose shape is ca's shape
1460
- with the listed axes removed. Used by count_masked/count_not_masked
1461
- when there is no mask. */
1819
+ /* The no-mask answer of count_masked / count_not_masked along `axes`:
1820
+ every reduced cell holds `fill`. A reduction over every axis is the
1821
+ number itself. */
1462
1822
  static VALUE
1463
- ca_make_reduced_int64 (CArray *ca, int argc, VALUE *argv, ca_size_t fill_val)
1823
+ ca_count_uniform (CArray *ca, const int8_t *axes, int8_t naxes, ca_size_t fill)
1464
1824
  {
1465
1825
  ca_size_t dim[CA_RANK_MAX];
1466
1826
  int8_t ndim = 0;
1467
1827
  int i, j;
1468
- int64_t v = (int64_t) fill_val;
1828
+ int64_t v = (int64_t) fill;
1469
1829
  VALUE result;
1470
1830
  CArray *co;
1471
1831
 
1832
+ if ( naxes == ca->ndim ) {
1833
+ return SIZE2NUM(fill);
1834
+ }
1472
1835
  for (i = 0; i < ca->ndim; i++) {
1473
1836
  int is_axis = 0;
1474
- for (j = 0; j < argc; j++) {
1475
- int ax = NUM2INT(argv[j]);
1476
- if (ax < 0) ax += ca->ndim;
1477
- if (ax == i) { is_axis = 1; break; }
1837
+ for (j = 0; j < naxes; j++) {
1838
+ if ( axes[j] == i ) { is_axis = 1; break; }
1478
1839
  }
1479
1840
  if (!is_axis) dim[ndim++] = ca->dim[i];
1480
1841
  }
1481
-
1482
1842
  result = rb_carray_new(CA_INT64, ndim, dim, 0, NULL);
1483
1843
  TypedData_Get_Struct(result, CArray, &carray_data_type, co);
1484
1844
  ca_fill(co, &v);
@@ -1492,111 +1852,95 @@ ca_make_reduced_int64 (CArray *ca, int argc, VALUE *argv, ca_size_t fill_val)
1492
1852
  extern VALUE rb_ca_count_true_ki (int argc, VALUE *argv, VALUE self);
1493
1853
  extern VALUE rb_ca_count_false_ki (int argc, VALUE *argv, VALUE self);
1494
1854
 
1495
- /* count_masked(*axis) -- number of masked elements, optionally reduced
1496
- along axes (no args -> Integer; axis args -> int64 CArray with those
1497
- axes collapsed).
1855
+ /* count_masked / count_not_masked -- the number of masked (present)
1856
+ cells, over every axis (an Integer) or along `axis:` (an int64 CArray).
1857
+ The keywords are those of the count kernels (axis:, keep_axis:,
1858
+ min_count:, fill_value:), checked by them: the mask is counted with
1859
+ count_true_ki / count_false_ki. Without a mask the answer is known
1860
+ from the shape; an axis: alone takes that short cut after the same
1861
+ axis check the kernels make, and any other keyword runs the kernel on
1862
+ an all-false view that occupies one cell.
1498
1863
 
1499
1864
  Public (non-static) linkage so rb_ca_count in carray_count.c can forward
1500
1865
  count(UNDEF) here directly without going through Ruby method dispatch. */
1501
- VALUE
1502
- rb_ca_count_masked (int argc, VALUE *argv, VALUE self)
1866
+ static VALUE
1867
+ ca_count_by_mask (int argc, VALUE *argv, VALUE self, int masked)
1503
1868
  {
1504
1869
  CArray *ca;
1870
+ VALUE kw_hash = Qnil, mask_obj;
1505
1871
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1506
-
1507
- /* Accepts the axis: kwarg only (positional axis is not accepted),
1508
- matching the aggregate _ki family. */
1509
- VALUE kw_hash, axis_val = Qnil;
1510
1872
  rb_scan_args(argc, argv, "0:", &kw_hash);
1511
- rb_scan_options(kw_hash, "axis", &axis_val);
1512
1873
 
1513
- /* No mask -> nothing is masked. Short-circuit without materialising a
1514
- zero mask (axis form returns a zero-filled reduced int64 array). */
1515
- if (!ca_has_mask(ca)) {
1516
- if (NIL_P(axis_val)) {
1517
- return SIZE2NUM(0);
1874
+ if ( ! ca_has_mask(ca) ) {
1875
+ VALUE axis_val = Qnil;
1876
+ int only_axis = 1;
1877
+ if ( ! NIL_P(kw_hash) ) {
1878
+ VALUE given = rb_hash_lookup2(kw_hash, ID2SYM(rb_intern("axis")), Qundef);
1879
+ axis_val = ( given == Qundef ) ? Qnil : given;
1880
+ only_axis = ( RHASH_SIZE(kw_hash) == ( given == Qundef ? 0u : 1u ) );
1881
+ }
1882
+ if ( only_axis ) {
1883
+ int8_t axes[CA_RANK_MAX];
1884
+ int8_t naxes = rb_ca_parse_reduce_axes_kw(axis_val, ca, axes);
1885
+ ca_size_t vol = 1;
1886
+ int i;
1887
+ for ( i = 0; i < naxes; i++ ) vol *= ca->dim[axes[i]];
1888
+ return ca_count_uniform(ca, axes, naxes, masked ? 0 : vol);
1889
+ }
1890
+ {
1891
+ ca_size_t one[CA_RANK_MAX];
1892
+ VALUE shape = rb_ary_new_capa(ca->ndim), cell;
1893
+ boolean8_t zero = 0;
1894
+ CArray *cc;
1895
+ int i;
1896
+ for ( i = 0; i < ca->ndim; i++ ) {
1897
+ one[i] = 1;
1898
+ rb_ary_push(shape, SIZE2NUM(ca->dim[i]));
1899
+ }
1900
+ cell = rb_carray_new(CA_BOOLEAN, ca->ndim, one, 0, NULL);
1901
+ TypedData_Get_Struct(cell, CArray, &carray_data_type, cc);
1902
+ ca_fill(cc, &zero);
1903
+ mask_obj = rb_funcallv(cell, rb_intern("broadcast_to"),
1904
+ (int) RARRAY_LEN(shape), RARRAY_CONST_PTR(shape));
1518
1905
  }
1519
- VALUE axis_ary = (TYPE(axis_val) == T_ARRAY) ? axis_val : rb_ary_new3(1, axis_val);
1520
- return ca_make_reduced_int64(ca, (int) RARRAY_LEN(axis_ary),
1521
- (VALUE *) RARRAY_CONST_PTR(axis_ary), 0);
1522
1906
  }
1523
-
1524
- /* Masked count = count of `true` in the mask, routed through the
1525
- count_true_ki kernel for both the flat (no axis -> scalar Integer) and
1526
- per-axis (-> reduced CArray) forms. The scalar C primitive
1527
- ca_count_masked stays separately for internal C callers (grid /
1528
- median / order) that need a raw ca_size_t without Ruby allocation. */
1529
- VALUE mask_obj = rb_ca_mask_array(self);
1530
- if (NIL_P(axis_val)) {
1531
- return rb_ca_count_true_ki(0, NULL, mask_obj);
1907
+ else {
1908
+ mask_obj = rb_ca_mask_array(self);
1909
+ }
1910
+ if ( NIL_P(kw_hash) ) {
1911
+ return masked ? rb_ca_count_true_ki(0, NULL, mask_obj)
1912
+ : rb_ca_count_false_ki(0, NULL, mask_obj);
1913
+ }
1914
+ {
1915
+ VALUE kargv[1] = { kw_hash };
1916
+ return masked ? rb_ca_count_true_ki(1, kargv, mask_obj)
1917
+ : rb_ca_count_false_ki(1, kargv, mask_obj);
1532
1918
  }
1533
- VALUE kw = rb_hash_new();
1534
- rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
1535
- VALUE kargv[1] = { kw };
1536
- return rb_ca_count_true_ki(1, kargv, mask_obj);
1537
1919
  }
1538
1920
 
1539
- /* @overload count_not_masked(*axis)
1540
-
1541
- (Masking, Statistics)
1542
- Returns the number of not-masked elements, optionally reduced along axes.
1543
- With no arguments returns an Integer. With axis arguments returns a
1544
- CArray of int64 with the specified axes collapsed.
1545
- */
1546
- /* C-callable entry: skip rb_scan_args (call-frame state dependency).
1547
- * axis_val = Qnil -> scalar Integer count over whole array
1548
- * axis_val = Integer/Array -> reduced CArray int64
1549
- * For ext authors who need to call count_not_masked from C. */
1550
1921
  VALUE
1551
- rb_ca_count_not_masked_c (VALUE self, VALUE axis_val)
1922
+ rb_ca_count_masked (int argc, VALUE *argv, VALUE self)
1552
1923
  {
1553
- CArray *ca;
1554
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1555
-
1556
- /* No mask -> every element is not-masked. Short-circuit without
1557
- materialising a mask (no axis -> elements; axis form -> reduced int64
1558
- filled with the product of the reduced-axis extents). */
1559
- if (!ca_has_mask(ca)) {
1560
- if (NIL_P(axis_val)) {
1561
- return SIZE2NUM(ca->elements);
1562
- }
1563
- VALUE axis_ary = (TYPE(axis_val) == T_ARRAY) ? axis_val : rb_ary_new3(1, axis_val);
1564
- int n_axes = (int) RARRAY_LEN(axis_ary);
1565
- VALUE *axes_argv = (VALUE *) RARRAY_CONST_PTR(axis_ary);
1566
- ca_size_t axis_vol = 1;
1567
- int j;
1568
- for (j = 0; j < n_axes; j++) {
1569
- int ax = NUM2INT(axes_argv[j]);
1570
- if (ax < 0) ax += ca->ndim;
1571
- axis_vol *= ca->dim[ax];
1572
- }
1573
- return ca_make_reduced_int64(ca, n_axes, axes_argv, axis_vol);
1574
- }
1575
-
1576
- /* Not-masked count = count of `false` in the mask, routed through the
1577
- count_false_ki kernel for both the flat (no axis -> scalar Integer) and
1578
- per-axis (-> reduced CArray) forms. The scalar C primitive
1579
- ca_count_not_masked (= elements - ca_count_masked) stays separately for
1580
- internal C callers (median / order) that need a raw ca_size_t. */
1581
- VALUE mask_obj = rb_ca_mask_array(self);
1582
- if (NIL_P(axis_val)) {
1583
- return rb_ca_count_false_ki(0, NULL, mask_obj);
1584
- }
1585
- VALUE kw = rb_hash_new();
1586
- rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
1587
- VALUE kargv[1] = { kw };
1588
- return rb_ca_count_false_ki(1, kargv, mask_obj);
1924
+ return ca_count_by_mask(argc, argv, self, 1);
1589
1925
  }
1590
1926
 
1591
- /* Ruby binding entry: parses (axis: kwarg) -> forwards to _c. */
1592
1927
  VALUE
1593
1928
  rb_ca_count_not_masked (int argc, VALUE *argv, VALUE self)
1594
1929
  {
1595
- /* Accepts the axis: kwarg only. */
1596
- VALUE kw_hash, axis_val = Qnil;
1597
- rb_scan_args(argc, argv, "0:", &kw_hash);
1598
- rb_scan_options(kw_hash, "axis", &axis_val);
1599
- return rb_ca_count_not_masked_c(self, axis_val);
1930
+ return ca_count_by_mask(argc, argv, self, 0);
1931
+ }
1932
+
1933
+ /* count_not_masked(axis: axis_val) for C callers. */
1934
+ VALUE
1935
+ rb_ca_count_not_masked_c (VALUE self, VALUE axis_val)
1936
+ {
1937
+ VALUE kw;
1938
+ if ( NIL_P(axis_val) ) {
1939
+ return ca_count_by_mask(0, NULL, self, 0);
1940
+ }
1941
+ kw = rb_hash_new();
1942
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
1943
+ return rb_funcall_with_block_kw(self, rb_intern("count_not_masked"), 1, &kw, Qnil, RB_PASS_KEYWORDS);
1600
1944
  }
1601
1945
 
1602
1946
  /* ============================================================================
@@ -1818,6 +2162,43 @@ Init_carray_mask_fast_paths (void)
1818
2162
  }
1819
2163
 
1820
2164
 
2165
+ /* The Ruby forms of inherit_mask take CArray arguments only: refuse a
2166
+ keyword and any argument that is not a CArray, which the C API would
2167
+ pass over as maskless. Returns the arguments without the keyword Hash. */
2168
+ static VALUE
2169
+ ca_inherit_mask_check_args (int argc, VALUE *argv, const char *name)
2170
+ {
2171
+ VALUE rest, ropt;
2172
+ long i;
2173
+ rb_scan_args(argc, argv, "*:", &rest, &ropt);
2174
+ rb_reject_options(ropt);
2175
+ for (i=0; i<RARRAY_LEN(rest); i++) {
2176
+ VALUE v = RARRAY_AREF(rest, i);
2177
+ if ( ! rb_obj_is_carray(v) ) {
2178
+ rb_raise(rb_eTypeError, "%s: argument %ld must be a CArray (got %s)",
2179
+ name, i, rb_obj_classname(v));
2180
+ }
2181
+ }
2182
+ return rest;
2183
+ }
2184
+
2185
+ static VALUE
2186
+ rb_ca_inherit_mask_ruby (int argc, VALUE *argv, VALUE self)
2187
+ {
2188
+ volatile VALUE rest = ca_inherit_mask_check_args(argc, argv, "inherit_mask");
2189
+ return rb_ca_inherit_mask_method((int) RARRAY_LEN(rest),
2190
+ (VALUE *) RARRAY_CONST_PTR(rest), self);
2191
+ }
2192
+
2193
+ static VALUE
2194
+ rb_ca_inherit_mask_replace_ruby (int argc, VALUE *argv, VALUE self)
2195
+ {
2196
+ volatile VALUE rest = ca_inherit_mask_check_args(argc, argv,
2197
+ "inherit_mask_replace");
2198
+ return rb_ca_inherit_mask_replace_method((int) RARRAY_LEN(rest),
2199
+ (VALUE *) RARRAY_CONST_PTR(rest), self);
2200
+ }
2201
+
1821
2202
  void
1822
2203
  Init_carray_mask (void)
1823
2204
  {
@@ -1854,9 +2235,9 @@ Init_carray_mask (void)
1854
2235
  rb_define_method(rb_cCArray, "mask_invalid", rb_ca_mask_invalid, 0);
1855
2236
  rb_define_method(rb_cCArray, "mask_where", rb_ca_mask_where, -1);
1856
2237
 
1857
- rb_define_method(rb_cCArray, "inherit_mask", rb_ca_inherit_mask_method, -1);
2238
+ rb_define_method(rb_cCArray, "inherit_mask", rb_ca_inherit_mask_ruby, -1);
1858
2239
  rb_define_method(rb_cCArray, "inherit_mask_replace",
1859
- rb_ca_inherit_mask_replace_method, -1);
2240
+ rb_ca_inherit_mask_replace_ruby, -1);
1860
2241
 
1861
2242
  rb_define_method(rb_cCArray, "count_masked", rb_ca_count_masked, -1);
1862
2243
  rb_define_method(rb_cCArray, "count_not_masked", rb_ca_count_not_masked, -1);