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
data/lib/carray/basics.rb CHANGED
@@ -13,9 +13,19 @@
13
13
 
14
14
  class CArray
15
15
 
16
+ # The order sort puts two object values in (ca_obj_order_cmp in C): a
17
+ # Float NaN after every other value, NaN ties NaN, other pairs by `<=>`.
18
+ # nil when `<=>` cannot order the pair; the caller says what refused.
19
+ private_class_method def self.object_order_cmp (a, b)
20
+ a_nan = a.is_a?(Float) && a.nan?
21
+ b_nan = b.is_a?(Float) && b.nan?
22
+ return (a_nan ? 1 : 0) - (b_nan ? 1 : 0) if a_nan || b_nan
23
+ a <=> b
24
+ end
25
+
16
26
  # reshape / flatten / transpose! are defined in C
17
27
  # (ext/ca_obj_refer.c and ext/ca_obj_transpose.c). The bare size-1
18
- # insertion primitive is `__insert_axis_size1__` (C); `insert_axis` below
28
+ # insertion primitive is `insert_size_one_axes` (C); `insert_axis` below
19
29
  # is the user-facing method that owns the name and adds `repeat:`.
20
30
 
21
31
  # @overload insert_axis(*positions, repeat: nil)
@@ -58,7 +68,7 @@ class CArray
58
68
 
59
69
  # No repeat: -> plain size-1 insertion. The source-frame C primitive
60
70
  # handles normalization, range check and multiplicity directly.
61
- return __insert_axis_size1__(*flat) if repeat.nil?
71
+ return insert_size_one_axes(*flat) if repeat.nil?
62
72
 
63
73
  # Source frame: each position names the source axis the new axis goes
64
74
  # before. Gaps live in [0, ndim] (ndim = append at end); negatives count
@@ -80,31 +90,31 @@ class CArray
80
90
  Array.new(flat.length, repeat)
81
91
  end
82
92
 
83
- # Validate each value. A positive Integer only; nil is not a valid
84
- # per-axis repeat.
85
- reps.each do |r|
86
- unless r.is_a?(Integer)
87
- raise ArgumentError,
88
- "insert_axis: repeat must be a positive Integer, got #{r.inspect}"
93
+ # Validate each value. A positive Integer only; nil is not a valid
94
+ # per-axis repeat.
95
+ reps.each do |r|
96
+ unless r.is_a?(Integer)
97
+ raise ArgumentError,
98
+ "insert_axis: repeat must be a positive Integer, got #{r.inspect}"
99
+ end
100
+ raise ArgumentError, "insert_axis: repeat count must be >= 1" if r < 1
89
101
  end
90
- raise ArgumentError, "insert_axis: repeat count must be >= 1" if r < 1
91
- end
92
102
 
93
- # Final output layout: stable order by (gap, argument index) keeps
94
- # same-gap axes in argument order; the k-th inserted axis lands at output
95
- # position gap + k. This output position only drives broadcast_to; the
96
- # insertion itself always goes through the source-frame primitive.
97
- order = (0...flat.length).sort_by { |i| [gaps[i], i] }
98
- final = {}
99
- order.each_with_index { |i, k| final[i] = gaps[i] + k }
103
+ # Final output layout: stable order by (gap, argument index) keeps
104
+ # same-gap axes in argument order; the k-th inserted axis lands at output
105
+ # position gap + k. This output position only drives broadcast_to; the
106
+ # insertion itself always goes through the source-frame primitive.
107
+ order = (0...flat.length).sort_by { |i| [gaps[i], i] }
108
+ final = {}
109
+ order.each_with_index { |i, k| final[i] = gaps[i] + k }
100
110
 
101
- inter = __insert_axis_size1__(*order.map { |i| gaps[i] })
102
- return inter unless order.any? { |i| reps[i] > 1 }
111
+ inter = insert_size_one_axes(*order.map { |i| gaps[i] })
112
+ return inter unless order.any? { |i| reps[i] > 1 }
103
113
 
