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/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 */
@@ -163,6 +163,7 @@ ca_triop_setup (CATriOp *ca, CArray *op1, CArray *op2, CArray *op3,
163
163
  CATriOp *
164
164
  ca_triop_new (CArray *op1, CArray *op2, CArray *op3, uint16_t op_id)
165
165
  {
166
+ /* check: the setup checks nothing */
166
167
  CATriOp *ca = (CATriOp *) ca_array_alloc(CA_OBJ_TRIOP, op1->ndim);
167
168
  ca_triop_setup(ca, op1, op2, op3, op_id);
168
169
  return ca;
@@ -210,7 +211,7 @@ ca_triop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
210
211
  ca_size_t s;
211
212
 
212
213
  if ( dir != CA_XFER_GET ) {
213
- rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_index PUT)");
214
+ ca_refuse_read_only();
214
215
  }
215
216
 
216
217
  s = ca->bytes;
@@ -222,25 +223,6 @@ ca_triop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
222
223
  ca_triop_func_xfer_stride(ca, starts, counts, strides, data, CA_XFER_GET);
223
224
  }
224
225
 
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
226
  /* Test / observability counters (mirror CABinOp's counters). */
245
227
  ca_size_t ca_triop_scratch_acquire_count = 0;
246
228
  ca_size_t ca_triop_materialise_call_count = 0;
@@ -249,8 +231,7 @@ ca_size_t ca_triop_materialise_call_count = 0;
249
231
  be broadcast at step=0). Returns the scratch pointer; updates
250
232
  *step_out to 0 (scalar) or 1 (same-shape). */
251
233
  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,
234
+ pull_operand (CArray *op, int is_scalar, const ca_lazy_req_t *req,
254
235
  ca_size_t *step_out)
255
236
  {
256
237
  void *scratch;
@@ -274,54 +255,40 @@ pull_operand (CArray *op, int is_scalar, ca_size_t *starts,
274
255
  *step_out = 0;
275
256
  }
276
257
  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
258
  scratch = ( op->data_type == CA_OBJECT )
284
- ? ca_lazy_arena_acquire_object(slab_n)
285
- : ca_lazy_arena_acquire(slab_n * bytes);
259
+ ? ca_lazy_arena_acquire_object(req->n)
260
+ : ca_lazy_arena_acquire(req->n * bytes);
286
261
  ca_triop_scratch_acquire_count++;
287
- ca_xfer_stride(op, starts, counts, op_strides, scratch, CA_XFER_GET);
262
+ ca_lazy_req_pull(op, req, scratch, CA_XFER_GET);
288
263
  *step_out = 1;
289
264
  }
290
265
  return scratch;
291
266
  }
292
267
 
268
+ /* Evaluate the cells a request names into `data` (packed, n cells). */
293
269
  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)
270
+ ca_triop_eval (CATriOp *to, const ca_lazy_req_t *req, void *data)
296
271
  {
297
- CATriOp *to = (CATriOp *) ap;
298
- ca_size_t slab_n;
299
- int8_t k;
272
+ ca_size_t slab_n = req->n;
300
273
  void *scratch2, *scratch3;
301
274
  ca_size_t op2_step, op3_step;
302
275
 
303
- if ( dir != CA_XFER_GET ) {
304
- rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_stride PUT)");
305
- }
306
276
  ca_triop_materialise_call_count++;
307
277
 
308
- slab_n = 1;
309
- for ( k = 0; k < to->ndim; k++ ) slab_n *= counts[k];
310
-
311
278
  /* Step 1: pull op1 into the output buffer. */
312
- ca_xfer_stride(to->parent, starts, counts, strides, data, CA_XFER_GET);
279
+ ca_lazy_req_pull(to->parent, req, data, CA_XFER_GET);
313
280
 
