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_triop.c CHANGED
@@ -20,9 +20,9 @@
20
20
  op1), src2 == scratch2, src3 == scratch3.
21
21
 
22
22
  Mask handling: none of the currently-defined triops (fma / fms /
23
- clip) trap on integer zero divisor, so m=NULL always at the
24
- kernel walk (SIMD fast path). Output mask is a blind OR of
25
- operand masks (create_mask time).
23
+ clip / select) trap on integer zero divisor, so m=NULL always at the
24
+ kernel walk (SIMD fast path). Output mask is the OR of the operand
25
+ masks, except for select (create_mask time).
26
26
 
27
27
  Cross-ndim promotion: intentional design rejection, mirroring
28
28
  CABinOp. Callers must reshape explicitly.
@@ -33,6 +33,7 @@
33
33
  #include "carray_internal.h" /* ca_lazy_arena_* */
34
34
  #include "ca_triop_dispatch.h"
35
35
  #include "ca_monop_dispatch.h" /* CA_MONOP_CAST_BASE */
36
+ #include "carray_math_kernel.h" /* ca_triop_not_implement */
36
37
 
37
38
  extern VALUE ca_broadcast_view (VALUE src, int8_t ndim,
38
39
  ca_size_t *target_dim);
@@ -41,7 +42,6 @@ int8_t CA_OBJ_TRIOP;
41
42
  VALUE rb_cCATriOp;
42
43
 
43
44
  extern int8_t CA_OBJ_LAZY_MARKER;
44
- extern VALUE ca_lazy_wrap_scalar (VALUE other, CArray *self_ca);
45
45
 
46
46
  /* ------------------------------------------------------------------- */
47
47
  /* CATriOp struct */
@@ -222,25 +222,6 @@ ca_triop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
222
222
  ca_triop_func_xfer_stride(ca, starts, counts, strides, data, CA_XFER_GET);
223
223
  }
224
224
 
225
- static void
226
- ca_triop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
227
- void *data, int dir)
228
- {
229
- CATriOp *ca = (CATriOp *) ap;
230
- ca_size_t i;
231
- char *out = (char *) data;
232
-
233
- if ( dir != CA_XFER_GET ) {
234
- rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_addrs PUT)");
235
- }
236
-
237
- for ( i = 0; i < n; i++ ) {
238
- ca_size_t idx[CA_RANK_MAX];
239
- ca_addr2index((CArray *)ca, addrs[i], idx);
240
- ca_triop_func_xfer_index(ca, idx, out + i * ca->bytes, CA_XFER_GET);
241
- }
242
- }
243
-
244
225
  /* Test / observability counters (mirror CABinOp's counters). */
245
226
  ca_size_t ca_triop_scratch_acquire_count = 0;
246
227
  ca_size_t ca_triop_materialise_call_count = 0;
@@ -249,8 +230,7 @@ ca_size_t ca_triop_materialise_call_count = 0;
249
230
  be broadcast at step=0). Returns the scratch pointer; updates
250
231
  *step_out to 0 (scalar) or 1 (same-shape). */
251
232
  static void *
