carray 3.0.0 → 3.0.2

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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +845 -3
  4. data/{NEWS.md → CHANGELOG.v1.md} +1 -1
  5. data/README.md +33 -33
  6. data/carray.gemspec +12 -8
  7. data/ext/ca_axis_dispatch.c +33 -4
  8. data/ext/ca_axis_group.c +256 -114
  9. data/ext/ca_binop_dispatch.c +5 -6
  10. data/ext/ca_binop_dispatch.h +0 -7
  11. data/ext/ca_categorical_iterator.c +121 -67
  12. data/ext/ca_for_each_element.h +6 -8
  13. data/ext/ca_group_iter.c +1 -1
  14. data/ext/ca_kernel_iterator.c +386 -74
  15. data/ext/ca_kernel_iterator.h +145 -45
  16. data/ext/ca_obj_array.c +62 -20
  17. data/ext/ca_obj_bincmp.c +57 -27
  18. data/ext/ca_obj_binop.c +86 -30
  19. data/ext/ca_obj_block.c +4 -4
  20. data/ext/ca_obj_const_string.c +88 -27
  21. data/ext/ca_obj_face.c +104 -0
  22. data/ext/ca_obj_face.h +48 -1
  23. data/ext/ca_obj_fake.c +11 -0
  24. data/ext/ca_obj_fixlen_string.c +21 -6
  25. data/ext/ca_obj_grid.c +11 -3
  26. data/ext/ca_obj_meld.c +125 -22
  27. data/ext/ca_obj_moncmp.c +33 -14
  28. data/ext/ca_obj_monop.c +19 -7
  29. data/ext/ca_obj_object.c +107 -4
  30. data/ext/ca_obj_record.c +3 -1
  31. data/ext/ca_obj_refer.c +30 -6
  32. data/ext/ca_obj_roll.c +13 -5
  33. data/ext/ca_obj_select.c +49 -34
  34. data/ext/ca_obj_select_axis.c +16 -6
  35. data/ext/ca_obj_shift.c +3 -3
  36. data/ext/ca_obj_stack.c +3 -8
  37. data/ext/ca_obj_stride.c +178 -24
  38. data/ext/ca_obj_string.c +11 -5
  39. data/ext/ca_obj_tile.c +13 -5
  40. data/ext/ca_obj_time.c +3 -1
  41. data/ext/ca_obj_timedelta.c +3 -1
  42. data/ext/ca_obj_transpose.c +1 -1
  43. data/ext/ca_obj_triop.c +62 -23
  44. data/ext/ca_obj_window.c +94 -19
  45. data/ext/ca_op_cmplx64.h +123 -0
  46. data/ext/ca_op_ipower.c +1 -5
  47. data/ext/ca_rng_normal.h +42 -0
  48. data/ext/ca_rng_xoshiro256pp.h +105 -0
  49. data/ext/ca_sort_kernels.h +5 -5
  50. data/ext/ca_sweep_engine.c +362 -155
  51. data/ext/ca_sweep_engine.h +39 -9
  52. data/ext/ca_transform_common.c +7 -0
  53. data/ext/carray.h +148 -132
  54. data/ext/carray_access.c +83 -49
  55. data/ext/carray_address_basis.c +590 -0
  56. data/ext/carray_bincount.c +8 -8
  57. data/ext/carray_broadcast.c +102 -9
  58. data/ext/carray_build_flags.h +3 -0
  59. data/ext/carray_call_cfunc.c +2010 -335
  60. data/ext/carray_call_cfunc.h +153 -0
  61. data/ext/carray_cast.c +184 -72
  62. data/ext/carray_conversion.c +22 -22
  63. data/ext/carray_copy.c +67 -32
  64. data/ext/carray_core.c +140 -13
  65. data/ext/carray_count.c +10 -11
  66. data/ext/carray_element.c +7 -7
  67. data/ext/carray_factorize.c +72 -51
  68. data/ext/carray_hold.c +1 -1
  69. data/ext/carray_index_classifier.c +7 -21
  70. data/ext/carray_internal.h +36 -3
  71. data/ext/carray_kernels_bincmp.c +1 -0
  72. data/ext/carray_kernels_binop.c +320 -298
  73. data/ext/carray_kernels_init.c +1095 -0
  74. data/ext/carray_kernels_map.c +1 -0
  75. data/ext/carray_kernels_moncmp.c +1 -0
  76. data/ext/carray_kernels_monop.c +412 -411
  77. data/ext/carray_kernels_reduce_aggregate.c +256 -87
  78. data/ext/carray_kernels_reduce_boolean.c +1 -0
  79. data/ext/carray_kernels_reduce_cumulative.c +324 -54
  80. data/ext/carray_kernels_reduce_extreme.c +605 -58
  81. data/ext/carray_kernels_reduce_variance.c +1 -0
  82. data/ext/carray_kernels_scan.c +5 -4
  83. data/ext/carray_kernels_search.c +187 -106
  84. data/ext/carray_kernels_sort.c +1 -0
  85. data/ext/carray_kernels_triop.c +1 -0
  86. data/ext/carray_lazy.c +175 -5
  87. data/ext/carray_loop.c +7 -1
  88. data/ext/carray_mask.c +29 -13
  89. data/ext/carray_median_percentile.c +56 -1
  90. data/ext/carray_memory_view.c +46 -21
  91. data/ext/carray_operator.c +50 -53
  92. data/ext/carray_order.c +1 -1
  93. data/ext/carray_partition.c +4 -4
  94. data/ext/carray_random.c +391 -47
  95. data/ext/carray_scatter.c +1 -1
  96. data/ext/carray_slab.c +13 -0
  97. data/ext/carray_sort.c +22 -24
  98. data/ext/carray_sort_kernel.c +10 -10
  99. data/ext/carray_test.c +1 -1
  100. data/ext/extconf.rb +18 -0
  101. data/ext/mk_call_cfunc.rb +323 -145
  102. data/ext/mkkernel.rb +651 -129
  103. data/ext/ruby_carray.c +28 -17
  104. data/ext/version.h +4 -4
  105. data/lib/carray/autoload_carray.rb +24 -3
  106. data/lib/carray/autoload_method_extension.rb +14 -1
  107. data/lib/carray/axis_group.rb +84 -7
  108. data/lib/carray/basics.rb +63 -59
  109. data/lib/carray/bincount_nd.rb +12 -12
  110. data/lib/carray/block_iterator.rb +111 -25
  111. data/lib/carray/categorical.rb +154 -37
  112. data/lib/carray/categorical_iterator.rb +300 -151
  113. data/lib/carray/conditional.rb +14 -14
  114. data/lib/carray/const_string.rb +131 -27
  115. data/lib/carray/construct.rb +61 -0
  116. data/lib/carray/core_extensions.rb +62 -5
  117. data/lib/carray/data_type_extension.rb +21 -1
  118. data/lib/carray/data_type_limits.rb +91 -0
  119. data/lib/carray/fixlen_string.rb +1 -1
  120. data/lib/carray/frame/concat.rb +3 -3
  121. data/lib/carray/frame/convert.rb +1 -1
  122. data/lib/carray/frame/csv_parser.rb +11 -4
  123. data/lib/carray/frame/frame.rb +85 -21
  124. data/lib/carray/frame/group.rb +36 -3
  125. data/lib/carray/frame/io.rb +224 -15
  126. data/lib/carray/frame/records.rb +18 -4
  127. data/lib/carray/frame/sort.rb +1 -1
  128. data/lib/carray/frame/verbs.rb +32 -12
  129. data/lib/carray/fuse_source.rb +123 -0
  130. data/lib/carray/fusion.rb +218 -0
  131. data/lib/carray/histogram.rb +16 -16
  132. data/lib/carray/inspect.rb +43 -16
  133. data/lib/carray/iterator.rb +147 -3
  134. data/lib/carray/lazy.rb +88 -73
  135. data/lib/carray/mask_gap_fill.rb +3 -1
  136. data/lib/carray/meld_reduce.rb +2 -2
  137. data/lib/carray/methods/align_addr.rb +1 -1
  138. data/lib/carray/methods/composition.rb +1 -1
  139. data/lib/carray/methods/discovery_along.rb +74 -0
  140. data/lib/carray/methods/factorize.rb +50 -0
  141. data/lib/carray/methods/is_in.rb +25 -14
  142. data/lib/carray/methods/locate_addr.rb +81 -3
  143. data/lib/carray/methods/mask_duplicates.rb +36 -2
  144. data/lib/carray/methods/meshgrid.rb +4 -5
  145. data/lib/carray/methods/mode.rb +2 -2
  146. data/lib/carray/methods/nunique.rb +23 -2
  147. data/lib/carray/methods/repeat.rb +110 -0
  148. data/lib/carray/methods/snap.rb +7 -2
  149. data/lib/carray/methods/unique.rb +44 -4
  150. data/lib/carray/methods/value_counts.rb +2 -2
  151. data/lib/carray/rng.rb +86 -0
  152. data/lib/carray/runtime.rb +0 -19
  153. data/lib/carray/slab_iterator.rb +77 -19
  154. data/lib/carray/string_operation_extension.rb +10 -6
  155. data/lib/carray/time.rb +906 -491
  156. data/lib/carray/window_iterator.rb +421 -27
  157. data/lib/carray.rb +7 -5
  158. data/yard-stubs/ca_obj_array.rb +385 -0
  159. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  160. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  161. data/yard-stubs/ca_obj_block.rb +68 -0
  162. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  163. data/yard-stubs/ca_obj_fake.rb +31 -0
  164. data/yard-stubs/ca_obj_farray.rb +32 -0
  165. data/yard-stubs/ca_obj_field.rb +45 -0
  166. data/yard-stubs/ca_obj_grid.rb +35 -0
  167. data/yard-stubs/ca_obj_refer.rb +72 -0
  168. data/yard-stubs/ca_obj_roll.rb +45 -0
  169. data/yard-stubs/ca_obj_shift.rb +43 -0
  170. data/yard-stubs/ca_obj_stride.rb +181 -0
  171. data/yard-stubs/ca_obj_tile.rb +29 -0
  172. data/yard-stubs/ca_obj_transpose.rb +40 -0
  173. data/yard-stubs/ca_obj_window.rb +57 -0
  174. data/yard-stubs/carray_access.rb +131 -0
  175. data/yard-stubs/carray_attribute.rb +246 -0
  176. data/yard-stubs/carray_broadcast.rb +37 -0
  177. data/yard-stubs/carray_cast.rb +489 -0
  178. data/yard-stubs/carray_class.rb +65 -0
  179. data/yard-stubs/carray_conversion.rb +76 -0
  180. data/yard-stubs/carray_copy.rb +79 -0
  181. data/yard-stubs/carray_core.rb +34 -0
  182. data/yard-stubs/carray_count.rb +84 -0
  183. data/yard-stubs/carray_element.rb +108 -0
  184. data/yard-stubs/carray_generate.rb +66 -0
  185. data/yard-stubs/carray_lazy.rb +228 -0
  186. data/yard-stubs/carray_loop.rb +140 -0
  187. data/yard-stubs/carray_mask.rb +259 -0
  188. data/yard-stubs/carray_math.rb +1615 -0
  189. data/yard-stubs/carray_mathfunc.rb +45 -0
  190. data/yard-stubs/carray_median_percentile.rb +103 -0
  191. data/yard-stubs/carray_memory_view.rb +163 -0
  192. data/yard-stubs/carray_order.rb +252 -0
  193. data/yard-stubs/carray_random.rb +89 -0
  194. data/yard-stubs/carray_scatter.rb +106 -0
  195. data/yard-stubs/carray_slab.rb +59 -0
  196. data/yard-stubs/carray_sort.rb +165 -0
  197. data/yard-stubs/carray_test.rb +85 -0
  198. data/yard-stubs/carray_undef.rb +64 -0
  199. data/yard-stubs/carray_utils.rb +97 -0
  200. data/yard-stubs/ruby_carray.rb +193 -0
  201. metadata +67 -10
  202. data/ext/ca_obj_unbound_repeat.c +0 -496
