carray 3.0.2 → 3.0.4

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
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
- }
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;
233
274
 
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
- }
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,296 @@ 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_mask_standin_new (CArray *parent)
427
+ {
428
+ CArray *z = carray_new(CA_BOOLEAN, parent->ndim, parent->dim, 0, NULL);
429
+ MEMZERO(z->ptr, boolean8_t, z->elements);
430
+ if ( ca_is_readonly(parent) ) {
431
+ ca_set_flag(z, CA_FLAG_READ_ONLY);
432
+ }
433
+ return z;
434
+ }
435
+
436
+ /* A value array or a mask array: a parent that can never be given a mask,
437
+ whose stand-in therefore stays in place for good. */
438
+ static int
439
+ ca_mask_standin_parent_unmaskable (CArray *parent)
440
+ {
441
+ return ca_is_value_array(parent) || ca_is_mask_array(parent);
442
+ }
443
+
444
+ CArray *
445
+ ca_mask_standin_current (CArray *mask_view, CArray *reading, CArray *parent,
446
+ CArray *standin)
447
+ {
448
+ if ( standin == NULL || reading != standin
449
+ || ca_mask_standin_parent_unmaskable(parent) ) {
450
+ return reading;
451
+ }
452
+ /* Not inside an attach window whose buffer is out: that buffer and the
453
+ detach to come are those of the stand-in. The attach and allocate
454
+ slots run before the buffer is published, and may still give way. */
455
+ if ( CAVIEW(mask_view)->attach > 0 && mask_view->ptr != NULL ) {
456
+ return reading;
457
+ }
458
+ /* A read-only parent (a lazy expression, a broadcast) is never given a
459
+ mask by a write, but can still have one later through its operand or
460
+ root. */
461
+ if ( ! ca_has_mask(parent) ) {
462
+ return reading;
463
+ }
464
+ ca_create_mask(parent);
465
+ return ( parent->mask != NULL ) ? parent->mask : reading;
466
+ }
467
+
468
+ void
469
+ ca_mask_standin_settle (CArray *parent, CArray *standin)
470
+ {
471
+ volatile VALUE holder = 0;
472
+ boolean8_t *z, one = 1;
473
+ ca_size_t *addrs;
474
+ ca_size_t i, m = 0, n;
475
+
476
+ if ( standin == NULL || ca_test_flag(standin, CA_FLAG_READ_ONLY) ) {
477
+ return;
478
+ }
479
+ z = (boolean8_t *) standin->ptr;
480
+ n = standin->elements;
481
+ for ( i = 0; i < n && ! z[i]; i++ ) ;
482
+ if ( i == n ) {
483
+ return;
484
+ }
485
+ if ( ca_mask_standin_parent_unmaskable(parent) ) {
486
+ MEMZERO(z, boolean8_t, n);
487
+ rb_raise(rb_eTypeError, "can not create mask array for the %s array",
488
+ ca_is_value_array(parent) ? "value" : "mask");
489
+ }
490
+ ca_create_mask(parent);
491
+ /* The trues are added to the parent's mask, not copied over it: the
492
+ parent may have come to have masked cells of its own meanwhile. */
493
+ addrs = ALLOCV_N(ca_size_t, holder, n);
494
+ for ( ; i < n; i++ ) {
495
+ if ( z[i] ) {
496
+ addrs[m++] = i;
497
+ }
498
+ }
499
+ ca_fill_addrs(parent->mask, m, addrs, &one);
500
+ ALLOCV_END(holder);
501
+ MEMZERO(z, boolean8_t, n);
502
+ }
503
+
504
+ CArray *
505
+ ca_multi_parent_mask (CArray *p, CArray **standin)
506
+ {
507
+ *standin = NULL;
508
+ if ( ! ca_has_mask(p) ) {
509
+ *standin = ca_mask_standin_new(p);
510
+ return *standin;
511
+ }
512
+ ca_create_mask(p);
513
+ return p->mask;
514
+ }
515
+
516
+ void
517
+ ca_multi_parent_mask_refresh (CArray *mask, CArray **parents,
518
+ CArray **standins, int32_t n)
519
+ {
520
+ CAMultiParent *mp = (CAMultiParent *) mask;
521
+ int32_t k;
522
+ if ( standins == NULL ) {
523
+ return;
524
+ }
525
+ /* A replaced stand-in stays allocated until the view is freed: a copy
526
+ of the mask (dup) may still point at it. */
527
+ for ( k = 0; k < n; k++ ) {
528
+ mp->parents[k] = ca_mask_standin_current(mask, mp->parents[k], parents[k],
529
+ standins[k]);
530
+ }
531
+ CAVIEW(mask)->parent = mp->parents[0];
532
+ }
533
+
534
+ typedef struct {
535
+ CArray *mask;
536
+ CArray **blanks;
537
+ CArray **saved;
538
+ CArray *saved_parent;
539
+ int32_t n;
540
+ int finished;
541
+ VALUE (*body)(VALUE);
542
+ VALUE arg;
543
+ } ca_mp_trial_t;
544
+
545
+ static VALUE
546
+ ca_mp_trial_body (VALUE p)
547
+ {
548
+ ca_mp_trial_t *t = (ca_mp_trial_t *) p;
549
+ t->body(t->arg);
550
+ t->finished = 1;
551
+ return Qnil;
552
+ }
553
+
554
+ static void
555
+ ca_mp_trial_free_blanks (ca_mp_trial_t *t)
556
+ {
557
+ int32_t k;
558
+ for ( k = 0; k < t->n; k++ ) {
559
+ ca_free(t->blanks[k]);
560
+ t->blanks[k] = NULL;
561
+ }
562
+ }
563
+
564
+ static VALUE
565
+ ca_mp_trial_restore (VALUE p)
566
+ {
567
+ ca_mp_trial_t *t = (ca_mp_trial_t *) p;
568
+ CAMultiParent *mp = (CAMultiParent *) t->mask;
569
+ int32_t k;
570
+ for ( k = 0; k < t->n; k++ ) {
571
+ mp->parents[k] = t->saved[k];
572
+ }
573
+ CAVIEW(t->mask)->parent = t->saved_parent;
574
+ if ( ! t->finished ) {
575
+ ca_mp_trial_free_blanks(t);
576
+ }
577
+ return Qnil;
578
+ }
579
+
580
+ void
581
+ ca_multi_parent_mask_refuse_first (CArray *mask, CArray **parents,
582
+ CArray **standins, int32_t n,
583
+ VALUE (*body)(VALUE), VALUE arg)
584
+ {
585
+ CAMultiParent *mp = (CAMultiParent *) mask;
586
+ volatile VALUE h_blank = 0, h_saved = 0;
587
+ ca_mp_trial_t t;
588
+ int32_t k, refused = -1;
589
+ if ( standins == NULL ) {
590
+ return;
591
+ }
592
+ for ( k = 0; k < n; k++ ) {
593
+ if ( standins[k] != NULL && mp->parents[k] == standins[k]
594
+ && ca_mask_standin_parent_unmaskable(parents[k]) ) {
595
+ break;
596
+ }
597
+ }
598
+ if ( k == n ) {
599
+ return;
600
+ }
601
+ t.mask = mask;
602
+ t.n = n;
603
+ t.finished = 0;
604
+ t.body = body;
605
+ t.arg = arg;
606
+ t.saved_parent = CAVIEW(mask)->parent;
607
+ t.blanks = ALLOCV_N(CArray *, h_blank, n);
608
+ t.saved = ALLOCV_N(CArray *, h_saved, n);
609
+ for ( k = 0; k < n; k++ ) {
610
+ t.blanks[k] = NULL;
611
+ }
612
+ for ( k = 0; k < n; k++ ) {
613
+ CArray *m = mp->parents[k];
614
+ t.saved[k] = m;
615
+ t.blanks[k] = carray_new(CA_BOOLEAN, m->ndim, m->dim, 0, NULL);
616
+ MEMZERO(t.blanks[k]->ptr, boolean8_t, t.blanks[k]->elements);
617
+ }
618
+ for ( k = 0; k < n; k++ ) {
619
+ mp->parents[k] = t.blanks[k];
620
+ }
621
+ CAVIEW(mask)->parent = t.blanks[0];
622
+ rb_ensure(ca_mp_trial_body, (VALUE) &t, ca_mp_trial_restore, (VALUE) &t);
623
+ for ( k = 0; k < n && refused < 0; k++ ) {
624
+ CArray *z = t.blanks[k];
625
+ ca_size_t i;
626
+ if ( standins[k] == NULL || t.saved[k] != standins[k]
627
+ || ! ca_mask_standin_parent_unmaskable(parents[k]) ) {
628
+ continue;
629
+ }
630
+ for ( i = 0; i < z->elements && ! ((boolean8_t *) z->ptr)[i]; i++ ) ;
631
+ if ( i < z->elements ) {
632
+ refused = k;
633
+ }
634
+ }
635
+ ca_mp_trial_free_blanks(&t);
636
+ ALLOCV_END(h_blank);
637
+ ALLOCV_END(h_saved);
638
+ if ( refused >= 0 ) {
639
+ rb_raise(rb_eTypeError, "can not create mask array for the %s array",
640
+ ca_is_value_array(parents[refused]) ? "value" : "mask");
641
+ }
642
+ }
643
+
644
+ void
645
+ ca_multi_parent_mask_settle (CArray *mask, CArray **parents,
646
+ CArray **standins, int32_t n)
647
+ {
648
+ CAMultiParent *mp = (CAMultiParent *) mask;
649
+ int32_t k, pass;
650
+ if ( standins == NULL ) {
651
+ return;
652
+ }
653
+ /* The parts that can have no mask first, so that a refused true leaves
654
+ the other parts as they were. */
655
+ for ( pass = 0; pass < 2; pass++ ) {
656
+ for ( k = 0; k < n; k++ ) {
657
+ if ( standins[k] == NULL || mp->parents[k] != standins[k]
658
+ || ca_mask_standin_parent_unmaskable(parents[k]) != ( pass == 0 ) ) {
659
+ continue;
660
+ }
661
+ ca_mask_standin_settle(parents[k], standins[k]);
662
+ }
663
+ }
664
+ }
665
+
377
666
  void
