carray 3.0.2 → 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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  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 +66 -71
  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 +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  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 +132 -22
  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 +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -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 +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  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 +543 -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 +47 -49
  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 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  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 +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. 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);
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;
235
274
 
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
- }
246
-
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,8 +917,7 @@ 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
  }
@@ -677,17 +939,21 @@ ca_unmask_copy_fill (VALUE arg)
677
939
 
678
940
  ca_copy_data(ca, co->ptr);
679
941
 
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. */
680
944
  if ( a->fill_value && ca_has_mask(ca) ) {
681
- ca_attach(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);
682
949
  q = co->ptr;
683
- m = (boolean8_t *) ca->mask->ptr;
684
950
  for (i=0; i<ca->elements; i++) {
685
- if ( *m ) {
951
+ if ( m[i] ) {
686
952
  memcpy(q, a->fill_value, ca->bytes);
687
953
  }
688
- m++; q+=co->bytes;
954
+ q+=co->bytes;
689
955
  }
690
- ca_detach(ca);
956
+ ALLOCV_END(holder);
691
957
  }
692
958
  return Qnil;
693
959
  }
@@ -709,16 +975,12 @@ ca_invert_mask (void *ap)
709
975
  CArray *ca = (CArray *) ap;
710
976
  boolean8_t *m;
711
977
 
712
- ca_update_mask(ca);
713
-
714
- if ( ! ca->mask ) {
715
- ca_create_mask(ca);
716
- }
978
+ ca_create_mask(ca);
717
979
 
718
- ca_attach(ca->mask);
980
+ ca_attach(ca->mask); /* window: nothing raises inside */
719
981
  m = (boolean8_t *) ca->mask->ptr;
720
982
  ca_mask_word_not(m, m, ca->elements); /* in-place 1 - m, LSB-XOR word loop */
721
- ca_detach(ca->mask);
983
+ ca_sync_detach(ca->mask);
722
984
 
723
985
  return;
724
986
  }
@@ -727,9 +989,10 @@ boolean8_t *
727
989
  ca_allocate_mask_iterator_n (int n, CArray **slist)
