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
@@ -321,6 +321,7 @@ class CArray
321
321
 
322
322
  if weights
323
323
  raise ArgumentError, "weights given but accumulator is unweighted" unless @weighted
324
+ # coerce: the accumulator's type, declared by weights_data_type:
324
325
  weights = CArray.wrap_readonly(weights, @counts_data_type)
325
326
  expected_w_shape = chunk.shape.dup
326
327
  expected_w_shape.delete_at(channel_ax)
@@ -447,7 +448,7 @@ class CArray
447
448
  # @param weights [CArray, nil] optional per-sample weights.
448
449
  # @return [Histogram] 1-D accumulator (`m == 1`).
449
450
  def histogram1d (edges:, axis: -1, include_max: false, weights: nil)
450
- ax = normalize_axis(axis, "histogram1d")
451
+ ax = normalize_axis(axis.nil? ? -1 : axis, "histogram1d")
451
452
 
452
453
  new_shape = shape + [1]
453
454
  arr_with_channel = reshape(*new_shape)
@@ -491,6 +492,10 @@ class CArray
491
492
  def histogram (edges:, axis: [-2, -1], include_max: false, weights: nil)
492
493
  raise ArgumentError, "edges must be an Array of edges arrays" unless edges.is_a?(Array)
493
494
  arr = self
495
+ axis = [-2, -1] if axis.nil?
496
+ unless axis.is_a?(Array) && axis.size == 2
497
+ raise ArgumentError, "histogram: axis: is a [sample, channel] pair (got #{axis.inspect})"
498
+ end
494
499
  sample_ax = normalize_axis(axis[0], "histogram sample axis")
495
500
  channel_ax = normalize_axis(axis[1], "histogram channel axis")
496
501
  fiber_shape = arr.shape.dup
@@ -17,13 +17,12 @@ class CArray::Inspector # :nodoc:
17
17
  raise "can't inspect CArray of ndim == 0"
18
18
  end
19
19
  formatter = get_formatter()
20
- class_name = get_class_name()
21
- type_name = get_type_name()
20
+ label = get_label()
22
21
  shape = get_shape()
23
22
  data_spec = get_data_spec(0, Array.new(@carray.ndim){0}, formatter, abbrev)
24
23
  info_list = get_info_list()
