carray 3.0.1 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/ca_obj_binop.c CHANGED
@@ -34,6 +34,7 @@
34
34
  #include "carray_internal.h" /* ca_lazy_arena_* */
35
35
  #include "ca_binop_dispatch.h"
36
36
  #include "ca_monop_dispatch.h" /* CA_MONOP_CAST_BASE */
37
+ #include "carray_math_kernel.h" /* ca_binop_not_implement */
37
38
 
38
39
  /* carray_broadcast.c */
39
40
  extern VALUE ca_broadcast_view (VALUE src, int8_t ndim,
@@ -48,7 +49,6 @@ extern int8_t CA_OBJ_LAZY_MARKER;
48
49
 
49
50
  /* Defined in carray_lazy.c: wrap a non-CArray Ruby value as a
50
51
  CScalar carrying the other operand's natural data_type. */
51
- extern VALUE ca_lazy_wrap_scalar (VALUE other, CArray *self_ca);
52
52
 
53
53
  /* ------------------------------------------------------------------- */
54
54
  /* CABinOp struct */
@@ -239,58 +239,30 @@ ca_binop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
239
239
  ca_binop_func_xfer_stride(ca, starts, counts, strides, data, CA_XFER_GET);
240
240
  }
241
241
 
242
- static void
243
- ca_binop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
244
- void *data, int dir)
245
- {
246
- CABinOp *ca = (CABinOp *) ap;
247
- ca_size_t i;
248
- char *out = (char *) data;
249
-
250
- if ( dir != CA_XFER_GET ) {
251
- rb_raise(rb_eRuntimeError, "CABinOp is read-only (xfer_addrs PUT)");
252
- }
253
-
254
- for ( i = 0; i < n; i++ ) {
255
- ca_size_t idx[CA_RANK_MAX];
256
- ca_addr2index((CArray *)ca, addrs[i], idx);
257
- ca_binop_func_xfer_index(ca, idx, out + i * ca->bytes, CA_XFER_GET);
258
- }
259
- }
260
-
261
- /* Test / observability counters bumped from the xfer_stride hot
262
- path so specs can assert scratch acquisition (one per node,
263
- independent of left-chain depth) and materialise vs
264
- non-materialise behaviour without attaching the view. */
242
+ /* Test / observability counters bumped from the evaluation hot path
243
+ so specs can assert scratch acquisition (one per node, independent
244
+ of left-chain depth) and materialise vs non-materialise behaviour
245
+ without attaching the view. */
265
246
  ca_size_t ca_binop_scratch_acquire_count = 0;
266
247
  ca_size_t ca_binop_materialise_call_count = 0;
267
248
 
249
+ /* Evaluate the cells a request names into `data` (packed, n cells). */
268
250
  static void
269
- ca_binop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
270
- ca_size_t *strides, void *data, int dir)
251
+ ca_binop_eval (CABinOp *bo, const ca_lazy_req_t *req, void *data)
271
252
  {
272
- CABinOp *bo = (CABinOp *) ap;
273
- ca_size_t slab_n;
253
+ ca_size_t slab_n = req->n;
274
254
  int8_t k;
275
255
  void *scratch;
276
256
  volatile VALUE holder;
277
257
  ca_size_t right_bytes;
278
258
  ca_size_t right_step; /* element-stride for the kernel walk */
279
259
 
280
- if ( dir != CA_XFER_GET ) {
281
- rb_raise(rb_eRuntimeError, "CABinOp is read-only (xfer_stride PUT)");
282
- }
283
260
  ca_binop_materialise_call_count++;
284
261
 
285
- slab_n = 1;
286
- for ( k = 0; k < bo->ndim; k++ ) slab_n *= counts[k];
287
-
288
262
  /* === 1. Pull left into the output buffer ===
289
- CAREFUL: left.data_type == output.data_type by the cast-before
290
- invariant, so the caller's `strides` (output byte layout) is
291
- also valid for left. A mismatch would silently write past the
292
- output row. */
293
- ca_xfer_stride(bo->parent, starts, counts, strides, data, CA_XFER_GET);
263
+ left.data_type == output.data_type by the cast-before invariant,
264
+ so the output buffer holds left's cells as they are. */
265
+ ca_lazy_req_pull(bo->parent, req, data, CA_XFER_GET);
294
266
 
295
267
  /* === 2. Pull right into an arena scratch === */
296
268
  right_bytes = bo->right->bytes;
@@ -315,17 +287,12 @@ ca_binop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
315
287
  right_step = 0;
316
288
  }
