carray 2.0.0 → 3.0.0

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 (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +774 -298
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +230 -792
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +208 -241
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -330
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1342 -199
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +4 -14
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
@@ -0,0 +1,150 @@
1
+ # Complex-component accessors (real / real= / imag / imag= / real? /
2
+ # is_real). Loaded eagerly BEFORE carray/lazy.rb, because lazy.rb does
3
+ # `alias_method :__real_eager__, :real` (and the imag equivalent) at load
4
+ # time -- these defs must exist by then.
5
+
6
+ class CArray
7
+
8
+ # complex number
9
+ #
10
+ # ---------------------------------------------------------------------------
11
+ # DO NOT RETIRE real / real= / imag / imag= IN FAVOUR OF THE LAZY FRAMEWORK
12
+ # ---------------------------------------------------------------------------
13
+ #
14
+ # A recurring temptation is to consider these methods redundant now that the
15
+ # lazy substrate provides `c.lazy.real` / `c.lazy.imag` via CAMonOp chains
16
+ # (cast_<float> for real; imag_i + cast_<float> for imag — see
17
+ # lib/carray/lazy.rb). They are NOT redundant. The two paths are
18
+ # **complementary** and serve different abstractions:
19
+ #
20
+ # c.real (here) -> CAField: mutable zero-copy view into
21
+ # the byte slot of complex
22
+ # storage. Read AND write.
23
+ # c.lazy.real (lazy.rb) -> CAMonOp: read-only lazy chain node
24
+ # for streaming consumption.
25
+ #
26
+ # The CAField path supports in-place mutation idioms that the lazy framework
27
+ # cannot express:
28
+ #
29
+ # c = CArray.cmplx128(10)
30
+ # c.real.seq!(1) # write 1..10 into real slot, zero-copy
31
+ # c.imag.seq!(-1,-1) # write -1..-10 into imag slot
32
+ # c.real = some_array # bulk write into real slot
33
+ #
34
+ # These are used by spec/Features/feature_stat_spec.rb and by user code
35
+ # patterns CArray's "byte-slot mutable view" capability is designed to
36
+ # support (= writing to `.real` stores into the real part in place;
37
+ # CAField makes it a zero-copy abstraction available across all
38
+ # CAStride-based view algebra).
39
+ #
40
+ # Architecturally:
41
+ # - lib/carray/lazy.rb's `real`/`imag` overrides dispatch to CAMonOp
42
+ # when self is a lazy view, and fall through to the defs here for eager
43
+ # parents (via `alias_method :__real_eager__, :real` at load time).
44
+ # This file MUST be required before carray/lazy.rb (see carray.rb), and
45
+ # removing these defs would break that alias and the lazy.rb load.
46
+ #
47
+ # - ext/ca_obj_stride.c recognises `CAField .real/.imag over complex`
48
+ # as a first-class byte-mismatched reinterpret pattern. Architectural
49
+ # commitment is already written into the C layer.
50
+ #
51
+ # Outright deletion is a regression on a documented capability (the lazy
52
+ # framework's read-only chain does not replace the CAField mutable view).
53
+ # ---------------------------------------------------------------------------
54
+
55
+ # @overload real
56
+ # Returns the real part of `self` as a zero-copy view. For a
57
+ # complex array the view is a mutable {CAField} into the
58
+ # real-part slot; for a real numeric array it is a {CARefer}
59
+ # over `self`. Writing to the view updates `self` in place.
60
+ # @return [CArray]
61
+ def real
62
+ if not @__real__
63
+ if complex?
64
+ @__real__ = case data_type
65
+ when CA_CMPLX64
66
+ field(0, CA_FLOAT32)
67
+ when CA_CMPLX128
68
+ field(0, CA_FLOAT64)
69
+ end
70
+ else
71
+ @__real__ = self[]
72
+ end
73
+ end
74
+ @__real__
75
+ end
76
+
77
+ # @overload real=(val)
78
+ # Sets the real-part slot to `val` via {#real}.
79
+ # @param val [CArray, Numeric] value to broadcast.
80
+ # @return [Object] `val`.
81
+ def real= (val)
82
+ real[] = val
83
+ end
84
+
85
+ # @overload imag
86
+ # Returns the imaginary part of `self`. For a complex array the
87
+ # result is a mutable {CAField} view of the imaginary slot;
88
+ # writing to it updates `self` in place. For a real array the
89
+ # result is a fresh independent CArray filled with 0.
90
+ # @return [CArray]
91
+ def imag
92
+ if not @__imag__
93
+ if complex?
94
+ @__imag__ = case data_type
95
+ when CA_CMPLX64
96
+ field(4, CA_FLOAT32)
97
+ when CA_CMPLX128
98
+ field(8, CA_FLOAT64)
99
+ end
100
+ else
101
+ @__imag__ = self.template { 0 }
102
+ end
103
+ end
104
+ return @__imag__
105
+ end
106
+
107
+ # @overload imag=(val)
108
+ # Sets the imaginary-part slot to `val` (complex arrays only).
109
+ # @param val [CArray, Numeric] value to broadcast.
110
+ # @return [Object] `val`.
111
+ # @raise [RuntimeError] when `self` is not a complex array.
112
+ def imag= (val)
113
+ if complex?
114
+ imag[] = val
115
+ else
116
+ raise "not a complex array"
117
+ end
118
+ end
119
+
120
+ # @overload real?
121
+ # Returns whether every element of `self` is real (imaginary
122
+ # part is zero for complex arrays; always `true` for real
123
+ # numeric arrays; `nil` for non-numeric arrays).
124
+ # @return [Boolean, nil]
125
+ def real?
126
+ if complex?
127
+ imag.eq(0).all
128
+ elsif numeric?
129
+ true
130
+ else
131
+ nil
132
+ end
133
+ end
134
+
135
+ # @overload is_real
136
+ # Returns an element-wise boolean CArray marking cells whose
137
+ # imaginary part is zero (all-true for real numeric arrays,
138
+ # `nil` for non-numeric arrays).
139
+ # @return [CArray, nil]
140
+ def is_real
141
+ if complex?
142
+ imag.eq(0)
143
+ elsif numeric?
144
+ self.true
145
+ else
146
+ nil
147
+ end
148
+ end
149
+
150
+ end
@@ -0,0 +1,216 @@
1
+ # ----------------------------------------------------------------------------
2
+ #
3
+ # carray/conditional.rb
4
+ #
5
+ # Element-wise conditional selection primitives. Three flavours share the
6
+ # same "pick a value per cell based on a boolean condition" contract but
7
+ # differ in where the condition lives and how the branches are supplied:
8
+ #
9
+ # cond.then_else(x, y) # boolean receiver, 2 values (eager)
10
+ # self.replace_where(cond, b) # value receiver, 1 replacement (eager)
11
+ # self.conditional(cond, f_then, f_else) # value receiver, 2 callables (per-region)
12
+ #
13
+ # `then_else` and `replace_where` evaluate both branches over the full array
14
+ # and pick per cell — fast but requires every branch to be domain-safe over
15
+ # the whole receiver. `conditional` extracts the two subsets first and
16
+ # applies each callable only to its own region, which is domain-safe by
17
+ # construction (e.g. `x.log` is never evaluated on negative cells).
18
+ #
19
+ # ----------------------------------------------------------------------------
20
+
21
+ class CArray
22
+
23
+ # @overload then_else(x, y)
24
+ # Returns a ternary selection on `self` (a boolean CArray),
25
+ # reading as "if `self` then `x` else `y`". The boolean 2-way
26
+ # case of {#choose} that additionally propagates `self`'s mask
27
+ # (`UNDEF` in `self` produces `UNDEF` in the result).
28
+ # @param x [CArray, Numeric, Object] true-branch value(s).
29
+ # @param y [CArray, Numeric, Object] false-branch value(s).
30
+ # @return [CArray] new array with the same shape as `self`.
31
+ # @raise [ArgumentError] when `self` is not a boolean CArray.
32
+ def then_else (x, y)
33
+ # Guard: self must be boolean. Integer / float receiver would be
34
+ # silently reinterpreted by the indexer setter (`result[self] = ...`)
35
+ # as an index array, producing surprising scatter rather than the
36
+ # intended ternary select. Fail fast.
37
+ unless self.boolean?
38
+ raise ArgumentError,
39
+ "then_else: receiver must be a boolean CArray (data_type == CA_BOOLEAN), got #{self.data_type}"
40
+ end
41
+ # Promote data_type from both branches via CArray.result_type
42
+ # (a CScalar contributes its own data_type, so CA_INT32(0) keeps int32
43
+ # where a bare Ruby Integer would widen to int64).
44
+ dt = CArray.result_type(x, y)
45
+ # A CScalar (scalar? CArray) is treated as a scalar value, not as a
46
+ # self-shaped operand: full CArray -> gather/copy, scalar -> broadcast.
47
+ y_full = y.is_a?(CArray) && !y.scalar?
48
+ result =
49
+ if y_full
50
+ y.data_type == dt ? y.copy : y.to_type(dt)
51
+ else
52
+ CArray.new(dt, self.shape).fill(y.is_a?(CArray) ? y[0] : y)
53
+ end
54
+ x_full = x.is_a?(CArray) && !x.scalar?
55
+ result[self] = x_full ? x[self] : x
56
+ # Propagate cond's mask: UNDEF in self -> UNDEF in result.
57
+ if self.has_mask?
58
+ result[self.is_masked] = UNDEF
59
+ end
60
+ result
61
+ end
62
+
63
+ # @overload replace_where(cond, b)
64
+ # Returns a copy of `self` with cells where `cond` is true
65
+ # replaced by `b`. Functional sibling of the destructive
66
+ # indexer `a[cond] = b`; mask handling matches the indexer.
67
+ # Preserves `self`'s `data_type` (unlike {#then_else}, which
68
+ # promotes via `CArray.result_type`).
69
+ # @param cond [CArray] boolean selector; same shape as `self`
70
+ # or broadcastable.
71
+ # @param b [CArray, Numeric, Object] replacement value(s).
72
+ # @return [CArray] new array.
73
+ # @raise [ArgumentError] when `cond` is not a boolean CArray.
74
+ def replace_where (cond, b)
75
+ unless cond.is_a?(CArray) && cond.boolean?
76
+ raise ArgumentError,
77
+ "replace_where: cond must be a boolean CArray (data_type == CA_BOOLEAN)"
78
+ end
79
+ result = self.copy
80
+ result[cond] = b.is_a?(CArray) ? b[cond] : b
81
+ result
82
+ end
83
+
84
+ # @overload conditional(cond, then_fn, else_fn, dtype: nil)
85
+ # Returns per-cell `then_fn.call(self[cond])` where `cond` is
86
+ # true and `else_fn.call(self[cond.not])` where it is false.
87
+ # The two callables are applied only to their own subset of
88
+ # `self`, so a branch that would fail on the other region
89
+ # (e.g. `->(v) { v.log }` on negative cells) stays safe.
90
+ #
91
+ # Scalar returns from a callable (e.g. `->(v) { 0 }`) broadcast
92
+ # to the subset shape. The result `data_type` is the promotion
93
+ # of the two subset results via `CArray.result_type`, or
94
+ # `dtype` when given. Masked cells in `cond` propagate to
95
+ # `UNDEF` in the result.
96
+ #
97
+ # @param cond [CArray] boolean selector; same shape as `self`.
98
+ # @param then_fn [#call] callable applied to `self[cond]`.
99
+ # @param else_fn [#call] callable applied to `self[cond.not]`.
100
+ # @param dtype [Symbol, Integer, nil] override for the result
101
+ # `data_type`.
102
+ # @return [CArray] new array with the same shape as `self`.
103
+ # @raise [ArgumentError] when `cond` is not a same-shape
104
+ # boolean CArray.
105
+ # @example
106
+ # x = CArray.float64(6).span(-2.0..3.0)
107
+ # x.conditional(x > 0,
108
+ # ->(v) { v.log }, # domain-safe: only positive cells
109
+ # ->(v) { -v })
110
+ # # => [2.0, 1.0, -0.0, 0.0, 0.6931..., 1.0986...]
111
+ def conditional (cond, then_fn, else_fn, dtype: nil)
112
+ unless cond.is_a?(CArray) && cond.boolean? && cond.shape == self.shape
113
+ raise ArgumentError,
114
+ "conditional: cond must be a boolean CArray with same shape as self"
115
+ end
116
+
117
+ x_then = self[cond]
118
+ x_else = self[cond.not]
119
+ y_then = then_fn.call(x_then)
120
+ y_else = else_fn.call(x_else)
121
+
122
+ # A callable that returns a scalar (e.g. `->(v) { 0 }`) broadcasts
123
+ # to the subset shape; wrap it here so the scatter step below sees a
124
+ # same-length CArray.
125
+ unless y_then.is_a?(CArray)
126
+ y_then = CArray.new(dtype || CArray.result_type(y_then), x_then.shape).fill(y_then)
127
+ end
128
+ unless y_else.is_a?(CArray)
129
+ y_else = CArray.new(dtype || CArray.result_type(y_else), x_else.shape).fill(y_else)
130
+ end
131
+
132
+ dt = dtype || CArray.result_type(y_then, y_else)
133
+ out = CArray.new(dt, self.shape)
134
+ out[cond] = y_then
135
+ out[cond.not] = y_else
136
+ # Propagate cond's mask (mirrors then_else's rule): UNDEF in cond ->
137
+ # UNDEF in out. Kleene `cond.not` also carries UNDEF at the same
138
+ # positions, so both scatters leave the cell untouched — an explicit
139
+ # fix-up is required.
140
+ out[cond.is_masked] = UNDEF if cond.has_mask?
141
+ out
142
+ end
143
+
144
+ # @overload select(condlist, choicelist, default: 0, dtype: nil)
145
+ # Multi-way ternary select: for each cell, picks the value from
146
+ # the first `choicelist[k]` whose matching `condlist[k]` is true,
147
+ # falling back to `default` when no condition holds. When several
148
+ # conditions overlap, the earliest one in `condlist` wins.
149
+ #
150
+ # All entries in `condlist` must be same-shape boolean CArrays.
151
+ # Each `choicelist[k]` is either a same-shape CArray or a scalar
152
+ # broadcast to every cell. The result `data_type` is the promotion
153
+ # of every choice plus `default` via `CArray.result_type`, or
154
+ # `dtype` when given.
155
+ #
156
+ # @param condlist [Array<CArray>] boolean selectors.
157
+ # @param choicelist [Array<CArray, Numeric, Object>] values, one
158
+ # per condition (same length as `condlist`).
159
+ # @param default [CArray, Numeric, Object] value written where no
160
+ # condition holds.
161
+ # @param dtype [Symbol, Integer, nil] override for the result
162
+ # `data_type`.
163
+ # @return [CArray] new array with the shape of `condlist[0]`.
164
+ # @raise [ArgumentError] on size mismatch, empty `condlist`, or a
165
+ # non-boolean / wrong-shape entry in `condlist`.
166
+ # @example
167
+ # x = CArray.float64(6).span(-5.0..5.0)
168
+ # CArray.select([x < 0, x < 2],
169
+ # [-x, x * 10],
170
+ # default: 999)
171
+ # # => [5.0, 3.0, -10.0, 0.0, 10.0, 999.0]
172
+ def self.select (condlist, choicelist, default: 0, dtype: nil)
173
+ unless condlist.is_a?(Array) && choicelist.is_a?(Array)
174
+ raise ArgumentError, "select: condlist and choicelist must be Arrays"
175
+ end
176
+ if condlist.size != choicelist.size
177
+ raise ArgumentError,
178
+ "select: condlist (#{condlist.size}) and choicelist (#{choicelist.size}) size mismatch"
179
+ end
180
+ if condlist.empty?
181
+ raise ArgumentError, "select: at least one condition required"
182
+ end
183
+
184
+ first = condlist.first
185
+ unless first.is_a?(CArray) && first.boolean?
186
+ raise ArgumentError, "select: condlist[0] must be a boolean CArray"
187
+ end
188
+ shape = first.shape
189
+
190
+ dt = dtype || CArray.result_type(*choicelist, default)
191
+ # `default` can be either a same-shape CArray (per-cell fallback) or a
192
+ # scalar (broadcast to every cell).
193
+ default_full = default.is_a?(CArray) && !default.scalar?
194
+ out =
195
+ if default_full
196
+ default.data_type == dt ? default.copy : default.to_type(dt)
197
+ else
198
+ CArray.new(dt, shape).fill(default.is_a?(CArray) ? default[0] : default)
199
+ end
200
+
201
+ # Iterate from lowest priority to highest (reverse) so the earliest
202
+ # entry in `condlist` ends up on top — matches `np.select`'s
203
+ # first-match semantics.
204
+ (condlist.size - 1).downto(0) do |k|
205
+ c = condlist[k]
206
+ unless c.is_a?(CArray) && c.boolean? && c.shape == shape
207
+ raise ArgumentError,
208
+ "select: condlist[#{k}] must be a same-shape boolean CArray"
209
+ end
210
+ v = choicelist[k]
211
+ out[c] = v.is_a?(CArray) ? v[c] : v
212
+ end
213
+ out
214
+ end
215
+
216
+ end
@@ -0,0 +1,228 @@
1
+ # ----------------------------------------------------------------------------
2
+ #
3
+ # carray/const_string.rb
4
+ #
5
+ # CAConstString high-level construction + conversion surface.
6
+ #
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.
11
+ #
12
+ # Internal buffer format: concatenation of `[int32 length][bytes...]` records
13
+ # (self-delimiting → one offset per element, permutation-safe view).
14
+ #
15
+ # ----------------------------------------------------------------------------
16
+
17
+ class CArray
18
+
19
+ # Build a CAConstString (read-only variable-length string column) from Ruby data.
20
+ #
21
+ # CArray.const_string(["alpha", "", "gamma"]) # 1-D from Array
22
+ # CArray.const_string(3) { |i| "item#{i}" } # block form
23
+ # CArray.const_string([a, nil, b]) # nil → masked element
24
+ #
25
+ # B1: "" (length 0) is a valid empty string, distinct from a masked element
26
+ # (nil → masked). B2: element encoding must match :encoding (strict),
27
+ # pure-ASCII strings pass regardless (ASCII-compatible relaxation).
28
+ #
29
+ # Storage is one `(start, end)` byte-range pair per element over a
30
+ # pure-concatenation buffer (Arrow string layout). For a high-duplication
31
+ # column (categorical labels), use {CACategorical} (= Arrow DictionaryArray)
32
+ # instead — CAConstString stores every element's bytes, without dedup.
33
+ # @overload const_string(values, encoding: Encoding::UTF_8)
34
+ # Returns a read-only {CAConstString} column packing each String's
35
+ # bytes into a shared buffer. `values` may be any Array (or
36
+ # Array-like) of Strings; `nil` entries are masked.
37
+ # @param values [Array<String, nil>] source values.
38
+ # @param encoding [Encoding] column encoding.
39
+ # @return [CAConstString]
40
+ # @overload const_string(ca, encoding: Encoding::UTF_8)
41
+ # Builds from a string-bearing CArray (String Face / CA_OBJECT / raw
42
+ # CA_FIXLEN, the last read as NUL-stripped strings); always materialises.
43
+ # @param ca [CArray] source array.
44
+ # @return [CAConstString]
45
+ # @raise [CArray::DataTypeError] if `ca` is numeric / boolean.
46
+ # @overload const_string(n, encoding: Encoding::UTF_8) { |i| ... }
47
+ # Returns an `n`-element {CAConstString} column filled by the
48
+ # block, following the arity-0 broadcast convention.
49
+ # @param n [Integer] element count.
50
+ # @yieldparam i [Integer] cell index.
51
+ # @yieldreturn [String, nil] value for cell `i`.
52
+ # @return [CAConstString]
53
+ def self.const_string (arg, encoding: Encoding::UTF_8, &block)
54
+ if arg.is_a?(CArray)
55
+ return string_face_of(arg).to_const_string(encoding: encoding)
56
+ end
57
+ if block
58
+ n = Integer(arg)
59
+ # B5: follow CArray.<type>(n){ ... } arity-0 broadcast quirk for
60
+ # consistency — arity-0 block is evaluated once and broadcast.
61
+ if block.arity == 0
62
+ v = block.call
63
+ values = Array.new(n) { v }
64
+ else
65
+ values = Array.new(n) { |i| block.call(i) }
66
+ end
67
+ else
68
+ values = arg.to_a
69
+ end
70
+
71
+ # Arrow-style layout, built in one C pass: pure-concatenation buffer +
72
+ # one (start,end) int64 pair per element, mask for nil.
73
+ CAConstString.__build__(values, encoding)
74
+ end
75
+
76
+ end
77
+
78
+ # Read-only column of variable-length Strings: every element's bytes are
79
+ # packed into one shared buffer, with a `(start, end)` byte-range pair per
80
+ # element (the Arrow string layout).
81
+ #
82
+ # Built through `CArray.const_string`. For a column with heavy duplication
83
+ # (categorical labels) prefer {CACategorical}, which stores each distinct
84
+ # value once.
85
+ class CAConstString
86
+
87
+ # Read-only String Face: L1 string operations only (no in-place Mutable).
88
+ # Its C-native byte predicates / search / comparison (ext/ca_obj_const_string.c)
89
+ # override the L1 generics where present.
90
+ include CArray::StringOperationMixin
91
+
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.
96
+ # @return [CAConstString]
97
+ def sort
98
+ self[sort_index]
99
+ end
100
+
101
+ # @overload sort_copy
102
+ # Returns an owned sorted {CAConstString}; the materialised
103
+ # counterpart to {#sort}.
104
+ # @return [CAConstString]
105
+ def sort_copy
106
+ self[sort_index].copy
107
+ end
108
+
109
+ # CAConstString is the one Face with three separate entities -- storage
110
+ # (int64 offset), surface (fixlen), and the length-prefixed buffer -- so it
111
+ # has no byte-compatible blank form (other Faces have surface == storage or
112
+ # a struct tail, which the core template lift handles). A same-class
113
+ # template would be an unusable shell, so override it: with an explicit type
114
+ # defer to the generic (plain) path; with no type return a plain same-shape
115
+ # `object` array (a fillable string container), honouring a block.
116
+ # @overload template(type = nil, **opts)
117
+ # @return [CArray]
118
+ def template (*args, &block)
119
+ return super unless args.empty? # a type was given: generic plain path
120
+ out = CArray.object(*shape)
121
+ if block
122
+ if block.arity == 0
123
+ out[] = block.call
124
+ else
125
+ out.map_with_index! { |_, *idx| block.call(*idx) }
126
+ end
127
+ end
128
+ out
129
+ end
130
+
131
+ # ---- value-hash discovery family --------------------------------------
132
+ #
133
+ # A storage cell is a `(start, end)` byte range into the shared buffer, so
134
+ # storage equality is NOT string equality: "ab" appearing twice occupies two
135
+ # distinct ranges. The family's Face gate only descends a Face whose storage
136
+ # equality *is* its surface equality (docs/authoring/FaceOrderingSearch.md
137
+ # §5.0), and CAConstString is the Face it deliberately excludes -- without
138
+ # these overrides `%w[ab cd ab ef].nunique` answered 4.
139
+ #
140
+ # So the family runs on {#to_string}, where a cell *is* the string, and value
141
+ # outputs come back as a CAConstString. Counts, booleans and addresses need
142
+ # no conversion.
143
+
144
+ # @overload unique(sort: false)
145
+ # @return [CAConstString] the distinct strings.
146
+ def unique (sort: false)
147
+ to_string.unique(sort: sort).to_const_string
148
+ end
149
+
150
+ # @overload value_counts(sort: false)
151
+ # @return [Array(CAConstString, CArray)] `[values, counts]`.
152
+ def value_counts (sort: false)
153
+ values, counts = to_string.value_counts(sort: sort)
154
+ [values.to_const_string, counts]
155
+ end
156
+
157
+ # @overload nunique(axis: nil, keep_axis: false)
158
+ # @return [Integer, CArray] the number of distinct strings.
159
+ def nunique (axis: nil, keep_axis: false)
160
+ to_string.nunique(axis: axis, keep_axis: keep_axis)
161
+ end
162
+
163
+ # @overload mode(axis: nil)
164
+ # @return [CAConstString, Array] the most frequent string(s).
165
+ def mode (axis: nil)
166
+ 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
172
+ end
173
+
174
+ # @overload is_mode(axis: nil)
175
+ # @return [CArray] boolean, true where the cell holds a modal string.
176
+ def is_mode (axis: nil)
177
+ to_string.is_mode(axis: axis)
178
+ end
179
+
180
+ # @overload mask_duplicates(axis: nil)
181
+ # @return [CAConstString] a copy with every repeat occurrence masked.
182
+ def mask_duplicates (axis: nil)
183
+ to_string.mask_duplicates(axis: axis).to_const_string
184
+ end
185
+
186
+ # @overload is_in(values)
187
+ # @return [CArray] boolean, true where the cell's string is in `values`.
188
+ def is_in (values)
189
+ to_string.is_in(string_operand(values))
190
+ end
191
+
192
+ # @overload intersection(other, sort: false)
193
+ # @return [CAConstString] the distinct strings present in both.
194
+ def intersection (other, sort: false)
195
+ to_string.intersection(string_operand(other), sort: sort).to_const_string
196
+ end
197
+
198
+ # @overload difference(other, sort: false)
199
+ # @return [CAConstString] the distinct strings only `self` has.
200
+ def difference (other, sort: false)
201
+ to_string.difference(string_operand(other), sort: sort).to_const_string
202
+ end
203
+
204
+ # @overload union(other, sort: false)
205
+ # @return [CAConstString] the distinct strings of either side.
206
+ def union (other, sort: false)
207
+ to_string.union(string_operand(other), sort: sort).to_const_string
208
+ end
209
+
210
+ # @overload locate_addr(ref)
211
+ # @return [CArray] for each cell, its address in `ref` (UNDEF when absent).
212
+ def locate_addr (ref)
213
+ to_string.locate_addr(string_operand(ref))
214
+ end
215
+
216
+ # @overload categorize(labels: nil, sort_labels: false)
217
+ # @return [CACategorical] categories keyed on the strings.
218
+ def categorize (labels: nil, sort_labels: false)
219
+ to_string.categorize(labels: labels, sort_labels: sort_labels)
220
+ end
221
+
222
+ # A CAConstString operand has to be decoded too; anything else (a CAString,
223
+ # an object array, an Array) already compares as strings.
224
+ private def string_operand (other)
225
+ other.is_a?(CAConstString) ? other.to_string : other
226
+ end
227
+
228
+ end