728
990
  {
729
991
  boolean8_t *m, *mp, *ms;
730
- CArray *cs;
992
+ CArray *cs, **masks;
993
+ char *scalar;
731
994
  ca_size_t j, elements = -1;
732
- int i, some_has_mask = 0;
995
+ int i, k = 0, some_has_mask = 0;
733
996
 
734
997
  for (i=0; i<n; i++) {
735
998
  if ( slist[i] ) {
@@ -743,7 +1006,7 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
743
1006
  elements = slist[i]->elements;
744
1007
  }
745
1008
  else if ( ! ca_is_scalar(slist[i]) ) {
746
- rb_raise(rb_eRuntimeError,
1009
+ rb_raise(rb_eArgError,
747
1010
  "# of elements is different among the given arrays");
748
1011
  }
749
1012
  }
@@ -758,13 +1021,18 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
758
1021
  (degenerate call); guard so the alloc/memset never see a negative size
759
1022
  (which would wrap to a huge bound). Also silences GCC's VRP warnings. */
760
1023
  if ( elements < 0 ) elements = 0;
761
- m = xmalloc(sizeof(boolean8_t)*elements);
762
- memset(m, 0, elements);
763
1024
 
764
1025
  if ( ! some_has_mask ) {
1026
+ m = xmalloc(sizeof(boolean8_t)*elements);
1027
+ memset(m, 0, elements);
765
1028
  return m;
766
1029
  }
767
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);
768
1036
  for (i=0; i<n; i++) {
769
1037
  cs = slist[i];
770
1038
  if ( ! cs ) {
@@ -774,10 +1042,17 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
774
1042
  if ( ! cs->mask ) {
775
1043
  continue;
776
1044
  }
777
- ca_attach(cs->mask);
778
- 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;
779
1054
  mp = m;
780
- if ( ca_is_scalar(cs) ) {
1055
+ if ( scalar[i] ) {
781
1056
  if ( *ms ) {
782
1057
  for (j=0; j<elements; j++) {
783
1058
  *mp = 1;
@@ -788,7 +1063,9 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
788
1063
  else {
789
1064
  ca_mask_word_or(mp, ms, elements); /* m[j] |= ms[j], word-bulk OR */
790
1065
  }
791
- ca_detach(cs->mask);
1066
+ }
1067
+ while ( k > 0 ) {
1068
+ ca_detach(masks[--k]);
792
1069
  }
793
1070
  return m;
794
1071
  }
@@ -798,10 +1075,11 @@ ca_allocate_mask_iterator (int n, ...)
798
1075
  {
799
1076
  boolean8_t *m;
800
1077
  CArray **slist;
1078
+ volatile VALUE holder;
801
1079
  va_list args;
802
1080
  int i;
803
1081
 
804
- slist = xmalloc(sizeof(CArray *)*n);
1082
+ slist = ALLOCV_N(CArray *, holder, n);
805
1083
  va_start(args, n);
806
1084
  for (i=0; i<n; i++) {
807
1085
  slist[i] = va_arg(args, CArray *);
@@ -810,7 +1088,7 @@ ca_allocate_mask_iterator (int n, ...)
810
1088
 
811
1089
  m = ca_allocate_mask_iterator_n(n, slist);
812
1090
 
813
- xfree(slist);
1091
+ ALLOCV_END(holder);
814
1092
 
815
1093
  return m;
816
1094
  }
@@ -964,12 +1242,15 @@ rb_ca_mask_array (VALUE self)
964
1242
 
965
1243
  ca_update_mask(ca);
966
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
+ }
967
1251
  obj = TypedData_Wrap_Struct(ca_mask_class[ca->obj_type],
968
- ca_mask_typeddata[ca->obj_type], ca->mask);
1252
+ ca_mask_typeddata[ca->obj_type], ca->mask);
969
1253
  rb_ivar_set(obj, rb_intern("masked_array"), self);
970
- if ( OBJ_FROZEN(self) ) {
971
- rb_ca_freeze(obj);
972
- }
973
1254
  return obj;
974
1255
  }
975
1256
  else {
@@ -996,30 +1277,54 @@ rb_ca_set_mask (VALUE self, VALUE rval)
996
1277
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
997
1278
 
998
1279
  if ( ca_is_value_array(ca) ) {
999
- rb_raise(rb_eRuntimeError,
1280
+ rb_raise(rb_eTypeError,
1000
1281
  "can not create mask for the value array");
1001
1282
  }
1002
1283
 
1003
1284
  if ( ca_is_mask_array(ca) ) {
1004
- rb_raise(rb_eRuntimeError,
1285
+ rb_raise(rb_eTypeError,
1005
1286
  "can not create mask for the mask array");
1006
1287
  }
1007
1288
 
1008
- ca_update_mask(ca);
1009
- if ( ! ca->mask ) {
1010
- 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));
1011
1301
  }
1012
1302
 
1013
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);
1014
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
+ }
1015
1318
  if ( ! ca_is_boolean_type(cv) ) {
1016
- cv = ca_wrap_readonly(rval, CA_BOOLEAN);
1319
+ cv = ca_wrap_readonly(rmask, CA_BOOLEAN);
1017
1320
  }
1321
+ ca_create_mask(ca);
1018
1322
  ca_setup_mask(ca, cv);
1019
1323
  ca_copy_mask_overlay(ca, ca->elements, 1, cv);
1020
1324
  return rval;
1021
1325
  }
1022
1326
  else {
1327
+ ca_create_mask(ca);
1023
1328
  return rb_ca_store_all(rb_ca_mask_array(self), rmask);
1024
1329
  }
1025
1330
  }
@@ -1035,39 +1340,36 @@ with <code>self</code>. The returned array has 1 for the masked elements and
1035
1340
  VALUE
1036
1341
  rb_ca_is_masked (VALUE self)
1037
1342
  {
1038
- volatile VALUE mask;
1039
- CArray *ca, *cm, *co;
1343
+ volatile VALUE out;
1344
+ CArray *ca, *co;
1040
1345
  boolean8_t zero = 0;
1041
- boolean8_t *m, *p;
1346
+ boolean8_t *p;
1042
1347
  ca_size_t i;
1043
1348
 
1044
1349
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1045
1350
 
1046
1351
  if ( ca_is_scalar(ca) ) {
1047
- co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1352
+ co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1048
1353
  }
1049
1354
  else {
1050
- 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);
1051
1356
  }