317
289
  else {
318
- /* Same-shape right. `strides` is the step through the operand, the
319
- same one the left was pulled with -- recomputing a packed one here
320
- reads the right operand's cells 0, 1, 2 against the left's 0, 2, 4.
321
- right.bytes matches output.bytes because right.data_type ==
322
- out.data_type by cast-before, so the same steps serve both. */
290
+ /* Same-shape right, pulled over the same cells as the left. */
323
291
  scratch = ( bo->data_type == CA_OBJECT )
324
292
  ? ca_lazy_arena_acquire_object(slab_n)
325
293
  : ca_lazy_arena_acquire(slab_n * right_bytes);
326
294
  ca_binop_scratch_acquire_count++;
327
- ca_xfer_stride(bo->right, starts, counts, strides, scratch,
328
- CA_XFER_GET);
295
+ ca_lazy_req_pull(bo->right, req, scratch, CA_XFER_GET);
329
296
  right_step = 1;
330
297
  }
331
298
 
@@ -337,8 +304,9 @@ ca_binop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
337
304
  * Mask handling:
338
305
  * - non-trapping op → m = NULL (SIMD fast path; garbage compute
339
306
  * on masked cells is allowed).
340
- * - trapping op with any operand mask → build a per-slab OR
341
- * mask from the operand-side masks.
307
+ * - trapping op (one that can raise on a cell, or an object cell)
308
+ * with any operand mask → the cells either operand masks
309
+ * (ca_lazy_req_mask), which the kernel skips.
342
310
  *
343
311
  * CAREFUL: the trapping-op mask is built from left.mask and
344
312
  * right.mask directly, NOT from out->mask. The output's mask
@@ -353,67 +321,13 @@ ca_binop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
353
321
  {
354
322
  boolean8_t *slab_mask = NULL;
355
323
  void *mscratch = NULL;
356
- volatile VALUE mholder = Qnil;
357
- int is_trapping = ca_binop_is_trapping(bo->op_id, bo->data_type);
358
-
359
- if ( is_trapping && ( ca_has_mask(bo->parent) ||
360
- ca_has_mask(bo->right) ) ) {
361
- /* Build a slab-shaped mask = left.slab_mask | right.slab_mask.
362
- Pull each operand's mask (or fill with 0 if absent) into the
363
- output-shaped layout, then OR them in place. */
364
- ca_size_t k_;
365
- ca_size_t right_mask_strides[CA_RANK_MAX];
366
- (void) mholder; /* Kept for stack-shape compatibility; arena
367
- replaces the ALLOCV path here. */
368
-
369
- mscratch = ca_lazy_arena_acquire(slab_n);
370
- slab_mask = (boolean8_t *) mscratch;
371
-
372
- if ( ca_has_mask(bo->parent) ) {
373
- ca_size_t mstrides[CA_RANK_MAX];
374
- ca_size_t s = 1;
375
- for ( k_ = bo->ndim - 1; k_ >= 0; k_-- ) {
376
- mstrides[k_] = s; s *= counts[k_];
377
- }
378
- ca_xfer_stride(bo->parent->mask, starts, counts, mstrides,
379
- slab_mask, CA_XFER_GET);
380
- }
381
- else {
382
- memset(slab_mask, 0, slab_n);
383
- }
384
324
 
385
- if ( ca_has_mask(bo->right) ) {
386
- boolean8_t *rm = NULL;
387
- ca_size_t i_;
388
- if ( bo->right_is_scalar ) {
389
- /* CScalar right: pull the single mask bit and broadcast OR. */
390
- ca_size_t one_starts[CA_RANK_MAX] = {0};
391
- ca_size_t one_counts[CA_RANK_MAX];
392
- ca_size_t one_strides[CA_RANK_MAX];
393
- boolean8_t one_bit = 0;
394
- for ( k_ = 0; k_ < bo->right->ndim; k_++ ) {
395
- one_counts[k_] = 1;
396
- one_strides[k_] = 1;
397
- }
398
- ca_xfer_stride(bo->right->mask, one_starts, one_counts,
399
- one_strides, &one_bit, CA_XFER_GET);
400
- if ( one_bit ) {
401
- for ( i_ = 0; i_ < slab_n; i_++ ) slab_mask[i_] = 1;
402
- }
403
- }
404
- else {
405
- ca_size_t s = 1;
406
- for ( k_ = bo->ndim - 1; k_ >= 0; k_-- ) {
407
- right_mask_strides[k_] = s; s *= counts[k_];
408
- }
409
- /* Small mask-scratch for right; OR into slab_mask in
410
- place to avoid a persistent second buffer. */
411
- rm = (boolean8_t *) ALLOCA_N(boolean8_t, slab_n);
412
- ca_xfer_stride(bo->right->mask, starts, counts,
413
- right_mask_strides, rm, CA_XFER_GET);
414
- for ( i_ = 0; i_ < slab_n; i_++ ) slab_mask[i_] |= rm[i_];
415
- }
416
- }
325
+ if ( ca_binop_is_trapping(bo->op_id, bo->data_type) ) {
326
+ CArray *ops[2];
327
+ ops[0] = bo->parent;
328
+ ops[1] = bo->right;
329
+ slab_mask = ca_lazy_req_mask(req, 2, ops);
330
+ mscratch = slab_mask;
417
331
  }
418
332
 
419
333
  ca_binop_func_t fn = ca_binop_kernel_lookup_vv(bo->op_id, bo->data_type);
@@ -435,6 +349,33 @@ ca_binop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
435
349
  ca_lazy_arena_release(scratch);
436
350
  }
437
351
 
352
+ static void
353
+ ca_binop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
354
+ ca_size_t *strides, void *data, int dir)
355
+ {
356
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
357
+ ca_lazy_req_t req;
358
+ if ( dir != CA_XFER_GET ) {
359
+ rb_raise(rb_eRuntimeError, "CABinOp is read-only (xfer_stride PUT)");
360
+ }
361
+ ca_lazy_req_region(&req, ap, starts, counts, strides);
362
+ ca_binop_eval((CABinOp *) ap, &req, data);
363
+ }
364
+
365
+ /* An address list is evaluated in one pass: each operand is gathered at
366
+ the same addresses and the kernel runs once over all of them. */
367
+ static void
368
+ ca_binop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
369
+ void *data, int dir)
370
+ {
371
+ ca_lazy_req_t req;
372
+ if ( dir != CA_XFER_GET ) {
373
+ rb_raise(rb_eRuntimeError, "CABinOp is read-only (xfer_addrs PUT)");
374
+ }
375
+ ca_lazy_req_addrs(&req, n, addrs);
376
+ ca_binop_eval((CABinOp *) ap, &req, data);
377
+ }
378
+
438
379
  static void