378
667
  ca_create_mask (void *ap)
379
668
  {
380
669
  CArray *ca = (CArray *) ap;
381
670
 
671
+ if ( ca->mask ) {
672
+ return;
673
+ }
674
+
382
675
  if ( ca_is_value_array(ca) ) {
383
- rb_raise(rb_eRuntimeError,
676
+ rb_raise(rb_eTypeError,
384
677
  "can not create mask array for the value array");
385
678
  }
386
679
 
387
680
  if ( ca_is_mask_array(ca) ) {
388
- rb_raise(rb_eRuntimeError,
681
+ rb_raise(rb_eTypeError,
389
682
  "can not create mask array for the mask array");
390
683
  }
391
684
 
685
+ ca_func[ca->obj_type].create_mask(ca);
392
686
  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
- }
687
+ /* a lazy expression with no masked operand has no mask to make */
688
+ return;
689
+ }
690
+ ca_set_flag(ca->mask, CA_FLAG_MASK_ARRAY); /* set array as mask array */
691
+ if ( ca_is_view(ca) ) {
692
+ if ( CAVIEW(ca)->attach ) {
693
+ /* The new mask joins its view's level. Its attach reads the
694
+ parent's mask, which can raise (a Ruby-backed parent); the mask
695
+ then comes off again, since the view's sync and detach would
696
+ find it attached to nothing. */
697
+ int tag = 0;
698
+ /* protect: the mask comes off its view before it is freed */
699
+ rb_protect(ca_create_mask_attach, (VALUE) ca->mask, &tag);
700
+ if ( tag ) {
701
+ CArray *mask = ca->mask;
702
+ ca->mask = NULL;
703
+ ca_free(mask);
704
+ rb_jump_tag(tag);
705
+ }
706
+ if ( ca_is_view(ca->mask) ) {
707
+ CAVIEW(ca->mask)->attach = CAVIEW(ca)->attach;
401
708
  }
402
709
  }
403
710
  }
@@ -415,20 +722,42 @@ ca_clear_mask (void *ap)
415
722
  }
416
723
  }
417
724
 
725
+ typedef struct {
726
+ CArray *ca;
727
+ CArray *mask;
728
+ } ca_setup_mask_t;
729
+
730
+ /* Pushing the cells into ca's mask can raise (a CAObject's mask hooks). */
731
+ static VALUE
732
+ ca_setup_mask_body (VALUE arg)
733
+ {
734
+ ca_setup_mask_t *c = (ca_setup_mask_t *) arg;
735
+ ca_sync_data(c->ca->mask, c->mask->ptr);
736
+ return Qnil;
737
+ }
738
+
418
739
  void
419
740
  ca_setup_mask (void *ap, CArray *mask)