1357
+ out = ca_wrap_struct(co); /* owned by Ruby before anything can raise */
1052
1358
 
1053
1359
  ca_update_mask(ca);
1054
1360
  if ( ! ca->mask ) {
1055
1361
  ca_fill(co, &zero);
1056
1362
  }
1057
1363
  else {
1058
- mask = rb_ca_mask_array(self);
1059
- TypedData_Get_Struct(mask, CArray, &carray_data_type, cm);
1060
- ca_attach(cm);
1061
- m = (boolean8_t *) cm->ptr;
1364
+ /* Copied straight into the result, then normalised in place. */
1365
+ ca_copy_data(ca->mask, co->ptr);
1062
1366
  p = (boolean8_t *) co->ptr;
1063
1367
  for (i=0; i<ca->elements; i++) {
1064
- *p = ( *m ) ? 1 : 0;
1065
- m++; p++;
1368
+ p[i] = ( p[i] ) ? 1 : 0;
1066
1369
  }
1067
- ca_detach(cm);
1068
1370
  }
1069
1371
 
1070
- return ca_wrap_struct(co);
1372
+ return out;
1071
1373
  }
1072
1374
 
1073
1375
  /* @overload is_not_masked
@@ -1081,39 +1383,52 @@ The returned array has 0 for the masked elements and
1081
1383
  VALUE
1082
1384
  rb_ca_is_not_masked (VALUE self)
1083
1385
  {
1084
- volatile VALUE mask;
1085
- CArray *ca, *cm, *co;
1386
+ volatile VALUE out;
1387
+ CArray *ca, *co;
1086
1388
  boolean8_t one = 1;
1087
- boolean8_t *m, *p;
1389
+ boolean8_t *p;
1088
1390
  ca_size_t i;
1089
1391
 
1090
1392
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1091
1393
 
1092
1394
  if ( ca_is_scalar(ca) ) {
1093
- co = (CArray *) cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1395
+ co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1094
1396
  }
1095
1397
  else {
1096
- 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);
1097
1399
  }
1400
+ out = ca_wrap_struct(co); /* owned by Ruby before anything can raise */
1098
1401
 
1099
1402
  ca_update_mask(ca);
1100
1403
  if ( ! ca->mask ) {
1101
1404
  ca_fill(co, &one);
1102
1405
  }
1103
1406
  else {
1104
- mask = rb_ca_mask_array(self);
1105
- TypedData_Get_Struct(mask, CArray, &carray_data_type, cm);
1106
- ca_attach(cm);
1107
- m = (boolean8_t *) cm->ptr;
1407
+ /* Copied straight into the result, then normalised in place. */
1408
+ ca_copy_data(ca->mask, co->ptr);
1108
1409
  p = (boolean8_t *) co->ptr;
1109
1410
  for (i=0; i<ca->elements; i++) {
1110
- *p = ( *m ) ? 0 : 1;
1111
- m++; p++;
1411
+ p[i] = ( p[i] ) ? 0 : 1;
1112
1412
  }
1113
- ca_detach(cm);
1114
1413
  }
1115
1414
 
1116
- 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));
1117
1432
  }
1118
1433
 
1119
1434
  /* @overload unmask (fill_value = nil)
@@ -1139,19 +1454,20 @@ rb_ca_unmask_method (int argc, VALUE *argv, VALUE self)
1139
1454
  rfval = argv[0];
1140
1455
  }
1141
1456
 
1457
+ /* Filling with UNDEF leaves the cells undefined, as storing UNDEF does. */
1458
+ if ( rfval == CA_UNDEF ) {
1459
+ return self;
1460
+ }
1461
+
1142
1462
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1143
1463
 
1144
1464
  if ( rfval != CA_UNSPECIFIED ) {
1145
- /* Face has surface != storage; the fill value is cast in the storage data_type. */
1146
- int8_t conv_type = ca->data_type;
1147
- if ( ca_is_face(ca) ) {
1148
- CArray *root = ca;
1149
- while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
1150
- if (root) conv_type = root->data_type;
1151
- }
1152
- 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);
1153
1468
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
1154
1469
  fval = cv->ptr;
