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/ruby_carray.c CHANGED
@@ -38,6 +38,7 @@ void Init_carray_attribute ();
38
38
  void Init_carray_loop ();
39
39
  void Init_carray_mask ();
40
40
  void Init_carray_hold ();
41
+ void Init_carray_key_windows ();
41
42
  void Init_carray_mask_fast_paths (); /* patches generated bit-op tables;
42
43
  must run AFTER Init_ca_op_ipower */
43
44
  void Init_carray_access ();
@@ -60,6 +61,7 @@ void Init_carray_median_percentile (); /* median / percentile / quantile */
60
61
  void Init_carray_sort (); /* sort / sort_copy / sort_addr / axis2addr */
61
62
  void Init_carray_count ();
62
63
  void Init_carray_kernels (); /* mkkernel-generated: reductions + maps */
64
+ void Init_carray_round ();
63
65
  void Init_carray_utils ();
64
66
  void Init_carray_generate ();
65
67
  void Init_carray_copy ();
@@ -183,13 +185,13 @@ Init_carray_ext (void)
183
185
  /* The flags this build's arithmetic was compiled with. Anything that
184
186
  computes what these kernels compute, somewhere other than in them, has
185
187
  to be built the same way to arrive at the same answer. */
186
- rb_define_const(rb_cCArray, "BUILD_FLAGS", rb_str_new2(CA_BUILD_FLAGS));
187
- rb_define_const(rb_cCArray, "VERSION", rb_str_new2(CA_VERSION));
188
+ rb_define_const(rb_cCArray, "BUILD_FLAGS", rb_obj_freeze(rb_str_new2(CA_BUILD_FLAGS)));
189
+ rb_define_const(rb_cCArray, "VERSION", rb_obj_freeze(rb_str_new2(CA_VERSION)));
188
190
  rb_define_const(rb_cCArray, "VERSION_CODE", INT2NUM(CA_VERSION_CODE));
189
191
  rb_define_const(rb_cCArray, "VERSION_MAJOR", INT2NUM(CA_VERSION_MAJOR));
190
192
  rb_define_const(rb_cCArray, "VERSION_MINOR", INT2NUM(CA_VERSION_MINOR));
191
193
  rb_define_const(rb_cCArray, "VERSION_TEENY", INT2NUM(CA_VERSION_TEENY));
192
- rb_define_const(rb_cCArray, "VERSION_DATE", rb_str_new2(CA_VERSION_DATE));
194
+ rb_define_const(rb_cCArray, "VERSION_DATE", rb_obj_freeze(rb_str_new2(CA_VERSION_DATE)));
193
195
 
194
196
  /* -- system -- */
195
197
  rb_define_const(rb_cObject, "CA_RANK_MAX", INT2NUM(CA_RANK_MAX));
@@ -199,8 +201,10 @@ Init_carray_ext (void)
199
201
  absence. C code only ever compares against it; the value is never
200
202
  read and must never be passed in from Ruby. The constant exists to
201
203
  anchor the object against the GC. */
202
- CA_UNSPECIFIED = rb_funcall(rb_cObject, rb_intern("new"), 0);
204
+ CA_UNSPECIFIED = rb_obj_freeze(rb_funcall(rb_cObject, rb_intern("new"), 0));
203
205
  rb_define_const(rb_cCArray, "UNSPECIFIED", CA_UNSPECIFIED);
206
+ /* Compared by identity in C, so it must not move. */
207
+ rb_gc_register_mark_object(CA_UNSPECIFIED);
204
208
 
205
209
  #ifdef HAVE_COMPLEX_H
206
210
  /* @private */
@@ -284,6 +288,7 @@ Init_carray_ext (void)
284
288
  Init_carray_undef();
285
289
  Init_carray_mask();
286
290
  Init_carray_hold();
291
+ Init_carray_key_windows();
287
292
  Init_carray_loop();
288
293
  Init_carray_access();
289
294
  Init_carray_index_classifier();
@@ -308,6 +313,7 @@ Init_carray_ext (void)
308
313
  Init_carray_sort(); /* sort / sort_copy / sort_addr / axis2addr */
309
314
  Init_carray_count();
310
315
  Init_carray_kernels(); /* mkkernel-generated: reductions + maps */
316
+ Init_carray_round(); /* round(ndigits); after the generated round */
311
317
 
312
318
  Init_carray_utils();
313
319
 
data/ext/version.h CHANGED
@@ -1,6 +1,6 @@
1
- #define CA_VERSION "3.0.3"
2
- #define CA_VERSION_CODE 30003
1
+ #define CA_VERSION "3.0.5"
2
+ #define CA_VERSION_CODE 30005
3
3
  #define CA_VERSION_MAJOR 3