25
24
  output = ["<",
26
- format("%s.%s(%s)", class_name, type_name, shape.join(",")),
25
+ format("%s(%s)", label, shape.join(",")),
27
26
  ": ",
28
27
  info_list.join(" "),
29
28
  "\n",
@@ -35,6 +34,39 @@ class CArray::Inspector # :nodoc:
35
34
 
36
35
  private
37
36
 
37
+ # The name before the shape: the class and the storage type, or for a
38
+ # Face its own name (see .face_label).
39
+ def get_label
40
+ return CArray::Inspector.face_label(@carray) if @carray.face?
41
+ return format("%s.%s", get_class_name, get_type_name)
42
+ end
43
+
44
+ public
45
+
46
+ # The name a Face goes by where a person reads it -- the head of inspect,
47
+ # a frame's column list. A Face's data_type is the surface the kernels
48
+ # dispatch on (fixlen for a time, so arithmetic on it raises), which says
49
+ # nothing to a reader, so it is named by its class: what a private
50
+ # inspect_label gives (CATime adds its tick), or the class, with the
51
+ # surface type only for a Face whose surface is numeric.
52
+ # The type of an array as a person reads it in a message or a table: a
53
+ # Face by its name (face_label), any other array by its data type name.
54
+ # Every message and listing that names a type goes through here, so a
55
+ # Face is never named by its storage (:fixlen for a time).
56
+ def self.type_label (ca)
57
+ ca.face? ? face_label(ca) : CArray.data_type_name(ca.data_type).to_s
58
+ end
59
+
60
+ def self.face_label (ca)
61
+ return ca.send(:inspect_label) if ca.respond_to?(:inspect_label, true)
62
+ klass = ca.class
63
+ klass = klass.superclass while klass.name.nil?
64
+ return klass.name if [CA_FIXLEN, CA_OBJECT].include?(ca.data_type)
65
+ "#{klass.name}.#{CArray.data_type_name(ca.data_type)}"
66
+ end
67
+
68
+ private
69
+
38
70
  def get_class_name
39
71
  return @carray.class.to_s
40
72
  end
@@ -110,15 +142,27 @@ class CArray::Inspector # :nodoc:
110
142
  end
111
143
 
112
144
  def get_formatter
113
- # A Face that defines storage_to_scalar decodes each cell into a surface
145
+ # A Face can write its cells itself through a private
146
+ # inspect_cell_formatter, a lambda taking the decoded cell.
147
+ if @carray.respond_to?(:inspect_cell_formatter, true)
148
+ return @carray.send(:inspect_cell_formatter)
149
+ end
150
+ # A Face that defines storage_to_element decodes each cell into a surface
114
151
  # value (CATime::Element, a String, a category label, ...) that has nothing
115
152
  # to do with the storage data_type, so the formatter must follow the decoded
116
153
  # value, not the storage. Faces without the hook (CAString) hand back the
117
154
  # stored value itself and fall through to the storage formatters below.
118
- if @carray.face? and @carray.respond_to?(:storage_to_scalar)
155
+ if @carray.face? and @carray.respond_to?(:storage_to_element)
119
156
  return lambda { |x| x.inspect }
120
157
  end
121
- case @carray.data_type
158
+ data_type = @carray.data_type
159
+ # The same Faces behind a gated (fixlen) surface hand back the value of
160
+ # their storage, so they take the storage's formatter.
161
+ if @carray.face? and data_type == CA_FIXLEN and not @carray.data_class
162
+ storage = @carray.parent.data_type
163
+ data_type = storage unless storage == CA_FIXLEN
164
+ end
165
+ case data_type
122
166
  when CA_BOOLEAN
123
167
  # Boolean cells fetch as true/false; show compact 1/0 (masked = _).
124
168
  # The type name in the inspect header distinguishes this from an int array.
@@ -134,11 +178,8 @@ class CArray::Inspector # :nodoc:
134
178
  x.real.inspect, (x.imag >= 0) ? "+" : "-", x.imag.abs.inspect) }
135
179
  when CA_FIXLEN
136
180
  if @carray.data_class
137
- if @carray.bytes <= 6
138
- lambda{|x| "%s" % x.encode.dump }
139
- else
140
- lambda{|x| "%s" % (x.encode.chomp[0, 5]+"...").dump }
141
- end
181
+ # A record shows its members, as the element's own inspect does.
182
+ lambda{|x| x.inspect }
142
183
  else
143
184
  if @carray.bytes <= 6
144
185
  lambda{|x| "%s" % x.dump }
@@ -259,10 +300,10 @@ class CArray
259
300
  case data_type
260
301
  when CA_FIXLEN
261
302
  output << sprintf("CArray.%s(%s, :bytes=>%i)",
262
- data_type_name, dim.inspect[1..-2], bytes)
303
+ data_type_name, shape.inspect[1..-2], bytes)
263
304
  else
264
305
  output << sprintf("CArray.%s(%s)",
265
- data_type_name, dim.inspect[1..-2])
306
+ data_type_name, shape.inspect[1..-2])
266
307
  end
267
308
  return output
268
309
  end
@@ -29,6 +29,55 @@ class CAIterator
29
29
  attr_reader :ndim, :shape
30
30
  alias dim shape
31
31
 