1470
+ rb_ca_obj2ptr(self, rfval, fval);
1155
1471
  }
1156
1472
 
1157
1473
  ca_unmask(ca, fval);
@@ -1196,19 +1512,21 @@ rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
1196
1512
  rfval = argv[0];
1197
1513
  }
1198
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
+
1199
1521
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1200
1522
 
1201
1523
  if ( rfval != CA_UNSPECIFIED ) {
1202
- /* Face has surface != storage; the fill value is cast in the storage data_type. */
1203
- int8_t conv_type = ca->data_type;
1204
- if ( ca_is_face(ca) ) {
1205
- CArray *root = ca;
1206
- while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
1207
- if (root) conv_type = root->data_type;
1208
- }
1209
- 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);
1210
1527
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
1211
1528
  fval = cv->ptr;
1529
+ rb_ca_obj2ptr(self, rfval, fval);
1212
1530
  }
1213
1531
 
1214
1532
  co = ca_unmask_copy(ca, fval);
@@ -1243,6 +1561,7 @@ VALUE
1243
1561
  rb_ca_invert_mask (VALUE self)
1244
1562
  {
1245
1563
  CArray *ca;
1564
+ rb_ca_modify(self);
1246
1565
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1247
1566
  ca_invert_mask(ca);
1248
1567
  return self;
@@ -1274,24 +1593,53 @@ rb_ca_s_guard_undef(int argc, VALUE *argv, VALUE klass)
1274
1593
 
1275
1594
  /* mask_eq(v) — return form of "mask cells equal to v".
1276
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. */
1277
1601
  static VALUE
1278
- rb_ca_mask_eq (VALUE self, VALUE v)
1602
+ ca_mask_copy_where (VALUE self, int argc, VALUE *argv)
1279
1603
  {
1280
- volatile VALUE obj = rb_ca_copy(self);
1281
- rb_funcall(obj, rb_intern("[]="), 3,
1282
- 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);
1283
1626
  return obj;
1284
1627
  }
1285
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
+
1286
1636
  /* mask_invalid — return form of "mask NaN/Inf cells".
1287
1637
  Equivalent to: obj = copy; obj[:is_invalid] = UNDEF; obj */
1288
1638
  static VALUE
1289
1639
  rb_ca_mask_invalid (VALUE self)
1290
1640
  {
1291
- volatile VALUE obj = rb_ca_copy(self);
1292
- rb_funcall(obj, rb_intern("[]="), 2,
1293
- ID2SYM(rb_intern("is_invalid")), CA_UNDEF);
1294
- return obj;
1641
+ VALUE args[1] = { ID2SYM(rb_intern("is_invalid")) };
1642
+ return ca_mask_copy_where(self, 1, args);
1295
1643
  }
1296
1644
 
1297
1645
  /* mask_where(*args) — generic return form predicate masking.
@@ -1300,17 +1648,10 @@ rb_ca_mask_invalid (VALUE self)
1300
1648
  static VALUE
1301
1649
  rb_ca_mask_where (int argc, VALUE *argv, VALUE self)
1302
1650
  {
1303
- volatile VALUE obj;
1304
- VALUE *all_args;
1305
1651
  if (argc == 0) {
1306
1652
  rb_raise(rb_eArgError, "mask_where requires at least 1 argument");
1307
1653
  }
1308
- obj = rb_ca_copy(self);
1309
- all_args = ALLOCA_N(VALUE, argc + 1);
1310
- memcpy(all_args, argv, argc * sizeof(VALUE));
1311
- all_args[argc] = CA_UNDEF;
1312
- rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
1313
- return obj;
1654
+ return ca_mask_copy_where(self, argc, argv);
1314
1655
  }
1315
1656
 
1316
1657
  /* @overload inherit_mask (*others):
@@ -1324,6 +1665,7 @@ static VALUE
1324
1665
  rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1325
1666
  {
1326
1667
  CArray **slist;
1668
+ volatile VALUE holder;
1327
1669
  CArray *ca, *cs;
1328
1670
  int i;
1329
1671
 
@@ -1331,7 +1673,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1331
1673
 
1332
1674
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1333
1675
 
1334
- slist = xmalloc(sizeof(CArray *)*argc);
1676
+ slist = ALLOCV_N(CArray *, holder, argc);
1335
1677
  for (i=0; i<argc; i++) {
1336
1678
  if ( rb_obj_is_carray(argv[i]) ) {
1337
1679
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
@@ -1343,7 +1685,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1343
1685
  }
1344
1686
  ca_copy_mask_overlay_n(ca, ca->elements, argc, slist);
1345
1687
 
1346
- xfree(slist);
1688
+ ALLOCV_END(holder);
1347
1689
 
1348
1690
  return self;
1349
1691
  }
@@ -1363,6 +1705,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1363
1705
  {
1364
1706
  VALUE other;
1365
1707
  CArray **slist;
1708
+ volatile VALUE holder;
1366
1709
  CArray *ca, *cs;
1367
1710
  int i;
1368
1711
  va_list rothers;
@@ -1372,7 +1715,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1372
1715
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1373
1716
 
1374
1717
  va_start(rothers, n);
1375
- slist = xmalloc(sizeof(CArray *)*n);
1718
+ slist = ALLOCV_N(CArray *, holder, n);
1376
1719
  for (i=0; i<n; i++) {
1377
1720
  other = va_arg(rothers, VALUE);
1378
1721
  if ( rb_obj_is_carray(other) ) {
@@ -1387,7 +1730,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1387
1730
 
1388
1731
  ca_copy_mask_overlay_n(ca, ca->elements, n, slist);
1389
1732
 
1390
- xfree(slist);
1733
+ ALLOCV_END(holder);
1391
1734
 
1392
1735
  return self;
1393
1736
  }
@@ -1403,6 +1746,7 @@ static VALUE
1403
1746
  rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1404
1747
  {
1405
1748
  CArray **slist;
1749
+ volatile VALUE holder;
1406
1750
  CArray *ca, *cs;
1407
1751
  int i;
1408
1752
 
@@ -1410,7 +1754,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1410
1754
 
1411
1755
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1412
1756
 
1413
- slist = xmalloc(sizeof(CArray *)*argc);
1757
+ slist = ALLOCV_N(CArray *, holder, argc);
1414
1758
  for (i=0; i<argc; i++) {
1415
1759
  if ( rb_obj_is_carray(argv[i]) ) {
1416
1760
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
@@ -1422,7 +1766,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1422
1766
  }
1423
1767
  ca_copy_mask_overwrite_n(ca, ca->elements, argc, slist);
1424
1768
 
1425
- xfree(slist);
1769
+ ALLOCV_END(holder);
1426
1770
 
1427
1771
  return self;
1428
1772
  }
@@ -1442,6 +1786,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1442
1786
  {
1443
1787
  VALUE other;
1444
1788
  CArray **slist;
1789
+ volatile VALUE holder;
1445
1790
  CArray *ca, *cs;
1446
1791
  int i;
1447
1792
  va_list rothers;
@@ -1451,7 +1796,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1451
1796
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1452
1797
 
1453
1798
  va_start(rothers, n);
1454
- slist = xmalloc(sizeof(CArray *)*n);
1799
+ slist = ALLOCV_N(CArray *, holder, n);
1455
1800
  for (i=0; i<n; i++) {
1456
1801
  other = va_arg(rothers, VALUE);
1457
1802
  if ( rb_obj_is_carray(other) ) {
@@ -1466,34 +1811,34 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1466
1811
 
1467
1812
  ca_copy_mask_overwrite_n(ca, ca->elements, n, slist);
1468
1813
 
1469
- xfree(slist);
1814
+ ALLOCV_END(holder);
1470
1815
 
1471
1816
  return self;
1472
1817
  }
1473
1818
 
1474
- /* Helper: build a fill-valued int64 CArray whose shape is ca's shape
1475
- with the listed axes removed. Used by count_masked/count_not_masked
1476
- 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. */
1477
1822
  static VALUE
1478
- 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)
1479
1824
  {
1480
1825
  ca_size_t dim[CA_RANK_MAX];
1481
1826
  int8_t ndim = 0;
1482
1827
  int i, j;
1483
- int64_t v = (int64_t) fill_val;
1828
+ int64_t v = (int64_t) fill;
1484
1829
  VALUE result;
1485
1830
  CArray *co;
1486
1831
 
1832
+ if ( naxes == ca->ndim ) {
1833
+ return SIZE2NUM(fill);
1834
+ }
1487
1835
  for (i = 0; i < ca->ndim; i++) {
1488
1836
  int is_axis = 0;
1489
- for (j = 0; j < argc; j++) {
1490
- int ax = NUM2INT(argv[j]);
1491
- if (ax < 0) ax += ca->ndim;
1492
- if (ax == i) { is_axis = 1; break; }
1837
+ for (j = 0; j < naxes; j++) {
1838
+ if ( axes[j] == i ) { is_axis = 1; break; }
1493
1839
  }
1494
1840
  if (!is_axis) dim[ndim++] = ca->dim[i];
1495
1841
  }
1496
-
1497
1842
  result = rb_carray_new(CA_INT64, ndim, dim, 0, NULL);
1498
1843
  TypedData_Get_Struct(result, CArray, &carray_data_type, co);
1499
1844
  ca_fill(co, &v);
@@ -1507,111 +1852,95 @@ ca_make_reduced_int64 (CArray *ca, int argc, VALUE *argv, ca_size_t fill_val)
1507
1852
  extern VALUE rb_ca_count_true_ki (int argc, VALUE *argv, VALUE self);
1508
1853
  extern VALUE rb_ca_count_false_ki (int argc, VALUE *argv, VALUE self);
1509
1854
 
1510
- /* count_masked(*axis) -- number of masked elements, optionally reduced
1511
- along axes (no args -> Integer; axis args -> int64 CArray with those
1512
- 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.
1513
1863
 
1514
1864
  Public (non-static) linkage so rb_ca_count in carray_count.c can forward
1515
1865
  count(UNDEF) here directly without going through Ruby method dispatch. */
1516
- VALUE
1517
- 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)
1518
1868
  {
1519
1869
  CArray *ca;
1870
+ VALUE kw_hash = Qnil, mask_obj;
1520
1871
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1521
-
1522
- /* Accepts the axis: kwarg only (positional axis is not accepted),
1523
- matching the aggregate _ki family. */
1524
- VALUE kw_hash, axis_val = Qnil;
1525
1872
  rb_scan_args(argc, argv, "0:", &kw_hash);
1526
- rb_scan_options(kw_hash, "axis", &axis_val);
1527
1873
 
1528
- /* No mask -> nothing is masked. Short-circuit without materialising a
1529
- zero mask (axis form returns a zero-filled reduced int64 array). */
1530
- if (!ca_has_mask(ca)) {
1531
- if (NIL_P(axis_val)) {
1532
- 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));
1533
1905
  }
1534
- VALUE axis_ary = (TYPE(axis_val) == T_ARRAY) ? axis_val : rb_ary_new3(1, axis_val);
1535
- return ca_make_reduced_int64(ca, (int) RARRAY_LEN(axis_ary),
1536
- (VALUE *) RARRAY_CONST_PTR(axis_ary), 0);
1537
1906
  }
