carray 3.0.0 → 3.0.2

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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +845 -3
  4. data/{NEWS.md → CHANGELOG.v1.md} +1 -1
  5. data/README.md +33 -33
  6. data/carray.gemspec +12 -8
  7. data/ext/ca_axis_dispatch.c +33 -4
  8. data/ext/ca_axis_group.c +256 -114
  9. data/ext/ca_binop_dispatch.c +5 -6
  10. data/ext/ca_binop_dispatch.h +0 -7
  11. data/ext/ca_categorical_iterator.c +121 -67
  12. data/ext/ca_for_each_element.h +6 -8
  13. data/ext/ca_group_iter.c +1 -1
  14. data/ext/ca_kernel_iterator.c +386 -74
  15. data/ext/ca_kernel_iterator.h +145 -45
  16. data/ext/ca_obj_array.c +62 -20
  17. data/ext/ca_obj_bincmp.c +57 -27
  18. data/ext/ca_obj_binop.c +86 -30
  19. data/ext/ca_obj_block.c +4 -4
  20. data/ext/ca_obj_const_string.c +88 -27
  21. data/ext/ca_obj_face.c +104 -0
  22. data/ext/ca_obj_face.h +48 -1
  23. data/ext/ca_obj_fake.c +11 -0
  24. data/ext/ca_obj_fixlen_string.c +21 -6
  25. data/ext/ca_obj_grid.c +11 -3
  26. data/ext/ca_obj_meld.c +125 -22
  27. data/ext/ca_obj_moncmp.c +33 -14
  28. data/ext/ca_obj_monop.c +19 -7
  29. data/ext/ca_obj_object.c +107 -4
  30. data/ext/ca_obj_record.c +3 -1
  31. data/ext/ca_obj_refer.c +30 -6
  32. data/ext/ca_obj_roll.c +13 -5
  33. data/ext/ca_obj_select.c +49 -34
  34. data/ext/ca_obj_select_axis.c +16 -6
  35. data/ext/ca_obj_shift.c +3 -3
  36. data/ext/ca_obj_stack.c +3 -8
  37. data/ext/ca_obj_stride.c +178 -24
  38. data/ext/ca_obj_string.c +11 -5
  39. data/ext/ca_obj_tile.c +13 -5
  40. data/ext/ca_obj_time.c +3 -1
  41. data/ext/ca_obj_timedelta.c +3 -1
  42. data/ext/ca_obj_transpose.c +1 -1
  43. data/ext/ca_obj_triop.c +62 -23
  44. data/ext/ca_obj_window.c +94 -19
  45. data/ext/ca_op_cmplx64.h +123 -0
  46. data/ext/ca_op_ipower.c +1 -5
  47. data/ext/ca_rng_normal.h +42 -0
  48. data/ext/ca_rng_xoshiro256pp.h +105 -0
  49. data/ext/ca_sort_kernels.h +5 -5
  50. data/ext/ca_sweep_engine.c +362 -155
  51. data/ext/ca_sweep_engine.h +39 -9
  52. data/ext/ca_transform_common.c +7 -0
  53. data/ext/carray.h +148 -132
  54. data/ext/carray_access.c +83 -49
  55. data/ext/carray_address_basis.c +590 -0
  56. data/ext/carray_bincount.c +8 -8
  57. data/ext/carray_broadcast.c +102 -9
  58. data/ext/carray_build_flags.h +3 -0
  59. data/ext/carray_call_cfunc.c +2010 -335
  60. data/ext/carray_call_cfunc.h +153 -0
  61. data/ext/carray_cast.c +184 -72
  62. data/ext/carray_conversion.c +22 -22
  63. data/ext/carray_copy.c +67 -32
  64. data/ext/carray_core.c +140 -13
  65. data/ext/carray_count.c +10 -11
  66. data/ext/carray_element.c +7 -7
  67. data/ext/carray_factorize.c +72 -51
  68. data/ext/carray_hold.c +1 -1
  69. data/ext/carray_index_classifier.c +7 -21
  70. data/ext/carray_internal.h +36 -3
  71. data/ext/carray_kernels_bincmp.c +1 -0
  72. data/ext/carray_kernels_binop.c +320 -298
  73. data/ext/carray_kernels_init.c +1095 -0
  74. data/ext/carray_kernels_map.c +1 -0
  75. data/ext/carray_kernels_moncmp.c +1 -0
  76. data/ext/carray_kernels_monop.c +412 -411
  77. data/ext/carray_kernels_reduce_aggregate.c +256 -87
  78. data/ext/carray_kernels_reduce_boolean.c +1 -0
  79. data/ext/carray_kernels_reduce_cumulative.c +324 -54
  80. data/ext/carray_kernels_reduce_extreme.c +605 -58
  81. data/ext/carray_kernels_reduce_variance.c +1 -0
  82. data/ext/carray_kernels_scan.c +5 -4
  83. data/ext/carray_kernels_search.c +187 -106
  84. data/ext/carray_kernels_sort.c +1 -0
  85. data/ext/carray_kernels_triop.c +1 -0
  86. data/ext/carray_lazy.c +175 -5
  87. data/ext/carray_loop.c +7 -1
  88. data/ext/carray_mask.c +29 -13
  89. data/ext/carray_median_percentile.c +56 -1
  90. data/ext/carray_memory_view.c +46 -21
  91. data/ext/carray_operator.c +50 -53
  92. data/ext/carray_order.c +1 -1
  93. data/ext/carray_partition.c +4 -4
  94. data/ext/carray_random.c +391 -47
  95. data/ext/carray_scatter.c +1 -1
  96. data/ext/carray_slab.c +13 -0
  97. data/ext/carray_sort.c +22 -24
  98. data/ext/carray_sort_kernel.c +10 -10
  99. data/ext/carray_test.c +1 -1
  100. data/ext/extconf.rb +18 -0
  101. data/ext/mk_call_cfunc.rb +323 -145
  102. data/ext/mkkernel.rb +651 -129
  103. data/ext/ruby_carray.c +28 -17
  104. data/ext/version.h +4 -4
  105. data/lib/carray/autoload_carray.rb +24 -3
  106. data/lib/carray/autoload_method_extension.rb +14 -1
  107. data/lib/carray/axis_group.rb +84 -7
  108. data/lib/carray/basics.rb +63 -59
  109. data/lib/carray/bincount_nd.rb +12 -12
  110. data/lib/carray/block_iterator.rb +111 -25
  111. data/lib/carray/categorical.rb +154 -37
  112. data/lib/carray/categorical_iterator.rb +300 -151
  113. data/lib/carray/conditional.rb +14 -14
  114. data/lib/carray/const_string.rb +131 -27
  115. data/lib/carray/construct.rb +61 -0
  116. data/lib/carray/core_extensions.rb +62 -5
  117. data/lib/carray/data_type_extension.rb +21 -1
  118. data/lib/carray/data_type_limits.rb +91 -0
  119. data/lib/carray/fixlen_string.rb +1 -1
  120. data/lib/carray/frame/concat.rb +3 -3
  121. data/lib/carray/frame/convert.rb +1 -1
  122. data/lib/carray/frame/csv_parser.rb +11 -4
  123. data/lib/carray/frame/frame.rb +85 -21
  124. data/lib/carray/frame/group.rb +36 -3
  125. data/lib/carray/frame/io.rb +224 -15
  126. data/lib/carray/frame/records.rb +18 -4
  127. data/lib/carray/frame/sort.rb +1 -1
  128. data/lib/carray/frame/verbs.rb +32 -12
  129. data/lib/carray/fuse_source.rb +123 -0
  130. data/lib/carray/fusion.rb +218 -0
  131. data/lib/carray/histogram.rb +16 -16
  132. data/lib/carray/inspect.rb +43 -16
  133. data/lib/carray/iterator.rb +147 -3
  134. data/lib/carray/lazy.rb +88 -73
  135. data/lib/carray/mask_gap_fill.rb +3 -1
  136. data/lib/carray/meld_reduce.rb +2 -2
  137. data/lib/carray/methods/align_addr.rb +1 -1
  138. data/lib/carray/methods/composition.rb +1 -1
  139. data/lib/carray/methods/discovery_along.rb +74 -0
  140. data/lib/carray/methods/factorize.rb +50 -0
  141. data/lib/carray/methods/is_in.rb +25 -14
  142. data/lib/carray/methods/locate_addr.rb +81 -3
  143. data/lib/carray/methods/mask_duplicates.rb +36 -2
  144. data/lib/carray/methods/meshgrid.rb +4 -5
  145. data/lib/carray/methods/mode.rb +2 -2
  146. data/lib/carray/methods/nunique.rb +23 -2
  147. data/lib/carray/methods/repeat.rb +110 -0
  148. data/lib/carray/methods/snap.rb +7 -2
  149. data/lib/carray/methods/unique.rb +44 -4
  150. data/lib/carray/methods/value_counts.rb +2 -2
  151. data/lib/carray/rng.rb +86 -0
  152. data/lib/carray/runtime.rb +0 -19
  153. data/lib/carray/slab_iterator.rb +77 -19
  154. data/lib/carray/string_operation_extension.rb +10 -6
  155. data/lib/carray/time.rb +906 -491
  156. data/lib/carray/window_iterator.rb +421 -27
  157. data/lib/carray.rb +7 -5
  158. data/yard-stubs/ca_obj_array.rb +385 -0
  159. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  160. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  161. data/yard-stubs/ca_obj_block.rb +68 -0
  162. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  163. data/yard-stubs/ca_obj_fake.rb +31 -0
  164. data/yard-stubs/ca_obj_farray.rb +32 -0
  165. data/yard-stubs/ca_obj_field.rb +45 -0
  166. data/yard-stubs/ca_obj_grid.rb +35 -0
  167. data/yard-stubs/ca_obj_refer.rb +72 -0
  168. data/yard-stubs/ca_obj_roll.rb +45 -0
  169. data/yard-stubs/ca_obj_shift.rb +43 -0
  170. data/yard-stubs/ca_obj_stride.rb +181 -0
  171. data/yard-stubs/ca_obj_tile.rb +29 -0
  172. data/yard-stubs/ca_obj_transpose.rb +40 -0
  173. data/yard-stubs/ca_obj_window.rb +57 -0
  174. data/yard-stubs/carray_access.rb +131 -0
  175. data/yard-stubs/carray_attribute.rb +246 -0
  176. data/yard-stubs/carray_broadcast.rb +37 -0
  177. data/yard-stubs/carray_cast.rb +489 -0
  178. data/yard-stubs/carray_class.rb +65 -0
  179. data/yard-stubs/carray_conversion.rb +76 -0
  180. data/yard-stubs/carray_copy.rb +79 -0
  181. data/yard-stubs/carray_core.rb +34 -0
  182. data/yard-stubs/carray_count.rb +84 -0
  183. data/yard-stubs/carray_element.rb +108 -0
  184. data/yard-stubs/carray_generate.rb +66 -0
  185. data/yard-stubs/carray_lazy.rb +228 -0
  186. data/yard-stubs/carray_loop.rb +140 -0
  187. data/yard-stubs/carray_mask.rb +259 -0
  188. data/yard-stubs/carray_math.rb +1615 -0
  189. data/yard-stubs/carray_mathfunc.rb +45 -0
  190. data/yard-stubs/carray_median_percentile.rb +103 -0
  191. data/yard-stubs/carray_memory_view.rb +163 -0
  192. data/yard-stubs/carray_order.rb +252 -0
  193. data/yard-stubs/carray_random.rb +89 -0
  194. data/yard-stubs/carray_scatter.rb +106 -0
  195. data/yard-stubs/carray_slab.rb +59 -0
  196. data/yard-stubs/carray_sort.rb +165 -0
  197. data/yard-stubs/carray_test.rb +85 -0
  198. data/yard-stubs/carray_undef.rb +64 -0
  199. data/yard-stubs/carray_utils.rb +97 -0
  200. data/yard-stubs/ruby_carray.rb +193 -0
  201. metadata +67 -10
  202. data/ext/ca_obj_unbound_repeat.c +0 -496