439
380
  ca_binop_func_xfer_all (void *ap, void *data, int dir)
440
381
  {
@@ -514,104 +455,25 @@ ca_binop_func_fill_data (void *ap, void *ptr)
514
455
  rb_raise(rb_eRuntimeError, "CABinOp is read-only (fill_data)");
515
456
  }
516
457
 
517
- /* Build out.mask. When either parent has a mask, materialise a
518
- freshly-allocated boolean8_t buffer. For most ops this is the blind
519
- OR `left.mask | right.mask`.
458
+ /* Build out.mask: the operands' masks ORed on each read (CAMaskOfOperands), so
459
+ that it follows them as the values do.
520
460
 
521
461
  Boolean AND / OR are three-valued (Kleene): a masked cell is resolved by
522
462
  the *known* side (`unknown | true = true`, `unknown & false = false`), so
523
- it is NOT masked in the result. This needs operand VALUES, not just
524
- masks, so both operands are attached; the blind value the value kernel
525
- later writes is already correct on a resolved cell because the absorbing
526
- element (true for OR, false for AND) dominates whatever garbage sits on
527
- the masked side. Nested trees compose: an inner boolean `&`/`|` node's
528
- mask is already Kleene-resolved by the time an outer node reads it.
529
-
530
- Not memory-optimal (a CARepeat-like view would share pages), but correct
531
- and decoupled from xfer_stride ordering. */
463
+ it is NOT masked in the result. The blind value the value kernel writes
464
+ is already correct on a resolved cell because the absorbing element (true
465
+ for OR, false for AND) dominates whatever sits on the masked side. */
532
466
  static void
533
467
  ca_binop_func_create_mask (void *ap)