420
741
  {
421
742
  CArray *ca = (CArray *) ap;
422
743
 
744
+ if ( mask ) {
745
+ if ( ! ca_is_boolean_type(mask) ) {
746
+ rb_raise(rb_eCADataTypeError, "a mask is a boolean array");
747
+ }
748
+ ca_check_same_elements(ca, mask);
749
+ }
750
+
423
751
  ca_update_mask(ca);
424
752
 
425
753
  if ( mask ) {
754
+ ca_setup_mask_t c;
426
755
  if ( ! ca->mask ) {
427
756
  ca_create_mask(ca);
428
757
  }
429
- ca_attach(mask);
430
- ca_sync_data(ca->mask, mask->ptr);
431
- ca_detach(mask);
758
+ c.ca = ca;
759
+ c.mask = mask;
760
+ ca_attach_window(1, &c.mask, "r", ca_setup_mask_body, (VALUE) &c);
432
761
  }
433
762
  else if ( ca->mask ) {
434
763
  boolean8_t zero = 0;
@@ -443,14 +772,76 @@ ca_setup_mask (void *ap, CArray *mask)
443
772
 
444
773
  */
445
774
 
446
- void
447
- ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
775
+ /* The overlay reads each source's mask into the destination's. A
776
+ source's mask can be made or read by calling Ruby (a CAObject's mask
777
+ hooks) and raise, so every mask is made first and the lot are read
778
+ inside one attach window.
779
+
780
+ A source whose cells live in the destination's root has its mask read
781
+ into a snapshot before the destination's mask is made, cleared or
782
+ written: the destination's mask may be that same memory, and the word
783
+ OR would otherwise read bytes it has already set. */
784
+ typedef struct {
785
+ CArray **list; /* [0] the destination mask, then the attached sources' */
786
+ boolean8_t **snap; /* per source: its mask read in advance, or NULL */
787
+ CArray **att; /* per source: its attached mask when not snapped */
788
+ char *scalar; /* per source: the source is a scalar */
789
+ int k; /* entries in list */
790
+ int ns; /* sources */
791
+ ca_size_t elements;
792
+ } ca_mask_overlay_t;
793
+
794
+ static VALUE
795
+ ca_mask_overlay_body (VALUE arg)
796
+ {
797
+ ca_mask_overlay_t *o = (ca_mask_overlay_t *) arg;
798
+ boolean8_t *ma = (boolean8_t *) o->list[0]->ptr;
799
+ int i;
800
+ for (i=0; i<o->ns; i++) {
801
+ boolean8_t *ms = o->snap[i] ? o->snap[i] : (boolean8_t *) o->att[i]->ptr;
802
+ /* scalar masked source -> broadcast to all (memset); array source
803
+ -> pure word OR (ca_mask_word_or). A defensive `if (*ms)` is
804
+ unnecessary: under the {0,1} invariant, `*ma |= *ms` equals
805
+ `if (*ms) *ma = 1`. Post-condition: the mask byte stays in {0,1}. */
806
+ if ( o->scalar[i] ) {
807
+ if ( *ms ) {
808
+ memset(ma, 1, o->elements);
809
+ }
810
+ }
811
+ else {
812
+ ca_mask_word_or(ma, ms, o->elements);
813
+ }
814
+ }
815
+ return Qnil;
816
+ }
817
+
818
+ /* overwrite: the destination's mask is cleared before the sources are
819
+ OR'd in (and left alone when it has none and no source has a mask). */
820
+ static void
821
+ ca_copy_mask_overlay_core (CArray *ca, ca_size_t elements, int n,
822
+ CArray **slist, int overwrite)
448
823
  {
449
- CArray *ca = (CArray *) ap;
450
824
  CArray *cs;
451
- boolean8_t *ma, *ms;
825
+ ca_mask_overlay_t o;
826
+ volatile VALUE *holders;
827
+ char *modes;
452
828
  int i, some_has_mask = 0;
453
829
 
830
+ if ( elements > ca->elements ) {
831
+ elements = ca->elements;
832
+ }
833
+
834
+ /* An array source is read cell by cell over the destination's cells;
835
+ a scalar source's one cell applies to all of them. */
836
+ for (i=0; i<n; i++) {
837
+ cs = slist[i];
838
+ if ( cs && ! ca_is_scalar(cs) && cs->elements < elements ) {
839
+ rb_raise(rb_eArgError,
840
+ "mask source has %lld elements, fewer than the %lld of the destination",
841
+ (long long) cs->elements, (long long) elements);
842
+ }
843
+ }
844
+
454
845
  for (i=0; i<n; i++) {
455
846
  if ( slist[i] && ca_has_mask(slist[i]) ) {
456
847
  some_has_mask = 1;
@@ -458,81 +849,111 @@ ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
458
849
  }
459
850
  }
460
851
 
461
- if ( some_has_mask ) {
852
+ if ( ! some_has_mask ) {
853
+ if ( overwrite ) {
854
+ ca_update_mask(ca);
855
+ if ( ca->mask ) {
856
+ boolean8_t zero = 0;
857
+ ca_fill(ca->mask, &zero);
858
+ }
859
+ }
860
+ return;
861
+ }
462
862
 
463
- ca_update_mask(ca);
464
- if ( ! ca->mask ) {
465
- ca_create_mask(ca);
863
+ o.list = ALLOCA_N(CArray *, n + 1);
864
+ o.snap = ALLOCA_N(boolean8_t *, n);
865
+ o.att = ALLOCA_N(CArray *, n);
866
+ o.scalar = ALLOCA_N(char, n);
867
+ holders = ALLOCA_N(VALUE, n);
868
+ modes = ALLOCA_N(char, n + 2);
869
+ o.elements = elements;
870
+ o.ns = 0;
871
+ for (i=0; i<n; i++) {
872
+ holders[i] = 0;
873
+ }
874
+ ca_update_mask(ca);
875
+
876
+ /* Every source mask is made, and those in the destination's root are
877
+ read, before the destination's mask is touched. */
878
+ for (i=0; i<n; i++) {
879
+ cs = slist[i];
880
+ if ( ! cs ) {
881
+ continue;
882
+ }
883
+ ca_update_mask(cs);
884
+ if ( ! cs->mask ) {
885
+ continue;
886
+ }
887
+ o.scalar[o.ns] = (char) ca_is_scalar(cs);
888
+ o.att[o.ns] = NULL;
889
+ o.snap[o.ns] = NULL;
890
+ if ( ca_shares_root(cs, ca) || ca_shares_root(cs->mask, ca) ||
891
+ ( ca->mask && ca_shares_root(cs->mask, ca->mask) ) ) {
892
+ o.snap[o.ns] = ALLOCV_N(boolean8_t, holders[o.ns], cs->mask->elements);
893
+ ca_xfer_all(cs->mask, (char *) o.snap[o.ns], CA_XFER_GET);
466
894
  }
895
+ else {
896
+ o.att[o.ns] = cs->mask;
897
+ }
898
+ o.ns++;
899
+ }
467
900
 
468
- if ( elements > ca->elements ) {
469
- elements = ca->elements;
901
+ ca_create_mask(ca);
902
+ if ( overwrite ) {
903
+ boolean8_t zero = 0;
904
+ ca_fill(ca->mask, &zero);
905
+ }
906
+
907
+ o.k = 0;
908
+ o.list[o.k] = ca->mask;
909
+ modes[o.k++] = 'w';
910
+ for (i=0; i<o.ns; i++) {
911
+ if ( o.att[i] ) {
912
+ o.list[o.k] = o.att[i];
913
+ modes[o.k++] = 'r';
470
914
  }
915
+ }
916
+ modes[o.k] = '\0';
917
+ ca_attach_window(o.k, o.list, modes, ca_mask_overlay_body, (VALUE) &o);
471
918
 
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);
919
+ for (i=0; i<n; i++) {
920
+ if ( holders[i] ) {
921
+ ALLOCV_END(holders[i]);
498
922
  }
499
- ca_sync(ca->mask);
500
- ca_detach(ca->mask);
501
923
  }
502
924
  }
503
925
 
926
+ void
927
+ ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
928
+ {
929
+ ca_copy_mask_overlay_core((CArray *) ap, elements, n, slist, 0);
930
+ }
931
+
504
932
  void
505
933
  ca_copy_mask_overlay (void *ap, ca_size_t elements, int n, ...)
506
934
  {
507
935
  CArray *ca = (CArray *) ap;
508
936
  CArray **slist;
937
+ volatile VALUE holder;
509
938
  va_list args;
510
939
  int i;
511
940
 
512
- slist = xmalloc(sizeof(CArray *)*n);
941
+ slist = ALLOCV_N(CArray *, holder, n);
513
942
  va_start(args, n);
514
943
  for (i=0; i<n; i++) {
515
944
  slist[i] = va_arg(args, CArray *);
516
945
  }
517
946
  va_end(args);
518
947
 
519
- ca_copy_mask_overlay_n(ca, elements, n, slist);
948
+ ca_copy_mask_overlay_core(ca, elements, n, slist, 0);
520
949
 
521
- xfree(slist);
950
+ ALLOCV_END(holder);
522
951
  }
523
952
 
524
953
  void
525
954
  ca_copy_mask_overwrite_n (void *ap, ca_size_t elements, int n, CArray **slist)
526
955
  {
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);
956
+ ca_copy_mask_overlay_core((CArray *) ap, elements, n, slist, 1);
536
957
  }
537
958
 
538
959
  void
@@ -540,25 +961,20 @@ ca_copy_mask_overwrite (void *ap, ca_size_t elements, int n, ...)
540
961
  {
541
962
  CArray *ca = (CArray *) ap;
542
963
  CArray **slist;
964
+ volatile VALUE holder;
543
965
  va_list args;
544
966
  int i;
545
967
 
546
- slist = xmalloc(sizeof(CArray*)*n);
968
+ slist = ALLOCV_N(CArray *, holder, n);
547
969
  va_start(args, n);
548
970
  for (i=0; i<n; i++) {
549
971
  slist[i] = va_arg(args, CArray*);
550
972
  }
551
973
  va_end(args);
552
974
 
553
- ca_update_mask(ca);
554
- if ( ca->mask ) {
555
- boolean8_t zero = 0;
556
- ca_fill(ca->mask, &zero);
557
- }
558
-
559
- ca_copy_mask_overlay_n(ca, elements, n, slist);
975
+ ca_copy_mask_overlay_core(ca, elements, n, slist, 1);
560
976
 
561
- xfree(slist);
977
+ ALLOCV_END(holder);
562
978
  }
563
979
 
564
980
  void
@@ -582,7 +998,7 @@ ca_count_masked (void *ap)
582
998
  if ( ca->mask ) {
583
999
  if ( ca_attach_is_alias(ca->mask) ) {
584
1000
  /* cheap mask: O(1) alias attach + word-bulk count. */
585
- ca_attach(ca->mask);
1001
+ ca_attach(ca->mask); /* window: nothing raises inside */
586
1002
  m = (boolean8_t *) ca->mask->ptr;
587
1003
  count = ca_mask_word_count(m, ca->elements);
588
1004
  ca_detach(ca->mask);
@@ -637,6 +1053,10 @@ ca_unmask (void *ap, char *fill_value)
637
1053
  {
638
1054
  CArray *ca = (CArray *) ap;
639
1055
 
1056
+ if ( ca_is_readonly(ca) ) {
1057
+ ca_refuse_read_only();
1058
+ }
1059
+
640
1060
  ca_update_mask(ca);
641
1061
  if ( ca->mask ) {
642
1062
  if ( ! fill_value ) {
@@ -644,7 +1064,7 @@ ca_unmask (void *ap, char *fill_value)
644
1064
  ca_fill(ca->mask, &zero);
645
1065
  }
646
1066
  else {
647
- ca_attach(ca);
1067
+ ca_attach(ca); /* window: nothing raises inside */
648
1068
 
649
1069
  switch ( ca->bytes ) {
650
1070
  case 1: proc_fill_bang_width(uint8_t); break;
@@ -654,8 +1074,7 @@ ca_unmask (void *ap, char *fill_value)
654
1074
  default: proc_fill_bang_bytes(); break; /* cmplx128 (16) + fixlen */
655
1075
  }
656
1076
 
657
- ca_sync(ca);
658
- ca_detach(ca);
1077
+ ca_sync_detach(ca);
659
1078
  }
660
1079
  }
661
1080
  }
@@ -677,17 +1096,21 @@ ca_unmask_copy_fill (VALUE arg)
677
1096
 
678
1097
  ca_copy_data(ca, co->ptr);
679
1098
 
1099
+ /* Only the mask is needed now that the values are in co, so read the
1100
+ mask alone rather than producing the view's cells a second time. */
680
1101
  if ( a->fill_value && ca_has_mask(ca) ) {
681
- ca_attach(ca);
1102
+ volatile VALUE holder = 0;
1103
+ ca_update_mask(ca);
1104
+ m = ALLOCV_N(boolean8_t, holder, ca->elements);
1105
+ ca_copy_data(ca->mask, (char *) m);
682
1106
  q = co->ptr;
683
- m = (boolean8_t *) ca->mask->ptr;
684
1107
  for (i=0; i<ca->elements; i++) {
685
- if ( *m ) {
1108
+ if ( m[i] ) {
686
1109
  memcpy(q, a->fill_value, ca->bytes);
687
1110
  }
688
- m++; q+=co->bytes;
1111
+ q+=co->bytes;
689
1112
  }
690
- ca_detach(ca);
1113
+ ALLOCV_END(holder);
691
1114
  }
692
1115
  return Qnil;
693
1116
  }
@@ -709,16 +1132,12 @@ ca_invert_mask (void *ap)
709
1132
  CArray *ca = (CArray *) ap;
710
1133
  boolean8_t *m;
711
1134
 
712
- ca_update_mask(ca);
713
-
714
- if ( ! ca->mask ) {
715
- ca_create_mask(ca);
716
- }
1135
+ ca_create_mask(ca);
717
1136
 
718
- ca_attach(ca->mask);
1137
+ ca_attach(ca->mask); /* window: nothing raises inside */
719
1138
  m = (boolean8_t *) ca->mask->ptr;
720
1139
  ca_mask_word_not(m, m, ca->elements); /* in-place 1 - m, LSB-XOR word loop */
721
- ca_detach(ca->mask);
1140
+ ca_sync_detach(ca->mask);
722
1141
 
723
1142
  return;
724
1143
  }
@@ -727,9 +1146,10 @@ boolean8_t *
727
1146
  ca_allocate_mask_iterator_n (int n, CArray **slist)
728
1147
  {
729
1148
  boolean8_t *m, *mp, *ms;
730
- CArray *cs;
1149
+ CArray *cs, **masks;
1150
+ char *scalar;
731
1151
  ca_size_t j, elements = -1;
732
- int i, some_has_mask = 0;
1152
+ int i, k = 0, some_has_mask = 0;
733
1153
 
734
1154
  for (i=0; i<n; i++) {
735
1155
  if ( slist[i] ) {
@@ -743,7 +1163,7 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
743
1163
  elements = slist[i]->elements;
744
1164
  }
745
1165
  else if ( ! ca_is_scalar(slist[i]) ) {
746
- rb_raise(rb_eRuntimeError,
1166
+ rb_raise(rb_eArgError,
747
1167
  "# of elements is different among the given arrays");
748
1168
  }
749
1169
  }
@@ -758,13 +1178,18 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
758
1178
  (degenerate call); guard so the alloc/memset never see a negative size
759
1179
  (which would wrap to a huge bound). Also silences GCC's VRP warnings. */
760
1180
  if ( elements < 0 ) elements = 0;
761
- m = xmalloc(sizeof(boolean8_t)*elements);
762
- memset(m, 0, elements);
763
1181
 
764
1182
  if ( ! some_has_mask ) {
1183
+ m = xmalloc(sizeof(boolean8_t)*elements);
1184
+ memset(m, 0, elements);
765
1185
  return m;
766
1186
  }
767
1187
 
1188
+ /* The sources' masks, made and attached before the result is allocated:
1189
+ making or reading one can call Ruby (a CAObject's mask hooks) and
1190
+ raise, and then nothing is left attached or allocated. */
1191
+ masks = ALLOCA_N(CArray *, n);
1192
+ scalar = ALLOCA_N(char, n);
768
1193
  for (i=0; i<n; i++) {
769
1194
  cs = slist[i];
770
1195
  if ( ! cs ) {
@@ -774,10 +1199,17 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
774
1199
  if ( ! cs->mask ) {
775
1200
  continue;
776
1201
  }
777
- ca_attach(cs->mask);
778
- ms = (boolean8_t *) cs->mask->ptr;
1202
+ masks[k] = cs->mask;
1203
+ scalar[k++] = (char) ca_is_scalar(cs);
1204
+ }
1205
+ ca_attach_all(masks, k); /* window: nothing raises inside */
1206
+
1207
+ m = xmalloc(sizeof(boolean8_t)*elements);
1208
+ memset(m, 0, elements);
1209
+ for (i=0; i<k; i++) {
1210
+ ms = (boolean8_t *) masks[i]->ptr;
779
1211
  mp = m;
780
- if ( ca_is_scalar(cs) ) {
1212
+ if ( scalar[i] ) {
781
1213
  if ( *ms ) {
782
1214
  for (j=0; j<elements; j++) {
783
1215
  *mp = 1;
@@ -788,7 +1220,9 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
788
1220
  else {
789
1221
  ca_mask_word_or(mp, ms, elements); /* m[j] |= ms[j], word-bulk OR */
790
1222
  }
791
- ca_detach(cs->mask);
1223
+ }
1224
+ while ( k > 0 ) {
1225
+ ca_detach(masks[--k]);
792
1226
  }
793
1227
  return m;
794
1228
  }
@@ -798,10 +1232,11 @@ ca_allocate_mask_iterator (int n, ...)
798
1232
  {
799
1233
  boolean8_t *m;
800
1234
  CArray **slist;
1235
+ volatile VALUE holder;
801
1236
  va_list args;
802
1237
  int i;
803
1238
 
804
- slist = xmalloc(sizeof(CArray *)*n);
1239
+ slist = ALLOCV_N(CArray *, holder, n);
805
1240
  va_start(args, n);
806
1241
  for (i=0; i<n; i++) {
807
1242
  slist[i] = va_arg(args, CArray *);
@@ -810,7 +1245,7 @@ ca_allocate_mask_iterator (int n, ...)
810
1245
 
811
1246
  m = ca_allocate_mask_iterator_n(n, slist);
812
1247
 
813
- xfree(slist);
1248
+ ALLOCV_END(holder);
814
1249
 
815
1250
  return m;
816
1251
  }
@@ -964,12 +1399,15 @@ rb_ca_mask_array (VALUE self)
964
1399
 
965
1400
  ca_update_mask(ca);
966
1401
  if ( ca->mask ) {
1402
+ /* The mask is reached from its array, not through a parent link, so
1403
+ ca_is_readonly on the mask never sees the array's read-only state.
1404
+ Pass it on here, where the mask is handed out. */
1405
+ if ( ca_is_readonly(ca) ) {
1406
+ ca_set_flag(ca->mask, CA_FLAG_READ_ONLY);
1407
+ }
967
1408
  obj = TypedData_Wrap_Struct(ca_mask_class[ca->obj_type],
968
- ca_mask_typeddata[ca->obj_type], ca->mask);
1409
+ ca_mask_typeddata[ca->obj_type], ca->mask);
969
1410
  rb_ivar_set(obj, rb_intern("masked_array"), self);
970
- if ( OBJ_FROZEN(self) ) {
971
- rb_ca_freeze(obj);
972
- }
973
1411
  return obj;
974
1412
  }
975
1413
  else {
@@ -977,6 +1415,72 @@ rb_ca_mask_array (VALUE self)
977
1415
  }
978
1416
  }
979
1417
 
1418
+ /* dup / clone of a mask. The mask of a view is a view over its parent's
1419
+ mask, and its dup, like the dup of any view, is another view onto the
1420
+ same parent mask: it follows later changes and a write to it lands in
1421
+ the parent's mask (copy makes a mask of its own). A mask over a cast, a
1422
+ byte swap, a Face or a lazy expression is shown as CArrayMask, whose
1423
+ allocator makes an entity, so Object#dup would copy its cells; it is
1424
+ rebuilt here from its own view instead. The mask of an entity is an
1425
+ entity and is copied. */
1426
+ static VALUE
1427
+ ca_mask_view_dup (VALUE self)
1428
+ {
1429
+ volatile VALUE obj;
1430
+ const rb_data_type_t *type;
1431
+ CArray *ca, *cp;
1432
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1433
+ cp = (CArray *) ca_func[ca->obj_type].clone(ca);
1434
+ ca_keep_chain_flags(cp, ca);
1435
+ type = ca_typeddata[ca->obj_type];
1436
+ if ( type->function.dfree != ca_free ) {
1437
+ type = &carray_data_type; /* the copy is this object's to free */
1438
+ }
1439
+ obj = TypedData_Wrap_Struct(rb_obj_class(self), type, cp);
1440
+ rb_ivar_set(obj, rb_intern("masked_array"),
1441
+ rb_ivar_get(self, rb_intern("masked_array")));
1442
+ return obj;
1443
+ }
1444
+
1445
+ static VALUE
1446
+ rb_ca_mask_dup (VALUE self)
1447
+ {
1448
+ CArray *ca;
1449
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1450
+ if ( ! ca_is_view(ca) ) {
1451
+ return rb_call_super(0, NULL);
1452
+ }
1453
+ return ca_mask_view_dup(self);
1454
+ }
1455
+
1456
+ static VALUE
1457
+ rb_ca_mask_clone (int argc, VALUE *argv, VALUE self)
1458
+ {
1459
+ VALUE opts = Qnil, vfreeze = Qnil;
1460
+ volatile VALUE obj;
1461
+ CArray *ca;
1462
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1463
+ if ( ! ca_is_view(ca) ) {
1464
+ return rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS);
1465
+ }
1466
+ rb_scan_args(argc, argv, "0:", &opts);
1467
+ if ( ! NIL_P(opts) ) {
1468
+ /* rb_get_kwargs refuses any key but freeze:, as Kernel#clone does. */
1469
+ ID kw[1];
1470
+ VALUE val[1];
1471
+ kw[0] = rb_intern("freeze");
1472
+ rb_get_kwargs(opts, kw, 0, 1, val);
1473
+ if ( val[0] != Qundef ) {
1474
+ vfreeze = val[0];
1475
+ }
1476
+ }
1477
+ obj = ca_mask_view_dup(self);
1478
+ if ( RTEST(vfreeze) || ( NIL_P(vfreeze) && OBJ_FROZEN(self) ) ) {
1479
+ rb_obj_freeze(obj);
1480
+ }
1481
+ return obj;
1482
+ }
1483
+
980
1484
  /* @overload mask= (new_mask)
981
1485
 
982
1486
  (Mask, Modification)
@@ -996,30 +1500,54 @@ rb_ca_set_mask (VALUE self, VALUE rval)
996
1500
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
997
1501
 
998
1502
  if ( ca_is_value_array(ca) ) {
999
- rb_raise(rb_eRuntimeError,
1503
+ rb_raise(rb_eTypeError,
1000
1504
  "can not create mask for the value array");
1001
1505
  }
1002
1506
 
1003
1507
  if ( ca_is_mask_array(ca) ) {
1004
- rb_raise(rb_eRuntimeError,
1508
+ rb_raise(rb_eTypeError,
1005
1509
  "can not create mask for the mask array");
1006
1510
  }
1007
1511
 
1008
- ca_update_mask(ca);
1009
- if ( ! ca->mask ) {
1010
- ca_create_mask(ca);
1512
+ if ( TYPE(rmask) == T_ARRAY ) {
1513
+ /* Flattened as a store into a boolean array of this shape flattens
1514
+ it, and then taken as a 1-D boolean array (UNDEF becomes a masked
1515
+ cell, as in any boolean array made from it). */
1516
+ CArray shape = *ca;
1517
+ shape.data_type = CA_BOOLEAN;
1518
+ shape.bytes = ca_sizeof[CA_BOOLEAN];
1519
+ rmask = rb_ary_flatten_for_elements(rmask, ca->elements, &shape);
1520
+ if ( NIL_P(rmask) ) {
1521
+ rb_raise(rb_eArgError, "failed to guess data size of given array");
1522
+ }
1523
+ rmask = rb_ca_wrap_readonly(rmask, INT2NUM(CA_BOOLEAN));
1011
1524
  }
1012
1525
 
1013
1526
  if ( rb_obj_is_carray(rmask) ) {
1527
+ /* The array owns the shape, as in a store: the new mask agrees with
1528
+ it, is 1-D or squeezes to it, or broadcasts along size-1 axes.
1529
+ Checked before the mask exists, so a refused one leaves none. */
1530
+ ca_broadcast_to_destination(self, &rmask);
1014
1531
  TypedData_Get_Struct(rmask, CArray, &carray_data_type, cv);
1532
+ /* A new mask taken from self's own root, or from self's mask, is
1533
+ read from a copy: making and writing self's mask would change it
1534
+ before it is read. */
1535
+ ca_update_mask(ca);
1536
+ if ( ca_shares_root(cv, ca) ||
1537
+ ( ca->mask && ca_shares_root(cv, ca->mask) ) ) {
1538
+ rmask = rb_ca_copy(rmask);
1539
+ TypedData_Get_Struct(rmask, CArray, &carray_data_type, cv);
1540
+ }
1015
1541
  if ( ! ca_is_boolean_type(cv) ) {
1016
- cv = ca_wrap_readonly(rval, CA_BOOLEAN);
1542
+ cv = ca_wrap_readonly(rmask, CA_BOOLEAN);
1017
1543
  }
1544
+ ca_create_mask(ca);
1018
1545
  ca_setup_mask(ca, cv);
1019
1546
  ca_copy_mask_overlay(ca, ca->elements, 1, cv);
1020
1547
  return rval;
1021
1548
  }
1022
1549
  else {
1550
+ ca_create_mask(ca);
1023
1551
  return rb_ca_store_all(rb_ca_mask_array(self), rmask);
1024
1552
  }
1025
1553
  }
@@ -1035,39 +1563,36 @@ with <code>self</code>. The returned array has 1 for the masked elements and
1035
1563
  VALUE
1036
1564
  rb_ca_is_masked (VALUE self)
1037
1565
  {
1038
- volatile VALUE mask;
1039
- CArray *ca, *cm, *co;
1566
+ volatile VALUE out;
1567
+ CArray *ca, *co;
1040
1568
  boolean8_t zero = 0;
1041
- boolean8_t *m, *p;
1569
+ boolean8_t *p;
1042
1570
  ca_size_t i;
1043
1571
 
1044
1572
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1045
1573
 
1046
1574
  if ( ca_is_scalar(ca) ) {
1047
- co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1575
+ co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1048
1576
  }
1049
1577
  else {
1050
- co = carray_new(CA_BOOLEAN, ca->ndim, ca->dim, ca->bytes, NULL);
1578
+ co = carray_new(CA_BOOLEAN, ca->ndim, ca->dim, ca->bytes, NULL);
1051
1579
  }
1580
+ out = ca_wrap_struct(co); /* owned by Ruby before anything can raise */
1052
1581
 
1053
1582
  ca_update_mask(ca);
1054
1583
  if ( ! ca->mask ) {
1055
1584
  ca_fill(co, &zero);
1056
1585
  }
1057
1586
  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;
1587
+ /* Copied straight into the result, then normalised in place. */
1588
+ ca_copy_data(ca->mask, co->ptr);
1062
1589
  p = (boolean8_t *) co->ptr;
1063
1590
  for (i=0; i<ca->elements; i++) {
1064
- *p = ( *m ) ? 1 : 0;
1065
- m++; p++;
1591
+ p[i] = ( p[i] ) ? 1 : 0;
1066
1592
  }
1067
- ca_detach(cm);
1068
1593
  }
1069
1594
 
1070
- return ca_wrap_struct(co);
1595
+ return out;
1071
1596
  }
1072
1597
 
1073
1598
  /* @overload is_not_masked
@@ -1081,39 +1606,52 @@ The returned array has 0 for the masked elements and
1081
1606
  VALUE
1082
1607
  rb_ca_is_not_masked (VALUE self)
1083
1608
  {
1084
- volatile VALUE mask;
1085
- CArray *ca, *cm, *co;
1609
+ volatile VALUE out;
1610
+ CArray *ca, *co;
1086
1611
  boolean8_t one = 1;
1087
- boolean8_t *m, *p;
1612
+ boolean8_t *p;
1088
1613
  ca_size_t i;
1089
1614
 
1090
1615
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1091
1616
 
1092
1617
  if ( ca_is_scalar(ca) ) {
1093
- co = (CArray *) cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1618
+ co = (CArray *)cscalar_new(CA_BOOLEAN, ca->bytes, NULL);
1094
1619
  }
1095
1620
  else {
1096
- co = carray_new(CA_BOOLEAN, ca->ndim, ca->dim, ca->bytes, NULL);
1621
+ co = carray_new(CA_BOOLEAN, ca->ndim, ca->dim, ca->bytes, NULL);
1097
1622
  }
1623
+ out = ca_wrap_struct(co); /* owned by Ruby before anything can raise */
1098
1624
 
1099
1625
  ca_update_mask(ca);
1100
1626
  if ( ! ca->mask ) {
1101
1627
  ca_fill(co, &one);
1102
1628
  }
1103
1629
  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;
1630
+ /* Copied straight into the result, then normalised in place. */
1631
+ ca_copy_data(ca->mask, co->ptr);
1108
1632
  p = (boolean8_t *) co->ptr;
1109
1633
  for (i=0; i<ca->elements; i++) {
1110
- *p = ( *m ) ? 0 : 1;
1111
- m++; p++;
1634
+ p[i] = ( p[i] ) ? 0 : 1;
1112
1635
  }
1113
- ca_detach(cm);
1114
1636
  }
1115
1637
 
1116
- return ca_wrap_struct(co);
1638
+ return out;
1639
+ }
1640
+
1641
+ VALUE
1642
+ rb_ca_scan_mask_count (VALUE self, VALUE axis)
1643
+ {
1644
+ VALUE present = rb_funcall(self, rb_intern("is_not_masked"), 0);
1645
+ VALUE count;
1646
+ if ( NIL_P(axis) ) {
1647
+ count = rb_funcall(present, rb_intern("cumsum"), 0);
1648
+ }
1649
+ else {
1650
+ VALUE kw = rb_hash_new();
1651
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis);
1652
+ count = rb_funcallv_kw(present, rb_intern("cumsum"), 1, &kw, RB_PASS_KEYWORDS);
1653
+ }
1654
+ return rb_funcall(count, rb_intern("to_type"), 1, INT2NUM(CA_INT64));
1117
1655
  }
1118
1656
 
1119
1657
  /* @overload unmask (fill_value = nil)
@@ -1139,19 +1677,20 @@ rb_ca_unmask_method (int argc, VALUE *argv, VALUE self)
1139
1677
  rfval = argv[0];
1140
1678
  }
1141
1679
 
1680
+ /* Filling with UNDEF leaves the cells undefined, as storing UNDEF does. */
1681
+ if ( rfval == CA_UNDEF ) {
1682
+ return self;
1683
+ }
1684
+
1142
1685
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1143
1686
 
1144
1687
  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);
1688
+ /* The fill is a value of the array (a struct, an instant for a Face):
1689
+ rb_ca_obj2ptr writes it as one cell of the storage. */
1690
+ rcs = rb_cscalar_new(ca_strip_face(ca)->data_type, ca->bytes, NULL);
1153
1691
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
1154
1692
  fval = cv->ptr;
1693
+ rb_ca_obj2ptr(self, rfval, fval);
1155
1694
  }
