carray 3.0.1 → 3.0.3

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
@@ -29,6 +29,16 @@ 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
+ def self.reduce_keywords (op, kw) # :nodoc:
36
+ if kw.key?(:axis)
37
+ raise ArgumentError, "#{op}: unknown keyword: :axis (the iterator sets the axes)"
38
+ end
39
+ kw
40
+ end
41
+
32
42
  # The required reduction surface, modelled on the two reference members
33
43
  # CASlabIterator and CACategoricalIterator (the surface they both provide).
34
44
  # Declared abstract so a later member
@@ -36,7 +46,115 @@ class CAIterator
36
46
  # rather than reading as "no such method" -- that gap is the member's to close.
37
47
  # A member that genuinely cannot provide one overrides it to raise with its
38
48
  # own reason.
39
- [
49
+ # The methods below are declared here, so that the family surface is
50
+ # documented in one place; each member generates its own implementation and
51
+ # a member for which one is ill-defined overrides it to raise with a reason.
52
+ # "Piece" is the unit a member iterates over: a slab, a window, a block, a
53
+ # category, or a group.
54
+ #
55
+ # @!method sum
56
+ # Returns the sum of each piece.
57
+ # @return [CArray] one value per piece, shaped {#shape}.
58
+ # @!method accumulate
59
+ # Returns the sum of each piece kept in the source's own `data_type`,
60
+ # wrapping at its width, where {#sum} answers in the type the core
61
+ # promotes to (`:float64` for integers).
62
+ # @return [CArray] one value per piece.
63
+ # @!method prod
64
+ # Returns the product of each piece.
65
+ # @return [CArray] one value per piece.
66
+ # @!method mean
67
+ # Returns the arithmetic mean of each piece.
68
+ # @return [CArray] one value per piece.
69
+ # @!method min
70
+ # Returns the smallest value in each piece.
71
+ # @return [CArray] one value per piece.
72
+ # @!method max
73
+ # Returns the largest value in each piece.
74
+ # @return [CArray] one value per piece.
75
+ # @!method minmax
76
+ # Returns the smallest and largest value of each piece, found in one pass.
77
+ # @return [Array<CArray>] the pair `[min, max]`.
78
+ # @!method variance
79
+ # Returns the sample variance (divisor `n - 1`) of each piece.
80
+ # @return [CArray] one value per piece.
81
+ # @!method variancep
82
+ # Returns the population variance (divisor `n`) of each piece.
83
+ # @return [CArray] one value per piece.
84
+ # @!method stddev
85
+ # Returns the sample standard deviation (divisor `n - 1`) of each piece.
86
+ # @return [CArray] one value per piece.
87
+ # @!method stddevp
88
+ # Returns the population standard deviation (divisor `n`) of each piece.
89
+ # @return [CArray] one value per piece.
90
+ # @!method all
91
+ # Returns whether every cell of each piece is true.
92
+ # @return [CArray] `:boolean`, one value per piece.
93
+ # @!method any
94
+ # Returns whether any cell of each piece is true.
95
+ # @return [CArray] `:boolean`, one value per piece.
96
+ # @!method min_index
97
+ # Returns the position of the smallest value **within** each piece.
98
+ # @return [CArray] one index per piece.
99
+ # @!method max_index
100
+ # Returns the position of the largest value within each piece.
101
+ # @return [CArray] one index per piece.
102
+ # @!method min_addr
103
+ # Returns the flat address **in the source** of the smallest value of each
104
+ # piece -- which source cell holds it, rather than where it sits inside the
105
+ # piece. Use it to read the same cell out of another source-shaped array.
106
+ # @return [CArray] one flat address per piece.
107
+ # @!method max_addr
108
+ # Returns the flat address in the source of the largest value of each piece.
109
+ # @return [CArray] one flat address per piece.
110
+ # @!method wsum(weights)
111
+ # Returns the weighted sum of each piece.
112
+ # @param weights [CArray] one weight per source cell, shaped like the source.
113
+ # @return [CArray] one value per piece.
114
+ # @!method wmean(weights)
115
+ # Returns the weighted mean of each piece.
116
+ # @param weights [CArray] one weight per source cell, shaped like the source.
117
+ # @return [CArray] one value per piece.
118
+ # @!method median
119
+ # Returns the median of each piece.
120
+ # @return [CArray] one value per piece.
121
+ # @!method percentile(*pers)
122
+ # Returns the requested percentile(s) of each piece.
123
+ # @param pers [Array<Numeric>] percentile positions in `0..100`.
124
+ # @return [CArray, Array<CArray>] one array per requested position; a
125
+ # single position returns that array directly.
126
+ # @!method quantile
127
+ # Returns the five-number summary of each piece,
128
+ # `[min, Q1, median, Q3, max]`.
129
+ # @return [Array<CArray>] five arrays, one per position.
130
+ # @!method count(value = nil)
131
+ # Returns a count per piece: with no argument the cells that are not
132
+ # masked, with `UNDEF` the masked cells, and with any other value the
133
+ # cells equal to it.
134
+ # @param value [Object] value to match, or `UNDEF`.
135
+ # @return [CArray] one count per piece.
136
+ # @!method count_not_masked
137
+ # Returns the number of cells of each piece that are not masked.
138
+ # @return [CArray] one count per piece.
139
+ # @!method count_masked
140
+ # Returns the number of masked cells of each piece.
141
+ # @return [CArray] one count per piece.
142
+ # @!method elements
143
+ # Returns the total number of cells in each piece, masked or not.
144
+ # @return [CArray] one count per piece.
145
+ # @!method each
146
+ # Yields each piece in turn as a CArray.
147
+ # @yieldparam piece [CArray]
148
+ # @return [Enumerator, self] an Enumerator when no block is given.
149
+ # @!method reduce(init = nil)
150
+ # Folds the pieces with a block, for a reduction the family does not name.
151
+ # Without `init` the first piece seeds the accumulator.
152
+ # @param init [Object] initial accumulator value.
153
+ # @yieldparam acc [Object] running accumulator.
154
+ # @yieldparam piece [CArray] next piece.
155
+ # @yieldreturn [Object] updated accumulator.
156
+ # @return [Object] the final accumulator.
157
+ REQUIRED_SURFACE = [
40
158
  :sum, :accumulate, :prod, :mean, :min, :max, # tier 1
41
159
  :variance, :stddev, :all, :any,
42
160
  :variancep, :stddevp, :minmax, # tier 2
@@ -45,7 +163,9 @@ class CAIterator
45
163
  :median, :percentile, :quantile, # tier 3
46
164
  :count, :count_not_masked, :count_masked, :elements, # count family
47
165
  :each, :reduce, # generic iterate
48
- ].each do |name|
166
+ ].freeze
167
+
168
+ REQUIRED_SURFACE.each do |name|
49
169
  define_method(name) do |*, **, &_blk|