@@ -81,7 +81,7 @@ class CArray
81
81
  result
82
82
  end
83
83
 
84
- # @overload conditional(cond, then_fn, else_fn, dtype: nil)
84
+ # @overload conditional(cond, then_fn, else_fn, data_type: nil)
85
85
  # Returns per-cell `then_fn.call(self[cond])` where `cond` is
86
86
  # true and `else_fn.call(self[cond.not])` where it is false.
87
87
  # The two callables are applied only to their own subset of
@@ -91,14 +91,14 @@ class CArray
91
91
  # Scalar returns from a callable (e.g. `->(v) { 0 }`) broadcast
92
92
  # to the subset shape. The result `data_type` is the promotion
93
93
  # of the two subset results via `CArray.result_type`, or
94
- # `dtype` when given. Masked cells in `cond` propagate to
94
+ # `data_type` when given. Masked cells in `cond` propagate to
95
95
  # `UNDEF` in the result.
96
96
  #
97
97
  # @param cond [CArray] boolean selector; same shape as `self`.
98
98
  # @param then_fn [#call] callable applied to `self[cond]`.
99
99
  # @param else_fn [#call] callable applied to `self[cond.not]`.
100
- # @param dtype [Symbol, Integer, nil] override for the result
101
- # `data_type`.
100
+ # @param data_type [Symbol, Integer, nil] override for the result
101
+ # data type.
102
102
  # @return [CArray] new array with the same shape as `self`.