1156
1695
 
1157
1696
  ca_unmask(ca, fval);
@@ -1175,14 +1714,9 @@ rb_ca_mask_fill (VALUE self, VALUE fval)
1175
1714
  return rb_ca_unmask_method(1, &fval, self);
1176
1715
  }
1177
1716
 
1178
- /* @overload unmask_copy (fill_value = nil)
1179
-
1180
- (Masking, Conversion)
1181
- Returns new unmasked array.
1182
- If the optional argument <code>fill_value</code> is given,
1183
- the masked elements are filled by <code>fill_value</code>.
1184
- The returned array doesn't have the mask array.
1185
- */
1717
+ /* A copy without the mask: with a fill value, the masked cells hold it.
1718
+ Behind rb_ca_unmask_copy / rb_ca_mask_fill_copy (C only; the Ruby
1719
+ surface is strip_mask). */
1186
1720
 
1187
1721
  static VALUE
1188
1722
  rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
@@ -1196,19 +1730,21 @@ rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
1196
1730
  rfval = argv[0];
1197
1731
  }
1198
1732
 
1733
+ /* Filling with UNDEF leaves the cells undefined, as storing UNDEF does:
1734
+ the copy keeps the mask. */
1735
+ if ( rfval == CA_UNDEF ) {
1736
+ return rb_ca_copy(self);
1737
+ }
1738
+
1199
1739
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1200
1740
 