104
- shp = inter.shape
105
- order.each { |i| shp[final[i]] = reps[i] if reps[i] > 1 }
106
- inter.broadcast_to(*shp)
107
- end
114
+ shp = inter.shape
115
+ order.each { |i| shp[final[i]] = reps[i] if reps[i] > 1 }
116
+ inter.broadcast_to(*shp)
117
+ end
108
118
 
109
119
  # @overload drop_axis
110
120
  # Returns a view of `self` with every size-1 axis dropped.
@@ -142,6 +152,28 @@ end
142
152
  return template(:boolean) { 1 }
143
153
  end
144
154
 
155
+ # A Face is clipped in its own terms: its comparisons decide which cells are
156
+ # out of range (a time array reads its bounds as times, a record array
157
+ # orders by its declared members, a Face that cannot be ordered refuses),
158
+ # and its store writes the bound or the fill value into them.
159
+ private def face_clip (min, max, fill_value, lfill, ufill)
160
+ lfill = fill_value if lfill.nil?
161
+ ufill = fill_value if ufill.nil?
162
+ out = copy
163
+ out[self < min] = lfill.nil? ? min : lfill unless min.nil?
164
+ out[self > max] = ufill.nil? ? max : ufill unless max.nil?
165
+ out
166
+ end
167
+
168
+ # An array of +shape+ of the same kind as self: its data type and cell
169
+ # width, or its Face over a zero storage. Nothing is written into it, so
170
+ # it can be built for a read-only Face too.
171
+ private def blank_like (shape)
172
+ storage = face_storage
173
+ out = CArray.new(storage.data_type, shape, bytes: storage.bytes)
174
+ face? ? out.face_lift(self) : out
175
+ end
176
+
145
177
  # Sub-region copy (paste / crop), the old C `ca_paste` / `ca_clip` pair.
146
178
  # `clip` is reserved for value clamping (clamp to a min/max range), so
147
179
  # the read-out side is `crop`. Kept together as a pair (crop is rare, but paste is reached
@@ -250,6 +282,31 @@ end
250
282
 
251
283
  # @!endgroup
252
284
 
285
+ # An Integer shift count on an integer array means what it means to
286
+ # Integer#<< and #>>: negative shifts the other way, and a count past the
287
+ # width shifts every bit out. The kernels do that for a count of the
288
+ # array's own type; this brings a count the type cannot hold -- -1 for a
289
+ # uint8 array, 300 for an int8 one -- to one it can.
290
+ module IntegerShiftCount
291
+ UNSIGNED = [CA_UINT8, CA_UINT16, CA_UINT32, CA_UINT64].freeze
292
+
293
+ def << (other)
294
+ return super unless other.is_a?(Integer) && integer? && ! face?
295
+ w = bytes * 8
296
+ return self >> -other if other < 0 && UNSIGNED.include?(data_type)
297
+ super(other.clamp(-w, w))
298
+ end
299
+
300
+ def >> (other)
301
+ return super unless other.is_a?(Integer) && integer? && ! face?
302
+ w = bytes * 8
303
+ return self << -other if other < 0 && UNSIGNED.include?(data_type)
304
+ super(other.clamp(-w, w))
305
+ end
306
+ end
307
+ # Prepended by carray/lazy, after it has wrapped << and >>: prepended
308
+ # here, the method lazy aliases as the eager form would be this one.
309
+
253
310
  # @overload clip(min, max = nil, fill_value = nil, lfill: nil, ufill: nil)
254
311
  # Returns `self` with every element clamped to `[min, max]`.
255
312
  #
@@ -258,7 +315,14 @@ end
258
315
  # `fill_value` (or `lfill` / `ufill`) is given, out-of-range
259
316
  # cells are replaced by the fill instead of clamped -- pass
260
317
  # `UNDEF` to mask that end. `fill_value` is sugar for symmetric
261
- # dual-fill; `lfill` / `ufill` override per side.
318
+ # dual-fill; `lfill` / `ufill` override per side, and a side
319
+ # given no fill is still clamped.
320
+ #
321
+ # `min` may not exceed `max` (in any cell, when a bound is an
322
+ # array); equal bounds give every unmasked cell that value.
323
+ # A NaN in a bound array leaves that cell unbounded on that side,
324
+ # as `nil` does for the whole side. A bound is never UNDEF or
325
+ # masked: a missing bound is not read as either, so fill it first.
262
326
  #