@@ -12,6 +12,22 @@
12
12
 
13
13
  #include "carray.h"
14
14
  #include "carray_internal.h" /* ca_lazy_arena_* */
15
+
16
+ /* Operand scratch for a chunked / gathered kernel run.
17
+
18
+ For CA_OBJECT the scratch holds VALUEs, and an object-lane kernel
19
+ calls rb_funcall per cell -- a collection in the middle would free
20
+ whatever the gather pulled in from a lazy operand, since those cells
21
+ exist nowhere else. The object form of the arena acquire keeps the
22
+ slot marked until it is released. */
23
+ static void *
24
+ ca_op_acquire_operand_scratch (CArray *ca, ca_size_t n_elements)
25
+ {
26
+ return ( ca->data_type == CA_OBJECT )
27
+ ? ca_lazy_arena_acquire_object(n_elements)
28
+ : ca_lazy_arena_acquire(n_elements * ca->bytes);
29
+ }
30
+
15
31
  #include "ca_obj_face.h" /* ca_face_reconcile_comparison (comparison Face gate) */
16
32
 
17
33
  VALUE rb_mCAMath;
@@ -324,8 +340,6 @@ rb_ca_call_monop (VALUE self, ca_monop_func_t func[])
324
340
  }
325
341
  }
326
342
 