4
4
  #define CA_VERSION_MINOR 0
5
- #define CA_VERSION_TEENY 3
6
- #define CA_VERSION_DATE "2026/10/13"
5
+ #define CA_VERSION_TEENY 5
6
+ #define CA_VERSION_DATE "2026/10/20"
@@ -31,11 +31,40 @@
31
31
  # The walk stops at a lazy operation view (CAMonOp / CABinOp /
32
32
  # CATriOp / CAMonCmp / CABinCmp): its cells are computed, not the
33
33
  # parent's cells, so the parent's attributes do not describe them.
34
- # `a.lazy` itself (CALazyMarker) is a view of `a` and walks through.
34
+ # `a.lazy` itself (CALazyMarker) is a view of `a` and walks through,
35
+ # and so does a lazy type conversion (the cast lane `promote_list`
36
+ # and `wrap_readonly` build), for the reason `to_type` carries them,
37
+ # and `swap_bytes`: like `refer`, `bitarray` and `bitfield`, a view
38
+ # that reads the same cells another way carries them.
39
+ # A view of a Face shows the attributes of the Face it was taken
40
+ # from (the walk goes to that Face, which the view's own parent
41
+ # chain skips).
42
+ # A view of several arrays (CAStack / CAMeld, so `stack`, `meld` and
43
+ # `montage`) shows, below its own @attr, the keys every one of its
44
+ # arrays shows with the same value; a key they disagree on, or one
45
+ # some of them lack, is not shown. An array with no cells is left out
46
+ # of that count, unless all of them are empty.
35
47
  #
36
- # Copies: `copy` and `to_type` give the new array the attributes the
37
- # source shows, as its own Hash. Arithmetic and reductions do not
38
- # carry them -- the result is a different quantity.
48
+ # Stored values are frozen copies, nested Arrays and Hashes included, so
49
+ # an attribute changes only through set_attr / set_attrs: a value read
50
+ # back cannot be changed in place, and a copy that shares it with its
51
+ # source cannot change the source. A frozen array takes no new
52
+ # attribute.
53
+ #
54
+ # A mask array (`a.mask`) holds no attributes: set_attr / set_attrs raise.
55
+ #
56
+ # Copies: `dup` and `clone` give the new array its own Hash of the
57
+ # receiver's own attributes. `copy` and `to_type` give the new array the attributes the
58
+ # source shows, as its own Hash; `concatenate`, `mosaic` and `tabulate`
59
+ # give it the keys every piece shows with the same value, as the views
60
+ # do. A conversion to another type or representation (`to_type`,
61
+ # `categorize`, a CAFrame `cast` / `parse_to_time` / `to_time`) carries
62
+ # them. Arithmetic and reductions do not -- the result is a different
63
+ # quantity -- including those that pick a value (`first`, `last`, `min`,
64
+ # `max`, `unique`) and the element-wise ones that keep the unit (`abs`,
65
+ # `round`, `clip`): an attribute does not say which operations leave it
66
+ # true, so none carries it. Set it on the result again. A CAFrame index built from a key column's values
67
+ # (group and pivot) shows the key column's attributes.
39
68
 
40
69
  class CArray
41
70
 
@@ -63,9 +92,15 @@ class CArray
63
92
  # store.
64
93
  # @param key [Symbol, String] attribute key.
65
94
  # @param value [Object] JSON-compatible value.
66
- # @return [Object] the coerced stored value.
95
+ # The value is stored as a frozen copy (nested Arrays and Hashes
96
+ # too).
97
+ # @return [Object] the stored value.
67
98
  # @raise [TypeError] when `key` or `value` is not accepted.
99
+ # @raise [ArgumentError] when `value` nests more than 32 levels.
100
+ # @raise [FrozenError] when `self` is frozen.
101
+ # @raise [TypeError] when `self` is a mask array.
68
102
  def set_attr (key, value)
103
+ attr_check_writable
69
104
  attr_validate_value(value)
70
105
  (@attr ||= {})[attr_normalize_key(key)] = attr_coerce_value(value)
71
106
  end
@@ -79,7 +114,11 @@ class CArray
79
114
  # @param hash [Hash{Symbol, String => Object}] attributes to set.
80
115
  # @return [CArray] self.
81
116
  # @raise [TypeError] when a key or a value is not accepted.