263
327
  # Boundary is strict `[min, max]`: values equal to a bound
264
328
  # remain unchanged in both the clamped and filled variants.
@@ -272,7 +336,9 @@ end
272
336
  # @param lfill [Object, nil] override below-range fill.
273
337
  # @param ufill [Object, nil] override above-range fill.
274
338
  # @return [CArray] new CArray with clamped or filled values.
275
- # @raise [ArgumentError] when both `min` and `max` are `nil`.
339
+ # @raise [ArgumentError] when both `min` and `max` are `nil`,
340
+ # when either is `UNDEF` or has a masked cell, or when `min`
341
+ # exceeds `max`.
276
342
  # @example
277
343
  # a.clip(0, 10) # strict clamp
278
344
  # a.clip(0, 10, -1) # both ends -> -1
@@ -281,20 +347,42 @@ end
281
347
  if min.nil? && max.nil?
282
348
  raise ArgumentError, "clip: at least one of (min, max) must be given"
283
349
  end
350
+ [min, max].each do |bound|
351
+ undefined = UNDEF.equal?(bound) ||
352
+ (bound.is_a?(CArray) && bound.any_masked?) ||
353
+ (bound.is_a?(Array) && bound.flatten.any? { |v| UNDEF.equal?(v) })
354
+ next unless undefined
355
+ raise ArgumentError, "clip: a bound can not be UNDEF or masked " \
356
+ "(fill the bound first; a NaN bound leaves its cell " \
357
+ "unbounded, and UNDEF as the fill masks the cells out of range)"
358
+ end
359
+ return face_clip(min, max, fill_value, lfill, ufill) if face?
360
+ if !min.nil? && !max.nil?
361
+ reversed = if min.is_a?(CArray) then min.gt(max).any
362
+ elsif max.is_a?(CArray) then max.lt(min).any
363
+ else min > max
364
+ end
365
+ if reversed
366
+ raise ArgumentError, "clip: min must be less than or equal to max"
367
+ end
368
+ end
284
369
 
285
370
  # `fill_value` as a single argument is sugar applied to both ends; kwargs override.
286
371
  lfill = fill_value if lfill.nil?
287
372
  ufill = fill_value if ufill.nil?
288
373
 
289
374
  if lfill.nil? && ufill.nil?
290
- return __clip_ki__(min, max) if !min.nil? && !max.nil?
375
+ return clamp_between(min, max) if !min.nil? && !max.nil?
291
376
  return pmax(min) if max.nil?
292
377
  return pmin(max)
293
378
  end
294
379
 
380
+ # A side without a fill still clamps.
295
381
  out = self.copy
296
- out[:lt, min] = lfill unless min.nil? || lfill.nil?
297
- out[:gt, max] = ufill unless max.nil? || ufill.nil?
382
+ out = out.pmax(min) if !min.nil? && lfill.nil?
383
+ out = out.pmin(max) if !max.nil? && ufill.nil?
384
+ out[self.lt(min)] = lfill unless min.nil? || lfill.nil?
385
+ out[self.gt(max)] = ufill unless max.nil? || ufill.nil?
298
386
  out
299
387
  end
300
388
 
@@ -483,3 +571,49 @@ class Enumerator::ArithmeticSequence
483
571
  return CA_OBJECT(to_a)
484
572
  end
485
573
  end