327
- out = rb_ca_rewrap_unbound_repeat(self, out);
328
-
329
343
  return out;
330
344
  }
331
345
 
@@ -479,8 +493,6 @@ rb_ca_call_monop_typed (VALUE self, ca_monop_func_t func[],
479
493
  }
480
494
  }
481
495
 
482
- out = rb_ca_rewrap_unbound_repeat(self, out);
483
-
484
496
  return out;
485
497
  }
486
498
 
@@ -543,7 +555,7 @@ kleene_gather_bool (CArray *ca, boolean8_t *vbuf, boolean8_t *mbuf, ca_size_t n)
543
555
  undetermined cells (U|U, U&U, U|F, U&T) keep the blind mask. All other
544
556
  cells were already correct from the value kernel.
545
557
 
546
- Gate: boolean dtype + output has a mask (else no-op -- the hot path is
558
+ Gate: boolean data type + output has a mask (else no-op -- the hot path is
547
559
  untouched, integer bitwise stays blind). `out` is a fresh entity, so
548
560
  out->ptr / out->mask->ptr are writable without attach. */
549
561
  VALUE
@@ -621,7 +633,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
621
633
  ca_size_t i1, i2, i3;
622
634
  int fast_path;
623
635
 
624
- /* do implicit casting and resolving unbound repeat array */
636
+ /* do implicit casting */
625
637
  rb_ca_cast_self_or_other(&self, &other);
626
638
 
627
639
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
@@ -870,8 +882,6 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
870
882
  }