314
281
  /* 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);
282
+ scratch2 = pull_operand(to->op2, to->op2_is_scalar, req, &op2_step);
317
283
 
318
284
  /* 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);
285
+ scratch3 = pull_operand(to->op3, to->op3_is_scalar, req, &op3_step);
321
286
 
322
- /* Step 4: apply the kernel. ptr1 == ptr4 (in-place op1); no
323
- currently-defined triop traps, so m=NULL. */
287
+ /* Step 4: apply the kernel. ptr1 == ptr4 (in-place op1). No numeric
288
+ triop traps, so m=NULL; object cells call Ruby, and skip the masked
289
+ cells as the eager triop does. */
324
290
  {
291
+ boolean8_t *slab_mask = NULL;
325
292
  ca_triop_func_t fn = ca_triop_kernel_lookup_vvv(to->op_id, to->data_type);
326
293
  if ( fn == NULL ) {
327
294
  ca_lazy_arena_release(scratch3);
@@ -330,17 +297,51 @@ ca_triop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
330
297
  "CATriOp: kernel not implemented (op_id=%u data_type=%d)",
331
298
  (unsigned) to->op_id, (int) to->data_type);
332
299
  }
333
- fn(slab_n, NULL,
300
+ if ( ca_op_raises_on_cell(CA_OP_KIND_TRIOP, to->op_id, to->data_type) ) {
301
+ CArray *ops[3];
302
+ ops[0] = to->parent;
303
+ ops[1] = to->op2;
304
+ ops[2] = to->op3;
305
+ slab_mask = ca_lazy_req_mask(req, 3, ops);
306
+ }
307
+ fn(slab_n, slab_mask,
334
308
  (char *)data, 1, /* src1 == dst (op1, in-place) */
335
309
  (char *)scratch2, op2_step, /* src2 (op2) */
336
310
  (char *)scratch3, op3_step, /* src3 (op3) */
337
311
  (char *)data, 1); /* dst */
312
+ if ( slab_mask ) ca_lazy_arena_release(slab_mask);
338
313
  }
339
314
 
340
315
  ca_lazy_arena_release(scratch3);
341
316
  ca_lazy_arena_release(scratch2);
342
317
  }
343
318
 
319
+ static void
320
+ ca_triop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
321
+ ca_size_t *strides, void *data, int dir)
322
+ {
323
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
324
+ ca_lazy_req_t req;
325
+ if ( dir != CA_XFER_GET ) {
326
+ ca_refuse_read_only();
327
+ }
328
+ ca_lazy_req_region(&req, ap, starts, counts, strides);
329
+ ca_triop_eval((CATriOp *) ap, &req, data);
330
+ }
331
+
332
+ /* An address list is evaluated in one pass, as a region is. */
333
+ static void
334
+ ca_triop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
335
+ void *data, int dir)
336
+ {
337
+ ca_lazy_req_t req;
338
+ if ( dir != CA_XFER_GET ) {
339
+ ca_refuse_read_only();
340
+ }
341
+ ca_lazy_req_addrs(&req, n, addrs);
342
+ ca_triop_eval((CATriOp *) ap, &req, data);
343
+ }
344
+
344
345
  static void
345
346
  ca_triop_func_xfer_all (void *ap, void *data, int dir)
