carray 3.0.2 → 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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
@@ -71,9 +71,11 @@ class CAWindowIterator < CAIterator
71
71
  # `:nearest` / `:truncate`). `fill_value:` is a constant margin value
72
72
  # (an escape for `:constant` padding); when given it overrides `:skip`.
73
73
  #
74
- # For backward compatibility `initialize(window_view)` accepts a CAWindow
75
- # view (the old `CAWindowIterator.new(a.window(...))` form): the geometry
76
- # (offset ranges, bounds, fill value) is read back from the view.
74
+ # For backward compatibility `initialize(window_view)` with no ranges
75
+ # accepts a CAWindow view (the old `CAWindowIterator.new(a.window(...))`
76
+ # form): the geometry (offset ranges, bounds, fill value) is read back
77
+ # from the view. Given ranges, a CAWindow view is rolled over like any
78
+ # other array.
77
79
  #
78
80
  # @param source [CArray, CAWindow] the array to roll over, or a CAWindow
79
81
  # view to read the geometry from.
@@ -81,7 +83,7 @@ class CAWindowIterator < CAIterator
81
83
  # @param bounds [Symbol] `:skip` / `:nearest` / `:truncate`.
82
84
  # @param fill_value [Object, nil] constant margin value, overriding :skip.
83
85
  def initialize (source, *ranges, bounds: :skip, fill_value: nil)
84
- if source.is_a?(CArray) && source.obj_type == CA_OBJ_WINDOW
86
+ if ranges.empty? && source.is_a?(CArray) && source.obj_type == CA_OBJ_WINDOW
85
87
  # Backward-compat: read geometry from a CAWindow view built by #window.
86
88
  # start[i] = lo, shape[i] (window width) = w, so hi = lo + w - 1.
87
89
  win = source
@@ -104,6 +106,7 @@ class CAWindowIterator < CAIterator
104
106
  "windows: expected #{@source.ndim} ranges (one per axis), " \
105
107
  "got #{@ranges.size}"
106
108
  end
109
+ @ranges = @ranges.each_with_index.map { |r, k| window_offsets(r, k) }
107
110
 
108
111
  @sndim = @source.ndim
109
112
  @widths = @ranges.map { |r| r.end - r.begin + 1 }
@@ -158,11 +161,38 @@ class CAWindowIterator < CAIterator
158
161
  # the padded entity.
159
162
  # @return [CArray]
160
163
  def sliding_view
161
- @sliding_view ||= anchored_buffer.sliding_windows(*@widths)
164
+ @sliding_view ||= @shape.include?(0) ? no_windows : anchored_buffer.sliding_windows(*@widths)
162
165
  end
163
166
 
164
167
  private
165
168
 
169
+ # An axis's offsets as an inclusive Integer range, as Ruby reads the range
170
+ # given (`-1...1` is the offsets -1 and 0).
171
+ def window_offsets (r, k)
172
+ unless r.is_a?(Range) && r.begin.is_a?(Integer) && r.end.is_a?(Integer)
173
+ raise TypeError,
174
+ "windows: axis #{k} takes a Range of Integer offsets, got #{r.inspect}"
175
+ end
176
+ last = r.exclude_end? ? r.end - 1 : r.end
177
+ if last < r.begin
178
+ raise ArgumentError, "windows: axis #{k} offsets #{r.inspect} are empty"
179
+ end
180
+ r.begin..last
181
+ end
182
+
183
+ # With no anchors (an empty source, or a :truncate window wider than the
184
+ # source) there is nothing to slide over, and sliding_windows refuses a
185
+ # window wider than its buffer. The view is then an empty one of the same
186
+ # shape: the source's unit tiles cut to the output and stretched along the
187
+ # window axes, so every reduction answers with an empty array of its type.
188
+ def no_windows
189
+ unit = @source.block_view(*[1] * @sndim)
190
+ cut = @shape.each_with_index.map { |n, k| n == unit.shape[k] ? nil : 0...n }
191
+ unit = unit[*cut, *[nil] * @sndim] unless cut.all?(&:nil?)
192
+ view = unit.broadcast_to(*(@shape + @widths))
193
+ @source.face? ? view.face_lift(@source) : view
194
+ end
195
+
166
196
  # The stretch of the padded buffer the windows are taken from: it begins at
167
197
  # the first window and holds one window per anchor. For a window that covers
168
198
  # its own anchor and a boundary policy that pads, that is the whole buffer.
