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/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,45 @@ 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
+ # A Face answers an operator by its own values (a label, an instant, a
334
+ # String) and refuses what it can't answer; the lazy kernels read the
335
+ # storage and see neither. With a Face among the operands the operator is
336
+ # the eager one, on the lazy operands computed.
337
+ module LazyDispatch
338
+ private
339
+
340
+ def face_among_operands? (*operands)
341
+ operands.any? { |o| o.is_a?(CArray) && o.face? }
342
+ end
343
+
344
+ def computed_operand (operand)
345
+ ( operand.is_a?(CArray) && operand.lazy_view? ) ? operand.copy : operand
346
+ end
347
+ end
348
+
349
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_BINOP_OP_IDS, template: <<~'RUBY')
350
+ def __NAME__(other)
351
+ if lazy_view? || (other.is_a?(CArray) && other.lazy_view?)
352
+ if face_among_operands?(self, other)
353
+ return computed_operand(self).__send__(__method__, computed_operand(other))
354
+ end
355
+ CABinOp.__build__(self, other, __VALUE__)
333
356
  else
334
- eager_um.bind_call(self, other)
357
+ super(other)
335
358
  end
336
359
  end
337
- end
360
+ RUBY
338
361
 
339
362
  # The shift count of an Integer is brought into the array's type before
340
363
  # either form sees it (see CArray::IntegerShiftCount).
341
364
  prepend IntegerShiftCount
342
365
 
343
366
  # 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.
367
+ # installed in CArray by rb_define_alias at Init time (see
368
+ # MkKernel.alias_binop in ext/carray_kernels_*.c) and name the eager C
369
+ # function, so `a.lazy.add(1)` would materialise. Made again here, an
370
+ # alias resolves through the prepended modules -- IntegerShiftCount, then
371
+ # LazyDispatch -- so the word form takes the operator's.
350
372
  {
351
373
  add: :+,
352
374
  sub: :-,
@@ -359,21 +381,20 @@ class CArray
359
381
  bit_lshift: :<<,
360
382
  bit_rshift: :>>,
361
383
  }.each do |word, op|
362
- alias_method(word, op)
384
+ alias_method word, op
363
385
  end
364
386
 
365
387
  # `pow` is the canonical C method (defined by rb_define_method in
366
388
  # 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 `**`.
389
+ # dispatch in this file does not reach `pow` through the C alias, so
390
+ # `a.lazy.pow(2)` would fall to eager; `pow` is aliased again here.
370
391
  alias_method :pow, :**
371
392
 
372
393
  # `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
394
+ # time (the same C alias); they are aliased again beside them, in
395
+ # LazyDispatch.
396
+ LazyDispatch.send(:alias_method, :fmax, :pmax)
397
+ LazyDispatch.send(:alias_method, :fmin, :pmin)
377
398
 
378
399
  # ---------------------------------------------------------------------------
379
400
  # Triop dispatch (fma / fms / clip). CATriOp is the CABinOp analog for
@@ -381,7 +402,7 @@ class CArray
381
402
  # dispatches to CATriOp.__build__ when any of self / op2 / op3 is a
382
403
  # lazy view, and falls to the eager C method otherwise.
383
404
  #
384
- # `clip` is the strict-clamp entry (`__clip_ki__`, called by the
405
+ # `clip` is the strict-clamp entry (`clamp_between`, called by the
385
406
  # lib/carray/basics.rb `clip` wrapper's both-bounds-present path). The
386
407
  # nil-bound one-sided cases route through the wrapper's `pmax` / `pmin`
387
408
  # calls, which themselves lazy-fuse via LAZY_BINOP_OP_IDS above — so
@@ -390,26 +411,30 @@ class CArray
390
411
  # ---------------------------------------------------------------------------
391
412
 
392
413
  LAZY_TRIOP_OP_IDS = {
393
- fma: CATriOp::OP_FMA,
394
- fms: CATriOp::OP_FMS,
395
- __clip_ki__: CATriOp::OP_CLIP,
414
+ fma: CATriOp::OP_FMA,
415
+ fms: CATriOp::OP_FMS,
416
+ clamp_between: CATriOp::OP_CLIP,
396
417
  }.freeze
397
418
 
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__?)
419
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_TRIOP_OP_IDS, template: <<~'RUBY')
420
+ def __NAME__(op2, op3)
421
+ lazy_receiver = lazy_view? ||
422
+ (op2.is_a?(CArray) && op2.lazy_view?) ||
423
+ (op3.is_a?(CArray) && op3.lazy_view?)
406
424
  if lazy_receiver
407
- CATriOp.__build__(self, op2, op3, op_id)
425
+ if face_among_operands?(self, op2, op3)
426
+ return computed_operand(self).__send__(__method__, computed_operand(op2),
427
+ computed_operand(op3))
428
+ end
429
+ CATriOp.__build__(self, op2, op3, __VALUE__)
408
430
  else
409
- eager_um.bind_call(self, op2, op3)
431
+ super(op2, op3)
410
432
  end
411
433
  end
412
- end
434
+ RUBY
435
+ # The clip kernel takes both bounds; the public entry is `clip`.
436
+ private :clamp_between
437
+ LazyDispatch.send(:private, :clamp_between)
413
438
 
414
439
  # `**` integer-exponent lazy fast path.
415
440
  #
@@ -431,16 +456,21 @@ class CArray
431
456
  # `power` binop kernel already uses `op_powi_<type>` for integer data_types
432
457
  # via the `int:` expr in MkKernel.binop :power, so OP_POW + int parent