103
103
  # @raise [ArgumentError] when `cond` is not a same-shape
104
104
  # boolean CArray.
@@ -108,7 +108,7 @@ class CArray
108
108
  # ->(v) { v.log }, # domain-safe: only positive cells
109
109
  # ->(v) { -v })
110
110
  # # => [2.0, 1.0, -0.0, 0.0, 0.6931..., 1.0986...]
111
- def conditional (cond, then_fn, else_fn, dtype: nil)
111
+ def conditional (cond, then_fn, else_fn, data_type: nil)
112
112
  unless cond.is_a?(CArray) && cond.boolean? && cond.shape == self.shape
113
113
  raise ArgumentError,
114
114
  "conditional: cond must be a boolean CArray with same shape as self"
@@ -123,13 +123,13 @@ class CArray
123
123
  # to the subset shape; wrap it here so the scatter step below sees a
124
124
  # same-length CArray.
125
125
  unless y_then.is_a?(CArray)
126
- y_then = CArray.new(dtype || CArray.result_type(y_then), x_then.shape).fill(y_then)
126
+ y_then = CArray.new(data_type || CArray.result_type(y_then), x_then.shape).fill(y_then)
127
127
  end
128
128
  unless y_else.is_a?(CArray)
129
- y_else = CArray.new(dtype || CArray.result_type(y_else), x_else.shape).fill(y_else)
129
+ y_else = CArray.new(data_type || CArray.result_type(y_else), x_else.shape).fill(y_else)
130
130
  end
131
131
 
132
- dt = dtype || CArray.result_type(y_then, y_else)
132
+ dt = data_type || CArray.result_type(y_then, y_else)
133
133
  out = CArray.new(dt, self.shape)
134
134
  out[cond] = y_then
135
135
  out[cond.not] = y_else
@@ -141,7 +141,7 @@ class CArray
141
141
  out
142
142
  end
143
143
 
144
- # @overload select(condlist, choicelist, default: 0, dtype: nil)
144
+ # @overload select(condlist, choicelist, default: 0, data_type: nil)
145
145
  # Multi-way ternary select: for each cell, picks the value from
146
146
  # the first `choicelist[k]` whose matching `condlist[k]` is true,
147
147
  # falling back to `default` when no condition holds. When several
@@ -151,15 +151,15 @@ class CArray
151
151
  # Each `choicelist[k]` is either a same-shape CArray or a scalar
152
152
  # broadcast to every cell. The result `data_type` is the promotion
153
153
  # of every choice plus `default` via `CArray.result_type`, or
154
- # `dtype` when given.
154
+ # `data_type` when given.
155
155
  #
156
156
  # @param condlist [Array<CArray>] boolean selectors.
157
157
  # @param choicelist [Array<CArray, Numeric, Object>] values, one
158
158
  # per condition (same length as `condlist`).
159
159
  # @param default [CArray, Numeric, Object] value written where no
160
160
  # condition holds.
161
- # @param dtype [Symbol, Integer, nil] override for the result
162
- # `data_type`.
161
+ # @param data_type [Symbol, Integer, nil] override for the result
162
+ # data type.
163
163
  # @return [CArray] new array with the shape of `condlist[0]`.
164
164
  # @raise [ArgumentError] on size mismatch, empty `condlist`, or a
165
165
  # non-boolean / wrong-shape entry in `condlist`.