@@ -170,7 +200,7 @@ class CAWindowIterator < CAIterator
170
200
  buffer = padded_entity
171
201
  wanted = @ndim.times.map { |k| @origins[k]...(@origins[k] + @shape[k] + @widths[k] - 1) }
172
202
  return buffer if wanted.each_with_index.all? { |range, k|
173
- range.begin.zero? && range.end == buffer.dim[k]
203
+ range.begin.zero? && range.end == buffer.shape[k]
174
204
  }
175
205
  buffer[*wanted]
176
206
  end
@@ -210,10 +240,11 @@ class CAWindowIterator < CAIterator
210
240
  # One allocation, the source copied into the interior region, then the
211
241
  # margins filled per mode.
212
242
  def pad_source (src, lefts, rights, mode, value)
243
+ return pad_face(src, lefts, rights, mode, value) if src.face?
213
244
  nd = src.ndim
214
245
  shape = src.shape
215
246
  pshape = nd.times.map { |i| shape[i] + lefts[i] + rights[i] }
216
- pad = CArray.new(src.data_type, pshape)
247
+ pad = CArray.new(src.data_type, pshape, bytes: src.bytes)
217
248
 
218
249
  # Fill the whole buffer first, then overwrite the interior with the source.
219
250
  case mode
@@ -222,8 +253,8 @@ class CAWindowIterator < CAIterator
222
253
  when :masked
223
254
  pad[] = UNDEF
224
255
  when :edge
225
- # Provisional fill; the edge margins are written below from the source.
226
- pad[] = 0
256
+ # Nothing to fill: every margin cell is written from the source below
257
+ # (a provisional 0 cannot be stored in a fixlen buffer).
227
258
  end
228
259
 
229
260
  interior = nd.times.map { |i| lefts[i]...(lefts[i] + shape[i]) }
@@ -234,6 +265,23 @@ class CAWindowIterator < CAIterator
234
265
  pad
235
266
  end
236
267
 
268
+ # A Face is padded on its storage and put back on at the end, so the margins
269
+ # hold storage -- UNDEF, a copy of an edge cell, or a `:constant` value the
270
+ # Face has turned into storage -- and the reductions over the windows are
271
+ # the Face's own. A buffer of the Face's surface type could not take the
272
+ # margin values at all.
273
+ def pad_face (src, lefts, rights, mode, value)
274
+ storage = src.__send__(:face_storage)
275
+ if mode == :constant && !value.nil?
276
+ cell = src.__send__(:blank_like, [1])
277
+ cell[0] = value
278
+ padded = pad_source(storage, lefts, rights, :constant, cell.parent[0])
279
+ else
280
+ padded = pad_source(storage, lefts, rights, mode, value)
281
+ end
282
+ padded.face_lift(src)
283
+ end
284
+
237
285
  # Edge-replicate the margins of a padded buffer whose interior already holds
238
286
  # the source. Per axis, the left margin rows copy the first interior row and
239
287
  # the right margin rows copy the last; done axis by axis over the whole
@@ -356,19 +404,13 @@ class CAWindowIterator < CAIterator
356
404
  # @param fill_value [Object, nil] value to put in place of a masked
357
405
  # result instead of leaving it masked.
358
406
  # @return [CArray] reference-shaped (or shrunk, for :truncate)
359
- # @!method all(min_count: nil, fill_value: nil)
407
+ # @!method all
360
408
  # Whether every cell of each rolling is true.
361
- # @param min_count [Integer, nil] fewest cells that must be present
362
- # for a result; a piece with fewer comes back masked.
363
- # @param fill_value [Object, nil] value to put in place of a masked
364
- # result instead of leaving it masked.
409
+ # Takes no `min_count:` or `fill_value:`, as the core `all` does not.
365
410
  # @return [CArray] reference-shaped (or shrunk, for :truncate)
366
- # @!method any(min_count: nil, fill_value: nil)
411
+ # @!method any
367
412
  # Whether any cell of each rolling is true.
368
- # @param min_count [Integer, nil] fewest cells that must be present
369
- # for a result; a piece with fewer comes back masked.
370
- # @param fill_value [Object, nil] value to put in place of a masked
371
- # result instead of leaving it masked.
413
+ # Takes no `min_count:` or `fill_value:`, as the core `any` does not.
372
414
  # @return [CArray] reference-shaped (or shrunk, for :truncate)
373
415
  # @!method variancep(min_count: nil, fill_value: nil)
374
416
  # Rolling population variance (divisor `n`).