871
883
  }
872
884
 
873
- out = rb_ca_rewrap_unbound_repeat(self, out);
874
- out = rb_ca_rewrap_unbound_repeat(other, out);
875
885
  return out;
876
886
  }
877
887
  /* else fall through to the use_chunked / ALLOCV path */
@@ -907,8 +917,6 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
907
917
  if ( a2 ) { ca_detach(ca2); } else { ALLOCV_END(h2); }
908
918
  if ( a1 ) { ca_detach(ca1); } else { ALLOCV_END(h1); }
909
919
 
910
- out = rb_ca_rewrap_unbound_repeat(self, out);
911
- out = rb_ca_rewrap_unbound_repeat(other, out);
912
920
  return out;
913
921
  }
914
922
 
@@ -936,7 +944,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
936
944
  p1_src = (char *) ca1->ptr;
937
945
  attached1 = 1;
938
946
  } else {
939
- s1_arena = ca_lazy_arena_acquire(ca1->bytes);
947
+ s1_arena = ca_op_acquire_operand_scratch(ca1, 1);
940
948
  ca_xfer_all(ca1, s1_arena, CA_XFER_GET);
941
949
  p1_src = (char *) s1_arena;
942
950
  }
@@ -945,7 +953,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
945
953
  p1_src = (char *) ca1->ptr;
946
954
  attached1 = 1;
947
955
  } else {
948
- s1_arena = ca_lazy_arena_acquire(chunk_n * ca1->bytes);
956
+ s1_arena = ca_op_acquire_operand_scratch(ca1, chunk_n);
949
957
  p1_src = (char *) s1_arena;
950
958
  gather_per_chunk1 = 1;
951
959
  }
@@ -957,7 +965,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
957
965
  p2_src = (char *) ca2->ptr;
958
966
  attached2 = 1;
959
967
  } else {
960
- s2_arena = ca_lazy_arena_acquire(ca2->bytes);
968
+ s2_arena = ca_op_acquire_operand_scratch(ca2, 1);
961
969
  ca_xfer_all(ca2, s2_arena, CA_XFER_GET);
962
970
  p2_src = (char *) s2_arena;
963
971
  }
@@ -966,7 +974,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
966
974
  p2_src = (char *) ca2->ptr;
967
975
  attached2 = 1;
968
976
  } else {
969
- s2_arena = ca_lazy_arena_acquire(chunk_n * ca2->bytes);
977
+ s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
970
978
  p2_src = (char *) s2_arena;
971
979
  gather_per_chunk2 = 1;
972
980
  }