346
347
  {
@@ -358,15 +359,7 @@ static void
358
359
  ca_triop_func_allocate (void *ap)
359
360
  {
360
361
  CATriOp *ca = (CATriOp *) ap;
361
- ca->ptr = xmalloc(ca_length(ca));
362
- /* CA_OBJECT cells are VALUEs and this buffer is about to be marked as
363
- soon as the view is, so it must not be handed to the GC as raw
364
- xmalloc garbage. */
365
- if ( ca->data_type == CA_OBJECT ) {
366
- VALUE *p = (VALUE *) ca->ptr;
367
- ca_size_t i;
368
- for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
369
- }
362
+ ca->ptr = ca_alloc_cells((CArray *) ca);
370
363
  }
371
364
 
372
365
  static void
@@ -381,19 +374,11 @@ ca_triop_func_attach (void *ap)
381
374
  int8_t k;
382
375
  ca_size_t s;
383
376
 
384
- ca->ptr = xmalloc(ca_length(ca));
377
+ ca->ptr = ca_alloc_cells((CArray *) ca);
385
378
 
386
379
  s = ca->bytes;
387
380
  for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
388
381
  for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
389
- /* CA_OBJECT cells are VALUEs and this buffer is about to be marked as
390
- soon as the view is, so it must not be handed to the GC as raw
391
- xmalloc garbage. */
392
- if ( ca->data_type == CA_OBJECT ) {
393
- VALUE *p = (VALUE *) ca->ptr;
394
- ca_size_t i;
395
- for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
396
- }
397
382
  ca_triop_func_xfer_stride(ca, starts, ca->dim, native, ca->ptr, CA_XFER_GET);
398
383
  }
399
384
 
@@ -416,64 +401,19 @@ static void
416
401
  ca_triop_func_fill_data (void *ap, void *ptr)
417
402
  {
418
403
  (void) ap; (void) ptr;
419
- rb_raise(rb_eRuntimeError, "CATriOp is read-only (fill_data)");
404
+ ca_refuse_read_only();
420
405
  }
421
406
 
422
- /* Build out.mask. Blind OR of the three operand masks — none of the
423
- currently-defined triops carry a Kleene 3-valued fixup. */
407
+ /* Build out.mask on each read (CAMaskOfOperands): the three operands'
408
+ masks ORed, except for select, which is masked where the condition is
409
+ or where the branch it chooses is. */
424
410
  static void
425
411
  ca_triop_func_create_mask (void *ap)
426
412
  {
427
413
  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);
414
+ int mode = ( to->op_id == CA_TRIOP_SELECT ) ? CA_LAZY_MASK_SELECT
415
+ : CA_LAZY_MASK_OR;
416
+ to->mask = ca_lazy_operation_mask((CArray *) to, mode);
477
417
  }
478
418
 
479
419
  ca_operation_function_t ca_triop_func = {
@@ -574,21 +514,32 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
574
514
  r2 = collapse_marker(cary2);
575
515
  r3 = collapse_marker(cary3);
576
516
 
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 ) {
517
+ /* select takes its branches already typed (CArray#then_else makes a
518
+ scalar branch a CScalar of the result type); the condition is boolean
519
+ and takes no part in the promotion. */
520
+ if ( op_id == CA_TRIOP_SELECT ) {
521
+ if ( ! rb_obj_is_carray(r1) || ! rb_obj_is_carray(r2) || ! rb_obj_is_carray(r3) ) {
522
+ rb_raise(rb_eArgError, "CATriOp: select takes three CArrays");
523
+ }
524
+ r2 = ca_lazy_settle_scalar(r2, RTEST(rb_obj_is_cscalar(r2)));
525
+ r3 = ca_lazy_settle_scalar(r3, RTEST(rb_obj_is_cscalar(r3)));
526
+ }
527
+ /* Scalar operands (clip's Numeric bounds, a CScalar) are wrapped and
528
+ promoted pairwise in the order the eager triop uses. */
529
+ else if ( ! rb_obj_is_carray(r1) || ! rb_obj_is_carray(r2) || ! rb_obj_is_carray(r3) ||
530
+ rb_obj_is_cscalar(r2) || rb_obj_is_cscalar(r3) ) {
531
+ if ( ! rb_obj_is_carray(r1) ) {
586
532
  rb_raise(rb_eArgError,
587
- "CATriOp: at least one operand must be a CArray");
533
+ "CATriOp: the first operand must be a CArray");
588
534
  }
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);
535
+ int r2_scalar = ! rb_obj_is_carray(r2) || rb_obj_is_cscalar(r2);
536
+ int r3_scalar = ! rb_obj_is_carray(r3) || rb_obj_is_cscalar(r3);
537
+ rb_ca_cast_self_or_other(&r1, &r2);
538
+ rb_ca_cast_self_or_other(&r1, &r3);
539
+ rb_ca_cast_self_or_other(&r2, &r3);
540
+ rb_ca_cast_self_or_other(&r1, &r2);
541
+ r2 = ca_lazy_settle_scalar(r2, r2_scalar);
542
+ r3 = ca_lazy_settle_scalar(r3, r3_scalar);
592
543
  }