1201
1741
  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);
1742
+ /* The fill is a value of the array (a struct, an instant for a Face):
1743
+ rb_ca_obj2ptr writes it as one cell of the storage. */
1744
+ rcs = rb_cscalar_new(ca_strip_face(ca)->data_type, ca->bytes, NULL);
1210
1745
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
1211
1746
  fval = cv->ptr;
1747
+ rb_ca_obj2ptr(self, rfval, fval);
1212
1748
  }
1213
1749
 
1214
1750
  co = ca_unmask_copy(ca, fval);
@@ -1243,6 +1779,7 @@ VALUE
1243
1779
  rb_ca_invert_mask (VALUE self)
1244
1780
  {
1245
1781
  CArray *ca;
1782
+ rb_ca_modify(self);
1246
1783
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1247
1784
  ca_invert_mask(ca);
1248
1785
  return self;
@@ -1274,24 +1811,53 @@ rb_ca_s_guard_undef(int argc, VALUE *argv, VALUE klass)
1274
1811
 
1275
1812
  /* mask_eq(v) — return form of "mask cells equal to v".
1276
1813
  Equivalent to: obj = copy; obj[:eq, v] = UNDEF; obj */
1814
+ /* Masks a copy of self at the cells the store key `argv` selects. A
1815
+ read-only Face (CAConstString, CACategorical) copies read-only, so the
1816
+ mask goes on a copy of its storage and the Face is put back; a predicate
1817
+ key (`:eq, v`) is evaluated on the Face, where it compares values rather
1818
+ than storage. */
1277
1819
  static VALUE
1278
- rb_ca_mask_eq (VALUE self, VALUE v)
1820
+ ca_mask_copy_where (VALUE self, int argc, VALUE *argv)
1279
1821
  {
1280
- volatile VALUE obj = rb_ca_copy(self);
1281
- rb_funcall(obj, rb_intern("[]="), 3,
1282
- ID2SYM(rb_intern("eq")), v, CA_UNDEF);
1822
+ CArray *ca;
1823
+ volatile VALUE obj;
1824
+ VALUE *all_args = ALLOCA_N(VALUE, argc + 1);
1825
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1826
+ if ( ca_is_face(ca) && ca_is_readonly(ca) ) {
1827
+ obj = rb_ca_copy(rb_ca_strip_face_value(self));
1828
+ if ( argc >= 1 && SYMBOL_P(argv[0]) ) {
1829
+ all_args[0] = rb_funcallv(self, SYM2ID(argv[0]), argc - 1, argv + 1);
1830
+ all_args[1] = CA_UNDEF;
1831
+ rb_funcallv(obj, rb_intern("[]="), 2, all_args);
1832
+ }
1833
+ else {
1834
+ memcpy(all_args, argv, argc * sizeof(VALUE));
1835
+ all_args[argc] = CA_UNDEF;
1836
+ rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
1837
+ }
1838
+ return rb_funcall(obj, rb_intern("face_lift"), 1, self);
1839
+ }
1840
+ obj = rb_ca_copy(self);
1841
+ memcpy(all_args, argv, argc * sizeof(VALUE));
1842
+ all_args[argc] = CA_UNDEF;
1843
+ rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
1283
1844
  return obj;
1284
1845
  }
1285
1846
 
1847
+ static VALUE
1848
+ rb_ca_mask_eq (VALUE self, VALUE v)
1849
+ {
1850
+ VALUE args[2] = { ID2SYM(rb_intern("eq")), v };
1851
+ return ca_mask_copy_where(self, 2, args);
1852
+ }
1853
+
1286
1854
  /* mask_invalid — return form of "mask NaN/Inf cells".
1287
1855
  Equivalent to: obj = copy; obj[:is_invalid] = UNDEF; obj */
1288
1856
  static VALUE
1289
1857
  rb_ca_mask_invalid (VALUE self)
1290
1858
  {
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;
1859
+ VALUE args[1] = { ID2SYM(rb_intern("is_invalid")) };
1860
+ return ca_mask_copy_where(self, 1, args);
1295
1861
  }
1296
1862
 
1297
1863
  /* mask_where(*args) — generic return form predicate masking.
@@ -1300,17 +1866,10 @@ rb_ca_mask_invalid (VALUE self)
1300
1866
  static VALUE
1301
1867
  rb_ca_mask_where (int argc, VALUE *argv, VALUE self)
1302
1868
  {
1303
- volatile VALUE obj;
1304
- VALUE *all_args;
1305
1869
  if (argc == 0) {
1306
1870
  rb_raise(rb_eArgError, "mask_where requires at least 1 argument");
1307
1871
  }
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;
1872
+ return ca_mask_copy_where(self, argc, argv);
1314
1873
  }
1315
1874
 
1316
1875
  /* @overload inherit_mask (*others):
@@ -1324,6 +1883,7 @@ static VALUE
1324
1883
  rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1325
1884
  {
1326
1885
  CArray **slist;
1886
+ volatile VALUE holder;
1327
1887
  CArray *ca, *cs;
1328
1888
  int i;
1329
1889
 
@@ -1331,7 +1891,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1331
1891
 
1332
1892
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1333
1893
 
1334
- slist = xmalloc(sizeof(CArray *)*argc);
1894
+ slist = ALLOCV_N(CArray *, holder, argc);
1335
1895
  for (i=0; i<argc; i++) {
1336
1896
  if ( rb_obj_is_carray(argv[i]) ) {
1337
1897
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
@@ -1343,7 +1903,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1343
1903
  }
1344
1904
  ca_copy_mask_overlay_n(ca, ca->elements, argc, slist);
1345
1905
 
1346
- xfree(slist);
1906
+ ALLOCV_END(holder);
1347
1907
 
1348
1908
  return self;
1349
1909
  }
@@ -1363,6 +1923,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1363
1923
  {
1364
1924
  VALUE other;
1365
1925
  CArray **slist;
1926
+ volatile VALUE holder;
1366
1927
  CArray *ca, *cs;
1367
1928
  int i;
1368
1929
  va_list rothers;
@@ -1372,7 +1933,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1372
1933
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1373
1934
 
1374
1935
  va_start(rothers, n);
1375
- slist = xmalloc(sizeof(CArray *)*n);
1936
+ slist = ALLOCV_N(CArray *, holder, n);
1376
1937
  for (i=0; i<n; i++) {
1377
1938
  other = va_arg(rothers, VALUE);
1378
1939
  if ( rb_obj_is_carray(other) ) {
@@ -1387,7 +1948,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1387
1948
 
1388
1949
  ca_copy_mask_overlay_n(ca, ca->elements, n, slist);
1389
1950
 
1390
- xfree(slist);
1951
+ ALLOCV_END(holder);
1391
1952
 
1392
1953
  return self;
1393
1954
  }
@@ -1403,6 +1964,7 @@ static VALUE
1403
1964
  rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1404
1965
  {
1405
1966
  CArray **slist;
1967
+ volatile VALUE holder;
1406
1968
  CArray *ca, *cs;
1407
1969
  int i;
1408
1970
 
@@ -1410,7 +1972,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1410
1972
 
1411
1973
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1412
1974
 
1413
- slist = xmalloc(sizeof(CArray *)*argc);
1975
+ slist = ALLOCV_N(CArray *, holder, argc);
1414
1976
  for (i=0; i<argc; i++) {
1415
1977
  if ( rb_obj_is_carray(argv[i]) ) {
1416
1978
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
@@ -1422,7 +1984,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1422
1984
  }
1423
1985
  ca_copy_mask_overwrite_n(ca, ca->elements, argc, slist);
1424
1986
 
1425
- xfree(slist);
1987
+ ALLOCV_END(holder);
1426
1988
 
1427
1989
  return self;
1428
1990
  }
@@ -1442,6 +2004,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1442
2004
  {
1443
2005
  VALUE other;
1444
2006
  CArray **slist;
2007
+ volatile VALUE holder;
1445
2008
  CArray *ca, *cs;
1446
2009
  int i;
1447
2010
  va_list rothers;
@@ -1451,7 +2014,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1451
2014
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1452
2015
 
1453
2016
  va_start(rothers, n);
1454
- slist = xmalloc(sizeof(CArray *)*n);
2017
+ slist = ALLOCV_N(CArray *, holder, n);
1455
2018
  for (i=0; i<n; i++) {
1456
2019
  other = va_arg(rothers, VALUE);
1457
2020
  if ( rb_obj_is_carray(other) ) {
@@ -1466,34 +2029,34 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1466
2029
 
1467
2030
  ca_copy_mask_overwrite_n(ca, ca->elements, n, slist);
1468
2031
 
1469
- xfree(slist);
2032
+ ALLOCV_END(holder);
1470
2033
 
1471
2034
  return self;
1472
2035
  }
1473
2036
 
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. */
2037
+ /* The no-mask answer of count_masked / count_not_masked along `axes`:
2038
+ every reduced cell holds `fill`. A reduction over every axis is the
2039
+ number itself. */
1477
2040
  static VALUE
1478
- ca_make_reduced_int64 (CArray *ca, int argc, VALUE *argv, ca_size_t fill_val)
2041
+ ca_count_uniform (CArray *ca, const int8_t *axes, int8_t naxes, ca_size_t fill)
1479
2042
  {
1480
2043
  ca_size_t dim[CA_RANK_MAX];
1481
2044
  int8_t ndim = 0;
1482
2045
  int i, j;
1483
- int64_t v = (int64_t) fill_val;
2046
+ int64_t v = (int64_t) fill;
1484
2047
  VALUE result;
1485
2048
  CArray *co;
1486
2049
 
2050
+ if ( naxes == ca->ndim ) {
2051
+ return SIZE2NUM(fill);
2052
+ }
1487
2053
  for (i = 0; i < ca->ndim; i++) {
1488
2054
  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; }
2055
+ for (j = 0; j < naxes; j++) {
2056
+ if ( axes[j] == i ) { is_axis = 1; break; }
1493
2057
  }
1494
2058
  if (!is_axis) dim[ndim++] = ca->dim[i];
1495
2059
  }
1496
-
1497
2060
  result = rb_carray_new(CA_INT64, ndim, dim, 0, NULL);
1498
2061
  TypedData_Get_Struct(result, CArray, &carray_data_type, co);
1499
2062
  ca_fill(co, &v);
@@ -1507,111 +2070,95 @@ ca_make_reduced_int64 (CArray *ca, int argc, VALUE *argv, ca_size_t fill_val)
1507
2070
  extern VALUE rb_ca_count_true_ki (int argc, VALUE *argv, VALUE self);
1508
2071
  extern VALUE rb_ca_count_false_ki (int argc, VALUE *argv, VALUE self);
1509
2072
 
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).
2073
+ /* count_masked / count_not_masked -- the number of masked (present)
2074
+ cells, over every axis (an Integer) or along `axis:` (an int64 CArray).
2075
+ The keywords are those of the count kernels (axis:, keep_axis:,
2076
+ min_count:, fill_value:), checked by them: the mask is counted with
2077
+ count_true_ki / count_false_ki. Without a mask the answer is known
2078
+ from the shape; an axis: alone takes that short cut after the same
2079
+ axis check the kernels make, and any other keyword runs the kernel on
2080
+ an all-false view that occupies one cell.
1513
2081
 
1514
2082
  Public (non-static) linkage so rb_ca_count in carray_count.c can forward
1515
2083
  count(UNDEF) here directly without going through Ruby method dispatch. */