@@ -169,7 +169,7 @@ class CArray
169
169
  # [-x, x * 10],
170
170
  # default: 999)
171
171
  # # => [5.0, 3.0, -10.0, 0.0, 10.0, 999.0]
172
- def self.select (condlist, choicelist, default: 0, dtype: nil)
172
+ def self.select (condlist, choicelist, default: 0, data_type: nil)
173
173
  unless condlist.is_a?(Array) && choicelist.is_a?(Array)
174
174
  raise ArgumentError, "select: condlist and choicelist must be Arrays"
175
175
  end
@@ -187,7 +187,7 @@ class CArray
187
187
  end
188
188
  shape = first.shape
189
189
 
190
- dt = dtype || CArray.result_type(*choicelist, default)
190
+ dt = data_type || CArray.result_type(*choicelist, default)
191
191
  # `default` can be either a same-shape CArray (per-cell fallback) or a
192
192
  # scalar (broadcast to every cell).
193
193
  default_full = default.is_a?(CArray) && !default.scalar?
@@ -5,12 +5,15 @@
5
5
  # CAConstString high-level construction + conversion surface.
6
6
  #
7
7
  # CAConstString itself (the Face + tail buffer + fetch decode + numeric gate) lives
8
- # in ext/ca_obj_const_string.c. This file provides the ergonomic builders that pack
9
- # Ruby Strings into the internal length-prefix buffer + int64 offset entity
10
- # and wrap them via the CAConstString.wrap C primitive.
8
+ # in ext/ca_obj_const_string.c. This file provides the ergonomic builders that
9
+ # pack Ruby Strings into the shared buffer + (start,end) pair entity, via the
10
+ # CAConstString.__build__ C primitive.
11
11
  #
12
- # Internal buffer format: concatenation of `[int32 length][bytes...]` records
13
- # (self-delimiting → one offset per element, permutation-safe view).
12
+ # Internal buffer format: a pure concatenation of the element bytes, with no
13
+ # per-record length prefix (= the Arrow values buffer). Each element carries
14
+ # its own `(start, end)` byte range in the storage, so it is self-describing
15
+ # and a gather / sort / select view over the pairs decodes correctly with no
16
+ # buffer copy.
14
17
  #
15
18
  # ----------------------------------------------------------------------------
16
19
 
@@ -89,21 +92,118 @@ class CAConstString
89
92
  # override the L1 generics where present.
90
93
  include CArray::StringOperationMixin
91
94
 
92
- # @overload sort
93
- # Returns a byte-order sorted view built by gathering the
94
- # offsets with `sort_index`. Neither bytes nor offsets are
95
- # copied.
95
+ # ---- ordering family ---------------------------------------------------
96
+ #
97
+ # A storage cell is a `(start, end)` byte range into the shared buffer, so
98
+ # storage order is the order the strings were packed in, not the order they
99
+ # compare in. Without these overrides the family sorted the offsets: the
100
+ # answers came back well-formed and wrong -- `%w[pear apple].sort_addr` gave
101
+ # the identity, and `partition_copy` gave NUL bytes.
102
+ #
103
+ # Every member therefore reads the bytes. The flat forms do it natively
104
+ # (a byte-memcmp scan over the packed buffer, an order of magnitude faster
105
+ # than decoding a Ruby String per cell), the per-axis forms through
106
+ # {#to_string}, where a cell is the string and CArray's own kernels apply.
107
+ # Arguments are forwarded verbatim, so `kind:` / `masked_position:` /
108
+ # `keep_axis:` mean here what they mean on CArray.
109
+ #
110
+ # Anything whose cells are strings comes back a CAConstString, gathered
111
+ # over the same buffer where it can be (sort / sort_copy) and rebuilt where
112
+ # it cannot. Indices, addresses and counts need no conversion.
113
+
114
+ # @overload sort(axis: nil, kind: :quick, masked_position: :last)
115
+ # Returns a sorted {CAConstString} view, gathered over the same buffer
116
+ # and offsets. With no `axis:` the array is flattened first, as
117
+ # `CArray#sort` does.
96
118
  # @return [CAConstString]
97
- def sort
98
- self[sort_index]
119
+ def sort (*args, **kw)
120
+ addr = sort_addr(*args, **kw)
121
+ kw[:axis].nil? ? self[addr.flatten] : self[addr]
99
122
  end
100
123
 
101
- # @overload sort_copy
102
- # Returns an owned sorted {CAConstString}; the materialised
103
- # counterpart to {#sort}.
124
+ # @overload sort_copy(axis: nil, kind: :quick, masked_position: :last)
125
+ # Returns an owned sorted {CAConstString}; the materialised counterpart
126
+ # to {#sort}.
104
127
  # @return [CAConstString]