32
+ # The keywords a member passes on to a core reduction over its own axes.
33
+ # The member sets `axis:`, so one from the caller is refused; the rest go
34
+ # to the core, which checks them.
35
+ # Whether a fill method carries values backward: :forward / :ffill carry
36
+ # them forward, :backward / :bfill back. A member fills within its own
37
+ # pieces by holding the nearest present value; :linear, which needs a
38
+ # coordinate, is the core's alone.
39
+ def self.hold_backward? (method) # :nodoc:
40
+ case method
41
+ when :forward, :ffill then false
42
+ when :backward, :bfill then true
43
+ else
44
+ raise ArgumentError,
45
+ "strip_mask: method must be :forward, :ffill, :backward or :bfill " \
46
+ "within a piece (got #{method.inspect})"
47
+ end
48
+ end
49
+
50
+ # A one-cell (1, 1) array of the kind `value` holds, for asking the core
51
+ # what a reduction or scan of it answers in: its data type, cell width and,
52
+ # for a Face, its Face. A member that answers by a route of its own
53
+ # (a fused kernel, a fold by offset) takes its output type from here, so
54
+ # the route and the core cannot disagree. A Face is a view of the values
55
+ # themselves rather than a blank -- a Face cannot be allocated from its
56
+ # surface data type alone, and a zeroed record is not a valid cell for every
57
+ # one (a const string's record indexes a shared pool). A Face with no cell
58
+ # is asked with the blank cell its resize gives, so a reduction over no
59
+ # groups answers in the same kind as over some; this is nil only when the
60
+ # Face has no blank cell.
61
+ def self.core_probe (value) # :nodoc:
62
+ unless value.face?
63
+ return CArray.new(value.data_type, [1, 1], bytes: value.bytes)
64
+ end
65
+ flat = value.reshape(value.elements)
66
+ return flat[[0]].reshape(1, 1) if value.elements > 0
67
+ begin
68
+ flat.resize(1).reshape(1, 1)
69
+ rescue StandardError
70
+ nil
71
+ end
72
+ end
73
+
74
+ def self.reduce_keywords (op, kw) # :nodoc:
75
+ if kw.key?(:axis)
76
+ raise ArgumentError, "#{op}: unknown keyword: :axis (the iterator sets the axes)"
77
+ end
78
+ kw
79
+ end
80
+
32
81
  # The required reduction surface, modelled on the two reference members
33
82
  # CASlabIterator and CACategoricalIterator (the surface they both provide).
34
83
  # Declared abstract so a later member
@@ -117,6 +166,18 @@ class CAIterator
117
166
  # Returns the five-number summary of each piece,
118
167
  # `[min, Q1, median, Q3, max]`.
119
168
  # @return [Array<CArray>] five arrays, one per position.
169
+ # @!method first
170
+ # Returns the first present value of each piece, in the piece's own
171
+ # order; a piece with no present value is UNDEF.
172
+ # @return [CArray] one value per piece, in the source's data type.
173
+ # @!method last
174
+ # Returns the last present value of each piece; a piece with no present
175
+ # value is UNDEF.
176
+ # @return [CArray] one value per piece, in the source's data type.
177
+ # @!method nunique
178
+ # Returns the number of distinct present values in each piece, as
179
+ # `CArray#nunique` counts them; a piece with none counts 0.
180
+ # @return [CArray] `:int64`, one count per piece.
120
181
  # @!method count(value = nil)
121
182
  # Returns a count per piece: with no argument the cells that are not
122
183
  # masked, with `UNDEF` the masked cells, and with any other value the
@@ -144,19 +205,23 @@ class CAIterator
144
205
  # @yieldparam piece [CArray] next piece.
145
206
  # @yieldreturn [Object] updated accumulator.
146
207
  # @return [Object] the final accumulator.