117
+ # @raise [ArgumentError] when a value nests more than 32 levels.
118
+ # @raise [FrozenError] when `self` is frozen.
119
+ # @raise [TypeError] when `self` is a mask array.
82
120
  def set_attrs (hash)
121
+ attr_check_writable
83
122
  unless hash.is_a?(Hash)
84
123
  raise TypeError, "set_attrs takes a Hash (got #{hash.class})"
85
124
  end
@@ -94,8 +133,23 @@ class CArray
94
133
  self
95
134
  end
96
135
 
136
+ # @overload delete_attr(key)
137
+ # Removes attribute `key` from `self` (not from any parent) and
138
+ # returns its value, or `nil` when `self` has no such key. A key a
139
+ # view shows from its parent stays visible after this, as `set_attr`
140
+ # writes to `self` only.
141
+ # @param key [Symbol, String] attribute key.
142
+ # @return [Object, nil] the removed value.
143
+ # @raise [FrozenError] when `self` is frozen.
144
+ def delete_attr (key)
145
+ k = attr_normalize_key(key)
146
+ return nil unless instance_variable_defined?(:@attr) && @attr
147
+ attr_check_not_frozen
148
+ @attr.delete(k)
149
+ end
150
+
97
151
  # @overload attrs
98
- # Returns a frozen shallow Hash of all attributes visible on
152
+ # Returns a frozen Hash of all attributes visible on
99
153
  # `self`, merged along the parent chain (deeper writes shadow
100
154
  # shallower ones on a per-key basis).
101
155
  # @return [Hash{String => Object}]
@@ -135,32 +189,93 @@ class CArray
135
189
  private
136
190
 
137
191
  # Yield each non-nil @attr Hash along the parent chain (self first).
138
- # Bounded to MAX_CHAIN_DEPTH for paranoia against unexpected cycles;
139
- # real CArray view chains rarely exceed a handful of levels.
140
- MAX_CHAIN_DEPTH = 64
141
- private_constant :MAX_CHAIN_DEPTH
142
-
192
+ # The chain has no fixed bound; an array seen twice ends the walk.
143
193
  def attr_each_chain
144
194
  ca = self
145
- MAX_CHAIN_DEPTH.times do
195
+ seen = nil
196
+ loop do
146
197
  if ca.instance_variable_defined?(:@attr)
147
198
  h = ca.instance_variable_get(:@attr)
148
199
  yield(h) if h
149
200
  end
150
201
  return nil if attr_computing_view?(ca)
151
- parent = ca.respond_to?(:parent) ? ca.parent : nil
152
- return nil if parent.nil? || parent.equal?(ca)
202
+ if ca.is_a?(CAStack) || ca.is_a?(CAMeld)
203
+ shared = CArray.__send__(:attrs_shared_by, ca.parents)
204
+ yield(shared) unless shared.empty?
205
+ return nil
206
+ end
207
+ parent = ca.is_a?(CArray) ? ca.__send__(:attribute_parent) : nil
208
+ return nil if parent.nil?
209
+ seen ||= {}.compare_by_identity
210
+ return nil if seen.key?(parent)
211
+ seen[ca] = true
153
212
  ca = parent
154
213
  end
155
- nil
156
214
  end
157
215
 
216
+ def attr_check_not_frozen
217
+ raise FrozenError.new("can't modify frozen #{self.class}: attributes", receiver: self) if frozen?
218
+ end
219
+
220
+ def attr_check_writable
221
+ attr_check_not_frozen
222
+ raise TypeError, "a mask array holds no attributes" if mask_array?
223
+ end
224
+
225
+ # Give `out`, a new array holding self's values in another type or
226
+ # representation, the attributes self shows; returns `out`. A view of
227
+ # self that already shows them is left alone.
228
+ def carry_attrs (out)
229
+ return out if out.equal?(self)
230
+ shown = attrs
231
+ out.set_attrs(shown) unless shown.empty? || out.attrs == shown
232
+ out
233
+ end
234
+
235
+ # Remove what `out`, a new array a reduction made, holds of self's
236
+ # attributes; returns `out`.
237
+ def drop_carried_attrs (out)
238
+ out.remove_instance_variable(:@attr) if out.instance_variable_defined?(:@attr)
239
+ out
240
+ end
241
+
242
+ # The attributes every array in `list` shows with the same value.
243
+ # An array with no cells holds no values for the attributes to describe,
244
+ # so it has no say (unless every one is empty), as CAFrame leaves a frame
245
+ # with no rows out of a meld.
246
+ def self.attrs_shared_by (list)
247
+ with_cells = list.reject { |a| a.elements == 0 }
248
+ list = with_cells unless with_cells.empty?
249
+ first, *rest = list.map(&:attrs)
250
+ return {} if first.nil?
251
+ first.select { |k, v| rest.all? { |h| h.key?(k) && h[k] == v } }
252
+ end
253
+ private_class_method :attrs_shared_by
254
+
158
255
  def attr_computing_view? (ca)