105
- def sort_copy
106
- self[sort_index].copy
128
+ def sort_copy (*args, **kw)
129
+ sort(*args, **kw).copy
130
+ end
131
+
132
+ # @overload sort_addr(axis: nil, kind: :quick, masked_position: :last)
133
+ # Returns the view-flat addresses that index a sort by string order.
134
+ # @return [CArray] `:int64` addresses.
135
+ def sort_addr (*args, **kw)
136
+ # The native scan has no notion of an incomparable sentinel, so a masked
137
+ # column goes the same way a per-axis one does.
138
+ return __sort_addr_bytes__ if args.empty? && kw.empty? && ! has_mask?
139
+ to_string.sort_addr(*args, **kw)
140
+ end
141
+
142
+ # @overload sort_index(axis: nil, kind: :quick, masked_position: :last)
143
+ # Returns the per-fiber indices that index a sort by string order.
144
+ # @return [CArray] `:int64` indices.
145
+ def sort_index (*args, **kw)
146
+ to_string.sort_index(*args, **kw)
147
+ end
148
+
149
+ # @overload rank_index(axis: nil)
150
+ # @return [CArray] each cell's rank in string order.
151
+ def rank_index (*args, **kw)
152
+ to_string.rank_index(*args, **kw)
153
+ end
154
+
155
+ # @overload order(*args)
156
+ # @return [CArray] the ordering of the cells by string order.
157
+ def order (*args, **kw)
158
+ to_string.order(*args, **kw)
159
+ end
160
+
161
+ # @overload min(axis: nil, keep_axis: false)
162
+ # Returns the byte-smallest string, skipping masked cells; UNDEF when
163
+ # every cell is masked or the array is empty.
164
+ # @return [String, CAConstString]
165
+ def min (*args, **kw)
166
+ return __min_bytes__ || UNDEF if args.empty? && kw.empty?
167
+ const_string_lift(to_string.min(*args, **kw))
168
+ end
169
+
170
+ # @overload max(axis: nil, keep_axis: false)
171
+ # Byte-largest counterpart of {#min}.
172
+ # @return [String, CAConstString]
173
+ def max (*args, **kw)
174
+ return __max_bytes__ || UNDEF if args.empty? && kw.empty?
175
+ const_string_lift(to_string.max(*args, **kw))
176
+ end
177
+
178
+ # @overload minmax(axis: nil, keep_axis: false)
179
+ # @return [Array] `[min, max]`.
180
+ def minmax (*args, **kw)
181
+ return [min, max] if args.empty? && kw.empty?
182
+ to_string.minmax(*args, **kw).map { |r| const_string_lift(r) }
183
+ end
184
+
185
+ # @overload min_index(axis: nil)
186
+ # @return [Integer, CArray] where the byte-smallest string sits.
187
+ def min_index (*args, **kw)
188
+ to_string.min_index(*args, **kw)
189
+ end
190
+
191
+ # @overload max_index(axis: nil)
192
+ # @return [Integer, CArray] where the byte-largest string sits.
193
+ def max_index (*args, **kw)
194
+ to_string.max_index(*args, **kw)
195
+ end
196
+
197
+ # @overload partition_copy(kth, axis: nil)
198
+ # @return [CAConstString] partitioned about the `kth` string in order.
199
+ def partition_copy (*args, **kw)
200
+ const_string_lift(to_string.partition_copy(*args, **kw))
201
+ end
202
+
203
+ # @overload partition_index(kth, axis: nil)
204
+ # @return [CArray] the indices that partition about the `kth` string.
205
+ def partition_index (*args, **kw)
206
+ to_string.partition_index(*args, **kw)
107
207
  end
108
208
 
109
209
  # CAConstString is the one Face with three separate entities -- storage
@@ -144,14 +244,14 @@ class CAConstString
144
244
  # @overload unique(sort: false)
145
245
  # @return [CAConstString] the distinct strings.
146
246
  def unique (sort: false)
147
- to_string.unique(sort: sort).to_const_string
247
+ const_string_lift(to_string.unique(sort: sort))
148
248
  end
149
249
 
150
250
  # @overload value_counts(sort: false)
151
251
  # @return [Array(CAConstString, CArray)] `[values, counts]`.
152
252
  def value_counts (sort: false)
153
253
  values, counts = to_string.value_counts(sort: sort)
154
- [values.to_const_string, counts]
254
+ [const_string_lift(values), counts]
155
255
  end
156
256
 
157
257
  # @overload nunique(axis: nil, keep_axis: false)
@@ -164,11 +264,7 @@ class CAConstString
164
264
  # @return [CAConstString, Array] the most frequent string(s).
165
265
  def mode (axis: nil)
166
266
  r = to_string.mode(axis: axis)
167
- case r
168
- when CArray then r.to_const_string
169
- when Array then r.map { |c| c.is_a?(CArray) ? c.to_const_string : c }
170
- else r
171
- end
267
+ r.is_a?(Array) ? r.map { |c| const_string_lift(c) } : const_string_lift(r)
172
268
  end
173
269
 
174
270
  # @overload is_mode(axis: nil)
@@ -180,7 +276,7 @@ class CAConstString
180
276
  # @overload mask_duplicates(axis: nil)
181
277
  # @return [CAConstString] a copy with every repeat occurrence masked.
182
278
  def mask_duplicates (axis: nil)
183
- to_string.mask_duplicates(axis: axis).to_const_string
279
+ const_string_lift(to_string.mask_duplicates(axis: axis))
184
280
  end
185
281
 
186
282
  # @overload is_in(values)
@@ -192,19 +288,19 @@ class CAConstString
192
288
  # @overload intersection(other, sort: false)
193
289
  # @return [CAConstString] the distinct strings present in both.
194
290
  def intersection (other, sort: false)
195
- to_string.intersection(string_operand(other), sort: sort).to_const_string
291
+ const_string_lift(to_string.intersection(string_operand(other), sort: sort))
196
292
  end
197
293
 
198
294
  # @overload difference(other, sort: false)
199
295
  # @return [CAConstString] the distinct strings only `self` has.
200
296
  def difference (other, sort: false)
201
- to_string.difference(string_operand(other), sort: sort).to_const_string
297
+ const_string_lift(to_string.difference(string_operand(other), sort: sort))
202
298
  end
203
299
 
204
300
  # @overload union(other, sort: false)
205
301
  # @return [CAConstString] the distinct strings of either side.
206
302
  def union (other, sort: false)
207
- to_string.union(string_operand(other), sort: sort).to_const_string
303
+ const_string_lift(to_string.union(string_operand(other), sort: sort))
208
304
  end
209
305
 
210
306
  # @overload locate_addr(ref)