1516
- VALUE
1517
- rb_ca_count_masked (int argc, VALUE *argv, VALUE self)
2084
+ static VALUE
2085
+ ca_count_by_mask (int argc, VALUE *argv, VALUE self, int masked)
1518
2086
  {
1519
2087
  CArray *ca;
2088
+ VALUE kw_hash = Qnil, mask_obj;
1520
2089
  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
2090
  rb_scan_args(argc, argv, "0:", &kw_hash);
1526
- rb_scan_options(kw_hash, "axis", &axis_val);
1527
2091
 
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);
2092
+ if ( ! ca_has_mask(ca) ) {
2093
+ VALUE axis_val = Qnil;
2094
+ int only_axis = 1;
2095
+ if ( ! NIL_P(kw_hash) ) {
2096
+ VALUE given = rb_hash_lookup2(kw_hash, ID2SYM(rb_intern("axis")), Qundef);
2097
+ axis_val = ( given == Qundef ) ? Qnil : given;
2098
+ only_axis = ( RHASH_SIZE(kw_hash) == ( given == Qundef ? 0u : 1u ) );
2099
+ }
2100
+ if ( only_axis ) {
2101
+ int8_t axes[CA_RANK_MAX];
2102
+ int8_t naxes = rb_ca_parse_reduce_axes_kw(axis_val, ca, axes);
2103
+ ca_size_t vol = 1;
2104
+ int i;
2105
+ for ( i = 0; i < naxes; i++ ) vol *= ca->dim[axes[i]];
2106
+ return ca_count_uniform(ca, axes, naxes, masked ? 0 : vol);
2107
+ }
2108
+ {
2109
+ ca_size_t one[CA_RANK_MAX];
2110
+ VALUE shape = rb_ary_new_capa(ca->ndim), cell;
2111
+ boolean8_t zero = 0;
2112
+ CArray *cc;
2113
+ int i;
2114
+ for ( i = 0; i < ca->ndim; i++ ) {
2115
+ one[i] = 1;
2116
+ rb_ary_push(shape, SIZE2NUM(ca->dim[i]));
2117
+ }
2118
+ cell = rb_carray_new(CA_BOOLEAN, ca->ndim, one, 0, NULL);
2119
+ TypedData_Get_Struct(cell, CArray, &carray_data_type, cc);
2120
+ ca_fill(cc, &zero);
2121
+ mask_obj = rb_funcallv(cell, rb_intern("broadcast_to"),
2122
+ (int) RARRAY_LEN(shape), RARRAY_CONST_PTR(shape));
1533
2123
  }
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
2124
  }
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);
2125
+ else {
2126
+ mask_obj = rb_ca_mask_array(self);
2127
+ }
2128
+ if ( NIL_P(kw_hash) ) {
2129
+ return masked ? rb_ca_count_true_ki(0, NULL, mask_obj)
2130
+ : rb_ca_count_false_ki(0, NULL, mask_obj);
2131
+ }
2132
+ {
2133
+ VALUE kargv[1] = { kw_hash };
2134
+ return masked ? rb_ca_count_true_ki(1, kargv, mask_obj)
2135
+ : rb_ca_count_false_ki(1, kargv, mask_obj);
1547
2136
  }
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
2137
  }
