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,490 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ Private histogram kernels called from lib/carray/histogram.rb (and
4
+ lib/carray/bincount_nd.rb for the discrete sibling):
5
+ histbin_ki(edges, include_max) val=self extended binning
6
+ histogram_scatter_ki(counts, edges, fused per-sample scatter
7
+ incmax, weights) into extended counts
8
+ bincount_nd_count_ki(counts, weights) discrete joint count (fiber)
9
+
10
+ Extended bin index layout produced by the binning helpers:
11
+ 0 under (v < edges[0])
12
+ 1..n in-range bin 0..n-1
13
+ n+1 over (v >= edges[-1], unless include_max snaps to n)
14
+
15
+ Orientation is val=self: the data array drives the output shape,
16
+ edges is a constant operand. Sample data is delivered strided via
17
+ the kernel_iterator (CA_FOR_EACH_FIBER_INOUT_MASKED / SLAB_AXES) so
18
+ channel views over a joint sample array are not materialised; only
19
+ `edges` (small, constant) is attached.
20
+
21
+ histbin_ki self must be float64 (Ruby side coerces).
22
+
23
+ ---------------------------------------------------------------------------- */
24
+
25
+ #include "carray.h"
26
+ #include <math.h>
27
+
28
+ /* Resolve `redges` to a contiguous, attached, 1-D float64 CArray.
29
+ Returns the CArray* and (via out params) the data pointer + element
30
+ count. Caller must ca_detach when done. */
31
+ static CArray *
32
+ histbin_attach_edges (VALUE self, VALUE redges, double **ep_out, ca_size_t *ne_out)
33
+ {
34
+ CArray *ce;
35
+
36
+ if ( ! rb_obj_is_carray(redges) ) {
37
+ redges = rb_ca_wrap_readonly(redges, INT2NUM(CA_FLOAT64));
38
+ }
39
+ GetCArray(redges, ce);
40
+
41
+ if ( ce->data_type != CA_FLOAT64 ) {
42
+ redges = rb_ca_wrap_readonly(redges, INT2NUM(CA_FLOAT64));
43
+ GetCArray(redges, ce);
44
+ }
45
+ if ( ce->ndim != 1 ) {
46
+ rb_raise(rb_eArgError, "histbin_ki: edges must be 1-D");
47
+ }
48
+ if ( ce->elements < 2 ) {
49
+ rb_raise(rb_eArgError, "histbin_ki: edges needs at least 2 values");
50
+ }
51
+
52
+ ca_attach(ce);
53
+ *ep_out = (double *) ce->ptr;
54
+ *ne_out = ce->elements;
55
+ return ce;
56
+ }
57
+
58
+ /* ---- shared binning helpers (used by histbin_ki + the fused kernel) ---- */
59
+
60
+ typedef struct {
61
+ const double *ep;
62
+ ca_size_t n; /* number of bins */
63
+ double lo, hi, inv_dx;
64
+ int uniform;
65
+ int include_max;
66
+ } histbin_axis_t;
67
+
68
+ /* Extended index 0..n+1 for finite v (caller handles NaN / masked cells).
69
+ The uniform fast path's linearised floor is corrected against the actual
70
+ stored edges, so boundary values bin identically to the binary-search
71
+ path — exact, no relative-tolerance "snap" magic. Correction is normally
72
+ 0 iterations; it fires only for the ~few values sitting on / within a ULP
73
+ of an edge where (v-lo)*inv_dx rounds to the wrong side. */
74
+ static inline ca_size_t
75
+ histbin_index (const histbin_axis_t *ax, double v)
76
+ {
77
+ if ( v < ax->lo ) return 0; /* under */
78
+ if ( v >= ax->hi )
79
+ return (ax->include_max && v == ax->hi) ? ax->n /* top bin */
80
+ : ax->n + 1; /* over */
81
+ if ( ax->uniform ) {
82
+ double pos = (v - ax->lo) * ax->inv_dx;
83
+ ca_size_t k = (ca_size_t) floor(pos);
84
+ double frac;
85
+ if ( k >= ax->n ) k = ax->n - 1; /* float guard near hi */
86
+ /* Only values sitting within a hair of a bin boundary can be on the wrong
87
+ side of the linearised floor; verify just those against the real edges.
88
+ Interior values (the bulk) skip the data-dependent edge loads, keeping
89
+ the fast path vectorisable. */
90
+ frac = pos - (double) k;
91
+ if ( frac < 1e-9 || frac > 1.0 - 1e-9 ) {
92
+ while ( k + 1 < ax->n && v >= ax->ep[k + 1] ) k++;
93
+ while ( k > 0 && v < ax->ep[k] ) k--;
94
+ }
95
+ return k + 1;
96
+ }
97
+ else {
98
+ ca_size_t a = 0, b = ax->n; /* ep[k] <= v < ep[k+1] */
99
+ while ( b - a > 1 ) {
100
+ ca_size_t mid = (a + b) / 2;
101
+ if ( ax->ep[mid] <= v ) a = mid; else b = mid;
102
+ }
103
+ return a + 1;
104
+ }
105
+ }
106
+
107
+ static void
108
+ histbin_axis_setup (histbin_axis_t *ax, CArray *ce, int include_max)
109
+ {
110
+ ca_size_t ne = ce->elements, kk;
111
+ double dx, tol;
112
+
113
+ ax->ep = (const double *) ce->ptr;
114
+ ax->n = ne - 1;
115
+ ax->lo = ax->ep[0];
116
+ ax->hi = ax->ep[ne - 1];
117
+ dx = (ax->hi - ax->lo) / (double) ax->n;
118
+ tol = fabs(dx) * 1e-9 + 1e-12;
119
+ ax->uniform = 1;
120
+ for ( kk = 0; kk <= ax->n; kk++ ) {
121
+ if ( fabs(ax->ep[kk] - (ax->lo + (double) kk * dx)) > tol ) { ax->uniform = 0; break; }
122
+ }
123
+ ax->inv_dx = (dx != 0.0) ? 1.0 / dx : 0.0;
124
+ ax->include_max = include_max;
125
+ }
126
+
127
+ static VALUE
128
+ rb_ca_histbin_ki (VALUE self, VALUE redges, VALUE rinclude_max)
129
+ {
130
+ CArray *ca, *co, *ce;
131
+ VALUE vout;
132
+ double *ep;
133
+ ca_size_t ne;
134
+ histbin_axis_t ax;
135
+
136
+ GetCArray(self, ca);
137
+ if ( ca->data_type != CA_FLOAT64 ) {
138
+ rb_raise(rb_eCADataTypeError,
139
+ "histbin_ki: self must be float64 (got %s)",
140
+ ca_type_name[ca->data_type]);
141
+ }
142
+
143
+ ce = histbin_attach_edges(self, redges, &ep, &ne);
144
+ (void) ep; (void) ne; /* axis setup reads ce directly */
145
+ histbin_axis_setup(&ax, ce, RTEST(rinclude_max));
146
+
147
+ /* output: fresh contiguous int64, same shape as self. */
148
+ vout = rb_carray_new(CA_INT64, ca->ndim, ca->dim, 0, NULL);
149
+ GetCArray(vout, co);
150
+
151
+ if ( ca->elements > 0 ) {
152
+ ca_iter_state st_in, st_out;
153
+ char *p_in, *p_out;
154
+ boolean8_t *m;
155
+ boolean8_t *omask = NULL; /* lazily created on first masked cell */
156
+ ca_size_t out_pos = 0; /* flat element offset of current fiber */
157
+ int8_t axis = (int8_t) (ca->ndim - 1);
158
+ ca_size_t i, nfib;
159
+
160
+ CA_FOR_EACH_FIBER_INOUT_MASKED(st_in, st_out, ca, co, axis, 0,
161
+ p_in, p_out, nfib, m) {
162
+ const double *vp = (const double *) p_in;
163
+ int64_t *op = (int64_t *) p_out;
164
+
165
+ for ( i = 0; i < nfib; i++ ) {
166
+ double v = vp[i];
167
+ if ( (m && m[i]) || isnan(v) ) {
168
+ if ( ! omask ) {
169
+ ca_create_mask(co);
170
+ omask = (boolean8_t *) co->mask->ptr;
171
+ }
172
+ omask[out_pos + i] = 1;
173
+ op[i] = 0;
174
+ continue;
175
+ }
176
+ op[i] = (int64_t) histbin_index(&ax, v);
177
+ }
178
+ out_pos += nfib;
179
+ }
180
+ }
181
+
182
+ ca_detach(ce);
183
+
184
+ return vout;
185
+ }
186
+
187
+ /* ===========================================================================
188
+ histogram_scatter_ki — fused histogram scatter over M channels per
189
+ sample, writing directly into the extended counts buffer with no
190
+ intermediate index arrays (peak-memory minimal).
191
+
192
+ self = chunk transposed to [fiber..., A, M].
193
+ counts = [fiber..., ext_0..ext_{M-1}] (int64 unweighted / float64 weighted).
194
+ weights = nil (unweighted, +1) OR float64 [fiber..., A] delivered by
195
+ a second iterator in lockstep so weights[fiber, a] aligns
196
+ with sample a of the chunk slab (no materialise).
197
+
198
+ Per fiber slab (slab axes = [sample, channel], outer = fiber):
199
+ fiber_base = Σ outer_idx[mm] * fiber_stride[mm]
200
+ for each sample a: union mask across channels; else
201
+ off = fiber_base + Σ_k histbin_index(axis_k, v_{a,k}) * ext_stride[k]
202
+ counts[off] += 1 (or += weight when weighted)
203
+ =========================================================================== */
204
+ static VALUE
205
+ rb_ca_histogram_scatter_ki (VALUE self, VALUE rcounts, VALUE redges,
206
+ VALUE rincmax, VALUE rweights)
207
+ {
208
+ CArray *ca, *cc, *cw = NULL;
209
+ long M, nf, k;
210
+ int weighted;
211
+ histbin_axis_t axes[CA_RANK_MAX];
212
+ CArray *edge_ca[CA_RANK_MAX];
213
+ ca_size_t ext_stride[CA_RANK_MAX];
214
+ ca_size_t fiber_stride[CA_RANK_MAX];
215
+ ca_size_t total_ext, s;
216
+
217
+ GetCArray(self, ca);
218
+ GetCArray(rcounts, cc);
219
+ Check_Type(redges, T_ARRAY);
220
+ Check_Type(rincmax, T_ARRAY);
221
+ M = RARRAY_LEN(redges);
222
+ weighted = ! NIL_P(rweights);
223
+
224
+ if ( ca->data_type != CA_FLOAT64 ) {
225
+ rb_raise(rb_eCADataTypeError, "histogram_scatter_ki: self must be float64");
226
+ }
227
+ if ( weighted ) {
228
+ GetCArray(rweights, cw);
229
+ if ( cw->data_type != CA_FLOAT64 ) {
230
+ rb_raise(rb_eCADataTypeError, "histogram_scatter_ki: weights must be float64");
231
+ }
232
+ if ( cc->data_type != CA_FLOAT64 ) {
233
+ rb_raise(rb_eCADataTypeError, "histogram_scatter_ki: weighted counts must be float64");
234
+ }
235
+ if ( cw->ndim != ca->ndim - 1 ) {
236
+ rb_raise(rb_eArgError,
237
+ "histogram_scatter_ki: weights ndim %d != fiber+sample", cw->ndim);
238
+ }
239
+ }
240
+ else if ( cc->data_type != CA_INT64 ) {
241
+ rb_raise(rb_eCADataTypeError, "histogram_scatter_ki: unweighted counts must be int64");
242
+ }
243
+ nf = cc->ndim - M;
244
+ if ( nf < 0 ) {
245
+ rb_raise(rb_eArgError, "histogram_scatter_ki: counts ndim %d < M %ld", cc->ndim, M);
246
+ }
247
+ if ( ca->ndim != nf + 2 ) {
248
+ rb_raise(rb_eArgError,
249
+ "histogram_scatter_ki: self ndim %d != fiber(%ld)+sample+channel",
250
+ ca->ndim, nf);
251
+ }
252
+ if ( (long) ca->dim[ca->ndim - 1] != M ) {
253
+ rb_raise(rb_eArgError,
254
+ "histogram_scatter_ki: self channel axis %d != M %ld",
255
+ (int) ca->dim[ca->ndim - 1], M);
256
+ }
257
+
258
+ for ( k = 0; k < M; k++ ) {
259
+ VALUE e = RARRAY_AREF(redges, k);
260
+ GetCArray(e, edge_ca[k]);
261
+ ca_attach(edge_ca[k]);
262
+ histbin_axis_setup(&axes[k], edge_ca[k], RTEST(RARRAY_AREF(rincmax, k)));
263
+ }
264
+
265
+ /* ext strides (row-major within the trailing M bin axes of counts). */
266
+ total_ext = 1;
267
+ for ( k = M - 1; k >= 0; k-- ) {
268
+ ext_stride[k] = total_ext;
269
+ total_ext *= cc->dim[nf + k];
270
+ }
271
+ /* fiber strides already fold in total_ext: counts flat = fiber_base + bin_off. */
272
+ s = total_ext;
273
+ for ( k = nf - 1; k >= 0; k-- ) {
274
+ fiber_stride[k] = s;
275
+ s *= cc->dim[k];
276
+ }
277
+
278
+ if ( ca->elements > 0 ) {
279
+ int64_t *cpi = (int64_t *) cc->ptr; /* unweighted */
280
+ double *cpd = (double *) cc->ptr; /* weighted */
281
+ ca_iter_state st, stw;
282
+ char *p, *pw = NULL;
283
+ boolean8_t *m, *mw = NULL;
284
+ ca_size_t cur_outer_idx[CA_RANK_MAX];
285
+ int8_t slab_axes[2] = { (int8_t)(ca->ndim - 2), (int8_t)(ca->ndim - 1) };
286
+ int8_t w_slab_axes[1] = { (int8_t)(ca->ndim - 2) }; /* weights sample axis */
287
+ int8_t mm;
288
+ int rc;
289
+
290
+ rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES, slab_axes, 2, 0);
291
+ if ( rc != CA_ITER_OK ) {
292
+ for ( k = 0; k < M; k++ ) ca_detach(edge_ca[k]);
293
+ rb_raise(rb_eRuntimeError, "histogram_scatter_ki: iter init failed rc=%d", rc);
294
+ }
295
+ if ( weighted ) {
296
+ rc = ca_iter_state_init_l2(&stw, cw, CA_SLAB_AXES, w_slab_axes, 1, 0);
297
+ if ( rc != CA_ITER_OK ) {
298
+ ca_iter_state_finish(&st);
299
+ for ( k = 0; k < M; k++ ) ca_detach(edge_ca[k]);
300
+ rb_raise(rb_eRuntimeError, "histogram_scatter_ki: weights iter init failed rc=%d", rc);
301
+ }
302
+ }
303
+ for ( mm = 0; mm < st.outer_ndim; mm++ ) cur_outer_idx[mm] = 0;
304
+
305
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
306
+ ca_size_t fiber_base = 0;
307
+ ca_size_t A, a, kk;
308
+ ca_size_t ss, cs, sms, cms, ws = 0, wms = 0;
309
+
310
+ if ( weighted && ! ca_iter_state_next_slab_axes(&stw, &pw, &mw) ) break;
311
+
312
+ for ( mm = 0; mm < st.outer_ndim; mm++ )
313
+ fiber_base += cur_outer_idx[mm] * fiber_stride[mm];
314
+
315
+ A = st.slab_dims[0]; /* samples */
316
+ ss = st.slab_strides[0]; /* sample byte stride */
317
+ cs = st.slab_strides[1]; /* channel byte stride */
318
+ sms = st.slab_mask_strides[0];
319
+ cms = st.slab_mask_strides[1];
320
+ if ( weighted ) {
321
+ ws = stw.slab_strides[0];
322
+ wms = stw.slab_mask_strides[0];
323
+ }
324
+
325
+ for ( a = 0; a < A; a++ ) {
326
+ ca_size_t off = fiber_base;
327
+ int masked = 0;
328
+ for ( kk = 0; kk < (ca_size_t) M; kk++ ) {
329
+ double v = *(const double *)(p + a * ss + kk * cs);
330
+ if ( (m && m[a * sms + kk * cms]) || isnan(v) ) { masked = 1; break; }
331
+ off += histbin_index(&axes[kk], v) * ext_stride[kk];
332
+ }
333
+ if ( masked ) continue;
334
+ if ( weighted ) {
335
+ double w = *(const double *)(pw + a * ws);
336
+ if ( (mw && mw[a * wms]) || isnan(w) ) continue; /* skip masked / NaN weight */
337
+ cpd[off] += w;
338
+ }
339
+ else {
340
+ cpi[off] += 1;
341
+ }
342
+ }
343
+
344
+ for ( mm = (int8_t)(st.outer_ndim - 1); mm >= 0; mm-- ) {
345
+ if ( ++cur_outer_idx[mm] < st.outer_dims[mm] ) break;
346
+ cur_outer_idx[mm] = 0;
347
+ }
348
+ }
349
+ ca_iter_state_finish(&st);
350
+ if ( weighted ) ca_iter_state_finish(&stw);
351
+ }
352
+
353
+ for ( k = 0; k < M; k++ ) ca_detach(edge_ca[k]);
354
+
355
+ return rcounts;
356
+ }
357
+
358
+ /* ===========================================================================
359
+ bincount_nd_count_ki — discrete sibling of histogram_scatter_ki for
360
+ the fiber case. Value == bin index directly; the upper overflow cell
361
+ (ext_k-1) absorbs v >= ext_k-1. Labels dispatch on their native
362
+ integer type so int32 input is read without coercion / materialise.
363
+
364
+ The flat case is handled on the Ruby side (ravel + bincount). Negative
365
+ labels are rejected on the Ruby side before this kernel runs, so here
366
+ v >= 0 is assumed.
367
+
368
+ self = chunk [fiber..., A, M] (integer).
369
+ counts = [fiber..., ext_0..ext_{M-1}] (int64 / float64).
370
+ weights = nil OR float64 [fiber..., A] (second iterator in lockstep).
371
+ =========================================================================== */
372
+
373
+ #define BINCOUNT_ND_BODY(LABEL_T) \
374
+ do { \
375
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) { \
376
+ ca_size_t fiber_base = 0, A, a, kk, ss, cs, sms, cms, ws = 0, wms = 0; \
377
+ if ( weighted && ! ca_iter_state_next_slab_axes(&stw, &pw, &mw) ) break; \
378
+ for ( mm = 0; mm < st.outer_ndim; mm++ ) \
379
+ fiber_base += cur_outer_idx[mm] * fiber_stride[mm]; \
380
+ A = st.slab_dims[0]; ss = st.slab_strides[0]; cs = st.slab_strides[1]; \
381
+ sms = st.slab_mask_strides[0]; cms = st.slab_mask_strides[1]; \
382
+ if ( weighted ) { ws = stw.slab_strides[0]; wms = stw.slab_mask_strides[0]; } \
383
+ for ( a = 0; a < A; a++ ) { \
384
+ ca_size_t off = fiber_base; int masked = 0; \
385
+ for ( kk = 0; kk < (ca_size_t) M; kk++ ) { \
386
+ int64_t v = (int64_t) *(const LABEL_T *)(p + a * ss + kk * cs); \
387
+ if ( m && m[a * sms + kk * cms] ) { masked = 1; break; } \
388
+ if ( v >= ext_dim[kk] - 1 ) v = ext_dim[kk] - 1; \
389
+ off += (ca_size_t) v * ext_stride[kk]; \
390
+ } \
391
+ if ( masked ) continue; \
392
+ if ( weighted ) { \
393
+ double w = *(const double *)(pw + a * ws); \
394
+ if ( (mw && mw[a * wms]) || isnan(w) ) continue; \
395
+ cpd[off] += w; \
396
+ } else { cpi[off] += 1; } \
397
+ } \
398
+ for ( mm = (int8_t)(st.outer_ndim - 1); mm >= 0; mm-- ) { \
399
+ if ( ++cur_outer_idx[mm] < st.outer_dims[mm] ) break; \
400
+ cur_outer_idx[mm] = 0; \
401
+ } \
402
+ } \
403
+ } while (0)
404
+
405
+ static VALUE
406
+ rb_ca_bincount_nd_count_ki (VALUE self, VALUE rcounts, VALUE rweights)
407
+ {
408
+ CArray *ca, *cc, *cw = NULL;
409
+ long M, nf, k;
410
+ int weighted;
411
+ ca_size_t ext_dim[CA_RANK_MAX], ext_stride[CA_RANK_MAX], fiber_stride[CA_RANK_MAX];
412
+ ca_size_t total_ext, s;
413
+
414
+ GetCArray(self, ca);
415
+ GetCArray(rcounts, cc);
416
+ weighted = ! NIL_P(rweights);
417
+ M = ca->dim[ca->ndim - 1];
418
+ nf = cc->ndim - M;
419
+ if ( nf < 0 || ca->ndim != nf + 2 ) {
420
+ rb_raise(rb_eArgError, "bincount_nd_count_ki: shape mismatch");
421
+ }
422
+ if ( weighted ) {
423
+ GetCArray(rweights, cw);
424
+ if ( cw->data_type != CA_FLOAT64 || cc->data_type != CA_FLOAT64 ) {
425
+ rb_raise(rb_eCADataTypeError, "bincount_nd_count_ki: weighted needs float64 weights/counts");
426
+ }
427
+ }
428
+ else if ( cc->data_type != CA_INT64 ) {
429
+ rb_raise(rb_eCADataTypeError, "bincount_nd_count_ki: unweighted counts must be int64");
430
+ }
431
+
432
+ total_ext = 1;
433
+ for ( k = M - 1; k >= 0; k-- ) {
434
+ ext_dim[k] = cc->dim[nf + k]; ext_stride[k] = total_ext; total_ext *= ext_dim[k];
435
+ }
436
+ s = total_ext;
437
+ for ( k = nf - 1; k >= 0; k-- ) { fiber_stride[k] = s; s *= cc->dim[k]; }
438
+
439
+ if ( ca->elements > 0 ) {
440
+ int64_t *cpi = (int64_t *) cc->ptr;
441
+ double *cpd = (double *) cc->ptr;
442
+ ca_iter_state st, stw;
443
+ char *p, *pw = NULL;
444
+ boolean8_t *m, *mw = NULL;
445
+ ca_size_t cur_outer_idx[CA_RANK_MAX];
446
+ int8_t slab_axes[2] = { (int8_t)(ca->ndim - 2), (int8_t)(ca->ndim - 1) };
447
+ int8_t w_slab_axes[1] = { (int8_t)(ca->ndim - 2) };
448
+ int8_t mm;
449
+ int rc;
450
+
451
+ rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES, slab_axes, 2, 0);
452
+ if ( rc != CA_ITER_OK ) rb_raise(rb_eRuntimeError, "bincount_nd_count_ki: iter init %d", rc);
453
+ if ( weighted ) {
454
+ rc = ca_iter_state_init_l2(&stw, cw, CA_SLAB_AXES, w_slab_axes, 1, 0);
455
+ if ( rc != CA_ITER_OK ) { ca_iter_state_finish(&st); rb_raise(rb_eRuntimeError, "weights iter %d", rc); }
456
+ }
457
+ for ( mm = 0; mm < st.outer_ndim; mm++ ) cur_outer_idx[mm] = 0;
458
+
459
+ switch ( ca->data_type ) {
460
+ case CA_INT8: BINCOUNT_ND_BODY(int8_t); break;
461
+ case CA_UINT8: BINCOUNT_ND_BODY(uint8_t); break;
462
+ case CA_INT16: BINCOUNT_ND_BODY(int16_t); break;
463
+ case CA_UINT16: BINCOUNT_ND_BODY(uint16_t); break;
464
+ case CA_INT32: BINCOUNT_ND_BODY(int32_t); break;
465
+ case CA_UINT32: BINCOUNT_ND_BODY(uint32_t); break;
466
+ case CA_INT64: BINCOUNT_ND_BODY(int64_t); break;
467
+ case CA_UINT64: BINCOUNT_ND_BODY(uint64_t); break;
468
+ default:
469
+ ca_iter_state_finish(&st);
470
+ if ( weighted ) ca_iter_state_finish(&stw);
471
+ rb_raise(rb_eCADataTypeError, "bincount_nd_count_ki: integer labels required");
472
+ }
473
+ ca_iter_state_finish(&st);
474
+ if ( weighted ) ca_iter_state_finish(&stw);
475
+ }
476
+
477
+ return rcounts;
478
+ }
479
+
480
+ #undef BINCOUNT_ND_BODY
481
+
482
+ void
483
+ Init_carray_histogram (void)
484
+ {
485
+ rb_define_private_method(rb_cCArray, "histbin_ki", rb_ca_histbin_ki, 2);
486
+ rb_define_private_method(rb_cCArray, "histogram_scatter_ki",
487
+ rb_ca_histogram_scatter_ki, 4);
488
+ rb_define_private_method(rb_cCArray, "bincount_nd_count_ki",
489
+ rb_ca_bincount_nd_count_ki, 2);
490
+ }