@@ -225,4 +321,12 @@ class CAConstString
225
321
  other.is_a?(CAConstString) ? other.to_string : other
226
322
  end
227
323
 
324
+ # Pack a string-bearing result back into a column of this one's encoding.
325
+ # The encoding has to be carried: the builder checks each element against
326
+ # the column's, so a Shift_JIS column rebuilt as the UTF-8 default raised
327
+ # rather than coming back.
328
+ private def const_string_lift (x)
329
+ x.is_a?(CArray) ? CArray.const_string(x, encoding: encoding) : x
330
+ end
331
+
228
332
  end
@@ -35,6 +35,7 @@ class CArray
35
35
  # CArray whose elements are `boolean`. Naming the element type as a
36
36
  # class lets a constructor be spelled `CArray::Boolean.new(...)`; see
37
37
  # {DataTypeNewConstructor} for what that form adds.
38
+ # @private
38
39
  class Boolean
39
40
  extend DataTypeNewConstructor
40
41
  extend DataTypeExtension
@@ -47,6 +48,7 @@ class CArray
47
48
  # CArray whose elements are `uint8`. Naming the element type as a
48
49
  # class lets a constructor be spelled `CArray::UInt8.new(...)`; see
49
50
  # {DataTypeNewConstructor} for what that form adds.
51
+ # @private
50
52
  class UInt8
51
53
  extend DataTypeNewConstructor
52
54
  extend DataTypeExtension
@@ -59,6 +61,7 @@ class CArray
59
61
  # CArray whose elements are `uint16`. Naming the element type as a
60
62
  # class lets a constructor be spelled `CArray::UInt16.new(...)`; see
61
63
  # {DataTypeNewConstructor} for what that form adds.
64
+ # @private
62
65
  class UInt16
63
66
  extend DataTypeNewConstructor
64
67
  extend DataTypeExtension
@@ -71,6 +74,7 @@ class CArray
71
74
  # CArray whose elements are `uint32`. Naming the element type as a
72
75
  # class lets a constructor be spelled `CArray::UInt32.new(...)`; see
73
76
  # {DataTypeNewConstructor} for what that form adds.
77
+ # @private
74
78
  class UInt32
75
79
  extend DataTypeNewConstructor
76
80
  extend DataTypeExtension
@@ -83,6 +87,7 @@ class CArray
83
87
  # CArray whose elements are `uint64`. Naming the element type as a
84
88
  # class lets a constructor be spelled `CArray::UInt64.new(...)`; see
85
89
  # {DataTypeNewConstructor} for what that form adds.
90
+ # @private
86
91
  class UInt64
87
92
  extend DataTypeNewConstructor
88
93
  extend DataTypeExtension
@@ -95,6 +100,7 @@ class CArray
95
100
  # CArray whose elements are `int8`. Naming the element type as a
96
101
  # class lets a constructor be spelled `CArray::Int8.new(...)`; see
97
102
  # {DataTypeNewConstructor} for what that form adds.
103
+ # @private
98
104
  class Int8
99
105
  extend DataTypeNewConstructor
100
106
  extend DataTypeExtension
@@ -107,6 +113,7 @@ class CArray
107
113
  # CArray whose elements are `int16`. Naming the element type as a
108
114
  # class lets a constructor be spelled `CArray::Int16.new(...)`; see
109
115
  # {DataTypeNewConstructor} for what that form adds.
116
+ # @private
110
117
  class Int16
111
118
  extend DataTypeNewConstructor
112
119
  extend DataTypeExtension
@@ -119,6 +126,7 @@ class CArray
119
126
  # CArray whose elements are `int32`. Naming the element type as a
120
127
  # class lets a constructor be spelled `CArray::Int32.new(...)`; see
121
128
  # {DataTypeNewConstructor} for what that form adds.
129
+ # @private
122
130
  class Int32
123
131
  extend DataTypeNewConstructor
124
132
  extend DataTypeExtension
@@ -131,6 +139,7 @@ class CArray
131
139
  # CArray whose elements are `int64`. Naming the element type as a
132
140
  # class lets a constructor be spelled `CArray::Int64.new(...)`; see
133
141
  # {DataTypeNewConstructor} for what that form adds.
142
+ # @private
134
143
  class Int64
135
144
  extend DataTypeNewConstructor
136
145
  extend DataTypeExtension
@@ -143,6 +152,7 @@ class CArray
143
152
  # CArray whose elements are `float32`. Naming the element type as a
144
153
  # class lets a constructor be spelled `CArray::Float32.new(...)`; see
145
154
  # {DataTypeNewConstructor} for what that form adds.
155
+ # @private
146
156
  class Float32
147
157
  extend DataTypeNewConstructor
148
158
  extend DataTypeExtension
@@ -153,11 +163,13 @@ class CArray
153
163
  end
154
164
 
155
165
  # Numo-compatible alias of {Float32}.
166
+ # @private
156
167
  SFloat = Float32
157
168
 
158
169
  # CArray whose elements are `float64`. Naming the element type as a
159
170
  # class lets a constructor be spelled `CArray::Float64.new(...)`; see
160
171
  # {DataTypeNewConstructor} for what that form adds.
172
+ # @private
161
173
  class Float64
162
174
  extend DataTypeNewConstructor
163
175
  extend DataTypeExtension
@@ -168,11 +180,13 @@ class CArray
168
180
  end
169
181
 
170
182
  # Numo-compatible alias of {Float64}.
183
+ # @private
171
184
  DFloat = Float64
172
185
 
173
186
  # CArray whose elements are `complex64`. Naming the element type as a
174
187
  # class lets a constructor be spelled `CArray::Complex64.new(...)`; see