1538
-
1539
- /* Masked count = count of `true` in the mask, routed through the
1540
- count_true_ki kernel for both the flat (no axis -> scalar Integer) and
1541
- per-axis (-> reduced CArray) forms. The scalar C primitive
1542
- ca_count_masked stays separately for internal C callers (grid /
1543
- median / order) that need a raw ca_size_t without Ruby allocation. */
1544
- VALUE mask_obj = rb_ca_mask_array(self);
1545
- if (NIL_P(axis_val)) {
1546
- 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);
1547
1918
  }
1548
- VALUE kw = rb_hash_new();
1549
- rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
1550
- VALUE kargv[1] = { kw };
1551
- return rb_ca_count_true_ki(1, kargv, mask_obj);
1552
1919
  }
1553
1920
 
1554
- /* @overload count_not_masked(*axis)
1555
-
1556
- (Masking, Statistics)
1557
- Returns the number of not-masked elements, optionally reduced along axes.
1558
- With no arguments returns an Integer. With axis arguments returns a
1559
- CArray of int64 with the specified axes collapsed.
1560
- */
1561
- /* C-callable entry: skip rb_scan_args (call-frame state dependency).
1562
- * axis_val = Qnil -> scalar Integer count over whole array
1563
- * axis_val = Integer/Array -> reduced CArray int64
1564
- * For ext authors who need to call count_not_masked from C. */
1565
1921
  VALUE
