carray 3.0.3 → 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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
data/lib/carray/lazy.rb CHANGED
@@ -2,12 +2,12 @@
2
2
  #
3
3
  # Per-op lazy-or-eager method redefinition on CArray.
4
4
  #
5
- # For each of the monop/monfunc methods, alias the existing eager
6
- # implementation as `__<name>_eager__` and redefine `<name>` to dispatch:
5
+ # For each of the monop/monfunc methods, keep the existing eager
6
+ # implementation as an UnboundMethod and redefine `<name>` to dispatch:
7
7
  #
8
- # - if self.__lazy_view__? → CAMonOp.__build__(self, OP_<NAME>)
8
+ # - if self.lazy_view? → CAMonOp.__build__(self, OP_<NAME>)
9
9
  # (cast node insertion handled in C if data_type mismatch)
10
- # - else → __<name>_eager__
10
+ # - else → the eager implementation
11
11
  #
12
12
  # CAREFUL: the redefinition must run once at load time AFTER the eager
13
13
  # methods are bound by Init_carray_math. carray_ext.bundle Init order
@@ -84,31 +84,42 @@ class CArray
84
84
  imag_i: CAMonOp::OP_IMAG_I,
85
85
  }.freeze
86
86
 
87
- LAZY_MONOP_OP_IDS.each do |name, op_id|
88
- eager_alias = :"__#{name}_eager__"
89
- # Preserve the existing eager method under a hidden alias.
90
- alias_method(eager_alias, name)
91
- # Capture the eager UnboundMethod for fast direct dispatch (avoids
92
- # Ruby method-lookup overhead of `__send__` on the hot eager path).
93
- eager_um = instance_method(eager_alias)
94
- # Redefine as lazy-or-eager dispatch.
95
- define_method(name) do
96
- if __lazy_view__?
97
- CAMonOp.__build__(self, op_id)
87
+ # The lazy-or-eager dispatch of the operators, in a module in front of
88
+ # the eager methods: each reaches its eager method with super. The
89
+ # blocks are shareable (they capture an op id at most), so the operators
90
+ # can be called from any Ractor.
91
+ module LazyDispatch; end
92
+ prepend LazyDispatch
93
+
94
+ CArray.send(:define_named_methods, LazyDispatch,
95
+ LAZY_MONOP_OP_IDS.reject { |name, _| name == :round }, template: <<~'RUBY')
96
+ def __NAME__
97
+ if lazy_view?
98
+ CAMonOp.__build__(self, __VALUE__)
98
99
  else
99
- eager_um.bind_call(self)
100
+ super()
101
+ end
102
+ end
103
+ RUBY
104
+
105
+ # round takes digits, which the lazy node does not carry: rounding to
106
+ # digits evaluates the expression and rounds the result.
107
+ module LazyDispatch
108
+ def round(*digits)
109
+ if digits.empty? && lazy_view?
110
+ CAMonOp.__build__(self, CAMonOp::OP_ROUND)
111
+ else
112
+ super
100
113
  end
101
114
  end
102
115
  end
103
116
 
104
- # Operator aliases (`~` -> bit_neg, `-@` -> neg) were installed by
105
- # rb_define_alias at Init time (see MkKernel.alias_monop in
106
- # ext/carray_kernels_monop.c) BEFORE this file redefines the target
107
- # methods. rb_define_alias snapshots the method entry, so the operator
108
- # form still points at the original eager C function and skips the
109
- # lazy dispatch above — `~a` / `-a` on a lazy view materialise instead
110
- # of building a CAMonOp node. Re-alias here so the operator form
111
- # follows the redefined lazy-or-eager dispatch.
117
+ # Operator aliases (`~` -> bit_neg, `-@` -> neg) were installed in
118
+ # CArray by rb_define_alias at Init time (see MkKernel.alias_monop in
119
+ # ext/carray_kernels_monop.c), and name the eager C function: `~a` on a
120
+ # lazy view would materialise instead of building a CAMonOp node. Made
121
+ # again here, an alias resolves through the prepended modules, so the
122
+ # operator form takes the dispatch above.
112
123
  alias_method :"~", :bit_neg