175
188
  # {DataTypeNewConstructor} for what that form adds.
189
+ # @private
176
190
  class Complex64
177
191
  extend DataTypeNewConstructor
178
192
  extend DataTypeExtension
@@ -183,11 +197,13 @@ class CArray
183
197
  end
184
198
 
185
199
  # Numo-compatible alias of {Complex64}.
200
+ # @private
186
201
  SComplex = Complex64
187
202
 
188
203
  # CArray whose elements are `complex128`. Naming the element type as a
189
204
  # class lets a constructor be spelled `CArray::Complex128.new(...)`; see
190
205
  # {DataTypeNewConstructor} for what that form adds.
206
+ # @private
191
207
  class Complex128
192
208
  extend DataTypeNewConstructor
193
209
  extend DataTypeExtension
@@ -198,11 +214,13 @@ class CArray
198
214
  end
199
215
 
200
216
  # Numo-compatible alias of {Complex128}.
217
+ # @private
201
218
  DComplex = Complex128
202
219
 
203
220
  # CArray whose elements are `object`. Naming the element type as a
204
221
  # class lets a constructor be spelled `CArray::Object.new(...)`; see
205
222
  # {DataTypeNewConstructor} for what that form adds.
223
+ # @private
206
224
  class Object
207
225
  extend DataTypeNewConstructor
208
226
  extend DataTypeExtension
@@ -213,11 +231,13 @@ class CArray
213
231
  end
214
232
 
215
233
  # Numo-compatible alias of {Object}.
234
+ # @private
216
235
  RObject = Object
217
236
 
218
237
  # CArray whose elements are `fixlen`. Naming the element type as a
219
238
  # class lets a constructor be spelled `CArray::Fixlen.new(...)`; see
220
239
  # {DataTypeNewConstructor} for what that form adds.
240
+ # @private
221
241
  class Fixlen
222
242
  extend DataTypeNewConstructor
223
243
  extend DataTypeExtension
@@ -232,9 +252,50 @@ end
232
252
  class CArray
233
253
  extend DataTypeExtension
234
254
  # @!visibility private
255
+ # @private
235
256
  TypeSymbol = nil
236
257
  # @!visibility private
237
258
  DataType = nil
259
+
260
+ # @overload empty(data_type, dim, bytes: nil)
261
+ # Returns a new CArray of `data_type` with the shape `dim`, whose
262
+ # contents are **undefined**. This is {CArray.new} without the fill:
263
+ # the arguments are read the same way, and the cost of zeroing the
264
+ # buffer is not paid. Use it when every cell is written before it is
265
+ # read; otherwise use `CArray.new`, which zero-fills.
266
+ #
267
+ # `CA_OBJECT` is the exception: its cells are zero-initialised
268
+ # anyway, because the garbage collector walks them.
269
+ #
270
+ # @param data_type [Symbol, String, Integer] element type.
271
+ # @param dim [Array<Integer>] shape of the new CArray.
272
+ # @param bytes [Integer, nil] element width, for `:fixlen` only.
273
+ # @return [CArray]
274
+ # @raise [ArgumentError] if a block is given -- filling an array is
275
+ # what `CArray.new` is for.
276
+ # @overload empty(*shape)
277
+ # (Numo / NumPy compatibility) Returns an uninitialised CArray of
278
+ # the given shape. The element type comes from the receiver, so
279
+ # `CArray` itself gives `CA_FLOAT64`; see
280
+ # {DataTypeExtension#empty}.
281
+ # @param shape [Array<Integer>, Array<Array<Integer>>] shape.
282
+ # @return [CArray]
283
+ def self.empty (*args, **options, &block)
284
+ if block
285
+ raise ArgumentError,
286
+ "CArray.empty does not take a block " \
287
+ "(its contents are left undefined); use CArray.new to fill."
288
+ end
289
+ if args.size == 2 and args[1].is_a?(Array)
290
+ return __empty__(*args, **options)
291
+ end
292
+ unless options.empty?
293
+ raise ArgumentError,
294
+ "CArray.empty(*shape) takes no options; bytes: belongs to " \
295
+ "CArray.empty(data_type, dim)"
296
+ end
297
+ super(*args)
298
+ end
238
299
  end
239
300
 
240
301
  class CArray
@@ -10,6 +10,8 @@
10
10
  # - Postfix math on Float/Integer/Rational (sqrt, exp, log, sin, ...)
11
11
  # enabling scalar/CArray polymorphism: the same expression
12
12
  # `(0.0415*(t-218.8)).tanh` works whether t is a Float or a CArray.
13
+ # - The same postfix math on Complex, so an expression written for a
14
+ # complex CArray still reads for a single cell taken out of it.
13
15
  # - Angle normalisation on Numeric (deg_360 / deg_180 / rad_2pi / rad_pi).
14
16
  # - Comparison helpers on Numeric (#eq / #ne) for symmetric usage:
15
17
  # `5.eq(ca)` dispatches to `ca.eq(5)`.
@@ -36,9 +38,9 @@ class CArray
36
38
  # `using CArray::CoreExtensions`.
37
39
  #
38
40
  # It provides postfix math on scalars (so `(0.0415*(t-218.8)).tanh` reads
39
- # the same whether `t` is a Float or a CArray), angle normalisation, and
40
- # comparison helpers. Being a refinement, it is lexically scoped: nothing
41
- # changes for code that does not opt in.
41
+ # the same whether `t` is a Float, a Complex or a CArray), angle
42
+ # normalisation, and comparison helpers. Being a refinement, it is
43
+ # lexically scoped: nothing changes for code that does not opt in.
42
44
  module CoreExtensions
43
45
 
44
46
  # @!visibility private