50
170
  raise NotImplementedError, "#{self.class} must implement ##{name}"
51
171
  end
@@ -66,6 +186,41 @@ class CAIterator
66
186
  # sort_addr. Un-overridden each is simply unavailable, not a contract
67
187
  # violation. min_addr / max_addr stay required: a single winner address is
68
188
  # well-defined even for an overlapping window.
189
+ # @!method map
190
+ # Returns a source-shaped array built by applying the block to each piece
191
+ # and scattering the result back into that piece's cells.
192
+ # @yieldparam piece [CArray]
193
+ # @yieldreturn [CArray, Numeric] replacement values for the piece.
194
+ # @return [CArray] shaped like the source.
195
+ # @raise [NotImplementedError] for a member whose pieces overlap, where a
196
+ # cell would receive more than one value.
197
+ # @!method sort_addr
198
+ # Returns a source-shaped array whose cells, read piece by piece, give the
199
+ # flat source addresses that put that piece in ascending order.
200
+ # @return [CArray] `:int64`, shaped like the source.
201
+ # @raise [NotImplementedError] for a member whose pieces overlap.
202
+ # @!method cumsum
203
+ # Returns a source-shaped array of the running sum within each piece.
204
+ # @return [CArray] shaped like the source.
205
+ # @raise [NotImplementedError] for a member whose pieces overlap, where a
206
+ # cell has no single running value.
207
+ # @!method cumprod
208
+ # Returns a source-shaped array of the running product within each piece.
209
+ # @return [CArray] shaped like the source.
210
+ # @raise [NotImplementedError] for a member whose pieces overlap.
211
+ # @!method cummax
212
+ # Returns a source-shaped array of the running maximum within each piece.
213
+ # @return [CArray] shaped like the source.
214
+ # @raise [NotImplementedError] for a member whose pieces overlap.
215
+ # @!method cummin
216
+ # Returns a source-shaped array of the running minimum within each piece.
217
+ # @return [CArray] shaped like the source.
218
+ # @raise [NotImplementedError] for a member whose pieces overlap.
219
+ # @!method cumcount
220
+ # Returns a source-shaped array of the running count of cells that are not
221
+ # masked within each piece.
222
+ # @return [CArray] shaped like the source.
223
+ # @raise [NotImplementedError] for a member whose pieces overlap.
69
224
  [:map, :sort_addr,
70
225
  :cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |name|
71
226
  define_method(name) do |*, **, &_blk|
data/lib/carray/lazy.rb CHANGED
@@ -133,6 +133,10 @@ class CArray
133
133
  __abs_eager_um__ = instance_method(:__abs_eager__)
134
134
  define_method(:abs) do
135
135
  if __lazy_view__?
136
+ # A boolean has no absolute value to change type for, as eager says.
137
+ raise CArray::DataTypeError,
138
+ "data_type-changing monop not implemented for input data_type " \
139
+ "#{CArray.data_type_code(CA_BOOLEAN)}" if boolean?
136
140
  abs_i_node = CAMonOp.__build__(self, CAMonOp::OP_ABS_I)
137
141
  if complex?
138
142
  # float_dt is a Symbol; convert via data_type_code so the cast
@@ -206,7 +210,11 @@ class CArray
206
210
  define_method(:imag) do
207
211
  if __lazy_view__?
208
212
  imag_i_node = CAMonOp.__build__(self, CAMonOp::OP_IMAG_I)
209
- if complex?
213
+ if boolean?
214
+ # imag_i reads a boolean as int64; the eager imag stays boolean.
215
+ CAMonOp.__build__(imag_i_node,
216
+ CAMonOp::CAST_BASE + CArray.data_type_code(CA_BOOLEAN))
217
+ elsif complex?
210
218
  float_dt = (data_type == CA_CMPLX64) ? CA_FLOAT32 : CA_FLOAT64
211
219
  # float_dt is a Symbol; convert via data_type_code for kernel
212
220
  # op_id arithmetic.
@@ -235,8 +243,7 @@ class CArray
235
243
  #
236
244
  # Both paths keep the operand's float / complex width (f32 → f32,
237
245
  # cmplx64 → f32), and both send integers to f64 since pi does not fit
238
- # an integer slot. That agrees with the general lazy-substrate rule
239
- # "scalar keeps operand's precision" (see ca_lazy_wrap_scalar header).
246
+ # an integer slot.
240
247
  # ---------------------------------------------------------------------------
241
248
 
242
249
  # @private
@@ -244,8 +251,11 @@ class CArray
244
251
  __arg_eager_um__ = instance_method(:__arg_eager__)
245
252
  define_method(:arg) do
246
253
  if __lazy_view__?
254
+ raise CArray::DataTypeError,
255
+ "data_type-changing monop not implemented for input data_type " \
256
+ "#{CArray.data_type_code(CA_BOOLEAN)}" if boolean?
247
257
  parent = self
248
- if integer? || boolean?
258
+ if integer?
249
259
  parent = CAMonOp.__build__(parent,
250
260
  CAMonOp::CAST_BASE +
251
261
  CArray.data_type_code(CA_FLOAT64))
@@ -326,6 +336,10 @@ class CArray
326
336
  end
327
337
  end
328
338
 
339
+ # The shift count of an Integer is brought into the array's type before
340
+ # either form sees it (see CArray::IntegerShiftCount).
341
+ prepend IntegerShiftCount
342
+
329
343
  # Binop word aliases (`add` -> `+`, `mul` -> `*`, `bit_and` -> `&`, …) were
330
344
  # installed by rb_define_alias at Init time (see MkKernel.alias_binop in
331
345
  # ext/carray_kernels_*.c) BEFORE the redefinitions above. The C alias
@@ -529,23 +543,48 @@ class CArray
529
543
  end
530
544
  end
531
545
 
546
+ # A lazy receiver takes the scalar the eager coerce makes, and stays
547
+ # lazy itself: the builder promotes the pair as the eager operator does.
532
548
  # @!visibility private
533
549
  alias_method :__coerce_eager__, :coerce
534
550
  coerce_um = instance_method(:__coerce_eager__)
535
551
  define_method(:coerce) do |other|
536
- if __lazy_view__?
537
- dt_name = data_type_name
538
- if CScalar.respond_to?(dt_name)
539
- s = CScalar.public_send(dt_name)
540
- s[0] = other
541
- [s, self]
542
- else
543
- coerce_um.bind_call(self, other)
544
- end
545
- else
546
- coerce_um.bind_call(self, other)
552
+ begin
553
+ scalar, receiver = coerce_um.bind_call(self, other)
554
+ rescue RangeError => e
555
+ raise unless other.is_a?(Integer)
556
+ return [OutOfRangeInteger.new(other, self, e), self]
557
+ end
558
+ __lazy_view__? ? [scalar, self] : [scalar, receiver]
559
+ end
560
+
561
+ # An Integer the array's type cannot hold, on the left of an operator.
562
+ # A comparison is answered from the array's side, where it compares by
563
+ # value; any other operator refuses the Integer as the array would.
564
+ class OutOfRangeInteger
565
+ def initialize (value, array, error)
566
+ @value, @array, @error = value, array, error
567
+ end
568
+
569
+ def < (other) = flip(other, :>)
570
+ def <= (other) = flip(other, :>=)
571
+ def > (other) = flip(other, :<)
572
+ def >= (other) = flip(other, :<=)
573
+
574
+ def method_missing (*)
575
+ raise @error
576
+ end
577
+
578
+ def respond_to_missing? (*) = false
579
+
580
+ private
581
+
582
+ def flip (other, op)
583
+ raise @error unless other.equal?(@array)
584
+ @array.send(op, @value)
547
585
  end
548
586
  end
587
+ private_constant :OutOfRangeInteger
549
588
  end
550
589
 
551
590
  # ---------------------------------------------------------------------------
@@ -618,14 +657,6 @@ class CAMonOp
618
657
  to_ca.to_a
619
658
  end
620
659
 
621
- # @overload sort(*args, **kwargs)
622
- # Materialises `self` into a copy and returns
623
- # `entity.sort(*args, **kwargs)`.
624
- # @return [CArray]
625
- def sort(*args, **kwargs)
626
- copy.sort(*args, **kwargs)
627
- end
628
-
629
660
  # --- MemoryView export → TypeError + .copy recommendation -----------
630
661
 
631
662
  # bulk-memory-view / Numo / Apache Arrow consumers go through the MV
@@ -714,13 +745,6 @@ class CABinOp
714
745
  to_ca.to_a
715
746
  end
716
747
 
717
- # @overload sort(*args, **kwargs)
718
- # Materialises `self` and sorts the resulting entity.
719
- # @return [CArray]
720
- def sort(*args, **kwargs)
721
- to_ca.sort(*args, **kwargs)
722
- end
723
-
724
748
  # @overload to_memory_view
725
749
  # Not supported for lazy binops.
726
750
  # @raise [TypeError] always.
@@ -901,41 +925,4 @@ class << CArray
901
925
  end
902
926
  CArray::FuseSource.evaluate(block)
903
927
  end
904
-
905
- # @overload jit_for (*extents) { |i, j, ...| ... }
906
- # Runs a block over an index space, so that a cell may reach the ones
907
- # around it -- a recurrence, a stencil. The block is compiled, and the
908
- # compiler is the carray-jit gem.
909
- # @raise [NotImplementedError] when that gem is not installed.
910
- def jit_for (*extents, **options)
911
- raise NotImplementedError, no_compiler("jit_for")
912
- end
913
-
914
- # @overload jit_each { ... }
915
- # Runs a block over the cells of arrays at once, naming each cell by the
916
- # array it came from, and writing what it computes into arrays of yours:
917
- # `CArray.jit_each { out = a + b * c }`. The block is compiled, and the
918
- # compiler is the carray-jit gem.
919
- # @raise [NotImplementedError] when that gem is not installed.
920
- def jit_each (&block)
921
- raise NotImplementedError, no_compiler("jit_each")
922
- end
923
-
924
- # @overload jit_map { ... }
925
- # The same block with its value asked for: the last statement is what
926
- # every cell of the result gets, and the result comes back. The block is
927
- # compiled, and the compiler is the carray-jit gem.
928
- # @raise [NotImplementedError] when that gem is not installed.
929
- def jit_map (&block)
930
- raise NotImplementedError, no_compiler("jit_map")
931
- end
932
-
933
- private
934
-
935
- def no_compiler (name)
936
- "CArray.#{name} compiles its block, and the compiler is the carray-jit " \
937
- "gem, which is not installed. An expression over whole arrays can be " \
938
- "written as `CArray.fuse { ... }` instead, which needs no compiler; " \
939
- "reaching a neighbouring cell, or writing the loop itself, does."
940
- end
941
928
  end
@@ -46,15 +46,10 @@ class CArray
46
46
  raise ArgumentError,
47
47
  "unmask: pass either a constant fill value or method:, not both"
48
48
  end
49
- held = __gap_fill__(method, axis)
50
- # Copy the filled values in place. A Face writes through its storage: a
51
- # bulk store into its surface would try to cast the storage values to the
52
- # surface type (int64 ticks to fixlen, for a time array).
53
- if face?
54
- parent.value[] = held.parent.value
55
- else
56
- value[] = held.value
57
- end
49
+ held = __gap_fill__(method, axis, "unmask")
50
+ # Copy the filled values in place (a Face takes a Face of its own kind
51
+ # as it is).
52
+ value[] = held.value
58
53
  if held.has_mask?
59
54
  self.mask = held.mask # residual leading/trailing mask
60
55
  else
@@ -71,7 +66,7 @@ class CArray
71
66
  raise ArgumentError,
72
67
  "strip_mask: pass either a constant fill value or method:, not both"
73
68
  end
74
- return __gap_fill__(method, axis)
69
+ return __gap_fill__(method, axis, "strip_mask")
75
70
  end
76
71
  if fill.equal?(MASK_FILL_UNSET)
77
72
  raise ArgumentError, "strip_mask: a fill value is required (or method:)"
@@ -85,7 +80,8 @@ class CArray
85
80
  # unfillable cells stay masked; a fully filled result carries no mask
86
81
  # (hold allocates the output mask only when a leading run goes UNDEF;
87
82
  # :linear masks only the exterior via mask_invalid).
88
- def __gap_fill__ (method, axis)
83
+ def __gap_fill__ (method, axis, name)
84
+ axis = normalize_axis(axis, name) unless axis.nil?
89
85
  case method
90
86
  when :forward, :ffill
91
87
  __hold_axis__(axis, false)
@@ -100,8 +96,8 @@ class CArray
100
96
  end
101
97
  end
102
98
 
103
- # Forward (backward = false) or backward (true) hold along `axis`
104
- # (axis nil = flatten). Backward reuses the forward primitive on the
99
+ # Forward (backward = false) or backward (true) hold along `axis`, already
100
+ # normalised by __gap_fill__ (axis nil = flatten). Backward reuses the forward primitive on the
105
101
  # reversed view; flatten flattens, holds axis 0, reshapes back.
106
102
  def __hold_axis__ (axis, backward)
107
103
  if axis.nil?
@@ -111,11 +107,10 @@ class CArray
111
107
  held = held.reverse if backward
112
108
  held.reshape(*shape)
113
109
  else
114
- ax = axis < 0 ? axis + ndim : axis
115
110
  if backward
116
- flip(ax).send(:__hold__, ax).flip(ax)
111
+ flip(axis).send(:__hold__, axis).flip(axis)
117
112
  else
118
- send(:__hold__, ax)
113
+ send(:__hold__, axis)
119
114
  end
120
115
  end
121
116
  end
@@ -123,11 +118,18 @@ class CArray
123
118
  # Linear-by-index gap fill: interpolate each masked cell from the two
124
119
  # bracketing valid cells, x = cell index along the axis. Cells outside the
125
120
  # valid range (leading/trailing) stay masked. A Face goes through its own
126
- # linear_fetch, which keeps its unit and rounds to its grid; whether that
127
- # means anything is the Face's call (an ORDERABLE numeric one answers, the
128
- # rest raise from there), so this gate only asks that it be one.
121
+ # linear_fetch, which keeps its unit and rounds to its grid. Defining one
122
+ # is how a Face says it interpolates: without it, the core linear_fetch
123
+ # reads the Face's storage and fails from inside.
129
124
  def __gap_fill_linear__ (axis)
130
- unless numeric? || face?
125
+ if face?
126
+ unless __interpolable__
127
+ raise ArgumentError,
128
+ "unmask/strip_mask(method: :linear): #{self.class} does not " \
129
+ "interpolate (it defines no linear_fetch of its own); " \
130
+ "use method: :forward or :backward"
131
+ end
132
+ elsif ! numeric?
131
133
  raise ArgumentError,
132
134
  "unmask/strip_mask(method: :linear): numeric or time data_type " \
133
135
  "required (got #{data_type_name})"
@@ -135,44 +137,58 @@ class CArray
135
137
  if axis.nil?
136
138
  return __linear_fiber__(flatten).reshape(*shape)
137
139
  end
138
- ax = axis < 0 ? axis + ndim : axis
139
- if ax < 0 || ax >= ndim
140
- raise ArgumentError, "axis #{axis} out of range for ndim #{ndim}"
141
- end
142
- # A Face assembles in storage space -- its linear_fetch has already rounded
143
- # to the grid, so there is nothing left to hold in float64. A numeric
144
- # array assembles in float64 and casts back once at the end.
145
- out = face? ? template : float64.copy
146
- sink = face? ? out.parent : out
140
+ ax = axis
141
+ # A fiber comes back in self's own type (a Face's linear_fetch has already
142
+ # rounded to its grid), so the result assembles in that type and the
143
+ # present cells are never carried through float64.
144
+ out = face? ? template : value.copy
147
145
  __each_fiber_key__(ax) do |key|
148
- fiber = __linear_fiber__(self[*key])
149
- sink[*key] = face? ? fiber.parent : fiber
146
+ out[*key] = __linear_fiber__(self[*key])
150
147
  end
151
- face? ? out : out.to_type(data_type)
148
+ out
152
149
  end
153
150
 
154
- # 1-D linear-by-index fill of a single (possibly masked) fiber. Present
155
- # cells reproduce exactly; interior masked cells interpolate; exterior
156
- # masked cells become UNDEF (linear_section returns NaN out of range,
157
- # mask_invalid marks it). Returns the same kind as `vec`: a Face fiber
158
- # comes back as that Face, on its own unit.
151
+ # Whether linear interpolation can fill this array: a numeric array, or a
152
+ # Face that defines a linear_fetch of its own -- which is how a Face says it
153
+ # interpolates (the core linear_fetch would read its storage).
154
+ private def __interpolable__
155
+ face? ? self.class.instance_method(:linear_fetch).owner != CArray : numeric?
156
+ end
157
+
158
+ # 1-D linear-by-index fill of a single (possibly masked) fiber.
159
159
  def __linear_fiber__ (vec)
160
- n = vec.elements
161
- present = vec.is_not_masked
160
+ vec.send(:__linear_fill_along__, CArray.float64(vec.elements) { |i| i.to_f })
161
+ end
162
+
163
+ # Fill the masked cells of this 1-D array by linear interpolation against
164
+ # the coordinate `x` (same length, ascending over the present cells).
165
+ # The one rule both the core (x = cell position) and CAFrame (x = index)
166
+ # use: present cells come back exactly as they are, in this array's own
167
+ # type; a masked cell inside the span of the present ones takes the
168
+ # interpolated value; one outside it stays masked. Outside is where
169
+ # linear_section answers NaN -- a property of x, not of the values, so a
170
+ # NaN or Inf stored in a present cell is left alone. A Face goes through
171
+ # its own linear_fetch, which keeps its unit and rounds to its grid.
172
+ def __linear_fill_along__ (x)
173
+ present = is_not_masked
162
174
  # Fewer than two valid points -> nothing to interpolate between; leave
163
- # every masked cell masked (the copy carries vec's mask).
164
- if present.count(true) < 2
165
- return vec.face? ? vec.copy : vec.float64.copy
166
- end
167
- pos = CArray.float64(n) { |i| i.to_f }
168
- addr = pos[present].linear_section(pos) # valid positions -> monotonic grid
169
- if vec.face?
175
+ # every masked cell masked.
176
+ return copy if present.count(true) < 2
177
+ addr = x[present].linear_section(x) # valid positions -> monotonic grid
178
+ if face?
170
179
  # The Face's linear_fetch already masks the exterior (out of range) and
171
- # lands on its own grid, so it needs no cast and no mask_invalid.
172
- return vec[present].linear_fetch(addr)
180
+ # lands on its own grid.
181
+ return self[present].linear_fetch(addr)
173
182
  end
174
- vval = vec.value.float64[present] # valid values
175
- vval.linear_fetch(addr).to_type(vec.data_type).mask_invalid
183
+ interp = value.float64[present].linear_fetch(addr)
184
+ # Built from the values alone, so a fully filled result carries no mask.
185
+ out = value.copy
186
+ masked = is_masked
187
+ fill = masked & addr.is_finite
188
+ exterior = masked & fill.not
189
+ out[fill] = interp[fill] if fill.any
190
+ out[exterior] = UNDEF if exterior.any
191
+ out
176
192
  end
177
193
 
178
194
  # Yield an index key (Array with `nil` at `axis`, integers elsewhere)
data/lib/carray/math.rb CHANGED
@@ -3,23 +3,30 @@ module CAMath
3
3
  module_function
4
4
 
5
5
  # Module-function front-ends for binop math methods registered on
6
- # CArray by mkkernel. The first argument is auto-wrapped to CArray
7
- # via `CArray.wrap_readonly`, so `CAMath.hypot(3, arr)` continues to
8
- # work. Integer input is unsupported at the kernel layer -- the
9
- # caller must cast to float64 explicitly (e.g.
10
- # `arr.as_float64.hypot(...)`).
6
+ # CArray by mkkernel. A CArray first argument answers through its own
7
+ # method, in its own data_type; any other first argument, and an
8
+ # integer CArray for a function defined only on floats, is taken as
9
+ # float64, so `CAMath.hypot(3, arr)` works.
11
10
 
12
11
  # @overload expm1(x)
13
- # Returns `exp(x) - 1` element-wise as float64.
12
+ # Returns `exp(x) - 1` element-wise. A CArray answers in its own
13
+ # `data_type` (integer input widens to float64, as {CArray#expm1}
14
+ # does); any other value is taken as float64.
14
15
  # @param x [CArray, Numeric] input value.
15
16
  # @return [CArray]
16
- def expm1(x); CArray.wrap_readonly(x, :float64).expm1; end
17
+ def expm1(x)
18
+ x.is_a?(CArray) ? x.expm1 : CArray.wrap_readonly(x, :float64).expm1
19
+ end
17
20
 
18
21
  # @overload log1p(x)
19
- # Returns `log(1 + x)` element-wise as float64.
22
+ # Returns `log(1 + x)` element-wise. A CArray answers in its own
23
+ # `data_type` (integer input widens to float64, as {CArray#log1p}
24
+ # does); any other value is taken as float64.
20
25
  # @param x [CArray, Numeric] input value.
21
26
  # @return [CArray]
22
- def log1p(x); CArray.wrap_readonly(x, :float64).log1p; end
27
+ def log1p(x)
28
+ x.is_a?(CArray) ? x.log1p : CArray.wrap_readonly(x, :float64).log1p
29
+ end
23
30
 
24
31
  # @overload atan2(y, x)
25
32
  # Returns the element-wise arc tangent of `y / x` with quadrant
@@ -27,21 +34,21 @@ module CAMath
27
34
  # @param y [CArray, Numeric] numerator.
28
35
  # @param x [CArray, Numeric] denominator.
29
36
  # @return [CArray]
30
- def atan2(y, x); CArray.wrap_readonly(y, :float64).atan2(x); end
37
+ def atan2(y, x); float_operand(y).atan2(x); end
31
38
 
32
39
  # @overload hypot(x, y)
33
40
  # Returns the element-wise Euclidean distance `sqrt(x^2 + y^2)`.
34
41
  # @param x [CArray, Numeric] first leg.
35
42
  # @param y [CArray, Numeric] second leg.
36
43
  # @return [CArray]
37
- def hypot(x, y); CArray.wrap_readonly(x, :float64).hypot(y); end
44
+ def hypot(x, y); float_operand(x).hypot(y); end
38
45
 
39
46
  # @overload copysign(x, y)
40
47
  # Returns `|x|` with the sign of `y`, element-wise.
41
48
  # @param x [CArray, Numeric] magnitude source.
42
49
  # @param y [CArray, Numeric] sign source.
43
50
  # @return [CArray]
44
- def copysign(x, y); CArray.wrap_readonly(x, :float64).copysign(y); end
51
+ def copysign(x, y); float_operand(x).copysign(y); end
45
52
 
46
53
  # @overload logaddexp(x, y)
47
54
  # Returns `log(exp(x) + exp(y))` computed to avoid overflow,
@@ -49,7 +56,7 @@ module CAMath
49
56
  # @param x [CArray, Numeric] first log-space value.
50
57
  # @param y [CArray, Numeric] second log-space value.
51
58
  # @return [CArray]
52
- def logaddexp(x, y); CArray.wrap_readonly(x, :float64).logaddexp(y); end
59
+ def logaddexp(x, y); float_operand(x).logaddexp(y); end
53
60
 
54
61
  # @overload nextafter(x, y)
55
62
  # Returns the next representable float from `x` toward `y`,
@@ -57,21 +64,27 @@ module CAMath
57
64
  # @param x [CArray, Numeric] starting value.
58
65
  # @param y [CArray, Numeric] direction target.
59
66
  # @return [CArray]
60
- def nextafter(x, y); CArray.wrap_readonly(x, :float64).nextafter(y); end
67
+ def nextafter(x, y); float_operand(x).nextafter(y); end
61
68
 
62
69
  # @overload fmod(x, y)
63
70
  # Returns the C-style `fmod(x, y)` element-wise (sign follows `x`).
64
71
  # @param x [CArray, Numeric] dividend.
65
72
  # @param y [CArray, Numeric] divisor.
66
73
  # @return [CArray]
67
- def fmod(x, y); CArray.wrap_readonly(x, :float64).fmod(y); end
74
+ def fmod(x, y); (x.is_a?(CArray) ? x : CArray.wrap_readonly(x, :float64)).fmod(y); end
75
+
76
+ # A float or complex CArray as it is; anything else as float64.
77
+ def float_operand(x)
78
+ x.is_a?(CArray) && (x.float? || x.complex?) ? x : CArray.wrap_readonly(x, :float64)
79
+ end
80
+ private_class_method :float_operand
68
81
 
69
82
  # @overload min(*argv)
70
83
  # Returns the element-wise minimum of the given CArray and other
71
84
  # arguments. At least one argument must be a CArray.
72
85
  # @param argv [Array<CArray, Numeric>] operands.
73
86
  # @return [CArray] fresh CArray holding the running min.
74
- # @raise [RuntimeError] when no CArray argument is present.
87
+ # @raise [ArgumentError] when no CArray argument is present.
75
88
  def min (*argv)
76
89
  if ary = argv.find{|x| x.is_a?(CArray) }
77
90
  out = ary.copy
@@ -80,7 +93,7 @@ module CAMath
80
93
  out.pmin!(x)
81
94
  end
82
95
  else
83
- raise "args should contain more than one CArray object"
96
+ raise ArgumentError, "args should contain more than one CArray object"
84
97
  end
85
98
  return out
86
99
  end
@@ -90,7 +103,7 @@ module CAMath
90
103
  # arguments. At least one argument must be a CArray.
91
104
  # @param argv [Array<CArray, Numeric>] operands.
92
105
  # @return [CArray] fresh CArray holding the running max.
93
- # @raise [RuntimeError] when no CArray argument is present.
106
+ # @raise [ArgumentError] when no CArray argument is present.
94
107
  def max (*argv)
95
108
  if ary = argv.find{|x| x.is_a?(CArray) }
96
109
  out = ary.copy
@@ -99,7 +112,7 @@ module CAMath
99
112
  out.pmax!(x)
100
113
  end
101
114
  else
102
- raise "args should contain more than one CArray object"
115
+ raise ArgumentError, "args should contain more than one CArray object"
103
116
  end
104
117
  return out
105
118
  end