1566
- rb_ca_count_not_masked_c (VALUE self, VALUE axis_val)
1922
+ rb_ca_count_masked (int argc, VALUE *argv, VALUE self)
1567
1923
  {
1568
- CArray *ca;
1569
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1570
-
1571
- /* No mask -> every element is not-masked. Short-circuit without
1572
- materialising a mask (no axis -> elements; axis form -> reduced int64
1573
- filled with the product of the reduced-axis extents). */
1574
- if (!ca_has_mask(ca)) {
1575
- if (NIL_P(axis_val)) {
1576
- return SIZE2NUM(ca->elements);
1577
- }
1578
- VALUE axis_ary = (TYPE(axis_val) == T_ARRAY) ? axis_val : rb_ary_new3(1, axis_val);
1579
- int n_axes = (int) RARRAY_LEN(axis_ary);
1580
- VALUE *axes_argv = (VALUE *) RARRAY_CONST_PTR(axis_ary);
1581
- ca_size_t axis_vol = 1;
1582
- int j;
1583
- for (j = 0; j < n_axes; j++) {
1584
- int ax = NUM2INT(axes_argv[j]);
1585
- if (ax < 0) ax += ca->ndim;
1586
- axis_vol *= ca->dim[ax];
1587
- }
1588
- return ca_make_reduced_int64(ca, n_axes, axes_argv, axis_vol);
1589
- }
1590
-
1591
- /* Not-masked count = count of `false` in the mask, routed through the
1592
- count_false_ki kernel for both the flat (no axis -> scalar Integer) and
1593
- per-axis (-> reduced CArray) forms. The scalar C primitive
1594
- ca_count_not_masked (= elements - ca_count_masked) stays separately for
1595
- internal C callers (median / order) that need a raw ca_size_t. */
1596
- VALUE mask_obj = rb_ca_mask_array(self);
1597
- if (NIL_P(axis_val)) {
1598
- return rb_ca_count_false_ki(0, NULL, mask_obj);
1599
- }
1600
- VALUE kw = rb_hash_new();
1601
- rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
1602
- VALUE kargv[1] = { kw };
1603
- return rb_ca_count_false_ki(1, kargv, mask_obj);
1924
+ return ca_count_by_mask(argc, argv, self, 1);
1604
1925
  }