433
458
  # 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)
459
+ module LazyDispatch
460
+ def **(other)
461
+ if ( lazy_view? || (other.is_a?(CArray) && other.lazy_view?) ) &&
462
+ face_among_operands?(self, other)
463
+ return computed_operand(self) ** computed_operand(other)
464
+ end
465
+ if other.is_a?(Integer) && (float? || complex?) &&
466
+ (lazy_view? || (other.is_a?(CArray) && other.lazy_view?))
467
+ CABinOp.__build__(self, CScalar.int64.tap { |s| s[0] = other },
468
+ CABinOp::OP_IPOWER)
469
+ elsif lazy_view? || (other.is_a?(CArray) && other.lazy_view?)
470
+ CABinOp.__build__(self, other, CABinOp::OP_POW)
471
+ else
472
+ super(other)
473
+ end
444
474
  end
445
475
  end
446
476
 
@@ -486,40 +516,40 @@ class CArray
486
516
  :feq => CABinCmp::OP_FEQ,
487
517
  }.freeze
488
518
 
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)
519
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_BINCMP_OP_IDS, template: <<~'RUBY')
520
+ def __NAME__(other)
521
+ if lazy_view? || (other.is_a?(CArray) && other.lazy_view?)
522
+ if face_among_operands?(self, other)
523
+ return computed_operand(self).__send__(__method__, computed_operand(other))
524
+ end
525
+ CABinCmp.__build__(self, other, __VALUE__)
496
526
  else
497
- eager_um.bind_call(self, other)
527
+ super(other)
498
528
  end
499
529
  end
500
- end
530
+ RUBY
501
531
 
502
532
  # tolerance-bearing bincmp ops (= is_close / is_equiv) use the same
503
533
  # 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
534
+ # separate dict because the LAZY_BINCMP_OP_IDS template above
505
535
  # uses arity 1 (|other|); these need arity 2 (|other, tol|).
506
536
  LAZY_BINCMP_TOL_OP_IDS = {
507
537
  :is_close => CABinCmp::OP_IS_CLOSE,
508
538
  :is_equiv => CABinCmp::OP_IS_EQUIV,
509
539
  }.freeze
510
540
 
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)
541
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_BINCMP_TOL_OP_IDS, template: <<~'RUBY')
542
+ def __NAME__(other, tol)
543
+ if lazy_view? || (other.is_a?(CArray) && other.lazy_view?)
544
+ if face_among_operands?(self, other)
545
+ return computed_operand(self).__send__(__method__, computed_operand(other), tol)
546
+ end
547
+ CABinCmp.__build__(self, other, __VALUE__, tol.to_f.abs)
518
548
  else
519
- eager_um.bind_call(self, other, tol)
549
+ super(other, tol)
520
550
  end
521
551
  end
522
- end
552
+ RUBY
523
553
 
524
554
  # @!visibility private
525
555
  LAZY_MONCMP_OP_IDS = {
@@ -530,32 +560,29 @@ class CArray
530
560
  signbit: CAMonCmp::OP_SIGNBIT,
531
561
  }.freeze
532
562
 
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)
563
+ CArray.send(:define_named_methods, LazyDispatch, LAZY_MONCMP_OP_IDS, template: <<~'RUBY')
564
+ def __NAME__
565
+ if lazy_view?
566
+ CAMonCmp.__build__(self, __VALUE__)
540
567
  else
541
- eager_um.bind_call(self)
568
+ super()
542
569
  end
543
570
  end
544
- end
571
+ RUBY
545
572
 
546
573
  # A lazy receiver takes the scalar the eager coerce makes, and stays
547
574
  # lazy itself: the builder promotes the pair as the eager operator does.
548
575
  # @!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]
576
+ module LazyDispatch
577
+ def coerce(other)
578
+ begin
579
+ scalar, receiver = super(other)
580
+ rescue RangeError => e
581
+ raise unless other.is_a?(Integer)
582
+ return [OutOfRangeInteger.new(other, self, e), self]
583
+ end
584
+ lazy_view? ? [scalar, self] : [scalar, receiver]
557
585
  end
558
- __lazy_view__? ? [scalar, self] : [scalar, receiver]
559
586
  end
560
587
 
561
588
  # An Integer the array's type cannot hold, on the left of an operator.
@@ -604,6 +631,8 @@ class CAMonOp
604
631
  op_id = __op_id__
605
632
  if op_id >= CAMonOp::CAST_BASE
606
633
  "cast_#{CArray.data_type_name(op_id - CAMonOp::CAST_BASE)}"
634
+ elsif op_id == CAMonOp::OP_BYTE_SWAP
635
+ "swap_bytes" # a view of its own, not a lazy operation by name
607
636
  else
608
637
  (OP_NAMES[op_id] || "op_#{op_id}").to_s
609
638
  end
@@ -880,8 +909,13 @@ class << CArray
880
909
  # is not an answer: one that raises is dropped, with a warning, and
881
910
  # expressions are walked from there on.
882
911
  #
912
+ # Only the main Ractor asks it: it is registered there, and is not
913
+ # shared with the others, where expressions are walked.
914
+ #
883
915
  # @return [#call, nil]
884
- attr_reader :expression_evaluator
916
+ def expression_evaluator
917
+ Ractor.current == Ractor.main ? @expression_evaluator : nil
918
+ end
885
919
 
886
920
  # @param object [#call, nil] see {#expression_evaluator}
887
921
  # @raise [ArgumentError] when it cannot be called.