113
124
  alias_method :"-@", :neg
114
125
 
@@ -129,26 +140,24 @@ class CArray
129
140
  # ---------------------------------------------------------------------------
130
141
 
131
142
  # @private
132
- alias_method :__abs_eager__, :abs
133
- __abs_eager_um__ = instance_method(:__abs_eager__)
134
- define_method(:abs) do
135
- if __lazy_view__?
136
- # A boolean has no absolute value to change type for, as eager says.
137
- raise CArray::DataTypeError,
138
- "data_type-changing monop not implemented for input data_type " \
139
- "#{CArray.data_type_code(CA_BOOLEAN)}" if boolean?
140
- abs_i_node = CAMonOp.__build__(self, CAMonOp::OP_ABS_I)
141
- if complex?
142
- # float_dt is a Symbol; convert via data_type_code so the cast
143
- # op_id can be computed by Integer arithmetic.
144
- float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
145
- CAMonOp.__build__(abs_i_node,
146
- CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
143
+ module LazyDispatch
144
+ def abs
145
+ if lazy_view?
146
+ # A boolean has no absolute value: the eager abs refuses it.
147
+ return super() if boolean?
148
+ abs_i_node = CAMonOp.__build__(self, CAMonOp::OP_ABS_I)
149
+ if complex?
150
+ # float_dt is a Symbol; convert via data_type_code so the cast
151
+ # op_id can be computed by Integer arithmetic.
152
+ float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
153
+ CAMonOp.__build__(abs_i_node,
154
+ CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
155
+ else
156
+ abs_i_node
157
+ end
147
158
  else
148
- abs_i_node
159
+ super()
149
160
  end
150
- else
151
- __abs_eager_um__.bind_call(self)
152
161
  end
153
162
  end
154
163
 
@@ -183,48 +192,48 @@ class CArray
183
192
  # ---------------------------------------------------------------------------
184
193
 
185
194
  # @private
186
- alias_method :__real_eager__, :real
187
- __real_eager_um__ = instance_method(:__real_eager__)
188
- define_method(:real) do
189
- if __lazy_view__?
190
- if complex?
191
- float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
192
- # float_dt is a Symbol; convert via data_type_code for kernel
193
- # op_id arithmetic.
194
- CAMonOp.__build__(self,
195
- CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
195
+ module LazyDispatch
196
+ def real
197
+ if lazy_view?
198
+ if complex?
199
+ float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
200
+ # float_dt is a Symbol; convert via data_type_code for kernel
201
+ # op_id arithmetic.
202
+ CAMonOp.__build__(self,
203
+ CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
204
+ else
205
+ # `real` of a real array is the array itself; preserve the lazy
206
+ # chain unchanged (eager returns self[] = CARefer wrap, but for
207
+ # lazy chain consumption returning self keeps streaming intact).
208
+ self
209
+ end
196
210
  else
197
- # `real` of a real array is the array itself; preserve the lazy
198
- # chain unchanged (eager returns self[] = CARefer wrap, but for
199
- # lazy chain consumption returning self keeps streaming intact).
200
- self
211
+ super()
201
212
  end
202
- else
203
- __real_eager_um__.bind_call(self)
204
213
  end
205
214
  end
206
215
 
207
216
  # @!visibility private
208
- alias_method :__imag_eager__, :imag
209
- __imag_eager_um__ = instance_method(:__imag_eager__)
210
- define_method(:imag) do
211
- if __lazy_view__?
212
- imag_i_node = CAMonOp.__build__(self, CAMonOp::OP_IMAG_I)
213
- if boolean?
214
- # imag_i reads a boolean as int64; the eager imag stays boolean.
215
- CAMonOp.__build__(imag_i_node,
216
- CAMonOp::CAST_BASE + CArray.data_type_code(CA_BOOLEAN))
217
- elsif complex?
218
- float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
219
- # float_dt is a Symbol; convert via data_type_code for kernel
220
- # op_id arithmetic.
221
- CAMonOp.__build__(imag_i_node,
222
- CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
217
+ module LazyDispatch
218
+ def imag
219
+ if lazy_view?
220
+ imag_i_node = CAMonOp.__build__(self, CAMonOp::OP_IMAG_I)
221
+ if boolean?
222
+ # imag_i reads a boolean as int64; the eager imag stays boolean.
223
+ CAMonOp.__build__(imag_i_node,
224
+ CAMonOp::CAST_BASE + CArray.data_type_code(CA_BOOLEAN))
225
+ elsif complex?
226
+ float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
227
+ # float_dt is a Symbol; convert via data_type_code for kernel
228
+ # op_id arithmetic.
229
+ CAMonOp.__build__(imag_i_node,
230
+ CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
231
+ else
232
+ imag_i_node
233
+ end
223
234
  else
224
- imag_i_node
235
+ super()
225
236
  end
226
- else
227
- __imag_eager_um__.bind_call(self)
228
237
  end
229
238
  end
230
239
 
@@ -247,29 +256,27 @@ class CArray
247
256
  # ---------------------------------------------------------------------------
248
257
 
249
258
  # @private
250
- alias_method :__arg_eager__, :arg
251
- __arg_eager_um__ = instance_method(:__arg_eager__)
252
- define_method(:arg) do
253
- if __lazy_view__?
254
- raise CArray::DataTypeError,
255
- "data_type-changing monop not implemented for input data_type " \
256
- "#{CArray.data_type_code(CA_BOOLEAN)}" if boolean?
257
- parent = self
258
- if integer?
259
- parent = CAMonOp.__build__(parent,
260
- CAMonOp::CAST_BASE +
261
- CArray.data_type_code(CA_FLOAT64))
262
- end
263
- arg_i_node = CAMonOp.__build__(parent, CAMonOp::OP_ARG_I)
264
- if complex?
265
- float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
266
- CAMonOp.__build__(arg_i_node,
267
- CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
259
+ module LazyDispatch
260
+ def arg
261
+ if lazy_view?
262
+ return super() if boolean? # the eager arg refuses it
263
+ parent = self
264
+ if integer?
265
+ parent = CAMonOp.__build__(parent,
266
+ CAMonOp::CAST_BASE +
267
+ CArray.data_type_code(CA_FLOAT64))
268
+ end
269
+ arg_i_node = CAMonOp.__build__(parent, CAMonOp::OP_ARG_I)
270
+ if complex?
271
+ float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
272
+ CAMonOp.__build__(arg_i_node,
273
+ CAMonOp::CAST_BASE + CArray.data_type_code(float_dt))
274
+ else
275
+ arg_i_node
276
+ end
268
277
  else
269
- arg_i_node
278
+ super()
270
279
  end
271
- else
272
- __arg_eager_um__.bind_call(self)
273
280
  end
274
281
  end
275
282
 
@@ -323,30 +330,26 @@ class CArray
323
330
  # branch uses C99 `remainder`, round-half-to-even).
324
331
  }.freeze
325
332
 
326
- LAZY_BINOP_OP_IDS.each do |method_name, op_id|
327
- eager_alias = :"__#{method_name}_eager__"
328
- alias_method(eager_alias, method_name)
329
- eager_um = instance_method(eager_alias)
330
- define_method(method_name) do |other|
331
- if __lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?)
332
- CABinOp.__build__(self, other, op_id)
333
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_BINOP_OP_IDS, template: <<~'RUBY')
334
+ def __NAME__(other)
335
+ if lazy_view? || (other.is_a?(CArray) && other.lazy_view?)
336
+ CABinOp.__build__(self, other, __VALUE__)
333
337
  else
334
- eager_um.bind_call(self, other)
338
+ super(other)
335
339
  end
336
340
  end
337
- end
341
+ RUBY
338
342
 
339
343
  # The shift count of an Integer is brought into the array's type before
340
344
  # either form sees it (see CArray::IntegerShiftCount).
341
345
  prepend IntegerShiftCount
342
346
 
343
347
  # Binop word aliases (`add` -> `+`, `mul` -> `*`, `bit_and` -> `&`, …) were
344
- # installed by rb_define_alias at Init time (see MkKernel.alias_binop in
345
- # ext/carray_kernels_*.c) BEFORE the redefinitions above. The C alias
346
- # snapshots the original eager method entry, so the word form still points
347
- # at the original C function and skips lazy dispatch — `a.lazy.add(1)`
348
- # materialises instead of building a CABinOp node. Re-alias here so the
349
- # word form follows the redefined operator method.
348
+ # installed in CArray by rb_define_alias at Init time (see
349
+ # MkKernel.alias_binop in ext/carray_kernels_*.c) and name the eager C
350
+ # function, so `a.lazy.add(1)` would materialise. Made again here, an
351
+ # alias resolves through the prepended modules -- IntegerShiftCount, then
352
+ # LazyDispatch -- so the word form takes the operator's.
350
353
  {
351
354
  add: :+,
352
355
  sub: :-,
@@ -359,21 +362,20 @@ class CArray
359
362
  bit_lshift: :<<,
360
363
  bit_rshift: :>>,
361
364
  }.each do |word, op|
362
- alias_method(word, op)
365
+ alias_method word, op
363
366
  end
364
367
 
365
368
  # `pow` is the canonical C method (defined by rb_define_method in
366
369
  # ext/ca_op_ipower.c) that `**` is rb_define_alias'd to. The `**`
367
- # override in this file (with the ipower Integer-exponent fast path)
368
- # does not propagate to `pow` through the C alias, so `a.lazy.pow(2)`
369
- # falls to eager. Re-alias so `pow` follows the redefined `**`.
370
+ # dispatch in this file does not reach `pow` through the C alias, so
371
+ # `a.lazy.pow(2)` would fall to eager; `pow` is aliased again here.
370
372
  alias_method :pow, :**
371
373
 
372
374
  # `fmax` / `fmin` are rb_define_alias'd to `pmax` / `pmin` at Init
373
- # time (same C-alias-snapshot trap). Re-alias so the C99-familiar
374
- # spellings follow the redefined lazy dispatch above.
375
- alias_method :fmax, :pmax
376
- alias_method :fmin, :pmin
375
+ # time (the same C alias); they are aliased again beside them, in
376
+ # LazyDispatch.
377
+ LazyDispatch.send(:alias_method, :fmax, :pmax)
378
+ LazyDispatch.send(:alias_method, :fmin, :pmin)
377
379
 
378
380
  # ---------------------------------------------------------------------------
379
381
  # Triop dispatch (fma / fms / clip). CATriOp is the CABinOp analog for
@@ -381,7 +383,7 @@ class CArray
381
383
  # dispatches to CATriOp.__build__ when any of self / op2 / op3 is a
382
384
  # lazy view, and falls to the eager C method otherwise.
383
385
  #
384
- # `clip` is the strict-clamp entry (`__clip_ki__`, called by the
386
+ # `clip` is the strict-clamp entry (`clamp_between`, called by the
385
387
  # lib/carray/basics.rb `clip` wrapper's both-bounds-present path). The
386
388
  # nil-bound one-sided cases route through the wrapper's `pmax` / `pmin`
387
389
  # calls, which themselves lazy-fuse via LAZY_BINOP_OP_IDS above — so
@@ -390,26 +392,26 @@ class CArray
390
392
  # ---------------------------------------------------------------------------
391
393
 
392
394
  LAZY_TRIOP_OP_IDS = {
393
- fma: CATriOp::OP_FMA,
394
- fms: CATriOp::OP_FMS,
395
- __clip_ki__: CATriOp::OP_CLIP,
395
+ fma: CATriOp::OP_FMA,
396
+ fms: CATriOp::OP_FMS,
397
+ clamp_between: CATriOp::OP_CLIP,
396
398
  }.freeze
397
399
 
398
- LAZY_TRIOP_OP_IDS.each do |method_name, op_id|
399
- eager_alias = :"__#{method_name}_eager_triop__"
400
- alias_method(eager_alias, method_name)
401
- eager_um = instance_method(eager_alias)
402
- define_method(method_name) do |op2, op3|
403
- lazy_receiver = __lazy_view__? ||
404
- (op2.is_a?(CArray) && op2.__lazy_view__?) ||
405
- (op3.is_a?(CArray) && op3.__lazy_view__?)
400
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_TRIOP_OP_IDS, template: <<~'RUBY')
401
+ def __NAME__(op2, op3)
402
+ lazy_receiver = lazy_view? ||
403
+ (op2.is_a?(CArray) && op2.lazy_view?) ||
404
+ (op3.is_a?(CArray) && op3.lazy_view?)
406
405
  if lazy_receiver
407
- CATriOp.__build__(self, op2, op3, op_id)
406
+ CATriOp.__build__(self, op2, op3, __VALUE__)
408
407
  else
409
- eager_um.bind_call(self, op2, op3)
408
+ super(op2, op3)
410
409
  end
411
410
  end
412
- end
411
+ RUBY
412
+ # The clip kernel takes both bounds; the public entry is `clip`.
413
+ private :clamp_between
414
+ LazyDispatch.send(:private, :clamp_between)
413
415
 
414
416
  # `**` integer-exponent lazy fast path.
415
417
  #
@@ -431,16 +433,17 @@ class CArray
431
433
  # `power` binop kernel already uses `op_powi_<type>` for integer data_types
432
434
  # via the `int:` expr in MkKernel.binop :power, so OP_POW + int parent
433
435
  # is already fast. The lazy-substrate gap was Float/Complex only.
434
- __pow_eager_um__ = instance_method(:"__**_eager__")
435
- define_method(:**) do |other|
436
- if other.is_a?(Integer) && (float? || complex?) &&
437
- (__lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?))
438
- CABinOp.__build__(self, CScalar.int64.tap { |s| s[0] = other },
439
- CABinOp::OP_IPOWER)
440
- elsif __lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?)
441
- CABinOp.__build__(self, other, CABinOp::OP_POW)
442
- else
443
- __pow_eager_um__.bind_call(self, other)
436
+ module LazyDispatch
437
+ def **(other)
438
+ if other.is_a?(Integer) && (float? || complex?) &&
439
+ (lazy_view? || (other.is_a?(CArray) && other.lazy_view?))
440
+ CABinOp.__build__(self, CScalar.int64.tap { |s| s[0] = other },
441
+ CABinOp::OP_IPOWER)
442
+ elsif lazy_view? || (other.is_a?(CArray) && other.lazy_view?)
443
+ CABinOp.__build__(self, other, CABinOp::OP_POW)
444
+ else
445
+ super(other)
446
+ end
444
447
  end
445
448
  end
446
449
 
@@ -486,40 +489,34 @@ class CArray
486
489
  :feq => CABinCmp::OP_FEQ,
487
490
  }.freeze