252
- pull_operand (CArray *op, int is_scalar, ca_size_t *starts,
253
- ca_size_t *counts, ca_size_t slab_n, int8_t ndim,
233
+ pull_operand (CArray *op, int is_scalar, const ca_lazy_req_t *req,
254
234
  ca_size_t *step_out)
255
235
  {
256
236
  void *scratch;
@@ -274,54 +254,40 @@ pull_operand (CArray *op, int is_scalar, ca_size_t *starts,
274
254
  *step_out = 0;
275
255
  }
276
256
  else {
277
- ca_size_t op_strides[CA_RANK_MAX];
278
- ca_size_t s = bytes;
279
- for ( k = ndim - 1; k >= 0; k-- ) {
280
- op_strides[k] = s;
281
- s *= counts[k];
282
- }
283
257
  scratch = ( op->data_type == CA_OBJECT )
284
- ? ca_lazy_arena_acquire_object(slab_n)
285
- : ca_lazy_arena_acquire(slab_n * bytes);
258
+ ? ca_lazy_arena_acquire_object(req->n)
259
+ : ca_lazy_arena_acquire(req->n * bytes);
286
260
  ca_triop_scratch_acquire_count++;
287
- ca_xfer_stride(op, starts, counts, op_strides, scratch, CA_XFER_GET);
261
+ ca_lazy_req_pull(op, req, scratch, CA_XFER_GET);
288
262
  *step_out = 1;
289
263
  }
290
264
  return scratch;
291
265
  }
292
266
 
267
+ /* Evaluate the cells a request names into `data` (packed, n cells). */
293
268
  static void
294
- ca_triop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
295
- ca_size_t *strides, void *data, int dir)
269
+ ca_triop_eval (CATriOp *to, const ca_lazy_req_t *req, void *data)
296
270
  {
297
- CATriOp *to = (CATriOp *) ap;
298
- ca_size_t slab_n;
299
- int8_t k;
271
+ ca_size_t slab_n = req->n;
300
272
  void *scratch2, *scratch3;
301
273
  ca_size_t op2_step, op3_step;
302
274
 
303
- if ( dir != CA_XFER_GET ) {
304
- rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_stride PUT)");
305
- }
306
275
  ca_triop_materialise_call_count++;
307
276
 
308
- slab_n = 1;
309
- for ( k = 0; k < to->ndim; k++ ) slab_n *= counts[k];
310
-
311
277
  /* Step 1: pull op1 into the output buffer. */
312
- ca_xfer_stride(to->parent, starts, counts, strides, data, CA_XFER_GET);
278
+ ca_lazy_req_pull(to->parent, req, data, CA_XFER_GET);
313
279
 
314
280
  /* Step 2: pull op2 into an arena scratch. */
315
- scratch2 = pull_operand(to->op2, to->op2_is_scalar, starts, counts,
316
- slab_n, to->ndim, &op2_step);
281
+ scratch2 = pull_operand(to->op2, to->op2_is_scalar, req, &op2_step);
317
282
 
318
283
  /* Step 3: pull op3 into another arena scratch. */
319
- scratch3 = pull_operand(to->op3, to->op3_is_scalar, starts, counts,
320
- slab_n, to->ndim, &op3_step);
284
+ scratch3 = pull_operand(to->op3, to->op3_is_scalar, req, &op3_step);
321
285
 
322
- /* Step 4: apply the kernel. ptr1 == ptr4 (in-place op1); no
323
- currently-defined triop traps, so m=NULL. */
286
+ /* Step 4: apply the kernel. ptr1 == ptr4 (in-place op1). No numeric
287
+ triop traps, so m=NULL; object cells call Ruby, and skip the masked
288
+ cells as the eager triop does. */
324
289
  {
290
+ boolean8_t *slab_mask = NULL;
325
291
  ca_triop_func_t fn = ca_triop_kernel_lookup_vvv(to->op_id, to->data_type);
326
292
  if ( fn == NULL ) {
327
293
  ca_lazy_arena_release(scratch3);
@@ -330,17 +296,51 @@ ca_triop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
330
296
  "CATriOp: kernel not implemented (op_id=%u data_type=%d)",
331
297
  (unsigned) to->op_id, (int) to->data_type);
332
298
  }
333
- fn(slab_n, NULL,
299
+ if ( to->data_type == CA_OBJECT ) {
300
+ CArray *ops[3];
301
+ ops[0] = to->parent;
302
+ ops[1] = to->op2;
303
+ ops[2] = to->op3;
304
+ slab_mask = ca_lazy_req_mask(req, 3, ops);
305
+ }
306
+ fn(slab_n, slab_mask,
334
307
  (char *)data, 1, /* src1 == dst (op1, in-place) */
335
308
  (char *)scratch2, op2_step, /* src2 (op2) */
336
309
  (char *)scratch3, op3_step, /* src3 (op3) */
337
310
  (char *)data, 1); /* dst */
311
+ if ( slab_mask ) ca_lazy_arena_release(slab_mask);
338
312
  }
339
313
 
340
314
  ca_lazy_arena_release(scratch3);
341
315
  ca_lazy_arena_release(scratch2);