593
544
 
594
545
  TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
@@ -599,6 +550,18 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
599
550
  ca_triop_kernel_input_data_types(op_id,
600
551
  op1->data_type, op2->data_type, op3->data_type,
601
552
  &dt1, &dt2, &dt3);
553
+
554
+ /* All boolean, and no boolean kernel (fma / fms): read the operands as
555
+ their 0/1 storage in int64, as rb_ca_call_triop does. */
556
+ if ( dt1 == CA_BOOLEAN &&
557
+ ca_triop_kernel_lookup_vvv(op_id, CA_BOOLEAN) == ca_triop_not_implement ) {
558
+ dt1 = dt2 = dt3 = CA_INT64;
559
+ }
560
+
561
+ /* No kernel for that data_type: raise as the eager triop does. */
562
+ if ( ca_triop_kernel_lookup_vvv(op_id, dt1) == ca_triop_not_implement ) {
563
+ ca_op_refuse_unimplemented(cary1, dt1, rb_eCADataTypeError);
564
+ }
602
565
  r1 = insert_cast(r1, dt1, &op1);
603
566
  r2 = insert_cast(r2, dt2, &op2);
604
567
  r3 = insert_cast(r3, dt3, &op3);
@@ -610,10 +573,11 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
610
573
  CArray *anchor = NULL;
611
574
  volatile VALUE r_anchor = Qnil;
612
575
  int8_t anchor_ndim;
613
- /* Pick the largest-ndim non-scalar operand as anchor. */
614
- if ( op1->elements > 1 ) { anchor = op1; r_anchor = r1; }
615
- else if ( op2->elements > 1 ) { anchor = op2; r_anchor = r2; }
616
- else if ( op3->elements > 1 ) { anchor = op3; r_anchor = r3; }
576
+ /* The first operand that is not a scalar is the anchor. An array of
577
+ no cells is one (its shape is the walk's), not a scalar. */
578
+ if ( ! ca_is_scalar(op1) ) { anchor = op1; r_anchor = r1; }
579
+ else if ( ! ca_is_scalar(op2) ) { anchor = op2; r_anchor = r2; }
580
+ else if ( ! ca_is_scalar(op3) ) { anchor = op3; r_anchor = r3; }
617
581
  if ( anchor && anchor != op1 ) {
618
582
  /* op1 is a CScalar but the walk shape is determined by another
619
583
  operand. Lift op1 up to anchor's shape via
@@ -663,11 +627,24 @@ static VALUE
663
627
  rb_ca_triop_s_build (VALUE klass, VALUE cary1, VALUE cary2, VALUE cary3,
664
628
  VALUE op_id_val)
665
629
  {
630
+ /* fits: an operation number from the library's own table */
666
631
  uint16_t op_id = (uint16_t) NUM2UINT(op_id_val);
667
632
  (void) klass;
668
633
  return rb_ca_triop_build(cary1, cary2, cary3, op_id);
669
634
  }
670
635
 
636
+ /* Whether this node's kernel skips the cells its operands mask, rather
637
+ than computing them: a kernel that can raise on a cell or calls Ruby
638
+ for it. CArray::Fusion reads it so a plan carries the same rule. */
639
+ static VALUE
640
+ rb_ca_triop_trapping (VALUE self)
641
+ {
642
+ CATriOp *to;
643
+ TypedData_Get_Struct(self, CATriOp, &catriop_data_type, to);
644
+ return ca_op_raises_on_cell(CA_OP_KIND_TRIOP, to->op_id, to->data_type)
645
+ ? Qtrue : Qfalse;
646
+ }
647
+
671
648
  static VALUE