534
468
  {
535
469
  CABinOp *bo = (CABinOp *) ap;
536
- CArray *l = bo->parent;
537
- CArray *r = bo->right;
538
- boolean8_t *dst;
539
- ca_size_t i, n;
540
- int has_l, has_r, kleene, need_l, need_r, is_or;
541
- boolean8_t *lm, *rm, *lv, *rv;
542
-
543
- has_l = ca_has_mask(l);
544
- has_r = ca_has_mask(r);
545
- if ( ! has_l && ! has_r ) return;
546
-
547
- kleene = ( bo->data_type == CA_BOOLEAN &&
548
- ( bo->op_id == CA_BINOP_BIT_AND || bo->op_id == CA_BINOP_BIT_OR ) );
549
- is_or = ( bo->op_id == CA_BINOP_BIT_OR );
550
-
551
- /* One masked operand and no Kleene resolution: the answer is that
552
- operand's mask, cell for cell. Share it the way CAMonOp shares its
553
- parent's, rather than allocating a copy per node -- a chain of k
554
- binops over one masked array then holds one boolean array, not k. */
555
- if ( ! kleene && ( has_l != has_r ) ) {
556
- CArray *src = has_l ? l : r;
557
- if ( src->elements == bo->elements ) {
558
- bo->mask = (CArray *) ca_refer_new(src->mask, CA_BOOLEAN,
559
- bo->ndim, bo->dim, 0, 0);
560
- return;
561
- }
562
- }
470
+ int mode = CA_LAZY_MASK_OR;
563
471
 
564
- bo->mask = (CArray *) carray_new(CA_BOOLEAN, bo->ndim, bo->dim, 0, NULL);
565
- dst = (boolean8_t *) bo->mask->ptr;
566
- n = bo->elements;
567
-
568
- /* Kleene needs operand values even where an operand is unmasked. */
569
- need_l = has_l || kleene;
570
- need_r = has_r || kleene;
571
- /* Only Kleene reads operand values. Every other op reads the operand
572
- masks alone, and attaching the operand to reach its mask materialises
573
- the whole subexpression under it -- so a chain of k masked binops
574
- evaluates its own subtree k times over. Attach what is read. */
575
- if ( kleene ) {
576
- if ( need_l ) ca_attach(l);
577
- if ( need_r ) ca_attach(r);
578
- }
579
- else {
580
- if ( has_l ) ca_attach(l->mask);
581
- if ( has_r ) ca_attach(r->mask);
582
- }
583
-
584
- lm = has_l ? (boolean8_t *) l->mask->ptr : NULL;
585
- rm = has_r ? (boolean8_t *) r->mask->ptr : NULL;
586
- lv = kleene ? (boolean8_t *) l->ptr : NULL;
587
- rv = kleene ? (boolean8_t *) r->ptr : NULL;
588
-
589
- for ( i = 0; i < n; i++ ) {
590
- /* right broadcast: repeat element 0 when right was a scalar. */
591
- ca_size_t ri = bo->right_is_scalar ? 0 : i;
592
- boolean8_t am = lm ? lm[i] : 0;
593
- boolean8_t bm = rm ? rm[ri] : 0;
594
- boolean8_t m = (boolean8_t)( am | bm );
595
- if ( kleene && m ) {
596
- boolean8_t av = lv[i], bv = rv[ri];
597
- if ( is_or ) {
598
- if ( ( ! am && av ) || ( ! bm && bv ) ) m = 0; /* known TRUE */
599
- }
600
- else {
601
- if ( ( ! am && ! av ) || ( ! bm && ! bv ) ) m = 0; /* known FALSE */
602
- }
603
- }
604
- dst[i] = m;
605
- }
606
-
607
- if ( kleene ) {
608
- if ( need_r ) ca_detach(r);
609
- if ( need_l ) ca_detach(l);
610
- }
611
- else {
612
- if ( has_r ) ca_detach(r->mask);
613
- if ( has_l ) ca_detach(l->mask);
472
+ if ( bo->data_type == CA_BOOLEAN ) {
473
+ if ( bo->op_id == CA_BINOP_BIT_OR ) mode = CA_LAZY_MASK_KLEENE_OR;
474
+ if ( bo->op_id == CA_BINOP_BIT_AND ) mode = CA_LAZY_MASK_KLEENE_AND;
614
475
  }
476
+ bo->mask = ca_lazy_operation_mask((CArray *) bo, mode);
615
477
  }
616
478
 
617
479
  ca_operation_function_t ca_binop_func = {
@@ -704,17 +566,22 @@ rb_ca_binop_build (VALUE l_cary, VALUE r_cary, uint16_t op_id)
704
566
  l_resolved = collapse_marker(l_cary);
705
567
  r_resolved = collapse_marker(r_cary);
706
568
 
707
- /* Promote a non-CArray Ruby value to a CScalar carrying the
708
- other side's natural data_type. */
709
- if ( ! rb_obj_is_carray(l_resolved) ) {
710
- rb_check_carray_object(r_resolved);
711
- TypedData_Get_Struct(r_resolved, CArray, &carray_data_type, r);
712
- l_resolved = ca_lazy_wrap_scalar(l_resolved, r);
713
- }
714
- if ( ! rb_obj_is_carray(r_resolved) ) {
715
- rb_check_carray_object(l_resolved);
716
- TypedData_Get_Struct(l_resolved, CArray, &carray_data_type, l);
717
- r_resolved = ca_lazy_wrap_scalar(r_resolved, l);
569
+ ca_binop_read_boolean_as_truth(
570
+ ca_binop_kernel_lookup_vv(op_id, CA_BOOLEAN) != ca_binop_not_implement,
571
+ &l_resolved, &r_resolved);
572
+
573
+ /* A scalar beside the array (a Ruby value or a CScalar) takes the
574
+ data_type the eager binop gives it, and the pair is promoted as the
575
+ eager binop promotes it. An integer power keeps its int64 exponent
576
+ (ca_binop_kernel_input_data_types). */
577
+ if ( op_id != CA_BINOP_IPOWER &&
578
+ ( ! rb_obj_is_carray(l_resolved) || ! rb_obj_is_carray(r_resolved) ||
579
+ rb_obj_is_cscalar(l_resolved) || rb_obj_is_cscalar(r_resolved) ) ) {
580
+ int l_scalar = ! rb_obj_is_carray(l_resolved) || rb_obj_is_cscalar(l_resolved);
581
+ int r_scalar = ! rb_obj_is_carray(r_resolved) || rb_obj_is_cscalar(r_resolved);
582
+ rb_ca_cast_self_or_other(&l_resolved, &r_resolved);
583
+ l_resolved = ca_lazy_settle_scalar(l_resolved, l_scalar);
584
+ r_resolved = ca_lazy_settle_scalar(r_resolved, r_scalar);
718
585
  }
719
586
 
720
587
  TypedData_Get_Struct(l_resolved, CArray, &carray_data_type, l);
@@ -725,6 +592,23 @@ rb_ca_binop_build (VALUE l_cary, VALUE r_cary, uint16_t op_id)
725
592
  ca_binop_kernel_input_data_types(op_id, l->data_type, r->data_type,
726
593
  &l_in_dt, &r_in_dt);
727
594
 
595
+ /* A boolean operand of an op with no boolean kernel (arithmetic) is
596
+ read as its 0/1 storage in int64, as rb_ca_call_binop does. */
597
+ if ( l_in_dt == CA_BOOLEAN &&
598
+ ca_binop_kernel_lookup_vv(op_id, CA_BOOLEAN) == ca_binop_not_implement ) {
599
+ l_in_dt = CA_INT64;
600
+ if ( r_in_dt == CA_BOOLEAN ) {
601
+ r_in_dt = CA_INT64;
602
+ }
603
+ }
604
+
605
+ /* No kernel for that data_type (fixlen arithmetic, ...): raise as the
606
+ eager binop does, before a view is built over it. */
607
+ if ( ca_binop_kernel_lookup_vv(op_id, l_in_dt) == ca_binop_not_implement ) {
608
+ rb_raise(rb_eCADataTypeError,
609
+ "invalid data_type for binop (not implemented)");
610
+ }
611
+
728
612
  if ( l_in_dt != l->data_type ) {
729
613
  VALUE cast_op = INT2NUM(CA_MONOP_CAST_BASE + l_in_dt);
730
614
  l_resolved = rb_funcall(rb_const_get(rb_cObject, rb_intern("CAMonOp")),
@@ -757,11 +641,11 @@ rb_ca_binop_build (VALUE l_cary, VALUE r_cary, uint16_t op_id)
757
641
  shape so the kernel sees matched shapes; cost is N
758
642
  reads of the scalar cell with stride 0. */
759
643
  if ( l->elements != r->elements ) {
760
- if ( r->elements == 1 && l->elements > 1 ) {
644
+ if ( r->elements == 1 ) {
761
645
  /* CScalar-vs-array, right. xfer_stride handles this with
762
646
  right_step = 0. */
763
647
  }
764
- else if ( l->elements == 1 && r->elements > 1 ) {
648
+ else if ( l->elements == 1 ) {
765
649
  int commutes = ( op_id == CA_BINOP_ADD ||
766
650
  op_id == CA_BINOP_MUL ||
767
651
  op_id == CA_BINOP_BIT_AND ||
@@ -816,6 +700,17 @@ rb_ca_binop_s_build (VALUE klass, VALUE l_cary, VALUE r_cary, VALUE op_id_val)
816
700
  }
817
701
 
818
702
  /* op_id accessor for inspect / dump_tree consumers in Ruby. */
703
+ /* Whether this node's kernel skips the cells its operands mask, rather
704
+ than computing them: a kernel that can raise on a cell or calls Ruby
705
+ for it. CArray::Fusion reads it so a plan carries the same rule. */
706
+ static VALUE
707
+ rb_ca_binop_trapping (VALUE self)
708
+ {
709
+ CABinOp *bo;
710
+ TypedData_Get_Struct(self, CABinOp, &cabinop_data_type, bo);
711
+ return ca_binop_is_trapping(bo->op_id, bo->data_type) ? Qtrue : Qfalse;
712
+ }
713
+
819
714
  static VALUE
820
715
  rb_ca_binop_op_id (VALUE self)
821
716
  {
@@ -872,6 +767,7 @@ rb_ca_binop_initialize_copy (VALUE self, VALUE other)
872
767
  {
873
768
  CABinOp *ca, *cs;
874
769
  TypedData_Get_Struct(self, CABinOp, &cabinop_data_type, ca);
770
+ ca_check_uninitialized(ca);
875
771
  TypedData_Get_Struct(other, CABinOp, &cabinop_data_type, cs);
876
772
  if ( ca_func[CA_OBJ_BINOP].pool_init ) {
877
773
  ca_array_pool_alloc(ca, CA_OBJ_BINOP, cs->parent->ndim);
@@ -933,6 +829,7 @@ Init_ca_obj_binop (void)
933
829
 
934
830
  rb_define_method(rb_cCABinOp, "__op_id__",
935
831
  rb_ca_binop_op_id, 0);
832
+ rb_define_method(rb_cCABinOp, "__trapping__", rb_ca_binop_trapping, 0);
936
833
  rb_define_method(rb_cCABinOp, "__binop_right__",
937
834
  rb_ca_binop_right, 0);
938
835
 
@@ -96,6 +96,21 @@ ca_bitarray_setup (CABitarray *ca, CArray *parent)
96
96
  rb_raise(rb_eCADataTypeError, "invalid data_type for bitarray");
97
97
  }
98
98
 
99
+ /* The bits of one byte would share that byte's one mask cell, so a bit
100
+ view takes no masked array and holds no mask of its own. */
101
+ if ( ca_has_mask(parent) ) {
102
+ rb_raise(rb_eArgError,
103
+ "bitarray: the array has a mask, which a bit view can not "
104
+ "carry (its bits would share one mask per byte); "
105
+ "use .value.bitarray to read the bits without it");
106
+ }
107
+
108
+ /* ca == NULL asks for the checks above only, so that a constructor can
109
+ run them before it allocates. */
110
+ if ( ca == NULL ) {
111
+ return 0;
112
+ }
113
+
99
114
  ndim = parent->ndim + 1;
100
115
  bitlen = 8 * parent->bytes;
101
116
  elements = bitlen * parent->elements;
@@ -122,17 +137,15 @@ ca_bitarray_setup (CABitarray *ca, CArray *parent)
122
137
  memcpy(ca->dim, parent->dim, (ndim-1) * sizeof(ca_size_t));
123
138
  ca->dim[ndim-1] = bitlen;
124
139
 
125
- if ( ca_has_mask(parent) ) {
126
- ca_create_mask(ca);
127
- }
128
-
129
140
  return 0;
130
141
  }
131
142
 
132
143
  CABitarray *
133
144
  ca_bitarray_new (CArray *parent)
134
145
  {
135
- CABitarray *ca = (CABitarray *) ca_array_alloc(CA_OBJ_BITARRAY, parent->ndim + 1);
146
+ CABitarray *ca;
147
+ ca_bitarray_setup(NULL, parent);
148
+ ca = (CABitarray *) ca_array_alloc(CA_OBJ_BITARRAY, parent->ndim + 1);
136
149
  ca_bitarray_setup(ca, parent);
137
150
  return ca;
138
151
  }
@@ -178,7 +191,7 @@ ca_bitarray_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
178
191
  ca_size_t bytes = ca->parent->bytes;
179
192
  ca_size_t offset = idx[ca->ndim-1];
180
193
  ca_size_t major, minor;
181
- uint8_t sbuf[32];
194
+ volatile VALUE holder;
182
195
  uint8_t *v;
183
196
 
184
197
  if ( ca_endian == CA_BIG_ENDIAN &&
@@ -191,7 +204,7 @@ ca_bitarray_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
191
204
  }
192
205
  minor = offset % 8;
193
206
 
194
- v = (ca->parent->bytes <= 32) ? sbuf : xmalloc(ca->parent->bytes);
207
+ v = ALLOCV_N(uint8_t, holder, ca->parent->bytes);
195
208
 
196
209
  if ( dir == CA_XFER_GET ) {
197
210
  ca_fetch_index(ca->parent, idx, v);
@@ -209,7 +222,7 @@ ca_bitarray_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
209
222
  ca_store_index(ca->parent, idx, v);
210
223
  }
211
224
 
212
- if ( v != sbuf ) xfree(v);
225
+ ALLOCV_END(holder);
213
226
  }
214
227
 
215
228
  /* Batched gather / scatter.
@@ -315,6 +328,7 @@ static void
315
328
  ca_bitarray_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
316
329
  ca_size_t *strides, void *data, int dir)
317
330
  {
331
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
318
332
  CABitarray *ca = (CABitarray *) ap;
319
333
  int8_t ndim = ca->ndim;
320
334
  ca_size_t native[CA_RANK_MAX], idx[CA_RANK_MAX];
@@ -478,29 +492,27 @@ ca_bitarray_func_fill_data (void *ap, void *ptr)
478
492
  CABitarray *ca = (CABitarray *) ap;
479
493
  ca_attach(ca->parent);
480
494
  ca_bitarray_fill(ca, ptr);
481
- ca_sync(ca->parent);
482
- ca_detach(ca->parent);
495
+ ca_sync_detach(ca->parent);
483
496
  }
484
497
 
498
+ /* Reached when a mask is about to be put on the view (mask=, storing UNDEF
499
+ or a masked array), or when the array it views has gained a mask since
500
+ the view was made. */
501
+ NORETURN(static void ca_bitarray_func_create_mask (void *ap));
502
+
485
503
  static void
486
504
  ca_bitarray_func_create_mask (void *ap)
487
505
  {
488
506
  CABitarray *ca = (CABitarray *) ap;
489
- ca_size_t count[CA_RANK_MAX];
490
- int8_t i;
491
-
492
- for (i=0; i<ca->ndim-1; i++) {
493
- count[i] = 0;
494
- }
495
- count[ca->ndim-1] = ca->bitlen;
496
-
497
- ca_update_mask(ca->parent);
498
- if ( ! ca->parent->mask ) {
499
- ca_create_mask(ca->parent);
507
+ if ( ca_has_mask(ca->parent) ) {
508
+ rb_raise(rb_eArgError,
509
+ "bitarray: the array this view reads has gained a mask, which "
510
+ "a bit view can not carry (its bits would share one mask per "
511
+ "byte); use .value.bitarray to read the bits without it");
500
512
  }
501
- ca->mask = (CArray *) ca_repeat_new(ca->parent->mask, ca->ndim, count);
502
-
503
- ca_unset_flag(ca->mask, CA_FLAG_READ_ONLY);
513
+ rb_raise(rb_eArgError,
514
+ "a bitarray view can not hold a mask "
515
+ "(its bits would share one mask per byte)");
504
516
  }
505
517
 
506
518
  ca_operation_function_t ca_bitarray_func = {
@@ -703,6 +715,7 @@ rb_ca_bitarray_initialize_copy (VALUE self, VALUE other)
703
715
  CABitarray *ca, *cs;
704
716
 
705
717
  TypedData_Get_Struct(self, CABitarray, &cabitarray_data_type, ca);
718
+ ca_check_uninitialized(ca);
706
719
  TypedData_Get_Struct(other, CABitarray, &cabitarray_data_type, cs);
707
720
 
708
721
  if ( ca_func[CA_OBJ_BITARRAY].pool_init ) {