342
316
  }
343
317
 
318
+ static void
319
+ ca_triop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
320
+ ca_size_t *strides, void *data, int dir)
321
+ {
322
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
323
+ ca_lazy_req_t req;
324
+ if ( dir != CA_XFER_GET ) {
325
+ rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_stride PUT)");
326
+ }
327
+ ca_lazy_req_region(&req, ap, starts, counts, strides);
328
+ ca_triop_eval((CATriOp *) ap, &req, data);
329
+ }
330
+
331
+ /* An address list is evaluated in one pass, as a region is. */
332
+ static void
333
+ ca_triop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
334
+ void *data, int dir)
335
+ {
336
+ ca_lazy_req_t req;
337
+ if ( dir != CA_XFER_GET ) {
338
+ rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_addrs PUT)");
339
+ }
340
+ ca_lazy_req_addrs(&req, n, addrs);
341
+ ca_triop_eval((CATriOp *) ap, &req, data);
342
+ }
343
+
344
344
  static void
345
345
  ca_triop_func_xfer_all (void *ap, void *data, int dir)
346
346
  {
@@ -419,61 +419,16 @@ ca_triop_func_fill_data (void *ap, void *ptr)
419
419
  rb_raise(rb_eRuntimeError, "CATriOp is read-only (fill_data)");
420
420
  }
421
421
 
422
- /* Build out.mask. Blind OR of the three operand masks — none of the
423
- currently-defined triops carry a Kleene 3-valued fixup. */
422
+ /* Build out.mask on each read (CAMaskOfOperands): the three operands'
423
+ masks ORed, except for select, which is masked where the condition is
424
+ or where the branch it chooses is. */
424
425
  static void
425
426
  ca_triop_func_create_mask (void *ap)
426
427
  {
427
428
  CATriOp *to = (CATriOp *) ap;
428
- CArray *op1 = to->parent;
429
- CArray *op2 = to->op2;
430
- CArray *op3 = to->op3;
431
- boolean8_t *dst, *m1, *m2, *m3;
432
- ca_size_t i, n;
433
- int has1, has2, has3;
434
-
435
- has1 = ca_has_mask(op1);
436
- has2 = ca_has_mask(op2);
437
- has3 = ca_has_mask(op3);
438
- if ( ! has1 && ! has2 && ! has3 ) return;
439
-
440
- /* Exactly one masked operand: the answer is that operand's mask, cell
441
- for cell. Share it rather than allocating a copy per node. */
442
- if ( has1 + has2 + has3 == 1 ) {
443
- CArray *src = has1 ? op1 : ( has2 ? op2 : op3 );
444
- if ( src->elements == to->elements ) {
445
- to->mask = (CArray *) ca_refer_new(src->mask, CA_BOOLEAN,
446
- to->ndim, to->dim, 0, 0);
447
- return;
448
- }
449
- }
450
-
451
- to->mask = (CArray *) carray_new(CA_BOOLEAN, to->ndim, to->dim, 0, NULL);
452
- dst = (boolean8_t *) to->mask->ptr;
453
- n = to->elements;
454
-
455
- /* The masks are what is read here; attaching the operand instead
456
- materialises the whole subexpression under it. */
457
- if ( has1 ) ca_attach(op1->mask);
458
- if ( has2 ) ca_attach(op2->mask);
459
- if ( has3 ) ca_attach(op3->mask);
460
-
461
- m1 = has1 ? (boolean8_t *) op1->mask->ptr : NULL;
462
- m2 = has2 ? (boolean8_t *) op2->mask->ptr : NULL;
463
- m3 = has3 ? (boolean8_t *) op3->mask->ptr : NULL;
464
-
465
- for ( i = 0; i < n; i++ ) {
466
- ca_size_t i2 = to->op2_is_scalar ? 0 : i;
467
- ca_size_t i3 = to->op3_is_scalar ? 0 : i;
468
- boolean8_t a = m1 ? m1[i] : 0;
469
- boolean8_t b = m2 ? m2[i2] : 0;
470
- boolean8_t c = m3 ? m3[i3] : 0;
471
- dst[i] = (boolean8_t) ( a | b | c );
472
- }
473
-
474
- if ( has3 ) ca_detach(op3->mask);
475
- if ( has2 ) ca_detach(op2->mask);
476
- if ( has1 ) ca_detach(op1->mask);
429
+ int mode = ( to->op_id == CA_TRIOP_SELECT ) ? CA_LAZY_MASK_SELECT
430
+ : CA_LAZY_MASK_OR;
431
+ to->mask = ca_lazy_operation_mask((CArray *) to, mode);
477
432
  }