672
649
  rb_ca_triop_op_id (VALUE self)
673
650
  {
@@ -689,6 +666,7 @@ rb_ca_triop_op3 (VALUE self)
689
666
  }
690
667
 
691
668
  /* Test instrumentation. */
669
+ #ifdef CARRAY_DEV_BUILD
692
670
  static VALUE
693
671
  rb_ca_triop_s_reset_scratch_counter (VALUE klass)
694
672
  {
@@ -718,6 +696,7 @@ rb_ca_triop_s_materialise_count (VALUE klass)
718
696
  (void) klass;
719
697
  return SIZE2NUM(ca_triop_materialise_call_count);
720
698
  }
699
+ #endif /* CARRAY_DEV_BUILD */
721
700
 
722
701
  static VALUE
723
702
  rb_ca_triop_s_allocate (VALUE klass)
@@ -731,6 +710,7 @@ rb_ca_triop_initialize_copy (VALUE self, VALUE other)
731
710
  {
732
711
  CATriOp *ca, *cs;
733
712
  TypedData_Get_Struct(self, CATriOp, &catriop_data_type, ca);
713
+ ca_check_uninitialized(ca);
734
714
  TypedData_Get_Struct(other, CATriOp, &catriop_data_type, cs);
735
715
  if ( ca_func[CA_OBJ_TRIOP].pool_init ) {
736
716
  ca_array_pool_alloc(ca, CA_OBJ_TRIOP, cs->parent->ndim);
@@ -758,6 +738,7 @@ Init_ca_obj_triop (void)
758
738
  rb_define_const(rb_cCATriOp, "OP_FMA", INT2NUM(CA_TRIOP_FMA));
759
739
  rb_define_const(rb_cCATriOp, "OP_FMS", INT2NUM(CA_TRIOP_FMS));
760
740
  rb_define_const(rb_cCATriOp, "OP_CLIP", INT2NUM(CA_TRIOP_CLIP));
741
+ rb_define_const(rb_cCATriOp, "OP_SELECT", INT2NUM(CA_TRIOP_SELECT));
761
742
 
762
743
  rb_define_alloc_func(rb_cCATriOp, rb_ca_triop_s_allocate);
763
744
  rb_define_method(rb_cCATriOp, "initialize_copy",
@@ -768,12 +749,14 @@ Init_ca_obj_triop (void)
768
749
 
769
750
  rb_define_method(rb_cCATriOp, "__op_id__",
770
751
  rb_ca_triop_op_id, 0);
752
+ rb_define_method(rb_cCATriOp, "__trapping__", rb_ca_triop_trapping, 0);
771
753
  rb_define_method(rb_cCATriOp, "__triop_op2__",
772
754
  rb_ca_triop_op2, 0);
773
755
  rb_define_method(rb_cCATriOp, "__triop_op3__",
774
756
  rb_ca_triop_op3, 0);
775
757
 
776
758
  /* Test instrumentation. */
759
+ #ifdef CARRAY_DEV_BUILD
777
760
  rb_define_singleton_method(rb_cCATriOp, "__reset_scratch_counter__",
778
761
  rb_ca_triop_s_reset_scratch_counter, 0);
779
762
  rb_define_singleton_method(rb_cCATriOp, "__scratch_count__",
@@ -782,4 +765,5 @@ Init_ca_obj_triop (void)
782
765
  rb_ca_triop_s_reset_materialise_counter, 0);
783
766
  rb_define_singleton_method(rb_cCATriOp, "__materialise_count__",
784
767
  rb_ca_triop_s_materialise_count, 0);
768
+ #endif
785
769
  }