1553
2138
 
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
2139
  VALUE
1566
- rb_ca_count_not_masked_c (VALUE self, VALUE axis_val)
2140
+ rb_ca_count_masked (int argc, VALUE *argv, VALUE self)
1567
2141
  {
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);
2142
+ return ca_count_by_mask(argc, argv, self, 1);
1604
2143
  }
1605
2144
 
1606
- /* Ruby binding entry: parses (axis: kwarg) -> forwards to _c. */
1607
2145
  VALUE
1608
2146
  rb_ca_count_not_masked (int argc, VALUE *argv, VALUE self)
1609
2147
  {
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);
2148
+ return ca_count_by_mask(argc, argv, self, 0);
2149
+ }
2150
+
2151
+ /* count_not_masked(axis: axis_val) for C callers. */
2152
+ VALUE
2153
+ rb_ca_count_not_masked_c (VALUE self, VALUE axis_val)
2154
+ {
2155
+ VALUE kw;
2156
+ if ( NIL_P(axis_val) ) {
2157
+ return ca_count_by_mask(0, NULL, self, 0);
2158
+ }
2159
+ kw = rb_hash_new();
2160
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
2161
+ return rb_funcall_with_block_kw(self, rb_intern("count_not_masked"), 1, &kw, Qnil, RB_PASS_KEYWORDS);
1615
2162
  }
