carray 3.0.3 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
@@ -1,120 +1,109 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- ca_categorical_iterator.c — counting-sort scatter for CACategoricalIterator.
4
-
5
- __categorical_scatter__ lays a flat payload out as a category-contiguous
6
- copy in a single pass, driven by the categorical codes and a per-category
7
- write cursor (a mutable copy of reduceat_index's segment starts). No
8
- permutation array is built; O(n), stable (ascending scan keeps each
9
- category's members in source order). It is the discrete, value-carrying
10
- sibling of histogram_scatter_ki (ext/carray_histogram.c): where the histogram
11
- scatters a +1 into counts, this scatters the payload cell into grouped.
12
-
13
- Two masks meet here and are kept distinct:
14
- - the CODES mask is authoritative for exclusion — a masked code cell does
15
- not join any group (the sentinel value never has to be read; `c < k` is a
16
- defensive assert only).
17
- - the VALUE mask propagates into grouped, so a group with a masked payload
18
- cell reduces as CArray does (the cell is skipped by mask-aware reductions).
19
-
20
- The output position is data-dependent (cursor[code]++), which the aligned
21
- kernel_iterator macros do not model, so the flat inputs are materialised here:
22
- ca_attach aliases a contiguous entity (codes / a contiguous value) and gathers
23
- a view. Codes dispatch on their native integer type (no coercion); the value
24
- move is a bytes-wide memcpy (grouped shares the value data type, so no value-type
25
- dispatch is needed).
26
-
27
- Surface (private): codes.__categorical_scatter__(value, cursor, grouped, k)
3
+ ca_categorical_iterator.c — kernels for CACategorical and CACategoricalIterator.
4
+
5
+ addresses_by_category orders the cells of a categorical by category, the
6
+ counting sort behind CACategorical#sort_addr: one pass over the codes
7
+ writes each cell's address at its category's cursor (a mutable copy of the
8
+ segment starts), so every cell of category 0 comes first, then 1, ..., in
9
+ source order within a category (stable). A cell in no category -- a masked
10
+ code, or a code outside 0...k -- is written in the same pass at a second
11
+ cursor that starts after the last category, so the excluded cells follow
12
+ in source order.
13
+
14
+ The output position depends on the code, which the aligned kernel_iterator
15
+ macros do not model, so the codes are materialised here: ca_attach aliases a
16
+ contiguous entity and gathers a view.
17
+
18
+ Surface (private): codes.addresses_by_category(cursor, k, nvalid)
28
19
  self = codes (integer, carries the exclusion mask), read flat
29
- value = payload (any data type, may carry a mask), read flat, same length
30
20
  cursor = int64 length-k segment starts (mutated in place, consumed)
31
- grouped = pre-allocated contiguous entity of the value data type, length nvalid
32
21
  k = number of categories
33
- Returns grouped.
22
+ nvalid = number of cells in a category (where the excluded cells start)
23
+ Returns a new int64 CArray of codes.elements addresses.
34
24
 
35
25
  --------------------------------------------------------------------------- */
36
26
 
37
27
  #include "carray.h"
38
28
  #include "carray_internal.h" /* CA_EXTREMUM_REPLACES */
29
+ #include "ca_sort_kernels.h" /* ca_partition_nan_f64 */
39
30
  #include <stdlib.h> /* qsort */
40
31
  #include <math.h> /* floor */
41
32
 
42
- #define CATEGORICAL_SCATTER_BODY(CODE_T) \
33
+ #define ADDRESSES_BY_CATEGORY_BODY(CODE_T) \
43
34
  do { \
44
35
  const CODE_T *cp = (const CODE_T *) codes->ptr; \
45
36
  for ( j = 0; j < n; j++ ) { \
46
- if ( cmask && cmask[j] ) continue; /* excluded by codes mask */ \
47
37
  c = (int64_t) cp[j]; \
48
- if ( c < 0 || c >= k ) continue; /* defensive */ \
49
- pos = (ca_size_t) cur[c]; \
50
- cur[c] = (int64_t) (pos + 1); \
51
- memcpy(gp + pos * bytes, vp + j * bytes, (size_t) bytes); \
52
- if ( vmask && vmask[j] ) gmask[pos] = 1; /* propagate value mask */ \
38
+ if ( ( cmask && cmask[j] ) || c < 0 || c >= k ) { \
39
+ if ( tail >= (int64_t) n ) { overrun = 1; break; } \
40
+ out[tail++] = (int64_t) j; /* in no category */ \
41
+ continue; \
42
+ } \
43
+ if ( cur[c] < 0 || cur[c] >= nvalid ) { overrun = 1; break; } \
44
+ out[cur[c]++] = (int64_t) j; \
53
45
  } \
54
46
  } while (0)
55
47
 
56
48
  static VALUE
57
- rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
58
- VALUE rgrouped, VALUE rk)
49
+ rb_ca_addresses_by_category (VALUE self, VALUE rcursor, VALUE rk, VALUE rnvalid)
59
50
  {
60
- CArray *codes, *value, *cursor, *grouped;
61
- int64_t k, c, *cur;
62
- ca_size_t n, j, pos, bytes;
63
- boolean8_t *cmask, *vmask, *gmask = NULL;
64
- char *gp, *vp;
65
-
66
- GetCArray(self, codes);
67
- GetCArray(rvalue, value);
68
- GetCArray(rcursor, cursor);
69
- GetCArray(rgrouped, grouped);
70
- k = (int64_t) NUM2LL(rk);
71
-
72
- n = codes->elements;
73
- bytes = value->bytes;
74
- if ( value->elements != n ) {
75
- rb_raise(rb_eArgError,
76
- "__categorical_scatter__: value length %lld != codes length %lld",
77
- (long long) value->elements, (long long) n);
51
+ CArray *codes, *cursor, *result;
52
+ VALUE rresult;
53
+ int64_t k, c, *cur, *out, tail, nvalid;
54
+ ca_size_t n, j, dim;
55
+ int overrun = 0;
56
+ boolean8_t *cmask;
57
+
58
+ GetCArray(self, codes);
59
+ GetCArray(rcursor, cursor);
60
+ k = (int64_t) NUM2LL(rk);
61
+ nvalid = (int64_t) NUM2LL(rnvalid);
62
+ n = codes->elements;
63
+
64
+ if ( cursor->data_type != CA_INT64 || cursor->elements != k ||
65
+ ! ca_is_entity(cursor) ) {
66
+ rb_raise(rb_eArgError, "addresses_by_category: cursor must be an int64[k] entity");
78
67
  }
79
- if ( cursor->data_type != CA_INT64 || cursor->elements != k ) {
80
- rb_raise(rb_eArgError, "__categorical_scatter__: cursor must be int64[k]");
68
+ if ( nvalid < 0 || nvalid > (int64_t) n ) {
69
+ rb_raise(rb_eArgError, "addresses_by_category: nvalid %lld out of 0..%lld",
70
+ (long long) nvalid, (long long) n);
81
71
  }
82
- if ( grouped->bytes != bytes ) {
83
- rb_raise(rb_eArgError, "__categorical_scatter__: grouped/value data type mismatch");
84
- }
85
-
86
- ca_attach_n(2, codes, value); /* window: detached before each raise inside */
87
- cmask = ca_mask_ptr(codes);
88
- vmask = ca_mask_ptr(value);
89
- cur = (int64_t *) cursor->ptr;
90
- gp = grouped->ptr;
91
- vp = value->ptr;
92
-
93
- if ( vmask ) { /* grouped needs a mask to receive it */
94
- ca_create_mask(grouped);
95
- gmask = (boolean8_t *) grouped->mask->ptr;
96
- }
97
-
98
72
  switch ( codes->data_type ) {
99
- case CA_INT8: CATEGORICAL_SCATTER_BODY(int8_t); break;
100
- case CA_UINT8: CATEGORICAL_SCATTER_BODY(uint8_t); break;
101
- case CA_INT16: CATEGORICAL_SCATTER_BODY(int16_t); break;
102
- case CA_UINT16: CATEGORICAL_SCATTER_BODY(uint16_t); break;
103
- case CA_INT32: CATEGORICAL_SCATTER_BODY(int32_t); break;
104
- case CA_UINT32: CATEGORICAL_SCATTER_BODY(uint32_t); break;
105
- case CA_INT64: CATEGORICAL_SCATTER_BODY(int64_t); break;
106
- case CA_UINT64: CATEGORICAL_SCATTER_BODY(uint64_t); break;
73
+ case CA_INT8: case CA_UINT8: case CA_INT16: case CA_UINT16:
74
+ case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
75
+ break;
107
76
  default:
108
- ca_detach(codes);
109
- ca_detach(value);
110
77
  rb_raise(rb_eCADataTypeError,
111
- "__categorical_scatter__: integer codes required (got data_type %d)",
78
+ "addresses_by_category: integer codes required (got data_type %d)",
112
79
  codes->data_type);
113
80
  }
114
81
 
82
+ dim = n;
83
+ rresult = rb_carray_new(CA_INT64, 1, &dim, 0, NULL);
84
+ GetCArray(rresult, result);
85
+ out = (int64_t *) result->ptr;
86
+ cur = (int64_t *) cursor->ptr;
87
+ tail = nvalid;
88
+
89
+ ca_attach(codes); /* window: nothing below raises */
90
+ cmask = ca_mask_ptr(codes);
91
+ switch ( codes->data_type ) {
92
+ case CA_INT8: ADDRESSES_BY_CATEGORY_BODY(int8_t); break;
93
+ case CA_UINT8: ADDRESSES_BY_CATEGORY_BODY(uint8_t); break;
94
+ case CA_INT16: ADDRESSES_BY_CATEGORY_BODY(int16_t); break;
95
+ case CA_UINT16: ADDRESSES_BY_CATEGORY_BODY(uint16_t); break;
96
+ case CA_INT32: ADDRESSES_BY_CATEGORY_BODY(int32_t); break;
97
+ case CA_UINT32: ADDRESSES_BY_CATEGORY_BODY(uint32_t); break;
98
+ case CA_INT64: ADDRESSES_BY_CATEGORY_BODY(int64_t); break;
99
+ case CA_UINT64: ADDRESSES_BY_CATEGORY_BODY(uint64_t); break;
100
+ }
115
101
  ca_detach(codes);
116
- ca_detach(value);
117
- return rgrouped;
102
+ if ( overrun ) {
103
+ rb_raise(rb_eArgError,
104
+ "addresses_by_category: the cursor and nvalid do not match the codes");
105
+ }
106
+ return rresult;
118
107
  }
119
108
 
120
109
  /* ---------------------------------------------------------------------------
@@ -151,7 +140,22 @@ rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
151
140
  #define CAT_NAN_NEVER(v) (0)
152
141
  #define CAT_NAN_CHECK(v) ((v) != (v))
153
142
 
154
- #define REDUCEAT_MOMENTS_BODY(T) \
143
+ /* Whether v takes the place of the accumulator acc: never when v is NaN,
144
+ always when acc is NaN and v is not. */
145
+ #define CAT_REPLACES(v, acc, CMP, ISNAN) \
146
+ ( ! ISNAN(v) && ( ISNAN(acc) || (v) CMP (acc) ) )
147
+
148
+ /* A segment without masked cells of at least CAT_LANES_FROM cells is read
149
+ with eight independent sums and eight min / max lanes, so no add or
150
+ compare waits on the one before it; a shorter one would spend more on
151
+ setting up and merging the lanes than it saves.
152
+ Each lane starts from the segment's first value; a lane can end on NaN
153
+ only if every value it saw was NaN, so merging the lanes with the same
154
+ rule gives the answer one lane would. The sums are added in a different
155
+ order than the cells, which is the license every float reduction has. */
156
+ #define CAT_LANES_FROM 64
157
+
158
+ #define REDUCEAT_MOMENTS_BODY(T, ISNAN, LANES_FROM) \
155
159
  do { \
156
160
  const T *gp = (const T *) grouped->ptr; \
157
161
  T *minv = (T *) minp, *maxv = (T *) maxp; \
@@ -161,16 +165,95 @@ rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
161
165
  ca_size_t j, cnt = 0; \
162
166
  double sacc = 0.0; \
163
167
  T mn = 0, mx = 0; int seen = 0; \
164
- for ( j = lo; j < hi; j++ ) { \
165
- if ( gm && gm[j] ) continue; /* masked value cell */ \
166
- { T v = gp[j]; \
167
- sacc += (double) v; \
168
- if ( ! seen ) { mn = v; mx = v; seen = 1; } \
169
- else { \
170
- if ( CA_EXTREMUM_REPLACES(v, mn, <) ) mn = v; \
171
- if ( CA_EXTREMUM_REPLACES(v, mx, >) ) mx = v; \
168
+ if ( ! gm && hi - lo < (ca_size_t) (LANES_FROM) ) { \
169
+ if ( hi > lo ) { \
170
+ mn = mx = gp[lo]; \
171
+ for ( j = lo; j < hi; j++ ) { \
172
+ T v = gp[j]; \
173
+ sacc += (double) v; \
174
+ if ( CAT_REPLACES(v, mn, <, ISNAN) ) mn = v; \
175
+ if ( CAT_REPLACES(v, mx, >, ISNAN) ) mx = v; \
172
176
  } \
173
- cnt++; } \
177
+ cnt = hi - lo; seen = 1; \
178
+ } \
179
+ } \
180
+ else if ( ! gm ) { \
181
+ { \
182
+ double s0 = 0, s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, s7 = 0; \
183
+ T n0, n1, n2, n3, n4, n5, n6, n7, x0, x1, x2, x3, x4, x5, x6, x7; \
184
+ n0 = n1 = n2 = n3 = n4 = n5 = n6 = n7 = gp[lo]; \
185
+ x0 = x1 = x2 = x3 = x4 = x5 = x6 = x7 = gp[lo]; \
186
+ for ( j = lo; j + 8 <= hi; j += 8 ) { \
187
+ T v0 = gp[j], v1 = gp[j+1], v2 = gp[j+2], v3 = gp[j+3]; \
188
+ T v4 = gp[j+4], v5 = gp[j+5], v6 = gp[j+6], v7 = gp[j+7]; \
189
+ s0 += (double) v0; s1 += (double) v1; \
190
+ s2 += (double) v2; s3 += (double) v3; \
191
+ s4 += (double) v4; s5 += (double) v5; \
192
+ s6 += (double) v6; s7 += (double) v7; \
193
+ if ( CAT_REPLACES(v0, n0, <, ISNAN) ) n0 = v0; \
194
+ if ( CAT_REPLACES(v1, n1, <, ISNAN) ) n1 = v1; \
195
+ if ( CAT_REPLACES(v2, n2, <, ISNAN) ) n2 = v2; \
196
+ if ( CAT_REPLACES(v3, n3, <, ISNAN) ) n3 = v3; \
197
+ if ( CAT_REPLACES(v4, n4, <, ISNAN) ) n4 = v4; \
198
+ if ( CAT_REPLACES(v5, n5, <, ISNAN) ) n5 = v5; \
199
+ if ( CAT_REPLACES(v6, n6, <, ISNAN) ) n6 = v6; \
200
+ if ( CAT_REPLACES(v7, n7, <, ISNAN) ) n7 = v7; \
201
+ if ( CAT_REPLACES(v0, x0, >, ISNAN) ) x0 = v0; \
202
+ if ( CAT_REPLACES(v1, x1, >, ISNAN) ) x1 = v1; \
203
+ if ( CAT_REPLACES(v2, x2, >, ISNAN) ) x2 = v2; \
204
+ if ( CAT_REPLACES(v3, x3, >, ISNAN) ) x3 = v3; \
205
+ if ( CAT_REPLACES(v4, x4, >, ISNAN) ) x4 = v4; \
206
+ if ( CAT_REPLACES(v5, x5, >, ISNAN) ) x5 = v5; \
207
+ if ( CAT_REPLACES(v6, x6, >, ISNAN) ) x6 = v6; \
208
+ if ( CAT_REPLACES(v7, x7, >, ISNAN) ) x7 = v7; \
209
+ } \
210
+ for ( ; j < hi; j++ ) { \
211
+ T v = gp[j]; \
212
+ s0 += (double) v; \
213
+ if ( CAT_REPLACES(v, n0, <, ISNAN) ) n0 = v; \
214
+ if ( CAT_REPLACES(v, x0, >, ISNAN) ) x0 = v; \
215
+ } \
216
+ sacc = ((s0 + s1) + (s2 + s3)) + ((s4 + s5) + (s6 + s7)); \
217
+ mn = n0; \
218
+ if ( CAT_REPLACES(n1, mn, <, ISNAN) ) mn = n1; \
219
+ if ( CAT_REPLACES(n2, mn, <, ISNAN) ) mn = n2; \
220
+ if ( CAT_REPLACES(n3, mn, <, ISNAN) ) mn = n3; \
221
+ if ( CAT_REPLACES(n4, mn, <, ISNAN) ) mn = n4; \
222
+ if ( CAT_REPLACES(n5, mn, <, ISNAN) ) mn = n5; \
223
+ if ( CAT_REPLACES(n6, mn, <, ISNAN) ) mn = n6; \
224
+ if ( CAT_REPLACES(n7, mn, <, ISNAN) ) mn = n7; \
225
+ mx = x0; \
226
+ if ( CAT_REPLACES(x1, mx, >, ISNAN) ) mx = x1; \
227
+ if ( CAT_REPLACES(x2, mx, >, ISNAN) ) mx = x2; \
228
+ if ( CAT_REPLACES(x3, mx, >, ISNAN) ) mx = x3; \
229
+ if ( CAT_REPLACES(x4, mx, >, ISNAN) ) mx = x4; \
230
+ if ( CAT_REPLACES(x5, mx, >, ISNAN) ) mx = x5; \
231
+ if ( CAT_REPLACES(x6, mx, >, ISNAN) ) mx = x6; \
232
+ if ( CAT_REPLACES(x7, mx, >, ISNAN) ) mx = x7; \
233
+ /* Of equal values a lane keeps its first, but the merge does not \
234
+ know which lane saw its value first in cell order. Only 0.0 and \
235
+ -0.0 are equal and tell apart: take the first zero, as one loop \
236
+ over the cells does. */ \
237
+ if ( mn == 0 ) { \
238
+ for ( j = lo; j < hi; j++ ) { if ( gp[j] == 0 ) { mn = gp[j]; break; } } \
239
+ } \
240
+ if ( mx == 0 ) { \
241
+ for ( j = lo; j < hi; j++ ) { if ( gp[j] == 0 ) { mx = gp[j]; break; } } \
242
+ } \
243
+ cnt = hi - lo; seen = 1; \
244
+ } \
245
+ } \
246
+ else { \
247
+ for ( j = lo; j < hi && gm[j]; j++ ) ; /* skip to the first present */ \
248
+ if ( j < hi ) { mn = mx = gp[j]; seen = 1; } \
249
+ for ( ; j < hi; j++ ) { \
250
+ T v = gp[j]; \
251
+ if ( gm[j] ) continue; /* masked value cell */ \
252
+ sacc += (double) v; \
253
+ cnt++; \
254
+ if ( CAT_REPLACES(v, mn, <, ISNAN) ) mn = v; \
255
+ if ( CAT_REPLACES(v, mx, >, ISNAN) ) mx = v; \
256
+ } \
174
257
  } \
175
258
  countp[c] = (int64_t) cnt; \
176
259
  sump[c] = sacc; \
@@ -221,16 +304,16 @@ rb_ca_reduceat_moments (VALUE self, VALUE roffsets, VALUE rcounts,
221
304
  maxm = (boolean8_t *) maxs->mask->ptr;
222
305
 
223
306
  switch ( grouped->data_type ) {
224
- case CA_INT8: REDUCEAT_MOMENTS_BODY(int8_t); break;
225
- case CA_UINT8: REDUCEAT_MOMENTS_BODY(uint8_t); break;
226
- case CA_INT16: REDUCEAT_MOMENTS_BODY(int16_t); break;
227
- case CA_UINT16: REDUCEAT_MOMENTS_BODY(uint16_t); break;
228
- case CA_INT32: REDUCEAT_MOMENTS_BODY(int32_t); break;
229
- case CA_UINT32: REDUCEAT_MOMENTS_BODY(uint32_t); break;
230
- case CA_INT64: REDUCEAT_MOMENTS_BODY(int64_t); break;
231
- case CA_UINT64: REDUCEAT_MOMENTS_BODY(uint64_t); break;
232
- case CA_FLOAT32: REDUCEAT_MOMENTS_BODY(float32_t); break;
233
- case CA_FLOAT64: REDUCEAT_MOMENTS_BODY(float64_t); break;
307
+ case CA_INT8: REDUCEAT_MOMENTS_BODY(int8_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
308
+ case CA_UINT8: REDUCEAT_MOMENTS_BODY(uint8_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
309
+ case CA_INT16: REDUCEAT_MOMENTS_BODY(int16_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
310
+ case CA_UINT16: REDUCEAT_MOMENTS_BODY(uint16_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
311
+ case CA_INT32: REDUCEAT_MOMENTS_BODY(int32_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
312
+ case CA_UINT32: REDUCEAT_MOMENTS_BODY(uint32_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
313
+ case CA_INT64: REDUCEAT_MOMENTS_BODY(int64_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
314
+ case CA_UINT64: REDUCEAT_MOMENTS_BODY(uint64_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
315
+ case CA_FLOAT32: REDUCEAT_MOMENTS_BODY(float32_t, CAT_NAN_CHECK, CAT_LANES_FROM); break;
316
+ case CA_FLOAT64: REDUCEAT_MOMENTS_BODY(float64_t, CAT_NAN_CHECK, CAT_LANES_FROM); break;
234
317
  default:
235
318
  rb_raise(rb_eCADataTypeError,
236
319
  "__reduceat_moments__: numeric value required (got data_type %d)",
@@ -294,19 +377,12 @@ ca_nth_element_double (double *a, ca_size_t n, ca_size_t kth)
294
377
  { double f = (double) (m - 1) * p / 100.0; \
295
378
  ca_size_t ki = (ca_size_t) floor(f); \
296
379
  double vlo, vhi; \
297
- /* Push NaN to the tail, which is where CArray's sort puts it, so the \
298
- ki-th element here is the ki-th of the sorted segment. Selection \
299
- runs on the numeric prefix, since a comparison-based quickselect \
300
- has nothing to say about a NaN; a position or an upper neighbour \
301
- landing in the tail is NaN. The blend is CArray#percentile's, so \
302
- both answer alike for NaN and infinities. */ \
303
- ca_size_t nnum = m, t0 = 0; \
304
- while ( t0 < nnum ) { \
305
- if ( scratch[t0] != scratch[t0] ) { \
306
- double tmp = scratch[--nnum]; \
307
- scratch[nnum] = scratch[t0]; scratch[t0] = tmp; \
308
- } else t0++; \
309
- } \
380
+ /* NaN to the tail, where CArray's sort puts it (ca_partition_nan), \
381
+ so the ki-th element here is the ki-th of the sorted segment. \
382
+ Selection runs on the numeric prefix; a position or an upper \
383
+ neighbour landing in the tail is NaN. The blend is \
384
+ CArray#percentile's, so both answer alike for NaN and infinities. */\
385
+ ca_size_t nnum = ca_partition_nan_f64(scratch, m); \
310
386
  if ( ki < nnum ) { \
311
387
  ca_nth_element_double(scratch, nnum, ki); /* scratch[ki] = ki-th */ \
312
388
  vlo = scratch[ki]; \
@@ -316,9 +392,7 @@ ca_nth_element_double (double *a, ca_size_t n, ca_size_t kth)
316
392
  for ( t = ki + 2; t < nnum; t++ ) if ( scratch[t] < mn ) mn = scratch[t]; \
317
393
  vhi = mn; \
318
394
  } else vhi = (double) NAN; \
319
- { double r = f - (double) ki; \
320
- outp[c] = ( r == 0.0 || ki + 1 >= m ) ? vlo \
321
- : vlo * (1.0 - r) + vhi * r; } } \
395
+ outp[c] = ca_percentile_linear(vlo, vhi, f - (double) ki, ki, m); } \
322
396
  } \
323
397
  } while (0)
324
398
 
@@ -693,19 +767,16 @@ cmp_double (const void *a, const void *b)
693
767
  scratch[m++] = (double) gp[jj]; \
694
768
  } \
695
769
  if ( m == 0 ) { for ( t = 0; t < 5; t++ ) { outp[t][c] = 0.0; outm[t][c] = 1; } continue; } \
770
+ /* NaN sorts last, as in the core's sort, so a position or an upper \
771
+ neighbour in the tail is NaN, as the core answers. */ \
696
772
  qsort(scratch, (size_t) m, sizeof(double), cmp_double); \
697
- { ca_size_t nnum = m; \
698
- while ( nnum > 0 && scratch[nnum-1] != scratch[nnum-1] ) nnum--; \
699
773
  for ( t = 0; t < 5; t++ ) { \
700
774
  double f = (double) (m - 1) * P[t] / 100.0; \
701
775
  ca_size_t ki = (ca_size_t) floor(f); \
702
- double vlo = scratch[ki]; \
703
- /* Clamp the upper neighbour into the numeric prefix: with NaN sorted \
704
- last, a position whose neighbour is a NaN interpolates against \
705
- itself, which is what the core answers. */ \
706
- double vhi = (ki + 1 < nnum) ? scratch[ki+1] : vlo; \
707
- outp[t][c] = vlo + (f - (double) ki) * (vhi - vlo); \
708
- } } \
776
+ double vhi = (ki + 1 < m) ? scratch[ki+1] : scratch[ki]; \
777
+ outp[t][c] = ca_percentile_linear(scratch[ki], vhi, f - (double) ki, \
778
+ ki, m); \
779
+ } \
709
780
  } \
710
781
  } while (0)
711
782
 
@@ -857,7 +928,7 @@ rb_ca_reduceat_wsum_wmean (VALUE self, VALUE roffsets, VALUE rwg,
857
928
 
858
929
  Not aligned kernel_iterator: output position depends on the codes value
859
930
  (data-dependent scatter), so ca_attach materialises the inputs into
860
- contiguous flat buffers — same pattern as sibling __categorical_scatter__.
931
+ contiguous flat buffers — same pattern as sibling addresses_by_category.
861
932
 
862
933
  Surface (private):
863
934
  h.__fiber_scatter_moments__(codes, axis, K, counts_out, sums_out)
@@ -1397,8 +1468,8 @@ rb_ca_fiber_scatter_wsum_wmean (VALUE self, VALUE rcodes, VALUE rweights,
1397
1468
  void
1398
1469
  Init_ca_categorical_iterator (void)
1399
1470
  {
1400
- rb_define_private_method(rb_cCArray, "__categorical_scatter__",
1401
- rb_ca_categorical_scatter, 4);
1471
+ rb_define_private_method(rb_cCArray, "addresses_by_category",
1472
+ rb_ca_addresses_by_category, 3);
1402
1473
  rb_define_private_method(rb_cCArray, "__fiber_scatter_moments__",
1403
1474
  rb_ca_fiber_scatter_moments, 7);
1404
1475
  rb_define_private_method(rb_cCArray, "__fiber_scatter_prod__",
data/ext/ca_compare.c CHANGED
@@ -14,7 +14,7 @@
14
14
  #include "ruby.h"
15
15
  #include "carray.h"
16
16
  #include "ca_compare.h"
17
- #include "carray_internal.h" /* ca_obj_cmp, CA_OBJ_ISNAN */
17
+ #include "carray_internal.h" /* CA_FLOAT_ORDER_CMP, ca_obj_order_cmp */
18
18
  #include <math.h>
19
19
 
20
20
  #define DEFINE_ELEM_CMP(type) \
@@ -28,18 +28,14 @@ elem_cmp_## type (const void *va, const void *vb) \
28
28
  return 0; \
29
29
  }
30
30
 
31
- /* Float order: NaN sorts last (matches the sort kernels' NaN-at-end). */
31
+ /* Float order: NaN after every number (CA_FLOAT_ORDER_CMP). */
32
32
  #define DEFINE_ELEM_CMP_FLOAT(type) \
33
33
  static int \
34
34
  elem_cmp_## type (const void *va, const void *vb) \
35
35
  { \
36
- const type *a = (const type *) va; \
37
- const type *b = (const type *) vb; \
38
- if ( isnan(*a) && ( ! isnan(*b) ) ) return 1; \
39
- if ( isnan(*b) && ( ! isnan(*a) ) ) return -1; \
40
- if ( *a > *b ) return 1; \
41
- if ( *a < *b ) return -1; \
42
- return 0; \
36
+ const type a = *(const type *) va; \
37
+ const type b = *(const type *) vb; \
38
+ return CA_FLOAT_ORDER_CMP(a, b); \
43
39
  }
44
40
 
45
41
  DEFINE_ELEM_CMP(boolean8_t)
@@ -54,18 +50,11 @@ DEFINE_ELEM_CMP(uint64_t)
54
50
  DEFINE_ELEM_CMP_FLOAT(float32_t)
55
51
  DEFINE_ELEM_CMP_FLOAT(float64_t)
56
52
 
57
- /* CA_OBJECT: compare by `<=>`, as sort orders object cells: a Float NaN
58
- after every other value, and a pair `<=>` cannot order raises
59
- "comparison of A with B failed". */
53
+ /* CA_OBJECT: the object order (ca_obj_order_cmp). */
60
54
  static int
61
55
  elem_cmp_VALUE (const void *va, const void *vb)
62
56
  {
63
- const VALUE *a = (const VALUE *) va;
64
- const VALUE *b = (const VALUE *) vb;
65
- int nan_a = CA_OBJ_ISNAN(*a);
66
- int nan_b = CA_OBJ_ISNAN(*b);
67
- if ( nan_a || nan_b ) return nan_a - nan_b;
68
- return ca_obj_cmp(*a, *b);
57
+ return ca_obj_order_cmp(*(const VALUE *) va, *(const VALUE *) vb);
69
58
  }
70
59
 
71
60
  /* FIXLEN / retired / otherwise unorderable data_types: callers must handle
data/ext/ca_group_iter.c CHANGED
@@ -14,13 +14,14 @@
14
14
  Split summary:
15
15
  - ca_argv_has_group / rb_ca_fetch_group : here (called by carray_access.c)
16
16
  - CAGroupIterator + the tier-1 reduction methods driving
17
- __axis_group_reduce__ : here
17
+ __reduce_by_axis_groups__ : here
18
18
  - reduce_plan / bundle assembly / output-axis permutation (metadata) : Ruby
19
- - the O(N) compute kernel __axis_group_reduce__ : ca_axis_group.c
19
+ - the O(N) compute kernel __reduce_by_axis_groups__ : ca_axis_group.c
20
20
 
21
21
  --------------------------------------------------------------------------- */
22
22
 
23
23
  #include "carray.h"
24
+ #include "carray_internal.h" /* ca_check_rank_fits */
24
25
 
25
26
  /* lib/carray/axis_group.rb registers these classes here on first load (and
26
27
  loading is wired so that load happens as soon as any categorical exists —
@@ -34,7 +35,7 @@ static VALUE rb_cCAGroupIterator = Qnil;
34
35
  static ID id_value, id_spec, id_axis_group, id_reduce_plan, id_parse_axis;
35
36
  static ID id_axis_group_reduce, id_reshape, id_transpose, id_labels;
36
37
 
37
- /* CArray.__register_axis_group_classes__(CACategorical, AxisGroup) — called
38
+ /* CArray.register_axis_group_classes(CACategorical, AxisGroup) — called
38
39
  from the top of lib/carray/axis_group.rb so the C type gate can recognise
39
40
  the classifier/spec types without triggering autoload on every [] call. */
40
41
  static VALUE
@@ -120,7 +121,7 @@ rb_ca_fetch_group (int argc, VALUE *argv, VALUE self)
120
121
  validates the slot layout = one slot per source axis). */
121
122
  spec = rb_funcallv(self, id_axis_group, argc, argv);
122
123
  }
123
- return rb_funcall(rb_cCAGroupIterator, rb_intern("__build__"), 2, self, spec);
124
+ return rb_funcall(rb_cCAGroupIterator, rb_intern("build_for"), 2, self, spec);
124
125
  }
125
126
 
126
127
  /* --- CAGroupIterator -------------------------------------------------------
@@ -175,9 +176,7 @@ group_iter_shape_output (VALUE result, VALUE group_dims, VALUE perm, VALUE squee
175
176
  ng = RARRAY_LEN(group_dims);
176
177
  nband = cr->ndim - 1;
177
178
  nd = ng + nband;
178
- if ( nd > CA_RANK_MAX ) {
179
- rb_raise(rb_eArgError, "axis_group: output ndim %ld too large", nd);
180
- }
179
+ ca_check_rank_fits(nd, "axis_group");
181
180
  for ( i = 0; i < ng; i++ ) {
182
181
  dims[i] = RARRAY_AREF(group_dims, i);
183
182
  }
@@ -219,7 +218,7 @@ group_iter_shape_output (VALUE result, VALUE group_dims, VALUE perm, VALUE squee
219
218
  static VALUE
220
219
  group_iter_reduce (int argc, VALUE *argv, VALUE self)
221
220
  {
222
- ID op_id = rb_frame_this_func();
221
+ ID op_id = rb_frame_this_func(); /* frame-name: picks the op, names no error */
223
222
  VALUE op_sym = ID2SYM(op_id);
224
223
  VALUE value = rb_ivar_get(self, id_value);
225
224
  VALUE spec = rb_ivar_get(self, id_spec);
@@ -247,7 +246,7 @@ group_iter_reduce (int argc, VALUE *argv, VALUE self)
247
246
 
248
247
  /* min_count: / fill_value: are applied to the grouped result; any other
249
248
  keyword is refused before the work is done. */
250
- rb_funcall(rb_cAxisGroup, rb_intern("__check_reduce_keywords__"), 2,
249
+ rb_funcall(self, rb_intern("check_reduce_keywords"), 2,
251
250
  op_sym, NIL_P(kw) ? rb_hash_new() : kw);
252
251
 
253
252
  /* group reduction */
@@ -262,14 +261,14 @@ group_iter_reduce (int argc, VALUE *argv, VALUE self)
262
261
  /* a count reads only the mask, so a payload the kernel cannot read
263
262
  (object, fixlen, ...) is counted through a boolean stand-in */
264
263
  if ( op_id == rb_intern("count") || op_id == rb_intern("count_not_masked") ) {
265
- value = rb_funcall(rb_cAxisGroup, rb_intern("__count_stand_in__"), 1, value);
264
+ value = rb_funcall(rb_cAxisGroup, rb_intern("count_stand_in"), 1, value);
266
265
  }
267
266
  result = rb_funcall(value, id_axis_group_reduce, 3,
268
267
  group_axes, bundles, op_sym);
269
268
  result = group_iter_shape_output(result, group_dims, perm, squeeze);
270
269
  if ( ! NIL_P(kw) ) {
271
- result = rb_funcall(rb_cAxisGroup, rb_intern("__finish_reduction__"), 4,
272
- self, op_sym, kw, result);
270
+ result = rb_funcall(self, rb_intern("finish_reduction"), 3,
271
+ op_sym, kw, result);
273
272
  }
274
273
  return result;
275
274
  }
@@ -282,21 +281,21 @@ Init_ca_group_iter (void)
282
281
  id_axis_group = rb_intern("axis_group");
283
282
  id_reduce_plan = rb_intern("reduce_plan");
284
283
  id_parse_axis = rb_intern("parse_axis");
285
- id_axis_group_reduce = rb_intern("__axis_group_reduce__");
284
+ id_axis_group_reduce = rb_intern("__reduce_by_axis_groups__");
286
285
  id_reshape = rb_intern("reshape");
287
286
  id_transpose = rb_intern("transpose");
288
287
  id_labels = rb_intern("labels");
289
288
 
290
- rb_define_singleton_method(rb_cCArray, "__register_axis_group_classes__",
291
- rb_ca_register_axis_group_classes, 2);
289
+ rb_define_private_method(rb_singleton_class(rb_cCArray), "register_axis_group_classes",
290
+ rb_ca_register_axis_group_classes, 2);
292
291
 
293
292
  rb_cCAGroupIterator =
294
293
  rb_define_class("CAGroupIterator",
295
294
  rb_const_get(rb_cObject, rb_intern("CAIterator")));
296
295
  rb_gc_register_address(&rb_cCAGroupIterator);
297
296
 
298
- rb_define_singleton_method(rb_cCAGroupIterator, "__build__",
299
- rb_ca_group_iter_build, 2);
297
+ rb_define_private_method(rb_singleton_class(rb_cCAGroupIterator), "build_for",
298
+ rb_ca_group_iter_build, 2);
300
299
  rb_define_method(rb_cCAGroupIterator, "value", rb_ca_group_iter_value, 0);
301
300
  rb_define_method(rb_cCAGroupIterator, "spec", rb_ca_group_iter_spec, 0);
302
301
  rb_define_method(rb_cCAGroupIterator, "labels", rb_ca_group_iter_labels, -1);