147
- [
208
+ REQUIRED_SURFACE = [
148
209
  :sum, :accumulate, :prod, :mean, :min, :max, # tier 1
149
210
  :variance, :stddev, :all, :any,
150
211
  :variancep, :stddevp, :minmax, # tier 2
151
212
  :min_index, :max_index, :min_addr, :max_addr, # position
152
213
  :wsum, :wmean, # weighted
153
214
  :median, :percentile, :quantile, # tier 3
215
+ :first, :last, :nunique, # position / distinct
154
216
  :count, :count_not_masked, :count_masked, :elements, # count family
155
217
  :each, :reduce, # generic iterate
156
- ].each do |name|
157
- define_method(name) do |*, **, &_blk|
218
+ ].freeze
219
+
220
+ REQUIRED_SURFACE.each do |name|
221
+ # define_method: a stub that names itself in its own message
222
+ define_method(name, &Ractor.make_shareable(proc do |*, **, &_blk|
158
223
  raise NotImplementedError, "#{self.class} must implement ##{name}"
159
- end
224
+ end))
160
225
  end
161
226
 
162
227
  # Recommended (should) surface -- template methods. These are well-defined
@@ -164,7 +229,8 @@ class CAIterator
164
229
  # required: `map` is a per-piece element-wise transform scattered back to the
165
230
  # source, `sort_addr` is a per-piece sort returning source flat addresses, and
166
231
  # the segment scans (cumsum / cumprod / cummax / cummin / cumcount) write a
167
- # per-cell running statistic of each piece. A member implements each when it
232
+ # per-cell running statistic of each piece, and `lag` the value some steps
233
+ # earlier in the same piece. A member implements each when it
168
234
  # is well-defined and overrides it to raise with a reason when it is not. The
169
235
  # scans belong here for the same reason as map: a running per-cell value is
170
236
  # single-valued only when each cell belongs to exactly one piece, so the
@@ -209,11 +275,35 @@ class CAIterator
209
275
  # masked within each piece.
210
276
  # @return [CArray] shaped like the source.
211
277
  # @raise [NotImplementedError] for a member whose pieces overlap.
212
- [:map, :sort_addr,
213
- :cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |name|
214
- define_method(name) do |*, **, &_blk|
278
+ # @!method lag(n = 1)
279
+ # Returns a source-shaped array whose cell holds the value `n` steps
280
+ # earlier within the same piece, as `CArray#lag` does along an axis; a
281
+ # cell with no such value in its piece is UNDEF, so a lag never reaches
282
+ # across into another piece.
283
+ # @param n [Integer] steps back; a negative `n` looks ahead.
284
+ # @return [CArray] shaped like the source.
285
+ # @raise [NotImplementedError] for a member whose pieces overlap, where a
286
+ # cell has no single position within a piece.
287
+ # @!method strip_mask(method:)
288
+ # Returns a source-shaped array with each masked cell filled from its own
289
+ # piece, as `CArray#strip_mask(method:)` fills: `:forward` (`:ffill`)
290
+ # carries the last present value forward, `:backward` (`:bfill`) the next
291
+ # one back, never across into another piece.
292
+ # @param method [Symbol] `:forward`, `:ffill`, `:backward` or `:bfill`.
293
+ # @return [CArray] shaped like the source.
294
+ # @raise [NotImplementedError] for a member whose pieces overlap.
295
+ # @!method order(descending: false, method: :ordinal, kind: :quick)
296
+ # Returns a source-shaped int64 array of each cell's rank among the cells
297
+ # of its piece, as `CArray#order` ranks them (`0` the smallest).
298
+ # @return [CArray] shaped like the source.
299
+ # @raise [NotImplementedError] for a member whose pieces overlap, where a
300
+ # cell has no single rank.
301
+ [:map, :sort_addr, :order, :strip_mask,
302
+ :cumsum, :cumprod, :cummax, :cummin, :cumcount, :lag].each do |name|
303
+ # define_method: a stub that names itself in its own message
304
+ define_method(name, &Ractor.make_shareable(proc do |*, **, &_blk|
215
305
  raise NotImplementedError, "#{self.class} does not provide ##{name} (optional)"
216
- end
306
+ end))
217
307
  end
218
308
 
219
309
  end