carray 3.0.3 → 3.0.5

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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +469 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_descriptor.h +27 -24
  7. data/ext/ca_axis_dispatch.c +14 -26
  8. data/ext/ca_axis_group.c +27 -27
  9. data/ext/ca_binop_dispatch.c +1 -29
  10. data/ext/ca_binop_dispatch.h +0 -1
  11. data/ext/ca_categorical_iterator.c +231 -153
  12. data/ext/ca_compare.c +9 -20
  13. data/ext/ca_composite_dispatch.c +9 -9
  14. data/ext/ca_group_iter.c +16 -17
  15. data/ext/ca_kernel_iterator.c +1240 -2000
  16. data/ext/ca_kernel_iterator.h +502 -519
  17. data/ext/ca_monop_dispatch.c +82 -59
  18. data/ext/ca_monop_dispatch.h +1 -0
  19. data/ext/ca_obj_array.c +152 -45
  20. data/ext/ca_obj_bincmp.c +25 -21
  21. data/ext/ca_obj_binop.c +16 -26
  22. data/ext/ca_obj_bitarray.c +8 -3
  23. data/ext/ca_obj_bitfield.c +63 -49
  24. data/ext/ca_obj_block.c +18 -9
  25. data/ext/ca_obj_byte_swap.c +7 -7
  26. data/ext/ca_obj_const_string.c +188 -22
  27. data/ext/ca_obj_face.c +280 -58
  28. data/ext/ca_obj_face.h +19 -0
  29. data/ext/ca_obj_fake.c +15 -29
  30. data/ext/ca_obj_farray.c +6 -3
  31. data/ext/ca_obj_field.c +30 -8
  32. data/ext/ca_obj_fixlen_string.c +2 -3
  33. data/ext/ca_obj_grid.c +455 -134
  34. data/ext/ca_obj_meld.c +172 -34
  35. data/ext/ca_obj_moncmp.c +23 -8
  36. data/ext/ca_obj_monop.c +113 -67
  37. data/ext/ca_obj_object.c +210 -52
  38. data/ext/ca_obj_record.c +15 -7
  39. data/ext/ca_obj_reduce.c +17 -8
  40. data/ext/ca_obj_refer.c +42 -35
  41. data/ext/ca_obj_remap.c +30 -9
  42. data/ext/ca_obj_repeat.c +9 -10
  43. data/ext/ca_obj_roll.c +31 -10
  44. data/ext/ca_obj_select.c +48 -14
  45. data/ext/ca_obj_select_axis.c +15 -47
  46. data/ext/ca_obj_shift.c +41 -7
  47. data/ext/ca_obj_stack.c +180 -34
  48. data/ext/ca_obj_stride.c +190 -134
  49. data/ext/ca_obj_string.c +80 -4
  50. data/ext/ca_obj_tile.c +32 -11
  51. data/ext/ca_obj_time.c +22 -6
  52. data/ext/ca_obj_timedelta.c +22 -6
  53. data/ext/ca_obj_transpose.c +13 -7
  54. data/ext/ca_obj_triop.c +26 -32
  55. data/ext/ca_obj_window.c +267 -74
  56. data/ext/ca_op_ipower.c +4 -4
  57. data/ext/ca_sort_kernels.h +6 -3
  58. data/ext/ca_sweep_engine.c +2 -2
  59. data/ext/caframe_csv_reader.c +80 -1
  60. data/ext/carray.h +214 -75
  61. data/ext/carray_access.c +200 -99
  62. data/ext/carray_address_basis.c +343 -87
  63. data/ext/carray_attribute.c +113 -6
  64. data/ext/carray_bincount.c +10 -8
  65. data/ext/carray_broadcast.c +13 -8
  66. data/ext/carray_build_flags.h +3 -0
  67. data/ext/carray_call_cfunc.c +108 -0
  68. data/ext/carray_cast.c +375 -96
  69. data/ext/carray_cast_func.rb +90 -20
  70. data/ext/carray_class.c +4 -3
  71. data/ext/carray_config.h +28 -0
  72. data/ext/carray_conversion.c +18 -9
  73. data/ext/carray_copy.c +51 -14
  74. data/ext/carray_core.c +150 -55
  75. data/ext/carray_count.c +56 -20
  76. data/ext/carray_element.c +81 -33
  77. data/ext/carray_factorize.c +138 -84
  78. data/ext/carray_generate.c +199 -52
  79. data/ext/carray_histogram.c +63 -18
  80. data/ext/carray_hold.c +12 -15
  81. data/ext/carray_index_classifier.c +388 -176
  82. data/ext/carray_internal.h +348 -29
  83. data/ext/carray_kernels_bincmp.c +4453 -0
  84. data/ext/carray_kernels_binop.c +11368 -0
  85. data/ext/carray_kernels_init.c +1734 -0
  86. data/ext/carray_kernels_moncmp.c +2104 -0
  87. data/ext/carray_kernels_monop.c +18369 -0
  88. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  89. data/ext/carray_kernels_reduce_boolean.c +406 -0
  90. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  91. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  92. data/ext/carray_kernels_reduce_variance.c +3849 -0
  93. data/ext/carray_kernels_scan.c +4302 -0
  94. data/ext/carray_kernels_search.c +33829 -0
  95. data/ext/carray_kernels_sort.c +10897 -0
  96. data/ext/carray_kernels_triop.c +1864 -0
  97. data/ext/carray_key_windows.c +220 -0
  98. data/ext/carray_lazy.c +170 -111
  99. data/ext/carray_mask.c +337 -94
  100. data/ext/carray_median_percentile.c +82 -28
  101. data/ext/carray_memory_view.c +669 -561
  102. data/ext/carray_operator.c +129 -52
  103. data/ext/carray_order.c +604 -77
  104. data/ext/carray_partition.c +28 -27
  105. data/ext/carray_random.c +85 -31
  106. data/ext/carray_round.c +273 -0
  107. data/ext/carray_scatter.c +87 -45
  108. data/ext/carray_segment.c +3 -3
  109. data/ext/carray_slab.c +123 -37
  110. data/ext/carray_sort.c +259 -51
  111. data/ext/carray_sort_kernel.c +5 -7
  112. data/ext/carray_struct.c +54 -61
  113. data/ext/carray_test.c +26 -5
  114. data/ext/carray_time_text.c +4 -2
  115. data/ext/carray_undef.c +4 -0
  116. data/ext/carray_utils.c +309 -66
  117. data/ext/mk_call_cfunc.rb +19 -2
  118. data/ext/mkkernel.rb +403 -350
  119. data/ext/ruby_carray.c +10 -4
  120. data/ext/version.h +4 -4
  121. data/lib/carray/attribute.rb +139 -22
  122. data/lib/carray/autoload_carray.rb +8 -31
  123. data/lib/carray/autoload_method_extension.rb +14 -5
  124. data/lib/carray/axis_group.rb +255 -106
  125. data/lib/carray/basics.rb +105 -17
  126. data/lib/carray/bincount_nd.rb +12 -2
  127. data/lib/carray/block_iterator.rb +126 -23
  128. data/lib/carray/boolean_reduce.rb +17 -17
  129. data/lib/carray/categorical.rb +195 -59
  130. data/lib/carray/categorical_iterator.rb +132 -29
  131. data/lib/carray/complex.rb +5 -5
  132. data/lib/carray/conditional.rb +67 -18
  133. data/lib/carray/const_string.rb +24 -5
  134. data/lib/carray/construct.rb +15 -11
  135. data/lib/carray/data_type_extension.rb +9 -1
  136. data/lib/carray/distance_limit.rb +77 -0
  137. data/lib/carray/fixlen_string.rb +48 -3
  138. data/lib/carray/frame/column_verbs.rb +71 -0
  139. data/lib/carray/frame/concat.rb +17 -3
  140. data/lib/carray/frame/convert.rb +69 -0
  141. data/lib/carray/frame/csv_parser.rb +111 -17
  142. data/lib/carray/frame/describe.rb +3 -2
  143. data/lib/carray/frame/frame.rb +252 -28
  144. data/lib/carray/frame/group.rb +603 -101
  145. data/lib/carray/frame/io.rb +117 -28
  146. data/lib/carray/frame/join.rb +312 -51
  147. data/lib/carray/frame/records.rb +83 -7
  148. data/lib/carray/frame/reshape.rb +42 -35
  149. data/lib/carray/frame/sort.rb +7 -19
  150. data/lib/carray/frame/verbs.rb +188 -48
  151. data/lib/carray/frame/windows.rb +117 -0
  152. data/lib/carray/frame.rb +2 -0
  153. data/lib/carray/fuse_source.rb +42 -6
  154. data/lib/carray/fusion.rb +40 -16
  155. data/lib/carray/histogram.rb +6 -1
  156. data/lib/carray/inspect.rb +31 -8
  157. data/lib/carray/iterator.rb +85 -7
  158. data/lib/carray/key_windows.rb +620 -0
  159. data/lib/carray/lazy.rb +217 -183
  160. data/lib/carray/mask_gap_fill.rb +63 -57
  161. data/lib/carray/meld_reduce.rb +12 -14
  162. data/lib/carray/methods/bin.rb +26 -18
  163. data/lib/carray/methods/bincount.rb +16 -0
  164. data/lib/carray/methods/bit_string.rb +1 -1
  165. data/lib/carray/methods/choose.rb +16 -3
  166. data/lib/carray/methods/composition.rb +3 -1
  167. data/lib/carray/methods/gather_nd.rb +14 -5
  168. data/lib/carray/methods/insert_block.rb +2 -2
  169. data/lib/carray/methods/is_between.rb +59 -0
  170. data/lib/carray/methods/is_in.rb +61 -37
  171. data/lib/carray/methods/join.rb +1 -6
  172. data/lib/carray/methods/lag.rb +49 -0
  173. data/lib/carray/methods/locate_addr.rb +47 -51
  174. data/lib/carray/methods/mode.rb +1 -1
  175. data/lib/carray/methods/nunique.rb +11 -6
  176. data/lib/carray/methods/resize.rb +3 -3
  177. data/lib/carray/methods/value_counts.rb +4 -11
  178. data/lib/carray/record_order.rb +3 -3
  179. data/lib/carray/rng.rb +3 -0
  180. data/lib/carray/runtime.rb +53 -8
  181. data/lib/carray/segment_iterator.rb +323 -88
  182. data/lib/carray/serialize.rb +45 -14
  183. data/lib/carray/slab_iterator.rb +50 -0
  184. data/lib/carray/stack.rb +15 -12
  185. data/lib/carray/string.rb +113 -5
  186. data/lib/carray/string_operation_extension.rb +21 -6
  187. data/lib/carray/struct.rb +9 -21
  188. data/lib/carray/struct_builder.rb +24 -11
  189. data/lib/carray/time.rb +819 -158
  190. data/lib/carray/window_iterator.rb +122 -16
  191. data/lib/carray.rb +7 -0
  192. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  193. data/yard-stubs/ca_obj_field.rb +4 -0
  194. data/yard-stubs/ca_obj_grid.rb +21 -10
  195. data/yard-stubs/ca_obj_refer.rb +2 -1
  196. data/yard-stubs/ca_obj_roll.rb +2 -2
  197. data/yard-stubs/ca_obj_shift.rb +5 -1
  198. data/yard-stubs/ca_obj_tile.rb +1 -1
  199. data/yard-stubs/ca_obj_transpose.rb +5 -4
  200. data/yard-stubs/ca_obj_window.rb +8 -3
  201. data/yard-stubs/carray_access.rb +6 -1
  202. data/yard-stubs/carray_attribute.rb +3 -6
  203. data/yard-stubs/carray_cast.rb +12 -0
  204. data/yard-stubs/carray_conversion.rb +7 -2
  205. data/yard-stubs/carray_copy.rb +4 -2
  206. data/yard-stubs/carray_count.rb +4 -4
  207. data/yard-stubs/carray_element.rb +17 -10
  208. data/yard-stubs/carray_generate.rb +21 -1
  209. data/yard-stubs/carray_mask.rb +32 -3
  210. data/yard-stubs/carray_math.rb +41 -5
  211. data/yard-stubs/carray_median_percentile.rb +5 -2
  212. data/yard-stubs/carray_memory_view.rb +40 -23
  213. data/yard-stubs/carray_order.rb +129 -22
  214. data/yard-stubs/carray_random.rb +13 -4
  215. data/yard-stubs/carray_scatter.rb +17 -10
  216. data/yard-stubs/carray_slab.rb +9 -2
  217. data/yard-stubs/carray_sort.rb +6 -5
  218. data/yard-stubs/carray_test.rb +5 -3
  219. metadata +25 -3
  220. data/lib/carray/arrow_tensor.rb +0 -401
  221. data/lib/carray/table.rb +0 -108