@@ -405,11 +447,11 @@ class CAWindowIterator < CAIterator
405
447
  # @param fill_value [Object, nil] value to put in place of a masked
406
448
  # result instead of leaving it masked.
407
449
  # @return [CArray] reference-shaped (or shrunk, for :truncate)
408
- [:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
450
+ [:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev,
409
451
  :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
410
452
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
411
453
  def #{op} (min_count: nil, fill_value: nil)
412
- folded = fold_by_offset(:#{op}, min_count, fill_value)
454
+ folded = @shape.include?(0) ? nil : fold_by_offset(:#{op}, min_count, fill_value)
413
455
  return folded unless folded.nil?
414
456
  kw = {}
415
457
  kw[:min_count] = min_count unless min_count.nil?
@@ -419,6 +461,41 @@ class CAWindowIterator < CAIterator
419
461
  RUBY
420
462
  end
421
463
 
464
+ # all / any take no min_count: or fill_value:, as the core ones do not.
465
+ [:all, :any].each do |op|
466
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
467
+ def #{op} (**kw)
468
+ if kw.empty?
469
+ folded = @shape.include?(0) ? nil : fold_by_offset(:#{op}, nil)
470
+ return folded unless folded.nil?
471
+ end
472
+ sliding_view.#{op}(axis: @window_axes, **CAIterator.reduce_keywords(:#{op}, kw))
473
+ end
474
+ RUBY
475
+ end
476
+
477
+ # @!method first
478
+ # Rolling first present value, in row-major order within the window;
479
+ # UNDEF for a window with none (the UNDEF margin of `:skip` is not
480
+ # present, so a window over the edge starts at its first source cell).
481
+ # @return [CArray] reference-shaped (or shrunk, for :truncate)
482
+ # @!method last
483
+ # Rolling last present value; UNDEF for a window with none.
484
+ # @return [CArray] reference-shaped (or shrunk, for :truncate)
485
+ # @!method nunique
486
+ # Rolling number of distinct present values (int64); 0 for a window with
487
+ # none.
488
+ # @return [CArray] reference-shaped (or shrunk, for :truncate)
489
+ #
490
+ # These take no `min_count:` or `fill_value:`, as the core ones do not.
491
+ [:first, :last, :nunique].each do |op|
492
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
493
+ def #{op} (**kw)
494
+ sliding_view.#{op}(axis: @window_axes, **CAIterator.reduce_keywords(:#{op}, kw))
495
+ end
496
+ RUBY
497
+ end
498
+
422
499
  # @overload min_addr
423
500
  # Rolling flat SOURCE address of the window minimum — which source cell holds
424
501
  # it, so `source.reshape(source.elements)[sw.min_addr]` are the window minima.
@@ -428,12 +505,12 @@ class CAWindowIterator < CAIterator
428
505
  # source cell it replicates, and with `bounds: :constant` (or `fill_value:`)
429
506
  # a winning margin cell has no source address and is a masked result.
430
507
  # @return [CArray] reference-shaped (or shrunk, for :truncate)
431
- def min_addr; window_winner_addr(:min_index); end
508
+ def min_addr (**kw); window_winner_addr(:min_index, kw); end
432
509
 
433
510
  # @overload max_addr
434
511
  # Rolling flat source address of the window maximum. See {#min_addr}.
435
512
  # @return [CArray] reference-shaped (or shrunk, for :truncate)
436
- def max_addr; window_winner_addr(:max_index); end
513
+ def max_addr (**kw); window_winner_addr(:max_index, kw); end
437
514
 
438
515
  private
439
516
 
@@ -464,22 +541,34 @@ class CAWindowIterator < CAIterator
464
541
  :min => :pmin!, :max => :pmax!,
465
542
  :all => :and!, :any => :or! }.freeze
466
543
 
544
+ # A boolean accumulates as the core's does, by parity, so its kernel is xor.
545
+ def offset_fold_kernel (op)
546
+ return :xor! if op == :accumulate && @source.data_type == CA_BOOLEAN
547
+ OFFSET_FOLD.fetch(op)
548
+ end
549
+
467
550
  # Widest window this path takes on any one axis.
468
551
  OFFSET_FOLD_MAX_WIDTH = 5
469
552
 
470
553
  # Runs the fold by offset, or returns nil when this window is not one it can
471
554
  # answer for -- in which case the caller delegates as before.
472
555
  def fold_by_offset (op, min_count, fill_value = nil)
556
+ return nil unless CArray.__fast_paths_enabled__
557
+ need = CArray.__present_cells_needed_per_result_cell__(op, min_count, 0)
473
558
  return nil unless op == :mean || OFFSET_FOLD.key?(op)
559
+ # The elementwise kernels act on a Face's storage; its reductions are its own.
560
+ return nil if @source.face?
474
561
  return nil if @widths.any? { |width| width > OFFSET_FOLD_MAX_WIDTH }
475
562
  return nil if neutral_value(op).nil?
563
+ # pmin / pmax let NaN lose, so a window holding only NaN would fold to the
564
+ # neutral value; the core answers NaN there.
565
+ return nil if (op == :min || op == :max) && source_has_nan?
476
566
 
477
567
  folded = op == :mean ? fold_mean : accumulate_offsets(op)
478
568
  folded = mask_empty_windows(folded) if EMPTY_WINDOW_IS_UNDEFINED.include?(op)
479
- folded = apply_min_count(folded, min_count)
569
+ folded = apply_min_count(folded, need)
480
570
  # A `fill_value:` on the call replaces a result that came out undefined.
481
- folded = folded.strip_mask(fill_value) if !fill_value.nil? && folded.has_mask?
482
- folded
571
+ CArray.__fill_undefined_result_cells__(folded, fill_value)
483
572
  end
484
573
 
485
574
  # The mean is the sum over the same offsets, divided by the number of cells
@@ -492,13 +581,14 @@ class CAWindowIterator < CAIterator
492
581
  divisor = counts.copy
493
582
  divisor[counts.eq(0)] = 1
494
583
  end
584
+ # coerce: the mean's fixed compute type
495
585
  accumulate_offsets(:sum).to_type(offset_fold_data_type(:mean)) / divisor
496
586
  end
497
587
 
498
588
  # Walks the window offsets, accumulating each shifted plane into the result.
499
589
  def accumulate_offsets (op, base = nil, kernel = nil, data_type = nil)
500
590
  base ||= offset_fold_base(op)
501
- kernel ||= OFFSET_FOLD.fetch(op)
591
+ kernel ||= offset_fold_kernel(op)
502
592
  data_type ||= offset_fold_data_type(op)
503
593
  accumulator = nil
504
594
  offset_grid.each do |offset|
@@ -540,14 +630,19 @@ class CAWindowIterator < CAIterator
540
630
  end
541
631
  end
542
632
 
543
- # `min_count` asks for a result only where the window held that many cells.
544
- def apply_min_count (folded, min_count)
545
- return folded if min_count.nil?
633
+ def source_has_nan?
634
+ return false unless @source.float? || @source.data_type == CA_OBJECT
635
+ @source.is_nan.any
636
+ end
637
+
638
+ # A result only where the window held `need` cells (CArray.__present_cells_needed_per_result_cell__).
639
+ def apply_min_count (folded, need)
640
+ return folded if need == 0
546
641
  counts = window_cell_counts
547
642
  if counts.is_a?(Integer)
548
- folded[] = UNDEF if counts < min_count
643
+ folded[] = UNDEF if counts < need
549
644
  else
550
- folded[counts.lt(min_count)] = UNDEF
645
+ folded[counts.lt(need)] = UNDEF
551
646
  end
552
647
  folded
553
648
  end
@@ -644,7 +739,7 @@ class CAWindowIterator < CAIterator
644
739
  def offset_fold_data_type (op)
645
740
  @offset_fold_data_type ||= {}
646
741
  @offset_fold_data_type[op] ||=
647
- CArray.new(@source.data_type, [1, 1]).send(op, axis: [1]).data_type
742
+ CAIterator.core_probe(@source).public_send(op, axis: [1]).data_type
648
743
  end
649
744
 
650
745
  # Every offset within the window, as a list of per-axis positions into the
@@ -662,8 +757,9 @@ class CAWindowIterator < CAIterator
662
757
  # offset is the range's begin for a padded margin, 0 for :truncate). A margin
663
758
  # winner (coordinate out of bounds) resolves per the boundary policy:
664
759
  # :nearest clamps to the edge source cell, :constant / :skip mask the result.
665
- def window_winner_addr (idx_op)
666
- mi = send(idx_op) # window-local flat index
760
+ def window_winner_addr (idx_op, kw = {})
761
+ kw = CAIterator.reduce_keywords(idx_op, kw)
762
+ mi = send(idx_op, **kw.slice(:min_count)) # window-local flat index
667
763
  n = @source.shape
668
764
  wstride = Array.new(@sndim); acc = 1
669
765
  (@sndim - 1).downto(0) { |i| wstride[i] = acc; acc *= @widths[i] }
@@ -687,7 +783,7 @@ class CAWindowIterator < CAIterator
687
783
  end
688
784
  addr[oob] = UNDEF unless @bounds == :nearest
689
785
  addr[mi.is_masked.eq(1)] = UNDEF if mi.has_mask?
690
- addr
786
+ CArray.__fill_undefined_result_cells__(addr, kw[:fill_value])
691
787
  end
692
788
 
693
789
  public
@@ -697,26 +793,29 @@ class CAWindowIterator < CAIterator
697
793
  # cells (the effective tap count, which drops near a :skip edge);
698
794
  # `count(UNDEF)` counts masked cells; `count(v)` counts cells equal to `v`.
699
795
  # @return [CArray]
700
- def count (*args)
701
- return count_not_masked if args.empty?
796
+ def count (*args, **kw)
797
+ return count_not_masked(**kw) if args.empty?
702
798
  # The sliding_windows view is a CAStride, so its #count is not shadowed;
703
799
  # dispatch CArray#count explicitly anyway, matching the family regularity.
704
- CArray.instance_method(:count).bind_call(sliding_view, *args, axis: @window_axes)
800
+ CArray.instance_method(:count).bind_call(sliding_view, *args, axis: @window_axes,
801
+ **CAIterator.reduce_keywords(:count, kw))
705
802
  end
706
803
 
707
804
  # @overload count_not_masked
708
805
  # Rolling count of present (non-masked) cells -- the denominator of a
709
806
  # renormalizing convolution.
710
807
  # @return [CArray]
711
- def count_not_masked
712
- sliding_view.count_not_masked(axis: @window_axes)
808
+ def count_not_masked (**kw)
809
+ sliding_view.count_not_masked(axis: @window_axes,
810
+ **CAIterator.reduce_keywords(:count_not_masked, kw))
713
811
  end
714
812
 
715
813
  # @overload count_masked
716
814
  # Rolling count of masked cells.
717
815
  # @return [CArray]
718
- def count_masked
719
- sliding_view.count_masked(axis: @window_axes)
816
+ def count_masked (**kw)
817
+ sliding_view.count_masked(axis: @window_axes,
818
+ **CAIterator.reduce_keywords(:count_masked, kw))
720
819
  end
721
820
 
722
821
  # @overload elements
@@ -788,40 +887,31 @@ class CAWindowIterator < CAIterator
788
887
 
789
888
  # ---- order statistics (median / percentile / quantile) ----------------
790
889
  #
791
- # Core per-axis order statistics take a single axis and do not accept a
792
- # masked input, so the window mode is dispatched:
793
- #
794
- # single window axis + unmasked margin -> `sliding_view.op(axis: window_axis)`
795
- # multi window axes + unmasked margin -> materialize the windows, flatten
796
- # the window axes into one, single-
797
- # axis core order-stat
798
- # :skip (UNDEF margin) -> raise (core has no masked per-axis
799
- # order-stat); guide to :nearest /
800
- # :truncate
801
- #
802
- # When core gains masked per-axis order statistics (a tracked refactor), the
803
- # :skip guard can be dropped and :skip served directly.
890
+ # Core per-axis order statistics take a single axis, so a single window
891
+ # axis goes straight to `sliding_view.op(axis: window_axis)`, and several
892
+ # are materialized with the window axes flattened into one. The UNDEF
893
+ # margin of :skip is left out of each window as any masked cell is.
804
894
 
805
- # @overload median
806
- # Rolling median. Requires an unmasked margin (`bounds: :nearest` or
807
- # `:truncate`); with the default `:skip` it raises.
895
+ # @overload median(min_count: nil, fill_value: nil)
896
+ # Rolling median.
808
897
  # @return [CArray]
809
- def median
810
- order_stat { |view, axis| view.median(axis: axis) }
898
+ def median (**kw)
899
+ kw = CAIterator.reduce_keywords(:median, kw)
900
+ order_stat { |view, axis| view.median(axis: axis, **kw) }
811
901
  end
812
902
 
813
- # @overload percentile(*pers)
903
+ # @overload percentile(*pers, min_count: nil, fill_value: nil)
814
904
  # Rolling percentile(s). One argument returns one CArray, several return
815
- # an array of CArrays (as `CArray#percentile`). Requires an unmasked
816
- # margin.
905
+ # an array of CArrays (as `CArray#percentile`).
817
906
  # @return [CArray, Array<CArray>]
818
- def percentile (*pers)
819
- order_stat { |view, axis| view.percentile(*pers, axis: axis) }
907
+ def percentile (*pers, **kw)
908
+ kw = CAIterator.reduce_keywords(:percentile, kw)
909
+ order_stat { |view, axis| view.percentile(*pers, axis: axis, **kw) }
820
910
  end
821
911
 
822
912
  # @overload quantile
823
913
  # Rolling five-number summary `[min, Q1, median, Q3, max]` (five CArrays),
824
- # as `CArray#quantile`. Requires an unmasked margin.
914
+ # as `CArray#quantile`.
825
915
  # @return [Array<CArray>]
826
916
  def quantile
827
917
  order_stat { |view, axis| view.quantile(axis: axis) }
@@ -830,14 +920,8 @@ class CAWindowIterator < CAIterator
830
920
  private
831
921
 
832
922
  # Drive an order statistic (yielded as `block.call(view, axis)`) through the
833
- # single-axis / multi-axis / :skip-reject dispatch above.
923
+ # single-axis / multi-axis dispatch above.
834
924
  def order_stat
835
- if @bounds == :skip
836
- raise ArgumentError,
837
- "windowed order statistics need an unmasked margin; " \
838
- "use bounds: :nearest (edge-extend) or bounds: :truncate (valid). " \
839
- "For an UNDEF-margin median use reduce { |w| w.median } (slower)."
840
- end
841
925
  sv = sliding_view
842
926
  if @window_axes.size == 1
843
927
  yield sv, @window_axes[0]
@@ -858,15 +942,17 @@ class CAWindowIterator < CAIterator
858
942
  # @overload wsum(weights)
859
943
  # Rolling weighted sum, `weights` shaped like a single window.
860
944
  # @return [CArray]
861
- def wsum (weights)
862
- weighted(weights) { |sv, w, axis| sv.wsum(w, axis: axis) }
945
+ def wsum (weights, **kw)
946
+ kw = CAIterator.reduce_keywords(:wsum, kw)
947
+ weighted(weights) { |sv, w, axis| sv.wsum(w, axis: axis, **kw) }
863
948
  end
864
949
 
865
950
  # @overload wmean(weights)
866
951
  # Rolling weighted mean, `weights` shaped like a single window.
867
952
  # @return [CArray]
868
- def wmean (weights)
869
- weighted(weights) { |sv, w, axis| sv.wmean(w, axis: axis) }
953
+ def wmean (weights, **kw)
954
+ kw = CAIterator.reduce_keywords(:wmean, kw)
955
+ weighted(weights) { |sv, w, axis| sv.wmean(w, axis: axis, **kw) }
870
956
  end
871
957
 
872
958
  private
@@ -1008,13 +1094,51 @@ class CAWindowIterator < CAIterator
1008
1094
  # winner is well-defined).
1009
1095
  # @raise [NotImplementedError]
1010
1096
  [:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
1011
- define_method(op) do |*, **|
1097
+ # define_method: a refusal that names itself in its own message
1098
+ define_method(op, &Ractor.make_shareable(proc do |*, **|
1012
1099
  raise NotImplementedError,
1013
1100
  "#{self.class} has no #{op}: a segment scan needs each cell to " \
1014
1101
  "belong to exactly one piece, but overlapping windows share cells, " \
1015
1102
  "so a per-cell running value is ill-defined; use reduce for a " \
1016
1103
  "custom per-window fold."
1017
- end
1104
+ end))
1105
+ end
1106
+
1107
+ # @!method lag(n = 1)
1108
+ # Not supported for a window iterator. A lag writes each cell the value
1109
+ # some steps earlier in its piece, which needs each cell to have one
1110
+ # position in one piece; overlapping windows put a cell in many windows.
1111
+ # Use `source.lag(n, axis:)` for the lag along an axis.
1112
+ # @raise [NotImplementedError]
1113
+ def lag (*)
1114
+ raise NotImplementedError,
1115
+ "#{self.class} has no lag: a cell belongs to many overlapping windows, " \
1116
+ "so it has no single position to step back from; use " \
1117
+ "source.lag(n, axis:) for the lag along an axis."
1118
+ end
1119
+
1120
+ # @!method strip_mask
1121
+ # Not supported for a window iterator: a cell belongs to many overlapping
1122
+ # windows, so it has no single value to be filled with. Use
1123
+ # `source.strip_mask(method:, axis:)` for a fill along an axis.
1124
+ # @raise [NotImplementedError]
1125
+ def strip_mask (*, **)
1126
+ raise NotImplementedError,
1127
+ "#{self.class} has no strip_mask: a cell belongs to many overlapping " \
1128
+ "windows, so it has no single fill; use source.strip_mask(method:, axis:) " \
1129
+ "for a fill along an axis."
1130
+ end
1131
+
1132
+ # @!method order
1133
+ # Not supported for a window iterator: a cell belongs to many overlapping
1134
+ # windows, so it has no single rank. Use `source.order(axis:)` for the
1135
+ # rank along an axis.
1136
+ # @raise [NotImplementedError]
1137
+ def order (*, **)
1138
+ raise NotImplementedError,
1139
+ "#{self.class} has no order: a cell belongs to many overlapping windows, " \
1140
+ "so it has no single rank; use source.order(axis:) for the rank along " \
1141
+ "an axis."
1018
1142
  end
1019
1143
 
1020
1144
  private
@@ -1040,7 +1164,44 @@ class CArray
1040
1164
  # @param bounds [Symbol] `:skip` / `:nearest` / `:truncate`.
1041
1165
  # @param fill_value [Object, nil] constant margin value.
1042
1166
  # @return [CAWindowIterator]
1043
- def windows (*ranges, bounds: :skip, fill_value: nil)
1167
+ # @overload windows(on:, before: nil, after: nil, exclude: :lower, by: nil, interval: nil, at: nil)
1168
+ # Returns a {CAKeyWindowIterator}: a window per value bounded by a width
1169
+ # of the key `on` (a time, a duration or a number) instead of a count of
1170
+ # cells. The window of a value whose key is t holds the values whose
1171
+ # key lies between t - before and t + after; `exclude:` leaves out the
1172
+ # lower end (default), the upper end, both or neither. `by:` keeps
1173
+ # each window within the values with the same by keys, and `interval:`
1174
+ # gives the key's grid step for {CAKeyWindowIterator#is_complete}. A
1175
+ # value whose key is masked, NaN or infinite is in no window and has no
1176
+ # window. `at:` places the windows at those points of the key instead
1177
+ # of at the values, one answer per point (a grid of points gives
1178
+ # windows on a grid); it cannot be given with `by:`.
1179
+ #
1180
+ # temp.windows(on: time, before: "1 hour").mean
1181
+ # temp.windows(on: time, at: hours, after: "3 hours", exclude: :upper).mean
1182
+ # @return [CAKeyWindowIterator]
1183
+ def windows (*ranges, on: nil, **key_window)
1184
+ unless on.nil?
1185
+ unless ranges.empty?
1186
+ raise ArgumentError, "windows: give offset ranges or on:, not both"
1187
+ end
1188
+ given = [:bounds, :fill_value] & key_window.keys
1189
+ unless given.empty?
1190
+ raise ArgumentError,
1191
+ "windows: #{given.map { |k| "#{k}:" }.join(', ')} " \
1192
+ "belong to offset windows; a window on a key has no padding " \
1193
+ "(a row with no window answers UNDEF)"
1194
+ end
1195
+ require "carray/key_windows"
1196
+ return CAKeyWindowIterator.new(self, on: on, **key_window)
1197
+ end
1198
+ bounds = key_window.delete(:bounds) { :skip }
1199
+ fill_value = key_window.delete(:fill_value)
1200
+ unless key_window.empty?
1201
+ raise ArgumentError,
1202
+ "windows: #{key_window.keys.map { |k| "#{k}:" }.join(', ')} " \
1203
+ "need on:, the key the widths are measured on"
1204
+ end
1044
1205
  if ranges.size == 1 && ranges[0].is_a?(CArray) && ranges[0].obj_type == CA_OBJ_WINDOW
1045
1206
  return CAWindowIterator.new(ranges[0])
1046
1207
  end
data/lib/carray.rb CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  require 'carray_ext'
4
4
 
5
+ # Errors name the method the user called: the outermost frame of the library
6
+ # before the user's code, and the library's Ruby frames are the ones here.
7
+ CArray.send(:library_root=, __dir__)
8
+
5
9
  # Top-level CAMath module -- math namespace for CArrays.
6
10
  # `CAMath.sin(ca)` works because module functions inherited
7
11
  # from Math (via include) accept any object that responds to
@@ -24,7 +28,7 @@ require 'carray/runtime' # load-bearing Ruby support the core depends on
24
28
  require 'carray/basics' # frequently-used convenience methods kept eager
25
29
  require 'carray/conditional' # then_else / replace_where / conditional
26
30
  require 'carray/mask_gap_fill' # unmask/strip_mask method: keyword (hold / linear gap-fill)
27
- require 'carray/attribute' # per-instance metadata Hash (#attribute / #has_attribute?)
31
+ require 'carray/attribute' # per-instance metadata Hash (#attr / #set_attr / #attrs / #has_attr?)
28
32
  require 'carray/boolean_reduce' # all/any/none skip_masked: keyword (Kleene fold)
29
33
  require 'carray/meld_reduce' # CAMeld per-parent reduce fast path (sum/mean/min/max along meld_axis)
30
34
  # carray/ordering CIFY (2026-06-21): translated to ext/carray_order.c
@@ -37,7 +41,7 @@ require 'carray/complex' # real / imag accessors; MUST precede carray/lazy
37
41
  require 'carray/lazy' # the lazy elementwise view layer
38
42
  # carray/face.rb was deleted (the Phase 1 skeleton CArray::Face module
39
43
  # became dead weight, replaced by C-layer macro deploy +
40
- # copy_state/storage_to_scalar convention).
44
+ # copy_state/storage_to_element convention).
41
45
  require 'carray/time' # CATime / CATimedelta
42
46
  # carray/methods/* (bincount / broadcast / gather_nd+put_nd) are small
43
47
  # single-feature method files, loaded lazily via autoload_carray.
@@ -49,6 +53,9 @@ require 'carray/string_operation_extension' # shared StringOperationMixin (must
49
53
  require 'carray/const_string' # CAConstString construction surface
50
54
  require 'carray/string' # CAString construction surface
51
55
  require 'carray/fixlen_string' # CAFixlenString construction surface
56
+ # Loaded here, not on first use: Ruby's own error messages call #inspect, and
57
+ # a Ractor other than the main one cannot load a file.
58
+ require 'carray/inspect'
52
59
  # CArray.format lives in carray/methods/string_format.rb, autoloaded on first call
53
60
 
54
61
  # carray/frame is loaded lazily via autoload_carray (CAFrame / GroupedFrame
@@ -43,9 +43,9 @@ class CArray
43
43
  # `data_type`, shape, mask, and element data into `self`. Result
44
44
  # is an independent entity.
45
45
  #
46
- # Note that for views, `dup`/`clone` produce a new view sharing
47
- # the parent (Ruby shallow-copy semantics); use {#copy} when an
48
- # independent owned array is required.
46
+ # For views, `dup` / `clone` produce a new view sharing the parent
47
+ # (Ruby shallow-copy semantics), so they do not copy. Don't use
48
+ # them to copy a CArray; use {#copy}.
49
49
  # @param other [CArray]
50
50
  # @return [self]
51
51
  # @api private
@@ -66,7 +66,7 @@ class CArray
66
66
  # @return [Class]
67
67
  # @overload fixlen(*shape, bytes:) { ... }
68
68
  # Equivalent to `CArray.new(:fixlen, shape, bytes: bytes) { ... }`.
69
- # The `bytes:` keyword is required for `:fixlen`.
69
+ # The `bytes:` keyword is required for `:fixlen`, and is 1 or more.
70
70
  # @param shape [Array<Integer>]
71
71
  # @param bytes [Integer]
72
72
  # @return [CArray]
@@ -11,6 +11,11 @@
11
11
  # On big-endian hosts the bit axis of a non-fixlen parent reflects
12
12
  # network byte order (parent bytes are walked in reverse within each
13
13
  # element); single-byte parents are linear on either endian.
14
+ #
15
+ # A bit view has no mask. The bits of one byte would share that byte's
16
+ # one mask cell, so the view refuses a masked parent and refuses to be
17
+ # masked (`mask=`, storing `UNDEF`, storing a masked array). Read the
18
+ # bits of a masked array through `value.bitarray`.
14
19
  class CABitarray < CAView
15
20
  end
16
21
 
@@ -27,6 +32,8 @@ class CArray
27
32
  # @return [CABitarray]
28
33
  # @raise [CADataTypeError] when `self.data_type` is a complex or
29
34
  # object type (bit-level access is not defined for those).
35
+ # @raise [ArgumentError] when `self` has a mask; use
36
+ # `value.bitarray` to read the bits without it.
30
37
  def bitarray; end
31
38
 
32
39
  # Alias for {#bitarray}.