1605
1926
 
1606
- /* Ruby binding entry: parses (axis: kwarg) -> forwards to _c. */
1607
1927
  VALUE
1608
1928
  rb_ca_count_not_masked (int argc, VALUE *argv, VALUE self)
1609
1929
  {
1610
- /* Accepts the axis: kwarg only. */
1611
- VALUE kw_hash, axis_val = Qnil;
1612
- rb_scan_args(argc, argv, "0:", &kw_hash);
1613
- rb_scan_options(kw_hash, "axis", &axis_val);
1614
- 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);
1615
1944
  }
1616
1945
 
1617
1946
  /* ============================================================================
@@ -1833,6 +2162,43 @@ Init_carray_mask_fast_paths (void)
1833
2162
  }
1834
2163
 
1835
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
+
1836
2202
  void
1837
2203
  Init_carray_mask (void)
1838
2204
  {
@@ -1869,9 +2235,9 @@ Init_carray_mask (void)
1869
2235
  rb_define_method(rb_cCArray, "mask_invalid", rb_ca_mask_invalid, 0);
1870
2236
  rb_define_method(rb_cCArray, "mask_where", rb_ca_mask_where, -1);
1871
2237
 
1872
- 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);
1873
2239
  rb_define_method(rb_cCArray, "inherit_mask_replace",
1874
- rb_ca_inherit_mask_replace_method, -1);
2240
+ rb_ca_inherit_mask_replace_ruby, -1);
1875
2241
 
1876
2242
  rb_define_method(rb_cCArray, "count_masked", rb_ca_count_masked, -1);
1877
2243
  rb_define_method(rb_cCArray, "count_not_masked", rb_ca_count_not_masked, -1);