574
+
575
+ class CArray
576
+ # map_slab / reduce_slab over a Face answer in that Face, as its other
577
+ # reductions do: each block value is written into a blank of the Face
578
+ # (which reads a CATime::Element, a string, a Face array ...) and handed
579
+ # to the slab engine as the storage it became; the result is lifted back
580
+ # at the end. A read-only Face has no blank to write into, so its values
581
+ # are collected as the surface objects they are. A `data_type:` from the
582
+ # caller is the caller's, and passes through.
583
+ module FaceSlabResult
584
+ def map_slab (**kw, &block)
585
+ return super unless face? && kw[:data_type].nil? && block
586
+ return super(**kw, data_type: :object, &block) if read_only?
587
+ out = super(**kw, data_type: face_storage.data_type) { |slab|
588
+ face_slab_storage(block.call(slab), slab.shape)
589
+ }
590
+ out.face_lift(self)
591
+ end
592
+
593
+ def reduce_slab (**kw, &block)
594
+ return super unless face? && kw[:data_type].nil? && block
595
+ return super(**kw, data_type: :object, &block) if read_only?
596
+ fiber = kw.key?(:init)
597
+ init = kw.delete(:init)
598
+ out = super(**kw, data_type: face_storage.data_type) { |slab|
599
+ value = if fiber
600
+ slab.to_a.flatten.inject(init) { |acc, e| block.call(acc, e) }
601
+ else
602
+ block.call(slab)
603
+ end
604
+ face_slab_storage(value, [1])[0]
605
+ }
606
+ out.face_lift(self)
607
+ end
608
+
609
+ private
610
+
611
+ def face_slab_storage (value, shape)
612
+ cell = blank_like(shape)
613
+ cell[] = value
614
+ cell.__send__(:face_storage)
615
+ end
616
+ end
617
+
618
+ prepend FaceSlabResult
619
+ end
@@ -78,8 +78,13 @@ class CArray
78
78
  # accumulators; `nil` for pure counts (int64).
79
79
  # @return [BincountND]
80
80
  def initialize (lengths:, fiber_shape: [], weights_data_type: nil)
81
- @lengths = lengths.map(&:to_i)
82
- raise ArgumentError, "lengths must be a non-empty list" if @lengths.empty?
81
+ raise ArgumentError, "lengths must be a non-empty list" if lengths.empty?
82
+ lengths.each_with_index do |l, k|
83
+ unless l.is_a?(Integer)
84
+ raise TypeError, "lengths[#{k}] must be an Integer (got #{l.inspect})"
85
+ end
86
+ end
87
+ @lengths = lengths.dup
83
88
  @lengths.each_with_index do |l, k|
84
89
  raise ArgumentError, "lengths[#{k}] must be >= 1" if l < 1
85
90
  end
@@ -209,6 +214,7 @@ class CArray
209
214
 
210
215
  if weights
211
216
  raise ArgumentError, "weights given but accumulator is unweighted" unless @weighted
217
+ # coerce: the accumulator's type, declared by weights_data_type:
212
218
  weights = CArray.wrap_readonly(weights, @counts_data_type)
213
219
  expected_w_shape = chunk.shape.dup
214
220
  expected_w_shape.delete_at(channel_ax)
@@ -269,6 +275,12 @@ class CArray
269
275
  term = strides_ext[k] == 1 ? b : b * strides_ext[k]
270
276
  ravel = ravel.nil? ? term : ravel + term
271
277
  end
278
+ # A NaN weight drops its sample, as the fiber kernel and histogram
279
+ # do; bincount itself would add the NaN in.
280
+ if tweights && tweights.float?
281
+ nan = tweights.is_nan
282
+ tweights = tweights.mask_where(nan) if nan.any
283
+ end
272
284
  chunk_counts = ravel.bincount(weights: tweights, length: total_ext)
273
285
  @full_counts[] = @full_counts + chunk_counts.reshape(*@full_counts.shape)
274
286
  else
@@ -338,6 +350,10 @@ class CArray
338
350
  # @return [BincountND]
339
351
  def bincount_nd (lengths:, axis: [-2, -1], weights: nil)
340
352
  raise ArgumentError, "lengths must be an Array of per-dim extents" unless lengths.is_a?(Array)
353
+ axis = [-2, -1] if axis.nil?
354
+ unless axis.is_a?(Array) && axis.size == 2
355
+ raise ArgumentError, "bincount_nd: axis: is a [sample, channel] pair (got #{axis.inspect})"
356
+ end
341
357
  sample_ax = normalize_axis(axis[0], "bincount_nd sample axis")
342
358
  channel_ax = normalize_axis(axis[1], "bincount_nd channel axis")
343
359
  fiber_shape = shape.dup