@@ -1005,10 +1013,6 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
1005
1013
  ca_lazy_arena_exit();
1006
1014
  }
1007
1015
 
1008
- out = rb_ca_rewrap_unbound_repeat(self, out);
1009
-
1010
- out = rb_ca_rewrap_unbound_repeat(other, out);
1011
-
1012
1016
  return out;
1013
1017
  }
1014
1018
 
@@ -1026,7 +1030,7 @@ rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
1026
1030
 
1027
1031
  rb_ca_modify(self);
1028
1032
 
1029
- /* do implicit casting and resolving unbound repeat array */
1033
+ /* do implicit casting */
1030
1034
  rb_ca_cast_other(&self, &other);
1031
1035
 
1032
1036
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
@@ -1035,13 +1039,13 @@ rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
1035
1039
  self_is_scalar = RTEST(rb_obj_is_cscalar(self));
1036
1040
  other_is_scalar = RTEST(rb_obj_is_cscalar(other));
1037
1041
 
1038
- /* shape compat: non-scalar self vs non-scalar other → elements must match.
1039
- scalar self vs array other historically raises (mismatch). */
1040
- if ( !self_is_scalar && !other_is_scalar &&
1041
- ca1->elements != ca2->elements ) {
1042
- rb_raise(rb_eRuntimeError, "elements mismatch in binop (%" PRId64 " <-> %" PRId64 ")",
1043
- (ca_size_t) ca1->elements,
1044
- (ca_size_t) ca2->elements);
1042
+ /* self is the write target, so its shape is the result's by definition
1043
+ and the destination rule applies (the same one assignment uses), not
1044
+ the symmetric one a binary operation is held to. */
1045
+ if ( !self_is_scalar && !other_is_scalar ) {
1046
+ ca_broadcast_to_destination(self, &other);
1047
+ TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
1048
+ other_is_scalar = RTEST(rb_obj_is_cscalar(other));
1045
1049
  }
1046
1050
  if ( self_is_scalar && !other_is_scalar &&
1047
1051
  ca1->elements != ca2->elements ) {
@@ -1291,14 +1295,14 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
1291
1295
  if ( ca_attach_is_alias(ca1) ) {
1292
1296
  ca_attach(ca1); p1_src = (char *) ca1->ptr; att1 = 1;
1293
1297
  } else {
1294
- s1_arena = ca_lazy_arena_acquire(ca1->bytes);
1298
+ s1_arena = ca_op_acquire_operand_scratch(ca1, 1);
1295
1299
  ca_xfer_all(ca1, s1_arena, CA_XFER_GET);
1296
1300
  p1_src = (char *) s1_arena;
1297
1301
  }
1298
1302
  } else if ( ca_attach_is_alias(ca1) ) {
1299
1303
  ca_attach(ca1); p1_src = (char *) ca1->ptr; att1 = 1;
1300
1304
  } else {
1301
- s1_arena = ca_lazy_arena_acquire(chunk_n * ca1->bytes);
1305
+ s1_arena = ca_op_acquire_operand_scratch(ca1, chunk_n);
1302
1306
  p1_src = (char *) s1_arena; gpc1 = 1;
1303
1307
  }
1304
1308
  /* ca2 acquire (mirror) */
@@ -1306,14 +1310,14 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
1306
1310
  if ( ca_attach_is_alias(ca2) ) {
1307
1311
  ca_attach(ca2); p2_src = (char *) ca2->ptr; att2 = 1;
1308
1312
  } else {
1309
- s2_arena = ca_lazy_arena_acquire(ca2->bytes);
1313
+ s2_arena = ca_op_acquire_operand_scratch(ca2, 1);
1310
1314
  ca_xfer_all(ca2, s2_arena, CA_XFER_GET);
1311
1315
  p2_src = (char *) s2_arena;
1312
1316
  }
1313
1317
  } else if ( ca_attach_is_alias(ca2) ) {
1314
1318
  ca_attach(ca2); p2_src = (char *) ca2->ptr; att2 = 1;
1315
1319
  } else {
1316
- s2_arena = ca_lazy_arena_acquire(chunk_n * ca2->bytes);
1320
+ s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
1317
1321
  p2_src = (char *) s2_arena; gpc2 = 1;
1318
1322
  }
1319
1323
  /* ca3 acquire (mirror) */