@@ -51,7 +53,11 @@ class CArray
51
53
  [Float, Integer, Rational].each do |klass|
52
54
  refine(klass) do
53
55
  MATH_METHODS.each do |m|
54
- define_method(m) { Math.send(m, self) }
56
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
57
+ def #{m}
58
+ Math.#{m}(self)
59
+ end
60
+ RUBY
55
61
  end
56
62
  def rad
57
63
  self.to_f * Math::PI / 180.0
@@ -65,7 +71,7 @@ class CArray
65
71
  # M.1 (PyTorch alignment) scalar polymorphism additions.
66
72
  # `trunc` is left to Ruby's built-in semantics (Float#truncate
67
73
  # returns Integer when arg=0) — its CArray counterpart preserves
68
- # dtype, so scalar-side use should call .to_f.trunc explicitly.
74
+ # data type, so scalar-side use should call .to_f.trunc explicitly.
69
75
  # Math.expm1 / Math.log1p don't exist in stdlib; hand-roll.
70
76
  def expm1
71
77
  Math.exp(self) - 1.0
@@ -85,6 +91,57 @@ class CArray
85
91
  end
86
92
  end
87
93
 
94
+ # The subset of MATH_METHODS that CArray's complex kernels implement,
95
+ # plus the two elementwise helpers that also accept complex input.
96
+ #
97
+ # Absent, because a complex CArray raises CArray::DataTypeError for
98
+ # them: log10 (C99 has no clog10) and expm1 / log1p (no complex form
99
+ # in C99 either). Leaving them undefined on Complex keeps the scalar
100
+ # side failing wherever the array side fails. rad / deg / distance /
101
+ # signbit are absent for the same reason from the other direction:
102
+ # they go through #to_f, which Complex does not have.
103
+ #
104
+ # @!visibility private
105
+ COMPLEX_MATH_METHODS = %i[
106
+ sqrt exp log
107
+ sin cos tan sinh cosh tanh
108
+ asin acos atan asinh acosh atanh
109
+ square rsqrt
110
+ ].freeze
111
+
112
+ # Complex is not handled the way Float / Integer / Rational are.
113
+ # Math.tanh(Complex(1,2)) raises RangeError -- of the stdlib Math
114
+ # methods only Math.sqrt accepts a Complex -- so the complex forms
115
+ # need their own implementation.
116
+ #
117
+ # Each one runs the value through a one-element cmplx128 CScalar, so
118
+ # the answer comes from the very kernel the array form would have
119
+ # used. That is the point: the branch cuts of csqrt / clog / casin /
120
+ # cacosh / catanh, and the sign of a zero on either side of them, are
121
+ # whatever the platform's C99 complex.h says they are, and they are
122
+ # not the same everywhere. On this machine, for instance,
123
+ # catanh(-1+0i) yields -Infinity+(pi/4)i where C99 Annex G describes
124
+ # -Infinity+0i. A hand-written Complex implementation would have to
125
+ # reproduce each such quirk to agree with the array form, and would
126
+ # stop agreeing on the next platform; delegating agrees by
127
+ # construction on all of them. The cost is one CScalar per call
128
+ # (~0.4 microseconds), which is the right trade for a scalar
129
+ # convenience whose whole purpose is to read the same as the array.
130
+ #
131
+ # `square` is delegated rather than written as `self * self`: Ruby's
132
+ # Complex multiplication and C's `double _Complex` multiplication part
133
+ # ways on infinities (Complex(1, Inf) squared gives -Inf+Inf*i in Ruby
134
+ # and -Inf+NaN*i in C).
135
+ refine Complex do
136
+ COMPLEX_MATH_METHODS.each do |m|
137
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
138
+ def #{m}
139
+ CA_CMPLX128(self).#{m}[0]
140
+ end
141
+ RUBY
142
+ end
143
+ end
144
+
88
145
  refine Numeric do
89
146
  def deg_360
90
147
  a = self.to_f
@@ -194,10 +194,27 @@ class CArray
194
194
  start = 0
195
195
  stop, = *args
196
196
  step = 1
197
+ else
198
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1..3)"
197
199
  end
200
+ raise ArgumentError, "step must not be 0" if step == 0
198
201
  data_type = self::DataType
199
202
  data_type ||= guess_data_type_from_values(start, stop, step)
200
- CArray.send(:__cast__, data_type, start..stop-step, step)
203
+ # Element count comes from the arguments as given, not from the
204
+ # target data type: CArray::Int32.arange(0, 1, 0.25) counts four
205
+ # elements from the float step and truncates them on store.
206
+ # Integer arguments count exactly (divmod) so that a step dividing
207
+ # the span evenly does not gain a spurious element through float
208
+ # rounding.
209
+ span = stop - start
210
+ if span.is_a?(Integer) && step.is_a?(Integer)
211
+ q, r = span.divmod(step)
212
+ n = r.zero? ? q : q + 1
213
+ else
214
+ n = (span.to_f / step).ceil
215
+ end
216
+ n = 0 if n < 0
217
+ CArray.new(data_type, [n]).seq(start, step)
201
218
  end
202
219
 
203
220
  # @overload full(shape, fill_value)
@@ -220,6 +237,9 @@ class CArray
220
237
  # **uninitialised**. The caller must overwrite the array
221
238
  # before reading from it. `CA_OBJECT` silently falls back to
222
239
  # a zero-VALUE init required for GC safety.
240
+ #
241
+ # carray's own spelling is `CArray.empty(data_type, dim)`, which
242
+ # names the element type instead of taking it from the receiver.
223
243
  # @param shape [Array<Integer>, Array<Array<Integer>>]
224
244
  # @return [CArray]
225
245
  def empty (*args)