488
491
 
489
- LAZY_BINCMP_OP_IDS.each do |method_name, op_id|
490
- eager_alias = :"__#{method_name}_eager_bincmp__"
491
- alias_method(eager_alias, method_name)
492
- eager_um = instance_method(eager_alias)
493
- define_method(method_name) do |other|
494
- if __lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?)
495
- CABinCmp.__build__(self, other, op_id)
492
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_BINCMP_OP_IDS, template: <<~'RUBY')
493
+ def __NAME__(other)
494
+ if lazy_view? || (other.is_a?(CArray) && other.lazy_view?)
495
+ CABinCmp.__build__(self, other, __VALUE__)
496
496
  else
497
- eager_um.bind_call(self, other)
497
+ super(other)
498
498
  end
499
499
  end
500
- end
500
+ RUBY
501
501
 
502
502
  # tolerance-bearing bincmp ops (= is_close / is_equiv) use the same
503
503
  # CABinCmp dispatch but with a 2nd `tol` positional arg. Kept in a
504
- # separate dict because the LAZY_BINCMP_OP_IDS define_method block above
504
+ # separate dict because the LAZY_BINCMP_OP_IDS template above
505
505
  # uses arity 1 (|other|); these need arity 2 (|other, tol|).
506
506
  LAZY_BINCMP_TOL_OP_IDS = {
507
507
  :is_close => CABinCmp::OP_IS_CLOSE,
508
508
  :is_equiv => CABinCmp::OP_IS_EQUIV,
509
509
  }.freeze