@@ -1321,14 +1325,14 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
1321
1325
  if ( ca_attach_is_alias(ca3) ) {
1322
1326
  ca_attach(ca3); p3_src = (char *) ca3->ptr; att3 = 1;
1323
1327
  } else {
1324
- s3_arena = ca_lazy_arena_acquire(ca3->bytes);
1328
+ s3_arena = ca_op_acquire_operand_scratch(ca3, 1);
1325
1329
  ca_xfer_all(ca3, s3_arena, CA_XFER_GET);
1326
1330
  p3_src = (char *) s3_arena;
1327
1331
  }
1328
1332
  } else if ( ca_attach_is_alias(ca3) ) {
1329
1333
  ca_attach(ca3); p3_src = (char *) ca3->ptr; att3 = 1;
1330
1334
  } else {
1331
- s3_arena = ca_lazy_arena_acquire(chunk_n * ca3->bytes);
1335
+ s3_arena = ca_op_acquire_operand_scratch(ca3, chunk_n);
1332
1336
  p3_src = (char *) s3_arena; gpc3 = 1;
1333
1337
  }
1334
1338
 
@@ -1367,10 +1371,6 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
1367
1371
  }
1368
1372
  }
1369
1373
 
1370
- out = rb_ca_rewrap_unbound_repeat(self, out);
1371
- out = rb_ca_rewrap_unbound_repeat(other2, out);
1372
- out = rb_ca_rewrap_unbound_repeat(other3, out);
1373
-
1374
1374
  return out;
1375
1375
  }
1376
1376
 
@@ -1392,14 +1392,15 @@ rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
1392
1392
  TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
1393
1393
  TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
1394
1394
 
1395
- /* element-count check (self is the destination) */
1396
- if ( ! rb_obj_is_cscalar(other2) && ca2->elements != ca1->elements ) {
1397
- rb_raise(rb_eRuntimeError, "elements mismatch in triop! (op2: %" PRId64 " != %" PRId64 ")",
1398
- (ca_size_t) ca2->elements, (ca_size_t) ca1->elements);
1395
+ /* self is the write target, so the destination rule applies to each
1396
+ input operand in turn (the same one assignment uses). */
1397
+ if ( ! rb_obj_is_cscalar(other2) ) {
1398
+ ca_broadcast_to_destination(self, &other2);
1399
+ TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
1399
1400
  }
1400
- if ( ! rb_obj_is_cscalar(other3) && ca3->elements != ca1->elements ) {
1401
- rb_raise(rb_eRuntimeError, "elements mismatch in triop! (op3: %" PRId64 " != %" PRId64 ")",
1402
- (ca_size_t) ca3->elements, (ca_size_t) ca1->elements);
1401
+ if ( ! rb_obj_is_cscalar(other3) ) {
1402
+ ca_broadcast_to_destination(self, &other3);
1403
+ TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
1403
1404
  }
1404
1405
 
1405
1406
  /* self IS the output (= write target; attach legit per refined invariant) */
@@ -1462,8 +1463,8 @@ rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
1462
1463
  }
1463
1464
 
1464
1465
  ca_lazy_arena_enter();
1465
- s2_arena = ca_lazy_arena_acquire(chunk_n * ca2->bytes);
1466
- s3_arena = ca_lazy_arena_acquire(chunk_n * ca3->bytes);
1466
+ s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
1467
+ s3_arena = ca_op_acquire_operand_scratch(ca3, chunk_n);
1467
1468
 
1468
1469
  for ( off = 0; off < n_total; off += chunk_n ) {
1469
1470
  ca_size_t n_done = (off + chunk_n > n_total) ? n_total - off
@@ -1535,8 +1536,6 @@ rb_ca_call_moncmp (VALUE self, ca_moncmp_func_t func[])
1535
1536
  EAGER_RELEASE_INPUT(ca1, h1, attached1);
1536
1537
  }
1537
1538
 
1538
- out = rb_ca_rewrap_unbound_repeat(self, out);
1539
-
1540
1539
  return out;
1541
1540
  }
1542
1541
 
@@ -1573,7 +1572,7 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
1573
1572
  for fixlen-storage Faces (memcmp is already correct there). */
1574
1573
  ca_face_reconcile_comparison(&self, &other);
1575
1574
 
1576
- /* do implicit casting and resolving unbound repeat array */
1575
+ /* do implicit casting */
1577
1576
  rb_ca_cast_self_or_other(&self, &other);
1578
1577
 
1579
1578
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
@@ -1683,8 +1682,8 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
1683
1682
  }