478
433
 
479
434
  ca_operation_function_t ca_triop_func = {
@@ -574,21 +529,32 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
574
529
  r2 = collapse_marker(cary2);
575
530
  r3 = collapse_marker(cary3);
576
531
 
577
- /* Promote non-CArray Ruby values (e.g. clip's Numeric bounds) to
578
- CScalars carrying an existing operand's data_type. */
579
- if ( ! rb_obj_is_carray(r1) || ! rb_obj_is_carray(r2) || ! rb_obj_is_carray(r3) ) {
580
- /* Find an anchor CArray for scalar promotion. */
581
- CArray *anchor = NULL;
582
- if ( rb_obj_is_carray(r1) ) TypedData_Get_Struct(r1, CArray, &carray_data_type, anchor);
583
- else if ( rb_obj_is_carray(r2) ) TypedData_Get_Struct(r2, CArray, &carray_data_type, anchor);
584
- else if ( rb_obj_is_carray(r3) ) TypedData_Get_Struct(r3, CArray, &carray_data_type, anchor);
585
- if ( anchor == NULL ) {
532
+ /* select takes its branches already typed (CArray#then_else makes a
533
+ scalar branch a CScalar of the result type); the condition is boolean
534
+ and takes no part in the promotion. */
535
+ if ( op_id == CA_TRIOP_SELECT ) {
536
+ if ( ! rb_obj_is_carray(r1) || ! rb_obj_is_carray(r2) || ! rb_obj_is_carray(r3) ) {
537
+ rb_raise(rb_eArgError, "CATriOp: select takes three CArrays");
538
+ }
539
+ r2 = ca_lazy_settle_scalar(r2, RTEST(rb_obj_is_cscalar(r2)));
540
+ r3 = ca_lazy_settle_scalar(r3, RTEST(rb_obj_is_cscalar(r3)));
541
+ }
542
+ /* Scalar operands (clip's Numeric bounds, a CScalar) are wrapped and
543
+ promoted pairwise in the order the eager triop uses. */
544
+ else if ( ! rb_obj_is_carray(r1) || ! rb_obj_is_carray(r2) || ! rb_obj_is_carray(r3) ||
545
+ rb_obj_is_cscalar(r2) || rb_obj_is_cscalar(r3) ) {
546
+ if ( ! rb_obj_is_carray(r1) ) {
586
547
  rb_raise(rb_eArgError,
587
- "CATriOp: at least one operand must be a CArray");
548
+ "CATriOp: the first operand must be a CArray");
588
549
  }
589
- if ( ! rb_obj_is_carray(r1) ) r1 = ca_lazy_wrap_scalar(r1, anchor);
590
- if ( ! rb_obj_is_carray(r2) ) r2 = ca_lazy_wrap_scalar(r2, anchor);
591
- if ( ! rb_obj_is_carray(r3) ) r3 = ca_lazy_wrap_scalar(r3, anchor);
550
+ int r2_scalar = ! rb_obj_is_carray(r2) || rb_obj_is_cscalar(r2);
551
+ int r3_scalar = ! rb_obj_is_carray(r3) || rb_obj_is_cscalar(r3);
552
+ rb_ca_cast_self_or_other(&r1, &r2);
553
+ rb_ca_cast_self_or_other(&r1, &r3);
554
+ rb_ca_cast_self_or_other(&r2, &r3);
555
+ rb_ca_cast_self_or_other(&r1, &r2);
556
+ r2 = ca_lazy_settle_scalar(r2, r2_scalar);
557
+ r3 = ca_lazy_settle_scalar(r3, r3_scalar);
592
558
  }
593
559
 
594
560
  TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
@@ -599,6 +565,19 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
599
565
  ca_triop_kernel_input_data_types(op_id,
600
566
  op1->data_type, op2->data_type, op3->data_type,
601
567
  &dt1, &dt2, &dt3);
568
+
569
+ /* All boolean, and no boolean kernel (fma / fms): read the operands as
570
+ their 0/1 storage in int64, as rb_ca_call_triop does. */
571
+ if ( dt1 == CA_BOOLEAN &&
572
+ ca_triop_kernel_lookup_vvv(op_id, CA_BOOLEAN) == ca_triop_not_implement ) {
573
+ dt1 = dt2 = dt3 = CA_INT64;
574
+ }
575
+
576
+ /* No kernel for that data_type: raise as the eager triop does. */
577
+ if ( ca_triop_kernel_lookup_vvv(op_id, dt1) == ca_triop_not_implement ) {
578
+ rb_raise(rb_eCADataTypeError,
579
+ "invalid data_type for triop (not implemented)");
580
+ }
602
581
  r1 = insert_cast(r1, dt1, &op1);
603
582
  r2 = insert_cast(r2, dt2, &op2);
604
583
  r3 = insert_cast(r3, dt3, &op3);
@@ -668,6 +647,17 @@ rb_ca_triop_s_build (VALUE klass, VALUE cary1, VALUE cary2, VALUE cary3,
668
647
  return rb_ca_triop_build(cary1, cary2, cary3, op_id);
669
648
  }
670
649
 
650
+ /* Whether this node's kernel skips the cells its operands mask, rather
651
+ than computing them: a kernel that can raise on a cell or calls Ruby
652
+ for it. CArray::Fusion reads it so a plan carries the same rule. */
653
+ static VALUE
654
+ rb_ca_triop_trapping (VALUE self)
655
+ {
656
+ CArray *ca;
657
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
658
+ return ( ca->data_type == CA_OBJECT ) ? Qtrue : Qfalse;
659
+ }
660
+
671
661
  static VALUE
672
662
  rb_ca_triop_op_id (VALUE self)
673
663
  {
@@ -731,6 +721,7 @@ rb_ca_triop_initialize_copy (VALUE self, VALUE other)
731
721
  {
732
722
  CATriOp *ca, *cs;
733
723
  TypedData_Get_Struct(self, CATriOp, &catriop_data_type, ca);
724
+ ca_check_uninitialized(ca);
734
725
  TypedData_Get_Struct(other, CATriOp, &catriop_data_type, cs);
735
726
  if ( ca_func[CA_OBJ_TRIOP].pool_init ) {
736
727
  ca_array_pool_alloc(ca, CA_OBJ_TRIOP, cs->parent->ndim);
@@ -758,6 +749,7 @@ Init_ca_obj_triop (void)
758
749
  rb_define_const(rb_cCATriOp, "OP_FMA", INT2NUM(CA_TRIOP_FMA));
759
750
  rb_define_const(rb_cCATriOp, "OP_FMS", INT2NUM(CA_TRIOP_FMS));
760
751
  rb_define_const(rb_cCATriOp, "OP_CLIP", INT2NUM(CA_TRIOP_CLIP));
752
+ rb_define_const(rb_cCATriOp, "OP_SELECT", INT2NUM(CA_TRIOP_SELECT));
761
753
 
762
754
  rb_define_alloc_func(rb_cCATriOp, rb_ca_triop_s_allocate);
763
755
  rb_define_method(rb_cCATriOp, "initialize_copy",
@@ -768,6 +760,7 @@ Init_ca_obj_triop (void)
768
760
 
769
761
  rb_define_method(rb_cCATriOp, "__op_id__",
770
762
  rb_ca_triop_op_id, 0);
763
+ rb_define_method(rb_cCATriOp, "__trapping__", rb_ca_triop_trapping, 0);
771
764
  rb_define_method(rb_cCATriOp, "__triop_op2__",
772
765
  rb_ca_triop_op2, 0);
773
766
  rb_define_method(rb_cCATriOp, "__triop_op3__",