data/ext/ca_obj_monop.c CHANGED
@@ -222,6 +222,7 @@ ca_monop_setup (CAMonOp *ca, CArray *parent, uint16_t op_id)
222
222
  CAMonOp *
223
223
  ca_monop_new (CArray *parent, uint16_t op_id)
224
224
  {
225
+ /* check: the setup checks nothing */
225
226
  CAMonOp *ca = (CAMonOp *) ca_array_alloc(CA_OBJ_MONOP, parent->ndim);
226
227
  ca_monop_setup(ca, parent, op_id);
227
228
  return ca;
@@ -282,7 +283,7 @@ ca_monop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
282
283
  /* PUT: only writable-view op_ids accept writes (cast, or byte_swap
283
284
  involution). */
284
285
  if ( ! ca_monop_is_writable_view(ca->op_id) ) {
285
- rb_raise(rb_eRuntimeError, "CAMonOp is read-only (xfer_index PUT)");
286
+ ca_refuse_read_only();
286
287
  }
287
288
  {
288
289
  volatile VALUE holder;
@@ -337,16 +338,14 @@ ca_size_t ca_monop_materialise_call_count = 0;
337
338
  #define CA_MAX_LAZY_DEPTH 256
338
339
  #endif
339
340
 
340
- /* Whether an op can raise on a cell, so that a masked cell must be
341
- skipped rather than computed: any op on an object cell (it calls Ruby),
342
- and an integer reciprocal (zero raises). */
341
+ /* Whether a cast from `from` to `to` raises on a NaN cell: a float or
342
+ complex value has no integer for NaN. */
343
343
  static int
344
- ca_monop_is_trapping (uint16_t op_id, int8_t dt)
344
+ ca_monop_cast_refuses_nan (int8_t from, int8_t to)
345
345
  {
346
- if ( dt == CA_OBJECT ) {
347
- return 1;
348
- }
349
- return ( op_id == CA_MONOP_RCP && dt >= CA_INT8 && dt <= CA_UINT64 );
346
+ int from_float = ( from == CA_FLOAT32 || from == CA_FLOAT64 ||
347
+ from == CA_CMPLX64 || from == CA_CMPLX128 );
348
+ return from_float && to >= CA_INT8 && to <= CA_UINT64;
350
349
  }
351
350
 
352
351
  /* Apply a single op in-place on `data` (src == dst, unit element stride).
@@ -371,12 +370,19 @@ apply_monop_in_place (uint16_t op_id, int8_t cur_dt,
371
370
  fn(slab_n, mask, (char *) data, 1, (char *) data, 1);
372
371
  }
373
372
 
374
- /* Minimal CArray-shaped stub used as ca_cast_block's data_type-only carrier.
375
- ca_cast_block only reads data_type to index into ca_cast_func_table. */
376
- typedef struct {
377
- int16_t obj_type;
378
- int8_t data_type;
379
- } ca_data_type_stub_t;
373
+ /* A zeroed CArray carrying only data_type and bytes, handed to
374
+ ca_cast_block in place of a real array. It is a whole CArray, not a
375
+ shorter struct cast to one: ca_cast_block reads data_type through a
376
+ CArray pointer, and a fixlen cast reads bytes as well. */
377
+ static CArray
378
+ ca_data_type_stub (int8_t data_type)
379
+ {
380
+ CArray stub;
381
+ memset(&stub, 0, sizeof(stub));
382
+ stub.data_type = data_type;
383
+ stub.bytes = ca_sizeof[data_type];
384
+ return stub;
385
+ }
380
386
 
381
387
  /* Pull leaf into the output buffer. Three paths:
382
388
 
@@ -429,8 +435,8 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
429
435
  if ( has_cast_at_leaf ) {
430
436
  /* Path 2: cast scratch → data with output layout in target data_type. */
431
437
  int8_t target_dt = (int8_t)(innermost_op - CA_MONOP_CAST_BASE);
432
- ca_data_type_stub_t src_stub = { 0, leaf->data_type };
433
- ca_data_type_stub_t dst_stub = { 0, target_dt };
438
+ CArray src_stub = ca_data_type_stub(leaf->data_type);
439
+ CArray dst_stub = ca_data_type_stub(target_dt);
434
440
  ca_cast_block(slab_n, &src_stub, scratch, &dst_stub, data);
435
441
  result_dt = target_dt;
436
442
  } else {
@@ -476,7 +482,27 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
476
482
  v = ALLOCV_N(char, holder, n * parent_bytes);
477
483
  if ( dir == CA_XFER_GET ) {
478
484
  ca_lazy_req_pull(mo->parent, req, v, CA_XFER_GET);
479
- if ( is_cast ) {
485
+ if ( is_cast && ca_monop_cast_refuses_nan(mo->parent->data_type,
486
+ mo->data_type) ) {
487
+ /* The cast raises on a NaN cell it is asked to convert; a masked
488
+ cell (mask_invalid keeps the NaN under the mask) is skipped.
489
+ The mask is copied off the arena first so a raise leaves no
490
+ arena slot held. */
491
+ CArray *parent = mo->parent;
492
+ boolean8_t *arena_mask = ca_lazy_req_mask(req, 1, &parent);
493
+ boolean8_t *m = NULL;
494
+ volatile VALUE mholder = 0;
495
+ if ( arena_mask ) {
496
+ m = ALLOCV_N(boolean8_t, mholder, n);
497
+ memcpy(m, arena_mask, n);
498
+ ca_lazy_arena_release(arena_mask);
499
+ }
500
+ ca_cast_block_with_mask(n, mo->parent, v, (CArray *) mo, data, m);
501
+ if ( m ) {
502
+ ALLOCV_END(mholder);
503
+ }
504
+ }
505
+ else if ( is_cast ) {
480
506
  ca_cast_block(n, mo->parent, v, (CArray *) mo, data);
481
507
  }
482
508
  else {
@@ -503,7 +529,7 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
503
529
  /* Writable-view op_id PUT/GET is handled above. Below: non-writable
504
530
  monop/monfunc. PUT is read-only. */
505
531
  if ( dir != CA_XFER_GET ) {
506
- rb_raise(rb_eRuntimeError, "CAMonOp is read-only (xfer_stride PUT)");
532
+ ca_refuse_read_only();
507
533
  }
508
534
  ca_monop_materialise_call_count++;
509
535
 
@@ -536,6 +562,30 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
536
562
  chain_apply_count = chain_len - 1;
537
563
  }
538
564
 
565
+ /* The chain runs in place, so the buffer must hold slab_n cells of the
566
+ widest type along it: the leaf, or a step that widens before a later
567
+ one narrows (cmplx128.lazy.abs pulls 16-byte cells and answers 8-byte
568
+ ones). When that is wider than the output, run in a scratch and copy
569
+ the answer into `data` at the end. */
570
+ void *out = data;
571
+ void *work = NULL;
572
+ {
573
+ ca_size_t max_bytes = leaf->bytes;
574
+ int8_t dt = leaf->data_type;
575
+ for ( i = chain_len - 1; i >= 0; i-- ) {
576
+ dt = ca_monop_is_cast(op_chain[i])
577
+ ? (int8_t)(op_chain[i] - CA_MONOP_CAST_BASE)
578
+ : ca_lazy_promote_monop(op_chain[i], dt);
579
+ if ( ca_sizeof[dt] > max_bytes ) max_bytes = ca_sizeof[dt];
580
+ }
581
+ if ( max_bytes > mo->bytes ) {
582
+ work = ( leaf->data_type == CA_OBJECT )
583
+ ? ca_lazy_arena_acquire_object((slab_n * max_bytes + sizeof(VALUE) - 1) / sizeof(VALUE))
584
+ : ca_lazy_arena_acquire(slab_n * max_bytes);
585
+ data = work;
586
+ }
587
+ }
588
+
539
589
  /* === 2. pull leaf into output buffer (with optional cast) === */
540
590
  cur_dt = pull_leaf_with_optional_cast(
541
591
  leaf,
@@ -557,8 +607,8 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
557
607
  ca_size_t src_bytes = ca_sizeof[cur_dt];
558
608
  ca_size_t total_bytes = slab_n * src_bytes;
559
609
  void *scratch;
560
- ca_data_type_stub_t src_stub = { 0, cur_dt };
561
- ca_data_type_stub_t dst_stub = { 0, target_dt };
610
+ CArray src_stub = ca_data_type_stub(cur_dt);
611
+ CArray dst_stub = ca_data_type_stub(target_dt);
562
612
 
563
613
  /* Copy current data → scratch (in src data_type), then cast scratch
564
614
  → data (in target data_type). Required because in-place cast
@@ -573,18 +623,22 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
573
623
  ca_lazy_arena_release(scratch);
574
624
  cur_dt = target_dt;
575
625
  } else {
576
- if ( ! mask_taken && ca_monop_is_trapping(op_id, cur_dt) ) {
626
+ if ( ! mask_taken && ca_op_raises_on_cell(CA_OP_KIND_MONOP, op_id, cur_dt) ) {
577
627
  slab_mask = ca_lazy_req_mask(req, 1, &leaf);
578
628
  mask_taken = 1;
579
629
  }
580
630
  apply_monop_in_place(op_id, cur_dt, slab_n, data,
581
- ca_monop_is_trapping(op_id, cur_dt) ? slab_mask : NULL);
631
+ ca_op_raises_on_cell(CA_OP_KIND_MONOP, op_id, cur_dt) ? slab_mask : NULL);
582
632
  cur_dt = ca_lazy_promote_monop(op_id, cur_dt);
583
633
  }
584
634
  }
585
635
  if ( slab_mask ) {
586
636
  ca_lazy_arena_release(slab_mask);
587
637
  }
638
+ if ( work ) {
639
+ memcpy(out, work, slab_n * mo->bytes);
640
+ ca_lazy_arena_release(work);
641
+ }
588
642
  }
589
643
 
590
644
  static void
@@ -638,16 +692,7 @@ ca_monop_func_allocate (void *ap)
638
692
  if ( ca_monop_is_writable_view(ca->op_id) ) {
639
693
  ca_attach(ca->parent);
640
694
  }
641
- ca->ptr = xmalloc(ca_length(ca));
642
- /* CA_OBJECT initialisation for GC safety. */
643
- if ( ca->data_type == CA_OBJECT ) {
644
- VALUE *p = (VALUE *) ca->ptr;
645
- VALUE zero = SIZE2NUM(0);
646
- ca_size_t i;
647
- for ( i = 0; i < ca->elements; i++ ) {
648
- *p++ = zero;
649
- }
650
- }
695
+ ca->ptr = ca_alloc_cells((CArray *) ca);
651
696
  }
652
697
 
653
698
  static void
@@ -676,22 +721,19 @@ ca_monop_func_attach (void *ap)
676
721
  Mirrors CAFake.attach. */
677
722
  if ( ca_monop_is_writable_view(ca->op_id) ) {
678
723
  ca_attach(ca->parent);
679
- ca->ptr = xmalloc(ca_length(ca));
680
- /* CA_OBJECT initialisation for GC safety (mirrors CAFake.allocate):
681
- zero-init the VALUE cells before any potential GC can observe
682
- them. */
683
- if ( ca->data_type == CA_OBJECT ) {
684
- VALUE *p = (VALUE *) ca->ptr;
685
- VALUE zero = SIZE2NUM(0);
686
- ca_size_t i;
687
- for ( i = 0; i < ca->elements; i++ ) {
688
- *p++ = zero;
689
- }
690
- }
724
+ ca->ptr = ca_alloc_cells((CArray *) ca);
691
725
  if ( ca_monop_is_cast(ca->op_id) ) {
692
- /* Cast parent.ptr (parent data_type) → ca.ptr (cast target data_type). */
693
- ca_cast_block(ca->elements, ca->parent, ca->parent->ptr,
694
- (CArray *) ca, ca->ptr);
726
+ /* Cast parent.ptr (parent data_type) → ca.ptr (cast target data_type).
727
+ A cast that raises on NaN skips the masked cells; the parent's
728
+ mask is attached with the parent. A raise here is undone by
729
+ ca_attach, which calls the detach slot once ca->ptr is set. */
730
+ boolean8_t *m = NULL;
731
+ if ( ca_monop_cast_refuses_nan(ca->parent->data_type, ca->data_type)
732
+ && ca_has_mask(ca->parent) ) {
733
+ m = (boolean8_t *) ca->parent->mask->ptr;
734
+ }
735
+ ca_cast_block_with_mask(ca->elements, ca->parent, ca->parent->ptr,
736
+ (CArray *) ca, ca->ptr, m);
695
737
  }
696
738
  else {
697
739
  /* byte_swap: memcpy parent.ptr → ca.ptr (same data_type same bytes),
@@ -702,19 +744,11 @@ ca_monop_func_attach (void *ap)
702
744
  return;
703
745
  }
704
746
 
705
- ca->ptr = xmalloc(ca_length(ca));
747
+ ca->ptr = ca_alloc_cells((CArray *) ca);
706
748
 
707
749
  s = ca->bytes;
708
750
  for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
709
751
  for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
710
- /* CA_OBJECT cells are VALUEs and this buffer is about to be marked as
711
- soon as the view is, so it must not be handed to the GC as raw
712
- xmalloc garbage. */
713
- if ( ca->data_type == CA_OBJECT ) {
714
- VALUE *p = (VALUE *) ca->ptr;
715
- ca_size_t i;
716
- for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
717
- }
718
752
  ca_monop_func_xfer_stride(ca, starts, ca->dim, native, ca->ptr, CA_XFER_GET);
719
753
  }
720
754
 
@@ -782,7 +816,7 @@ ca_monop_func_fill_data (void *ap, void *ptr)
782
816
  data_type (= same bytes for byte_swap, reverse-cast for cast).
783
817
  Non-writable monop/monfunc remains read-only. */
784
818
  if ( ! ca_monop_is_writable_view(ca->op_id) ) {
785
- rb_raise(rb_eRuntimeError, "CAMonOp is read-only (fill_data)");
819
+ ca_refuse_read_only();
786
820
  }
787
821
  {
788
822
  int is_cast = ca_monop_is_cast(ca->op_id);
@@ -915,8 +949,9 @@ rb_ca_monop_build (VALUE cary, uint16_t op_id)
915
949
  with no boolean kernel reads the 0/1 storage in int64. */
916
950
  if ( parent_dt == CA_BOOLEAN && ! ca_monop_is_writable_view(op_id) ) {
917
951
  if ( ca_monop_is_widening(op_id) ) {
918
- rb_raise(rb_eCADataTypeError,
919
- "invalid data type for monop (not implemented)");
952
+ /* the eager monop reads a boolean with no kernel as int64 (0/1), and
953
+ names that type in its refusal */
954
+ ca_op_refuse_unimplemented(cary, CA_INT64, rb_eCADataTypeError);
920
955
  }
921
956
  if ( ca_monop_kernel_lookup(op_id, CA_BOOLEAN) == ca_monop_not_implement ) {
922
957
  target = rb_ca_monop_new(target, CA_MONOP_CAST_BASE + CA_INT64);
@@ -934,8 +969,13 @@ rb_ca_monop_build (VALUE cary, uint16_t op_id)
934
969
  ( ca_monop_kernel_lookup(op_id, kernel_in_dt) == ca_monop_not_implement ||
935
970
  ( parent_dt == CA_FIXLEN &&
936
971
  ca_monop_kernel_lookup(op_id, CA_FIXLEN) == ca_monop_not_implement ) ) ) {
937
- rb_raise(rb_eCADataTypeError,
938
- "invalid data type for monop (not implemented)");
972
+ /* the eager monop takes an integer to float64 before it looks for the
973
+ kernel, and names the type it looked in */
974
+ ca_op_refuse_unimplemented(cary,
975
+ parent_dt == CA_FIXLEN ? CA_FIXLEN
976
+ : RTEST(rb_ca_is_integer_type(cary)) ? CA_FLOAT64
977
+ : kernel_in_dt,
978
+ rb_eCADataTypeError);
939
979
  }
940
980
 
941
981
  /* Insert cast node if the kernel wants a different input data_type. */
@@ -952,6 +992,7 @@ rb_ca_monop_build (VALUE cary, uint16_t op_id)
952
992
  static VALUE
953
993
  rb_ca_monop_s_build (VALUE klass, VALUE cary, VALUE op_id_val)
954
994
  {
995
+ /* fits: an operation number from the library's own table */
955
996
  uint16_t op_id = (uint16_t) NUM2UINT(op_id_val);
956
997
  (void) klass;
957
998
  return rb_ca_monop_build(cary, op_id);
@@ -976,7 +1017,7 @@ rb_ca_monop_trapping (VALUE self)
976
1017
  CAMonOp *mo;
977
1018
  TypedData_Get_Struct(self, CAMonOp, &camonop_data_type, mo);
978
1019
  return ( ! ca_monop_is_cast(mo->op_id)
979
- && ca_monop_is_trapping(mo->op_id, mo->parent->data_type) )
1020
+ && ca_op_raises_on_cell(CA_OP_KIND_MONOP, mo->op_id, mo->parent->data_type) )
980
1021
  ? Qtrue : Qfalse;
981
1022
  }
982
1023
 
@@ -996,6 +1037,7 @@ rb_ca_monop_op_id (VALUE self)
996
1037
  because in-place chain eval allocates no scratch; only attach (xfer_all
997
1038
  → output) and the parent's own xfer_stride alloc, neither of which we
998
1039
  count here. */
1040
+ #ifdef CARRAY_DEV_BUILD
999
1041
  static VALUE
1000
1042
  rb_ca_monop_s_reset_scratch_counter (VALUE klass)
1001
1043
  {
@@ -1021,6 +1063,7 @@ rb_ca_monop_s_materialise_count (VALUE klass)
1021
1063
  {
1022
1064
  return SIZE2NUM(ca_monop_materialise_call_count);
1023
1065
  }
1066
+ #endif /* CARRAY_DEV_BUILD */
1024
1067
 
1025
1068
  static VALUE
1026
1069
  rb_ca_monop_s_allocate (VALUE klass)
@@ -1108,6 +1151,7 @@ Init_ca_obj_monop (void)
1108
1151
  rb_define_const(rb_cCAMonOp, "OP_RAD_PI", INT2NUM(CA_MONOP_RAD_PI));
1109
1152
  rb_define_const(rb_cCAMonOp, "OP_SIGN", INT2NUM(CA_MONOP_SIGN));
1110
1153
  rb_define_const(rb_cCAMonOp, "OP_ARG_I", INT2NUM(CA_MONOP_ARG_I));
1154
+ rb_define_const(rb_cCAMonOp, "OP_BYTE_SWAP", INT2NUM(CA_MONOP_BYTE_SWAP));
1111
1155
  rb_define_const(rb_cCAMonOp, "CAST_BASE", INT2NUM(CA_MONOP_CAST_BASE));
1112
1156
 
1113
1157
  rb_define_alloc_func(rb_cCAMonOp, rb_ca_monop_s_allocate);
@@ -1119,10 +1163,10 @@ Init_ca_obj_monop (void)
1119
1163
  rb_define_singleton_method(rb_cCAMonOp, "__build__",
1120
1164
  rb_ca_monop_s_build, 2);
1121
1165
 
1122
- /* Lazy-view predicate exposed on CArray (covers CAMonOp and
1123
- CALazyMarker). */
1124
- rb_define_method(rb_cCArray, "__lazy_view__?",
1125
- rb_ca_is_lazy_view_p, 0);
1166
+ /* Lazy-view predicate on CArray (covers CAMonOp and CALazyMarker).
1167
+ Protected: the lazy dispatch asks it of the other operands. */
1168
+ rb_define_protected_method(rb_cCArray, "lazy_view?",
1169
+ rb_ca_is_lazy_view_p, 0);
1126
1170
 
1127
1171
  /* op_id accessor for inspect / dump_tree consumers in Ruby. */
1128
1172
  rb_define_method(rb_cCAMonOp, "__op_id__",
@@ -1130,6 +1174,7 @@ Init_ca_obj_monop (void)
1130
1174
  rb_define_method(rb_cCAMonOp, "__trapping__", rb_ca_monop_trapping, 0);
1131
1175
 
1132
1176
  /* Test instrumentation (not user-facing API). */
1177
+ #ifdef CARRAY_DEV_BUILD
1133
1178
  rb_define_singleton_method(rb_cCAMonOp, "__reset_scratch_counter__",
1134
1179
  rb_ca_monop_s_reset_scratch_counter, 0);
1135
1180
  rb_define_singleton_method(rb_cCAMonOp, "__scratch_count__",
@@ -1138,4 +1183,5 @@ Init_ca_obj_monop (void)
1138
1183
  rb_ca_monop_s_reset_materialise_counter, 0);
1139
1184
  rb_define_singleton_method(rb_cCAMonOp, "__materialise_count__",
1140
1185
  rb_ca_monop_s_materialise_count, 0);
1186
+ #endif
1141
1187
  }