1616
2163
 
1617
2164
  /* ============================================================================
@@ -1833,10 +2380,47 @@ Init_carray_mask_fast_paths (void)
1833
2380
  }
1834
2381
 
1835
2382
 
2383
+ /* The Ruby forms of inherit_mask take CArray arguments only: refuse a
2384
+ keyword and any argument that is not a CArray, which the C API would
2385
+ pass over as maskless. Returns the arguments without the keyword Hash. */
2386
+ static VALUE
2387
+ ca_inherit_mask_check_args (int argc, VALUE *argv, const char *name)
2388
+ {
2389
+ VALUE rest, ropt;
2390
+ long i;
2391
+ rb_scan_args(argc, argv, "*:", &rest, &ropt);
2392
+ rb_reject_options(ropt);
2393
+ for (i=0; i<RARRAY_LEN(rest); i++) {
2394
+ VALUE v = RARRAY_AREF(rest, i);
2395
+ if ( ! rb_obj_is_carray(v) ) {
2396
+ rb_raise(rb_eTypeError, "%s: argument %ld must be a CArray (got %s)",
2397
+ name, i, rb_obj_classname(v));
2398
+ }
2399
+ }
2400
+ return rest;
2401
+ }
2402
+
2403
+ static VALUE
2404
+ rb_ca_inherit_mask_ruby (int argc, VALUE *argv, VALUE self)
2405
+ {
2406
+ volatile VALUE rest = ca_inherit_mask_check_args(argc, argv, "inherit_mask");
2407
+ return rb_ca_inherit_mask_method((int) RARRAY_LEN(rest),
2408
+ (VALUE *) RARRAY_CONST_PTR(rest), self);
2409
+ }
2410
+
2411
+ static VALUE
2412
+ rb_ca_inherit_mask_replace_ruby (int argc, VALUE *argv, VALUE self)
2413
+ {
2414
+ volatile VALUE rest = ca_inherit_mask_check_args(argc, argv,
2415
+ "inherit_mask_replace");
2416
+ return rb_ca_inherit_mask_replace_method((int) RARRAY_LEN(rest),
2417
+ (VALUE *) RARRAY_CONST_PTR(rest), self);
2418
+ }
2419
+
1836
2420
  void
1837
2421
  Init_carray_mask (void)
1838
2422
  {
1839
- rb_define_private_method(rb_cCArray, "__create_mask__", rb_ca_create_mask, 0);
2423
+ rb_define_private_method(rb_cCArray, "add_mask", rb_ca_create_mask, 0);
1840
2424
  /*
1841
2425
  rb_define_private_method(rb_cCArray, "__update_mask__", rb_ca_update_mask, 0);
1842
2426
  */
@@ -1847,6 +2431,8 @@ Init_carray_mask (void)
1847
2431
 
1848
2432
  rb_define_method(rb_cCArray, "value", rb_ca_value_array, 0);
1849
2433
  rb_define_method(rb_cCArray, "mask", rb_ca_mask_array, 0);
2434
+ rb_define_method(rb_cCArrayMask, "dup", rb_ca_mask_dup, 0);
2435
+ rb_define_method(rb_cCArrayMask, "clone", rb_ca_mask_clone, -1);
1850
2436
  rb_define_method(rb_cCArray, "mask=", rb_ca_set_mask, 1);
1851
2437
  rb_define_method(rb_cCArray, "is_masked", rb_ca_is_masked, 0);
1852
2438
  rb_define_method(rb_cCArray, "is_not_masked", rb_ca_is_not_masked, 0);
@@ -1869,9 +2455,9 @@ Init_carray_mask (void)
1869
2455
  rb_define_method(rb_cCArray, "mask_invalid", rb_ca_mask_invalid, 0);
1870
2456
  rb_define_method(rb_cCArray, "mask_where", rb_ca_mask_where, -1);
1871
2457
 
1872
- rb_define_method(rb_cCArray, "inherit_mask", rb_ca_inherit_mask_method, -1);
2458
+ rb_define_method(rb_cCArray, "inherit_mask", rb_ca_inherit_mask_ruby, -1);
1873
2459
  rb_define_method(rb_cCArray, "inherit_mask_replace",
1874
- rb_ca_inherit_mask_replace_method, -1);
2460
+ rb_ca_inherit_mask_replace_ruby, -1);
1875
2461
 
1876
2462
  rb_define_method(rb_cCArray, "count_masked", rb_ca_count_masked, -1);
1877
2463
  rb_define_method(rb_cCArray, "count_not_masked", rb_ca_count_not_masked, -1);