510
510
 
511
- LAZY_BINCMP_TOL_OP_IDS.each do |method_name, op_id|
512
- eager_alias = :"__#{method_name}_eager_bincmp_tol__"
513
- alias_method(eager_alias, method_name)
514
- eager_um = instance_method(eager_alias)
515
- define_method(method_name) do |other, tol|
516
- if __lazy_view__? || (other.is_a?(CArray) && other.__lazy_view__?)
517
- CABinCmp.__build__(self, other, op_id, tol.to_f.abs)
511
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_BINCMP_TOL_OP_IDS, template: <<~'RUBY')
512
+ def __NAME__(other, tol)
513
+ if lazy_view? || (other.is_a?(CArray) && other.lazy_view?)
514
+ CABinCmp.__build__(self, other, __VALUE__, tol.to_f.abs)
518
515
  else
519
- eager_um.bind_call(self, other, tol)
516
+ super(other, tol)
520
517
  end
521
518
  end
522
- end
519
+ RUBY
523
520
 
524
521
  # @!visibility private
525
522
  LAZY_MONCMP_OP_IDS = {
@@ -530,32 +527,29 @@ class CArray
530
527
  signbit: CAMonCmp::OP_SIGNBIT,
531
528
  }.freeze
532
529
 
533
- LAZY_MONCMP_OP_IDS.each do |name, op_id|
534
- eager_alias = :"__#{name}_eager_moncmp__"
535
- alias_method(eager_alias, name)
536
- eager_um = instance_method(eager_alias)
537
- define_method(name) do
538
- if __lazy_view__?
539
- CAMonCmp.__build__(self, op_id)
530
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_MONCMP_OP_IDS, template: <<~'RUBY')
531
+ def __NAME__
532
+ if lazy_view?
533
+ CAMonCmp.__build__(self, __VALUE__)
540
534
  else