1684
1683
 
1685
1684
  ca_lazy_arena_enter();
1686
- s1_arena = ca_lazy_arena_acquire(chunk_n * b1);
1687
- s2_arena = ca_lazy_arena_acquire(chunk_n * b2);
1685
+ s1_arena = ca_op_acquire_operand_scratch(ca1, chunk_n);
1686
+ s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
1688
1687
 
1689
1688
  for ( off = 0; off < n_kernel; off += chunk_n ) {
1690
1689
  ca_size_t n_done = (off + chunk_n > n_kernel) ? n_kernel - off
@@ -1708,8 +1707,6 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
1708
1707
  }
1709
1708
  }
1710
1709
 
1711
- out = rb_ca_rewrap_unbound_repeat(self, out);
1712
-
1713
1710
  return out;
1714
1711
  }
1715
1712
 
@@ -1807,7 +1804,7 @@ rb_ca_coerce (VALUE self, VALUE other)
1807
1804
  return rb_ca_coerce(self, rb_funcall(other,rb_intern("to_ca"),0));
1808
1805
  }
1809
1806
  else {
1810
- /* do implicit casting and resolving unbound repeat array */
1807
+ /* do implicit casting */
1811
1808
  rb_ca_cast_self_or_other(&self, &other);
1812
1809
  return rb_assoc_new(other, self);
1813
1810
  }
data/ext/carray_order.c CHANGED
@@ -854,7 +854,7 @@ rb_ca_linear_section_m (int argc, VALUE *argv, VALUE self)
854
854
  }
855
855
  if ( self_comparable ) {
856
856
  if ( val_is_face ) {
857
- val = rb_ca_strip_face_value(val);
857
+ val = ca_face_operand_descend(val, "linear_section");
858
858
  }
859
859
  }
860
860
  else if ( self_is_face ) {
@@ -41,7 +41,7 @@ extern VALUE rb_ca_partition_addr_ki_mp (VALUE self, VALUE vaxis, VALUE vkth, in
41
41
  * Comparator-based dispatch keeps this routine data_type-generic (used
42
42
  * by the CA_FIXLEN partition_copy path); the mkkernel-generated
43
43
  * partition_index_quickselect_* functions inline the comparator per
44
- * dtype for the numeric paths.
44
+ * data type for the numeric paths.
45
45
  *
46
46
  * `swap_tmp` and `pivot` must be caller-provided scratch buffers of size
47
47
  * `bytes` (used for cell swaps via memcpy; avoids alloca / per-swap
@@ -222,7 +222,7 @@ rb_ca_partition_copy_c (VALUE self, VALUE vkth, VALUE vaxis)
222
222
  CArray *cao;
223
223
  TypedData_Get_Struct(vout, CArray, &carray_data_type, cao);
224
224
 
225
- /* Numeric path: per-dtype quickselect with inline cmp via the typed
225
+ /* Numeric path: per-type quickselect with inline cmp via the typed
226
226
  ca_partition_quick_* kernels (avoids the function-pointer
227
227
  indirection of the comparator-based ca_quickselect_bytes).
228
228
  Float NaN policy = pre-partition NaN to tail (same convention as
@@ -271,7 +271,7 @@ rb_ca_partition_copy_c (VALUE self, VALUE vkth, VALUE vaxis)
271
271
 
272
272
  /* masked_position: -aware twin of {rb_ca_partition_copy_c}. Masked
273
273
  * cells are an incomparable sentinel (same role NaN plays for float
274
- * dtypes): unmasked input takes the fast rb_ca_partition_copy_c path
274
+ * data types): unmasked input takes the fast rb_ca_partition_copy_c path
275
275
  * unchanged; masked input delegates to {partition} (which handles the
276
276
  * masked_position split via partition_addr_ki_mp) + copy, mirroring the
277
277
  * CA_FIXLEN / CA_OBJECT delegation pattern already used by sort_copy.
@@ -381,7 +381,7 @@ rb_ca_partitioned_view (int argc, VALUE *argv, VALUE self)
381
381
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
382
382
 
383
383
  /* CA_FIXLEN flows through the same partition_addr_ki fixlen dialect
384
- as numeric; masked_position: applies uniformly across dtypes. */
384
+ as numeric; masked_position: applies uniformly across data types. */
385
385
  VALUE target = self;
386
386
 
387
387
  /* partition_addr_ki_mp validates axis + kth, splits masked cells to