159
- ca.is_a?(CAMonOp) || ca.is_a?(CABinOp) || ca.is_a?(CATriOp) ||
256
+ if ca.is_a?(CAMonOp)
257
+ op = ca.__op_id__
258
+ return op < CAMonOp::CAST_BASE && op != CAMonOp::OP_BYTE_SWAP
259
+ end
260
+ ca.is_a?(CABinOp) || ca.is_a?(CATriOp) ||
160
261
  ca.is_a?(CAMonCmp) || ca.is_a?(CABinCmp)
161
262
  end
162
263
 
163
264
  def attr_normalize_key (key)
265
+ CArray.__send__(:attr_normalize_key, key)
266
+ end
267
+
268
+ def attr_validate_value (v)
269
+ CArray.__send__(:attr_validate_value, v)
270
+ end
271
+
272
+ def attr_coerce_value (v)
273
+ CArray.__send__(:attr_coerce_value, v)
274
+ end
275
+
276
+ # The key and value rules, as class methods so that CAFrame's attributes
277
+ # follow the same ones.
278
+ def self.attr_normalize_key (key)
164
279
  case key
165
280
  when String then key
166
281
  when Symbol then key.to_s
@@ -169,7 +284,7 @@ class CArray
169
284
  end
170
285
  end
171
286
 
172
- def attr_validate_value (v, depth = 0)
287
+ def self.attr_validate_value (v, depth = 0)
173
288
  raise ArgumentError, "attribute value nests too deep" if depth > 32
174
289
  case v
175
290
  when String, Integer, Float, true, false, nil, Symbol
@@ -189,17 +304,19 @@ class CArray
189
304
  end
190
305
  end
191
306
 
192
- def attr_coerce_value (v)
307
+ def self.attr_coerce_value (v)
193
308
  case v
194
- when Symbol then v.to_s
195
- when Array then v.map { |e| attr_coerce_value(e) }
309
+ when Symbol then v.to_s.freeze
310
+ when String then v.frozen? ? v : v.dup.freeze
311
+ when Array then v.map { |e| attr_coerce_value(e) }.freeze
196
312
  when Hash
197
313
  v.each_with_object({}) do |(k, vv), h|
198
- h[k.is_a?(Symbol) ? k.to_s : k] = attr_coerce_value(vv)
199
- end
314
+ h[(k.is_a?(Symbol) ? k.to_s : k.dup).freeze] = attr_coerce_value(vv)
315
+ end.freeze
200
316
  else
201
317
  v
202
318
  end
203
319
  end
320
+ private_class_method :attr_normalize_key, :attr_validate_value, :attr_coerce_value
204
321
 
205
322
  end
@@ -6,7 +6,7 @@
6
6
  # after autoload_method_extension is installed. Split by intent:
7
7
  #
8
8
  # (1) Top-level feature files under carray/ — constant class + entry
9
- # methods (Serializer, TableMethods, iterators, aggregation
9
+ # methods (Serializer, iterators, aggregation
10
10
  # accumulators, categorical / axis-group).
11
11
  # (2) Single-feature method files under carray/methods/ — each first
12
12
  # call requires its file, whose definitions overwrite the stubs
@@ -20,21 +20,6 @@
20
20
  # (1) Top-level feature files
21
21
  # ============================================================================
22
22
 
23
- # ---- Inspection (#inspect / #source_code) ----------------------------------
24
- #
25
- # CArray::Inspector (:nodoc:) and #inspect / #source_code live in carray/inspect.rb.
26
- # #inspect is an implicit method (p / pp / irb / error interpolation), so the
27
- # stub loads the file the first time any CArray is displayed. Nothing in the
28
- # eager core references it at load time; #desc is private (code-internal) and
29
- # Inspector is internal, so only the two public methods need stubs.
30
-
31
- class CArray
32
- inspect_rb = "carray/inspect"
33
- autoload_method "inspect", inspect_rb
34
- autoload_method "inspect_full", inspect_rb
35
- autoload_method "source_code", inspect_rb
36
- end
37
-
38
23
  # ---- Serializer / Marshal / dump / load ------------------------------------
39
24
 
40
25
  class CArray
@@ -64,21 +49,6 @@ class CARecord
64
49
  autoload_method "marshal_dump", "carray/serialize"