541
- eager_um.bind_call(self)
535
+ super()
542
536
  end
543
537
  end
544
- end
538
+ RUBY
545
539
 
546
540
  # A lazy receiver takes the scalar the eager coerce makes, and stays
547
541
  # lazy itself: the builder promotes the pair as the eager operator does.
548
542
  # @!visibility private
549
- alias_method :__coerce_eager__, :coerce
550
- coerce_um = instance_method(:__coerce_eager__)
551
- define_method(:coerce) do |other|
552
- begin
553
- scalar, receiver = coerce_um.bind_call(self, other)
554
- rescue RangeError => e
555
- raise unless other.is_a?(Integer)
556
- return [OutOfRangeInteger.new(other, self, e), self]
543
+ module LazyDispatch
544
+ def coerce(other)
545
+ begin
546
+ scalar, receiver = super(other)
547
+ rescue RangeError => e
548
+ raise unless other.is_a?(Integer)
549
+ return [OutOfRangeInteger.new(other, self, e), self]
550
+ end
551
+ lazy_view? ? [scalar, self] : [scalar, receiver]
557
552
  end
558
- __lazy_view__? ? [scalar, self] : [scalar, receiver]
559
553
  end
560
554
 
561
555
  # An Integer the array's type cannot hold, on the left of an operator.
@@ -880,8 +874,13 @@ class << CArray
880
874
  # is not an answer: one that raises is dropped, with a warning, and
881
875
  # expressions are walked from there on.
882
876
  #
877
+ # Only the main Ractor asks it: it is registered there, and is not
878
+ # shared with the others, where expressions are walked.
879
+ #
883
880
  # @return [#call, nil]
884
- attr_reader :expression_evaluator
881
+ def expression_evaluator
882
+ Ractor.current == Ractor.main ? @expression_evaluator : nil
883
+ end
885
884
 
886
885
  # @param object [#call, nil] see {#expression_evaluator}
887
886
  # @raise [ArgumentError] when it cannot be called.