65
50
  end
66
51
 
67
- # ---- Arrow tensor IPC (interop) --------------------------------------------
68
-
69
- class CArray
70
- arrow_tensor_rb = "carray/arrow_tensor"
71
- autoload :ArrowTensor, arrow_tensor_rb
72
- autoload_method "self.load_arrow_tensor", arrow_tensor_rb
73
- autoload_method "save_arrow_tensor", arrow_tensor_rb
74
- end
75
-
76
- # ---- Table (row-major dispatch helper) -------------------------------------
77
-
78
- class CArray
79
- autoload :TableMethods, "carray/table"
80
- end
81
-
82
52
  # ---- Fusion (reading a lazy expression as a plan) --------------------------
83
53
  #
84
54
  # Nothing in the core asks for a plan -- CArray can always walk the view --
@@ -101,6 +71,7 @@ end
101
71
 
102
72
  autoload :CASlabIterator, "carray/slab_iterator"
103
73
  autoload :CAWindowIterator, "carray/window_iterator"
74
+ autoload :CAKeyWindowIterator, "carray/key_windows"
104
75
  autoload :CABlockIterator, "carray/block_iterator"
105
76
 
106
77
  class CArray
@@ -174,6 +145,10 @@ autoload :CAUnion, "carray/struct"
174
145
  autoload :CAFrame, "carray/frame"
175
146
  autoload :GroupedFrame, "carray/frame"
176
147
 
148
+ class CArray
149
+ autoload_method "to_frame", "carray/frame"
150
+ end
151
+
177
152
  class CArray
178
153
  struct_rb = "carray/struct"
179
154
  autoload_method "self.struct", struct_rb
@@ -231,10 +206,12 @@ class CArray
231
206
  autoload_method "intersection", "carray/methods/is_in"
232
207
  autoload_method "difference", "carray/methods/is_in"
233
208
  autoload_method "union", "carray/methods/is_in"
209
+ autoload_method "is_between", "carray/methods/is_between"
234
210
  autoload_method "self.align_addr", "carray/methods/align_addr"
235
211
  autoload_method "self.align_nearest_addr", "carray/methods/align_addr"
236
212
  autoload_method "value_counts", "carray/methods/value_counts"
237
213
  autoload_method "nunique", "carray/methods/nunique"
214
+ autoload_method "lag", "carray/methods/lag"
238
215
  autoload_method "is_mode", "carray/methods/mode"
239
216
  autoload_method "mode", "carray/methods/mode"
240
217
  autoload_method "snap", "carray/methods/snap"
@@ -32,8 +32,18 @@ module AutoloadMethodExtension
32
32
  private
33
33
 
34
34
  def autoload_define (target, name, library, original_spec)
35
- stub = nil
36
- target.define_method(name) do |*args, **kwargs, &block|
35
+ library = Ractor.make_shareable(library.to_s.dup)
36
+ original_spec = Ractor.make_shareable(original_spec.to_s.dup)
37
+ # Shareable, so the stub answers in any Ractor. Only the main Ractor
38
+ # loads the library: Ractors that require at the same time can deadlock
39
+ # (Ruby 3.4), so another Ractor is told what to load there instead.
40
+ target.define_method(name, &Ractor.make_shareable(proc do |*args, **kwargs, &block|
41
+ unless Ractor.current == Ractor.main
42
+ raise Ractor::IsolationError,
43
+ "'#{original_spec}' is loaded on first use from '#{library}'; " \
44
+ "require it in the main Ractor before other Ractors use it"
45
+ end
46
+ stub_location = target.instance_method(name).source_location
37
47
  begin
38
48
  require library
39
49
  rescue LoadError
@@ -45,14 +55,13 @@ module AutoloadMethodExtension
45
55
  # forwarding would land straight back here and keep doing so until the
46
56
  # stack gave out -- naming neither the method nor the library. Say
47
57
  # which, once.
48
- if target.instance_method(name) == stub
58
+ if target.instance_method(name).source_location == stub_location
49
59
  raise NoMethodError,
50
60
  "'#{original_spec}' is registered for autoload from " \
51
61
  "'#{library}', but that library defines no such method"
52
62
  end
53
63
  send(name, *args, **kwargs, &block)
54
- end
55
- stub = target.instance_method(name)
64
+ end))
56
65
  # The library replaces the stub with a plain `def`, which `ruby -w`
57
66
  # reports as a redefinition. Replacing is what the stub is for, so mark
58
67
  # it as aliased: Ruby does not warn when the old definition has an alias.