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
@@ -0,0 +1,3849 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ carray_kernels.c -- GENERATED by ext/mkkernel.rb -- DO NOT EDIT
4
+
5
+ Kernel definitions live in mkkernel.rb (search for `MkKernel.reduce`).
6
+ Regenerate by running:
7
+
8
+ cd ext && ruby mkkernel.rb > carray_kernels.c
9
+
10
+ extconf.rb regenerates automatically when mkkernel.rb is touched.
11
+
12
+ --------------------------------------------------------------------------- */
13
+
14
+ #include "carray.h"
15
+ #include "ca_kernel_iterator.h"
16
+ #include "ca_sort_kernels.h" /* sort kernels: internal, not via the carray.h umbrella */
17
+ #include "carray_internal.h" /* ca_lazy_arena_*, ca_is_lazy_view (streaming reduce) */
18
+ #include "ca_obj_face.h" /* rb_ca_strip_face_value (Face ordering gate) */
19
+ #include <math.h>
20
+ #include <stdint.h>
21
+ #include <stdlib.h> /* qsort, mergesort (HAVE_MERGESORT) -- for :sort kind */
22
+
23
+ /* MEMO_REDUCTION_FASTPATH_ENTITY_ONLY_GUARD Tier 2: CAStack identity is
24
+ probed by operation-table function pointer (obj_type is runtime-
25
+ assigned, so no CA_OBJ_STACK compile-time constant exists). */
26
+ extern ca_operation_function_t ca_stack_func;
27
+ /* The square root of a variance: an ε-negative from rounding becomes 0,
28
+ a NaN stays NaN (fmax(NaN, 0.0) is 0.0). */
29
+ #ifndef CARRAY_SQRT_CLAMP_DEFINED
30
+ #define CARRAY_SQRT_CLAMP_DEFINED
31
+ static inline double ca_sqrt_clamp (double v) { return sqrt(v < 0.0 ? 0.0 : v); }
32
+ #endif
33
+
34
+ /* BOOL2VAL: bool -> Ruby (Qtrue/Qfalse). Used as ruby_scalar wrapper
35
+ for the all/any flat-reduction Ruby surface so `a.all` / `a.any`
36
+ return real true/false, not Integer 0/1. (BOOL2OBJ already exists
37
+ but returns Integer per CArray's boolean-as-int convention.) */
38
+ #ifndef CARRAY_BOOL2VAL_DEFINED
39
+ #define CARRAY_BOOL2VAL_DEFINED
40
+ static inline VALUE BOOL2VAL (boolean8_t x) { return x ? Qtrue : Qfalse; }
41
+ #endif
42
+
43
+ #include "ca_op_powi.h"
44
+ #include "ca_op_cmplx64.h"
45
+
46
+ /* ===== variancep_ki ============================================ */
47
+
48
+ #define __variancep_i8_P1EXPR(__x) ((double)(__x))
49
+ #define __variancep_i8_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
50
+ static VALUE
51
+ variancep_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
52
+ {
53
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
54
+ CArray *co;
55
+ GetCArray(vout, co);
56
+ double *op = (double *) co->ptr;
57
+ ca_iter_state st;
58
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
59
+ slab_axes, naxes, 0);
60
+ if ( rc != CA_ITER_OK ) {
61
+ rb_raise(rb_eRuntimeError,
62
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
63
+ }
64
+ char *p;
65
+ boolean8_t *m;
66
+ ca_size_t out_i = 0;
67
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
68
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
69
+ /* Pass 1: sum = Σx (+ masked_cnt) */
70
+ double sum = 0.0;
71
+ ca_size_t masked_cnt = 0;
72
+ CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, sum, 0.0, __variancep_i8_P1EXPR, masked_cnt);
73
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
74
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
75
+ if ( __trigger ) {
76
+ if ( ! op_mask ) {
77
+ ca_create_mask(co);
78
+ op_mask = (boolean8_t *) co->mask->ptr;
79
+ }
80
+ op_mask[out_i] = 1;
81
+ op[out_i++] = (double) 0.0;
82
+ } else {
83
+ double mean = sum / (double) n_valid;
84
+ /* Pass 2: M2 = Σ (x - mean)² */
85
+ double M2 = 0.0;
86
+ ca_size_t __unused_mc = 0;
87
+ CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, M2, 0.0, __variancep_i8_P2EXPR, __unused_mc);
88
+ op[out_i++] = (double)(M2 / (double)(n_valid));
89
+ }
90
+ }
91
+ ca_iter_state_finish(&st);
92
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
93
+ keep_axis means the output is a 1-element CArray; unwrap it to
94
+ the ruby scalar (matches the standard reduce emit path lines
95
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
96
+ if ( naxes == ca->ndim && !keep_axis ) {
97
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
98
+ return rb_float_new(op[0]);
99
+ }
100
+ return vout;
101
+ }
102
+ #undef __variancep_i8_P1EXPR
103
+ #undef __variancep_i8_P2EXPR
104
+
105
+ #define __variancep_u8_P1EXPR(__x) ((double)(__x))
106
+ #define __variancep_u8_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
107
+ static VALUE
108
+ variancep_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
109
+ {
110
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
111
+ CArray *co;
112
+ GetCArray(vout, co);
113
+ double *op = (double *) co->ptr;
114
+ ca_iter_state st;
115
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
116
+ slab_axes, naxes, 0);
117
+ if ( rc != CA_ITER_OK ) {
118
+ rb_raise(rb_eRuntimeError,
119
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
120
+ }
121
+ char *p;
122
+ boolean8_t *m;
123
+ ca_size_t out_i = 0;
124
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
125
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
126
+ /* Pass 1: sum = Σx (+ masked_cnt) */
127
+ double sum = 0.0;
128
+ ca_size_t masked_cnt = 0;
129
+ CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, sum, 0.0, __variancep_u8_P1EXPR, masked_cnt);
130
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
131
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
132
+ if ( __trigger ) {
133
+ if ( ! op_mask ) {
134
+ ca_create_mask(co);
135
+ op_mask = (boolean8_t *) co->mask->ptr;
136
+ }
137
+ op_mask[out_i] = 1;
138
+ op[out_i++] = (double) 0.0;
139
+ } else {
140
+ double mean = sum / (double) n_valid;
141
+ /* Pass 2: M2 = Σ (x - mean)² */
142
+ double M2 = 0.0;
143
+ ca_size_t __unused_mc = 0;
144
+ CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, M2, 0.0, __variancep_u8_P2EXPR, __unused_mc);
145
+ op[out_i++] = (double)(M2 / (double)(n_valid));
146
+ }
147
+ }
148
+ ca_iter_state_finish(&st);
149
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
150
+ keep_axis means the output is a 1-element CArray; unwrap it to
151
+ the ruby scalar (matches the standard reduce emit path lines
152
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
153
+ if ( naxes == ca->ndim && !keep_axis ) {
154
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
155
+ return rb_float_new(op[0]);
156
+ }
157
+ return vout;
158
+ }
159
+ #undef __variancep_u8_P1EXPR
160
+ #undef __variancep_u8_P2EXPR
161
+
162
+ #define __variancep_i16_P1EXPR(__x) ((double)(__x))
163
+ #define __variancep_i16_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
164
+ static VALUE
165
+ variancep_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
166
+ {
167
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
168
+ CArray *co;
169
+ GetCArray(vout, co);
170
+ double *op = (double *) co->ptr;
171
+ ca_iter_state st;
172
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
173
+ slab_axes, naxes, 0);
174
+ if ( rc != CA_ITER_OK ) {
175
+ rb_raise(rb_eRuntimeError,
176
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
177
+ }
178
+ char *p;
179
+ boolean8_t *m;
180
+ ca_size_t out_i = 0;
181
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
182
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
183
+ /* Pass 1: sum = Σx (+ masked_cnt) */
184
+ double sum = 0.0;
185
+ ca_size_t masked_cnt = 0;
186
+ CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, sum, 0.0, __variancep_i16_P1EXPR, masked_cnt);
187
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
188
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
189
+ if ( __trigger ) {
190
+ if ( ! op_mask ) {
191
+ ca_create_mask(co);
192
+ op_mask = (boolean8_t *) co->mask->ptr;
193
+ }
194
+ op_mask[out_i] = 1;
195
+ op[out_i++] = (double) 0.0;
196
+ } else {
197
+ double mean = sum / (double) n_valid;
198
+ /* Pass 2: M2 = Σ (x - mean)² */
199
+ double M2 = 0.0;
200
+ ca_size_t __unused_mc = 0;
201
+ CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, M2, 0.0, __variancep_i16_P2EXPR, __unused_mc);
202
+ op[out_i++] = (double)(M2 / (double)(n_valid));
203
+ }
204
+ }
205
+ ca_iter_state_finish(&st);
206
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
207
+ keep_axis means the output is a 1-element CArray; unwrap it to
208
+ the ruby scalar (matches the standard reduce emit path lines
209
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
210
+ if ( naxes == ca->ndim && !keep_axis ) {
211
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
212
+ return rb_float_new(op[0]);
213
+ }
214
+ return vout;
215
+ }
216
+ #undef __variancep_i16_P1EXPR
217
+ #undef __variancep_i16_P2EXPR
218
+
219
+ #define __variancep_u16_P1EXPR(__x) ((double)(__x))
220
+ #define __variancep_u16_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
221
+ static VALUE
222
+ variancep_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
223
+ {
224
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
225
+ CArray *co;
226
+ GetCArray(vout, co);
227
+ double *op = (double *) co->ptr;
228
+ ca_iter_state st;
229
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
230
+ slab_axes, naxes, 0);
231
+ if ( rc != CA_ITER_OK ) {
232
+ rb_raise(rb_eRuntimeError,
233
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
234
+ }
235
+ char *p;
236
+ boolean8_t *m;
237
+ ca_size_t out_i = 0;
238
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
239
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
240
+ /* Pass 1: sum = Σx (+ masked_cnt) */
241
+ double sum = 0.0;
242
+ ca_size_t masked_cnt = 0;
243
+ CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, sum, 0.0, __variancep_u16_P1EXPR, masked_cnt);
244
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
245
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
246
+ if ( __trigger ) {
247
+ if ( ! op_mask ) {
248
+ ca_create_mask(co);
249
+ op_mask = (boolean8_t *) co->mask->ptr;
250
+ }
251
+ op_mask[out_i] = 1;
252
+ op[out_i++] = (double) 0.0;
253
+ } else {
254
+ double mean = sum / (double) n_valid;
255
+ /* Pass 2: M2 = Σ (x - mean)² */
256
+ double M2 = 0.0;
257
+ ca_size_t __unused_mc = 0;
258
+ CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, M2, 0.0, __variancep_u16_P2EXPR, __unused_mc);
259
+ op[out_i++] = (double)(M2 / (double)(n_valid));
260
+ }
261
+ }
262
+ ca_iter_state_finish(&st);
263
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
264
+ keep_axis means the output is a 1-element CArray; unwrap it to
265
+ the ruby scalar (matches the standard reduce emit path lines
266
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
267
+ if ( naxes == ca->ndim && !keep_axis ) {
268
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
269
+ return rb_float_new(op[0]);
270
+ }
271
+ return vout;
272
+ }
273
+ #undef __variancep_u16_P1EXPR
274
+ #undef __variancep_u16_P2EXPR
275
+
276
+ #define __variancep_i32_P1EXPR(__x) ((double)(__x))
277
+ #define __variancep_i32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
278
+ static VALUE
279
+ variancep_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
280
+ {
281
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
282
+ CArray *co;
283
+ GetCArray(vout, co);
284
+ double *op = (double *) co->ptr;
285
+ ca_iter_state st;
286
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
287
+ slab_axes, naxes, 0);
288
+ if ( rc != CA_ITER_OK ) {
289
+ rb_raise(rb_eRuntimeError,
290
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
291
+ }
292
+ char *p;
293
+ boolean8_t *m;
294
+ ca_size_t out_i = 0;
295
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
296
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
297
+ /* Pass 1: sum = Σx (+ masked_cnt) */
298
+ double sum = 0.0;
299
+ ca_size_t masked_cnt = 0;
300
+ CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, sum, 0.0, __variancep_i32_P1EXPR, masked_cnt);
301
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
302
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
303
+ if ( __trigger ) {
304
+ if ( ! op_mask ) {
305
+ ca_create_mask(co);
306
+ op_mask = (boolean8_t *) co->mask->ptr;
307
+ }
308
+ op_mask[out_i] = 1;
309
+ op[out_i++] = (double) 0.0;
310
+ } else {
311
+ double mean = sum / (double) n_valid;
312
+ /* Pass 2: M2 = Σ (x - mean)² */
313
+ double M2 = 0.0;
314
+ ca_size_t __unused_mc = 0;
315
+ CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, M2, 0.0, __variancep_i32_P2EXPR, __unused_mc);
316
+ op[out_i++] = (double)(M2 / (double)(n_valid));
317
+ }
318
+ }
319
+ ca_iter_state_finish(&st);
320
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
321
+ keep_axis means the output is a 1-element CArray; unwrap it to
322
+ the ruby scalar (matches the standard reduce emit path lines
323
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
324
+ if ( naxes == ca->ndim && !keep_axis ) {
325
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
326
+ return rb_float_new(op[0]);
327
+ }
328
+ return vout;
329
+ }
330
+ #undef __variancep_i32_P1EXPR
331
+ #undef __variancep_i32_P2EXPR
332
+
333
+ #define __variancep_u32_P1EXPR(__x) ((double)(__x))
334
+ #define __variancep_u32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
335
+ static VALUE
336
+ variancep_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
337
+ {
338
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
339
+ CArray *co;
340
+ GetCArray(vout, co);
341
+ double *op = (double *) co->ptr;
342
+ ca_iter_state st;
343
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
344
+ slab_axes, naxes, 0);
345
+ if ( rc != CA_ITER_OK ) {
346
+ rb_raise(rb_eRuntimeError,
347
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
348
+ }
349
+ char *p;
350
+ boolean8_t *m;
351
+ ca_size_t out_i = 0;
352
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
353
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
354
+ /* Pass 1: sum = Σx (+ masked_cnt) */
355
+ double sum = 0.0;
356
+ ca_size_t masked_cnt = 0;
357
+ CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, sum, 0.0, __variancep_u32_P1EXPR, masked_cnt);
358
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
359
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
360
+ if ( __trigger ) {
361
+ if ( ! op_mask ) {
362
+ ca_create_mask(co);
363
+ op_mask = (boolean8_t *) co->mask->ptr;
364
+ }
365
+ op_mask[out_i] = 1;
366
+ op[out_i++] = (double) 0.0;
367
+ } else {
368
+ double mean = sum / (double) n_valid;
369
+ /* Pass 2: M2 = Σ (x - mean)² */
370
+ double M2 = 0.0;
371
+ ca_size_t __unused_mc = 0;
372
+ CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, M2, 0.0, __variancep_u32_P2EXPR, __unused_mc);
373
+ op[out_i++] = (double)(M2 / (double)(n_valid));
374
+ }
375
+ }
376
+ ca_iter_state_finish(&st);
377
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
378
+ keep_axis means the output is a 1-element CArray; unwrap it to
379
+ the ruby scalar (matches the standard reduce emit path lines
380
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
381
+ if ( naxes == ca->ndim && !keep_axis ) {
382
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
383
+ return rb_float_new(op[0]);
384
+ }
385
+ return vout;
386
+ }
387
+ #undef __variancep_u32_P1EXPR
388
+ #undef __variancep_u32_P2EXPR
389
+
390
+ #define __variancep_i64_P1EXPR(__x) ((double)(__x))
391
+ #define __variancep_i64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
392
+ static VALUE
393
+ variancep_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
394
+ {
395
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
396
+ CArray *co;
397
+ GetCArray(vout, co);
398
+ double *op = (double *) co->ptr;
399
+ ca_iter_state st;
400
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
401
+ slab_axes, naxes, 0);
402
+ if ( rc != CA_ITER_OK ) {
403
+ rb_raise(rb_eRuntimeError,
404
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
405
+ }
406
+ char *p;
407
+ boolean8_t *m;
408
+ ca_size_t out_i = 0;
409
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
410
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
411
+ /* Pass 1: sum = Σx (+ masked_cnt) */
412
+ double sum = 0.0;
413
+ ca_size_t masked_cnt = 0;
414
+ CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, sum, 0.0, __variancep_i64_P1EXPR, masked_cnt);
415
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
416
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
417
+ if ( __trigger ) {
418
+ if ( ! op_mask ) {
419
+ ca_create_mask(co);
420
+ op_mask = (boolean8_t *) co->mask->ptr;
421
+ }
422
+ op_mask[out_i] = 1;
423
+ op[out_i++] = (double) 0.0;
424
+ } else {
425
+ double mean = sum / (double) n_valid;
426
+ /* Pass 2: M2 = Σ (x - mean)² */
427
+ double M2 = 0.0;
428
+ ca_size_t __unused_mc = 0;
429
+ CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, M2, 0.0, __variancep_i64_P2EXPR, __unused_mc);
430
+ op[out_i++] = (double)(M2 / (double)(n_valid));
431
+ }
432
+ }
433
+ ca_iter_state_finish(&st);
434
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
435
+ keep_axis means the output is a 1-element CArray; unwrap it to
436
+ the ruby scalar (matches the standard reduce emit path lines
437
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
438
+ if ( naxes == ca->ndim && !keep_axis ) {
439
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
440
+ return rb_float_new(op[0]);
441
+ }
442
+ return vout;
443
+ }
444
+ #undef __variancep_i64_P1EXPR
445
+ #undef __variancep_i64_P2EXPR
446
+
447
+ #define __variancep_u64_P1EXPR(__x) ((double)(__x))
448
+ #define __variancep_u64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
449
+ static VALUE
450
+ variancep_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
451
+ {
452
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
453
+ CArray *co;
454
+ GetCArray(vout, co);
455
+ double *op = (double *) co->ptr;
456
+ ca_iter_state st;
457
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
458
+ slab_axes, naxes, 0);
459
+ if ( rc != CA_ITER_OK ) {
460
+ rb_raise(rb_eRuntimeError,
461
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
462
+ }
463
+ char *p;
464
+ boolean8_t *m;
465
+ ca_size_t out_i = 0;
466
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
467
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
468
+ /* Pass 1: sum = Σx (+ masked_cnt) */
469
+ double sum = 0.0;
470
+ ca_size_t masked_cnt = 0;
471
+ CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, sum, 0.0, __variancep_u64_P1EXPR, masked_cnt);
472
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
473
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
474
+ if ( __trigger ) {
475
+ if ( ! op_mask ) {
476
+ ca_create_mask(co);
477
+ op_mask = (boolean8_t *) co->mask->ptr;
478
+ }
479
+ op_mask[out_i] = 1;
480
+ op[out_i++] = (double) 0.0;
481
+ } else {
482
+ double mean = sum / (double) n_valid;
483
+ /* Pass 2: M2 = Σ (x - mean)² */
484
+ double M2 = 0.0;
485
+ ca_size_t __unused_mc = 0;
486
+ CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, M2, 0.0, __variancep_u64_P2EXPR, __unused_mc);
487
+ op[out_i++] = (double)(M2 / (double)(n_valid));
488
+ }
489
+ }
490
+ ca_iter_state_finish(&st);
491
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
492
+ keep_axis means the output is a 1-element CArray; unwrap it to
493
+ the ruby scalar (matches the standard reduce emit path lines
494
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
495
+ if ( naxes == ca->ndim && !keep_axis ) {
496
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
497
+ return rb_float_new(op[0]);
498
+ }
499
+ return vout;
500
+ }
501
+ #undef __variancep_u64_P1EXPR
502
+ #undef __variancep_u64_P2EXPR
503
+
504
+ #define __variancep_f32_P1EXPR(__x) ((double)(__x))
505
+ #define __variancep_f32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
506
+ static VALUE
507
+ variancep_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
508
+ {
509
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
510
+ CArray *co;
511
+ GetCArray(vout, co);
512
+ double *op = (double *) co->ptr;
513
+ ca_iter_state st;
514
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
515
+ slab_axes, naxes, 0);
516
+ if ( rc != CA_ITER_OK ) {
517
+ rb_raise(rb_eRuntimeError,
518
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
519
+ }
520
+ char *p;
521
+ boolean8_t *m;
522
+ ca_size_t out_i = 0;
523
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
524
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
525
+ /* Pass 1: sum = Σx (+ masked_cnt) */
526
+ double sum = 0.0;
527
+ ca_size_t masked_cnt = 0;
528
+ CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, sum, 0.0, __variancep_f32_P1EXPR, masked_cnt);
529
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
530
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
531
+ if ( __trigger ) {
532
+ if ( ! op_mask ) {
533
+ ca_create_mask(co);
534
+ op_mask = (boolean8_t *) co->mask->ptr;
535
+ }
536
+ op_mask[out_i] = 1;
537
+ op[out_i++] = (double) 0.0;
538
+ } else {
539
+ double mean = sum / (double) n_valid;
540
+ /* Pass 2: M2 = Σ (x - mean)² */
541
+ double M2 = 0.0;
542
+ ca_size_t __unused_mc = 0;
543
+ CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, M2, 0.0, __variancep_f32_P2EXPR, __unused_mc);
544
+ op[out_i++] = (double)(M2 / (double)(n_valid));
545
+ }
546
+ }
547
+ ca_iter_state_finish(&st);
548
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
549
+ keep_axis means the output is a 1-element CArray; unwrap it to
550
+ the ruby scalar (matches the standard reduce emit path lines
551
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
552
+ if ( naxes == ca->ndim && !keep_axis ) {
553
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
554
+ return rb_float_new(op[0]);
555
+ }
556
+ return vout;
557
+ }
558
+ #undef __variancep_f32_P1EXPR
559
+ #undef __variancep_f32_P2EXPR
560
+
561
+ #define __variancep_f64_P1EXPR(__x) ((double)(__x))
562
+ #define __variancep_f64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
563
+ static VALUE
564
+ variancep_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
565
+ {
566
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
567
+ CArray *co;
568
+ GetCArray(vout, co);
569
+ double *op = (double *) co->ptr;
570
+ ca_iter_state st;
571
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
572
+ slab_axes, naxes, 0);
573
+ if ( rc != CA_ITER_OK ) {
574
+ rb_raise(rb_eRuntimeError,
575
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
576
+ }
577
+ char *p;
578
+ boolean8_t *m;
579
+ ca_size_t out_i = 0;
580
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
581
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
582
+ /* Pass 1: sum = Σx (+ masked_cnt) */
583
+ double sum = 0.0;
584
+ ca_size_t masked_cnt = 0;
585
+ CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, sum, 0.0, __variancep_f64_P1EXPR, masked_cnt);
586
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
587
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
588
+ if ( __trigger ) {
589
+ if ( ! op_mask ) {
590
+ ca_create_mask(co);
591
+ op_mask = (boolean8_t *) co->mask->ptr;
592
+ }
593
+ op_mask[out_i] = 1;
594
+ op[out_i++] = (double) 0.0;
595
+ } else {
596
+ double mean = sum / (double) n_valid;
597
+ /* Pass 2: M2 = Σ (x - mean)² */
598
+ double M2 = 0.0;
599
+ ca_size_t __unused_mc = 0;
600
+ CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, M2, 0.0, __variancep_f64_P2EXPR, __unused_mc);
601
+ op[out_i++] = (double)(M2 / (double)(n_valid));
602
+ }
603
+ }
604
+ ca_iter_state_finish(&st);
605
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
606
+ keep_axis means the output is a 1-element CArray; unwrap it to
607
+ the ruby scalar (matches the standard reduce emit path lines
608
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
609
+ if ( naxes == ca->ndim && !keep_axis ) {
610
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
611
+ return rb_float_new(op[0]);
612
+ }
613
+ return vout;
614
+ }
615
+ #undef __variancep_f64_P1EXPR
616
+ #undef __variancep_f64_P2EXPR
617
+
618
+ #define __variancep_cmplx64_P1EXPR(__x) (__x)
619
+ #define __variancep_cmplx64_P2EXPR(__x) (creal((__x) - mean) * creal((__x) - mean) + cimag((__x) - mean) * cimag((__x) - mean))
620
+ static VALUE
621
+ variancep_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
622
+ {
623
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
624
+ CArray *co;
625
+ GetCArray(vout, co);
626
+ double *op = (double *) co->ptr;
627
+ ca_iter_state st;
628
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
629
+ slab_axes, naxes, 0);
630
+ if ( rc != CA_ITER_OK ) {
631
+ rb_raise(rb_eRuntimeError,
632
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
633
+ }
634
+ char *p;
635
+ boolean8_t *m;
636
+ ca_size_t out_i = 0;
637
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
638
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
639
+ /* Pass 1: sum = Σx (+ masked_cnt) */
640
+ cmplx64_t sum = 0;
641
+ ca_size_t masked_cnt = 0;
642
+ CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, cmplx64_t, st, p, m, sum, 0, __variancep_cmplx64_P1EXPR, masked_cnt);
643
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
644
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
645
+ if ( __trigger ) {
646
+ if ( ! op_mask ) {
647
+ ca_create_mask(co);
648
+ op_mask = (boolean8_t *) co->mask->ptr;
649
+ }
650
+ op_mask[out_i] = 1;
651
+ op[out_i++] = (double) 0.0;
652
+ } else {
653
+ cmplx64_t mean = sum / (cmplx64_t)(double) n_valid;
654
+ /* Pass 2: M2 = Σ (x - mean)² */
655
+ double M2 = 0.0;
656
+ ca_size_t __unused_mc = 0;
657
+ CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, double, st, p, m, M2, 0.0, __variancep_cmplx64_P2EXPR, __unused_mc);
658
+ op[out_i++] = (double)(M2 / (double)(n_valid));
659
+ }
660
+ }
661
+ ca_iter_state_finish(&st);
662
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
663
+ keep_axis means the output is a 1-element CArray; unwrap it to
664
+ the ruby scalar (matches the standard reduce emit path lines
665
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
666
+ if ( naxes == ca->ndim && !keep_axis ) {
667
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
668
+ return rb_float_new(op[0]);
669
+ }
670
+ return vout;
671
+ }
672
+ #undef __variancep_cmplx64_P1EXPR
673
+ #undef __variancep_cmplx64_P2EXPR
674
+
675
+ #define __variancep_cmplx128_P1EXPR(__x) (__x)
676
+ #define __variancep_cmplx128_P2EXPR(__x) (creal((__x) - mean) * creal((__x) - mean) + cimag((__x) - mean) * cimag((__x) - mean))
677
+ static VALUE
678
+ variancep_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
679
+ {
680
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
681
+ CArray *co;
682
+ GetCArray(vout, co);
683
+ double *op = (double *) co->ptr;
684
+ ca_iter_state st;
685
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
686
+ slab_axes, naxes, 0);
687
+ if ( rc != CA_ITER_OK ) {
688
+ rb_raise(rb_eRuntimeError,
689
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
690
+ }
691
+ char *p;
692
+ boolean8_t *m;
693
+ ca_size_t out_i = 0;
694
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
695
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
696
+ /* Pass 1: sum = Σx (+ masked_cnt) */
697
+ cmplx128_t sum = 0;
698
+ ca_size_t masked_cnt = 0;
699
+ CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, cmplx128_t, st, p, m, sum, 0, __variancep_cmplx128_P1EXPR, masked_cnt);
700
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
701
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
702
+ if ( __trigger ) {
703
+ if ( ! op_mask ) {
704
+ ca_create_mask(co);
705
+ op_mask = (boolean8_t *) co->mask->ptr;
706
+ }
707
+ op_mask[out_i] = 1;
708
+ op[out_i++] = (double) 0.0;
709
+ } else {
710
+ cmplx128_t mean = sum / (cmplx128_t)(double) n_valid;
711
+ /* Pass 2: M2 = Σ (x - mean)² */
712
+ double M2 = 0.0;
713
+ ca_size_t __unused_mc = 0;
714
+ CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, double, st, p, m, M2, 0.0, __variancep_cmplx128_P2EXPR, __unused_mc);
715
+ op[out_i++] = (double)(M2 / (double)(n_valid));
716
+ }
717
+ }
718
+ ca_iter_state_finish(&st);
719
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
720
+ keep_axis means the output is a 1-element CArray; unwrap it to
721
+ the ruby scalar (matches the standard reduce emit path lines
722
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
723
+ if ( naxes == ca->ndim && !keep_axis ) {
724
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
725
+ return rb_float_new(op[0]);
726
+ }
727
+ return vout;
728
+ }
729
+ #undef __variancep_cmplx128_P1EXPR
730
+ #undef __variancep_cmplx128_P2EXPR
731
+
732
+ #define __variancep_bool_P1EXPR(__x) ((double)(__x))
733
+ #define __variancep_bool_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
734
+ static VALUE
735
+ variancep_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
736
+ {
737
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
738
+ CArray *co;
739
+ GetCArray(vout, co);
740
+ double *op = (double *) co->ptr;
741
+ ca_iter_state st;
742
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
743
+ slab_axes, naxes, 0);
744
+ if ( rc != CA_ITER_OK ) {
745
+ rb_raise(rb_eRuntimeError,
746
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
747
+ }
748
+ char *p;
749
+ boolean8_t *m;
750
+ ca_size_t out_i = 0;
751
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
752
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
753
+ /* Pass 1: sum = Σx (+ masked_cnt) */
754
+ double sum = 0.0;
755
+ ca_size_t masked_cnt = 0;
756
+ CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, sum, 0.0, __variancep_bool_P1EXPR, masked_cnt);
757
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
758
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
759
+ if ( __trigger ) {
760
+ if ( ! op_mask ) {
761
+ ca_create_mask(co);
762
+ op_mask = (boolean8_t *) co->mask->ptr;
763
+ }
764
+ op_mask[out_i] = 1;
765
+ op[out_i++] = (double) 0.0;
766
+ } else {
767
+ double mean = sum / (double) n_valid;
768
+ /* Pass 2: M2 = Σ (x - mean)² */
769
+ double M2 = 0.0;
770
+ ca_size_t __unused_mc = 0;
771
+ CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, M2, 0.0, __variancep_bool_P2EXPR, __unused_mc);
772
+ op[out_i++] = (double)(M2 / (double)(n_valid));
773
+ }
774
+ }
775
+ ca_iter_state_finish(&st);
776
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
777
+ keep_axis means the output is a 1-element CArray; unwrap it to
778
+ the ruby scalar (matches the standard reduce emit path lines
779
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
780
+ if ( naxes == ca->ndim && !keep_axis ) {
781
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
782
+ return rb_float_new(op[0]);
783
+ }
784
+ return vout;
785
+ }
786
+ #undef __variancep_bool_P1EXPR
787
+ #undef __variancep_bool_P2EXPR
788
+
789
+ /* The object lane calls Ruby for every cell, so a walk can raise part
790
+ way. Its states live in the caller's frame, and ca_iter_ensure
791
+ finishes them however the body leaves. */
792
+ typedef struct {
793
+ ca_iter_state states[1];
794
+ VALUE self;
795
+ CArray *ca;
796
+ int8_t *slab_axes;
797
+ int8_t naxes;
798
+ int keep_axis;
799
+ ca_size_t min_count;
800
+ } variancep_ki_native_object_frame;
801
+
802
+ static VALUE
803
+ variancep_ki_native_object_body (VALUE arg)
804
+ {
805
+ variancep_ki_native_object_frame *fr = (variancep_ki_native_object_frame *) arg;
806
+ VALUE self = fr->self;
807
+ (void) self;
808
+ CArray *ca = fr->ca;
809
+ (void) ca;
810
+ int8_t *slab_axes = fr->slab_axes;
811
+ (void) slab_axes;
812
+ int8_t naxes = fr->naxes;
813
+ (void) naxes;
814
+ int keep_axis = fr->keep_axis;
815
+ (void) keep_axis;
816
+ ca_size_t min_count = fr->min_count;
817
+ (void) min_count;
818
+ #define st (fr->states[0])
819
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_OBJECT, keep_axis);
820
+ CArray *co;
821
+ GetCArray(vout, co);
822
+ VALUE *op = (VALUE *) co->ptr;
823
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
824
+ slab_axes, naxes, 0);
825
+ if ( rc != CA_ITER_OK ) {
826
+ rb_raise(rb_eRuntimeError,
827
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
828
+ }
829
+ char *p;
830
+ boolean8_t *m;
831
+ ca_size_t out_i = 0;
832
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
833
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
834
+ /* Pass 1: sum = Σx (+ masked_cnt) */
835
+ VALUE sum = INT2FIX(0);
836
+ ca_size_t masked_cnt = 0;
837
+ CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, sum, INT2FIX(0), sum = rb_funcall(sum, rb_intern("+"), 1, v), masked_cnt);
838
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
839
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
840
+ if ( __trigger ) {
841
+ if ( ! op_mask ) {
842
+ ca_create_mask(co);
843
+ op_mask = (boolean8_t *) co->mask->ptr;
844
+ }
845
+ op_mask[out_i] = 1;
846
+ op[out_i++] = (VALUE) INT2FIX(0);
847
+ } else {
848
+ VALUE mean = rb_funcall(sum, rb_intern("/"), 1, LONG2NUM(n_valid));
849
+ /* Pass 2: M2 = Σ (x - mean)² */
850
+ VALUE M2 = INT2FIX(0);
851
+ ca_size_t __unused_mc = 0;
852
+ CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, M2, INT2FIX(0), VALUE __d = rb_funcall(v, rb_intern("-"), 1, mean);
853
+ M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)), __unused_mc);
854
+ op[out_i++] = (VALUE)(rb_funcall(M2, rb_intern("/"), 1, LONG2NUM(n_valid)));
855
+ }
856
+ }
857
+ ca_iter_state_finish(&st);
858
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
859
+ keep_axis means the output is a 1-element CArray; unwrap it to
860
+ the ruby scalar (matches the standard reduce emit path lines
861
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
862
+ if ( naxes == ca->ndim && !keep_axis ) {
863
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
864
+ return (VALUE)(op[0]);
865
+ }
866
+ return vout;
867
+ #undef st
868
+ }
869
+
870
+ static VALUE
871
+ variancep_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
872
+ {
873
+ variancep_ki_native_object_frame fr;
874
+ fr.self = self;
875
+ fr.ca = ca;
876
+ fr.slab_axes = slab_axes;
877
+ fr.naxes = naxes;
878
+ fr.keep_axis = keep_axis;
879
+ fr.min_count = min_count;
880
+ return ca_iter_ensure(1, fr.states, variancep_ki_native_object_body, (VALUE) &fr);
881
+ }
882
+
883
+
884
+ VALUE
885
+ rb_ca_variancep_ki (int argc, VALUE *argv, VALUE self)
886
+ {
887
+ CArray *src;
888
+ GetCArray(self, src);
889
+ volatile VALUE ropt = rb_pop_options(&argc, &argv);
890
+ volatile VALUE raxis = Qnil, rkeep_axis = Qnil, rmin_count = Qnil, rfval = Qnil;
891
+ rb_scan_options(ropt, "axis,keep_axis,min_count,fill_value", &raxis, &rkeep_axis, &rmin_count, &rfval);
892
+ int keep_axis = RTEST(rkeep_axis);
893
+ int8_t slab_axes[CA_RANK_MAX];
894
+ int8_t naxes = rb_ca_parse_reduce_axes_kw(raxis, src, slab_axes);
895
+ if ( ca_is_lazy_view(src) ) {
896
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
897
+ if ( ! NIL_P(evaluated) ) {
898
+ self = evaluated;
899
+ GetCArray(self, src);
900
+ }
901
+ }
902
+ ca_size_t min_count = ca_min_count_arg(rmin_count, NULL);
903
+ if ( rfval == CA_UNDEF ) rfval = Qnil;
904
+ int fill_whole = 0;
905
+ if ( ! NIL_P(rfval) && naxes == src->ndim && ! keep_axis ) {
906
+ keep_axis = 1;
907
+ fill_whole = 1;
908
+ }
909
+ VALUE result;
910
+ if ( argc > 0 ) {
911
+ rb_raise(rb_eArgError, "%s: positional axis arguments are no longer accepted (got %d); use the axis: keyword, e.g. a.%s(axis: 0) or a.%s(axis: [0, 1])", ca_calling_method_name(), argc, ca_calling_method_name(), ca_calling_method_name());
912
+ }
913
+ switch ( src->data_type ) {
914
+ case CA_INT8: {
915
+ result = variancep_ki_native_i8(self, src, slab_axes, naxes, keep_axis, min_count);
916
+ RB_GC_GUARD(self);
917
+ break;
918
+ }
919
+ case CA_UINT8: {
920
+ result = variancep_ki_native_u8(self, src, slab_axes, naxes, keep_axis, min_count);
921
+ RB_GC_GUARD(self);
922
+ break;
923
+ }
924
+ case CA_INT16: {
925
+ result = variancep_ki_native_i16(self, src, slab_axes, naxes, keep_axis, min_count);
926
+ RB_GC_GUARD(self);
927
+ break;
928
+ }
929
+ case CA_UINT16: {
930
+ result = variancep_ki_native_u16(self, src, slab_axes, naxes, keep_axis, min_count);
931
+ RB_GC_GUARD(self);
932
+ break;
933
+ }
934
+ case CA_INT32: {
935
+ result = variancep_ki_native_i32(self, src, slab_axes, naxes, keep_axis, min_count);
936
+ RB_GC_GUARD(self);
937
+ break;
938
+ }
939
+ case CA_UINT32: {
940
+ result = variancep_ki_native_u32(self, src, slab_axes, naxes, keep_axis, min_count);
941
+ RB_GC_GUARD(self);
942
+ break;
943
+ }
944
+ case CA_INT64: {
945
+ result = variancep_ki_native_i64(self, src, slab_axes, naxes, keep_axis, min_count);
946
+ RB_GC_GUARD(self);
947
+ break;
948
+ }
949
+ case CA_UINT64: {
950
+ result = variancep_ki_native_u64(self, src, slab_axes, naxes, keep_axis, min_count);
951
+ RB_GC_GUARD(self);
952
+ break;
953
+ }
954
+ case CA_FLOAT32: {
955
+ result = variancep_ki_native_f32(self, src, slab_axes, naxes, keep_axis, min_count);
956
+ RB_GC_GUARD(self);
957
+ break;
958
+ }
959
+ case CA_FLOAT64: {
960
+ result = variancep_ki_native_f64(self, src, slab_axes, naxes, keep_axis, min_count);
961
+ RB_GC_GUARD(self);
962
+ break;
963
+ }
964
+ case CA_CMPLX64: {
965
+ result = variancep_ki_native_cmplx64(self, src, slab_axes, naxes, keep_axis, min_count);
966
+ RB_GC_GUARD(self);
967
+ break;
968
+ }
969
+ case CA_CMPLX128: {
970
+ result = variancep_ki_native_cmplx128(self, src, slab_axes, naxes, keep_axis, min_count);
971
+ RB_GC_GUARD(self);
972
+ break;
973
+ }
974
+ case CA_BOOLEAN: {
975
+ result = variancep_ki_native_bool(self, src, slab_axes, naxes, keep_axis, min_count);
976
+ RB_GC_GUARD(self);
977
+ break;
978
+ }
979
+ case CA_OBJECT: {
980
+ result = variancep_ki_native_object(self, src, slab_axes, naxes, keep_axis, min_count);
981
+ RB_GC_GUARD(self);
982
+ break;
983
+ }
984
+ default:
985
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
986
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, cmplx64, cmplx128, boolean, object)", ca_calling_method_name(), ca_type_name[src->data_type]);
987
+ }
988
+ if ( ! NIL_P(rfval) ) {
989
+ result = ca_reduce_fill(result, rfval, fill_whole);
990
+ }
991
+ return result;
992
+ }
993
+
994
+ /* ===== variance_ki ============================================ */
995
+
996
+ #define __variance_i8_P1EXPR(__x) ((double)(__x))
997
+ #define __variance_i8_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
998
+ static VALUE
999
+ variance_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1000
+ {
1001
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1002
+ CArray *co;
1003
+ GetCArray(vout, co);
1004
+ double *op = (double *) co->ptr;
1005
+ ca_iter_state st;
1006
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1007
+ slab_axes, naxes, 0);
1008
+ if ( rc != CA_ITER_OK ) {
1009
+ rb_raise(rb_eRuntimeError,
1010
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1011
+ }
1012
+ char *p;
1013
+ boolean8_t *m;
1014
+ ca_size_t out_i = 0;
1015
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1016
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1017
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1018
+ double sum = 0.0;
1019
+ ca_size_t masked_cnt = 0;
1020
+ CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, sum, 0.0, __variance_i8_P1EXPR, masked_cnt);
1021
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1022
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1023
+ if ( __trigger ) {
1024
+ if ( ! op_mask ) {
1025
+ ca_create_mask(co);
1026
+ op_mask = (boolean8_t *) co->mask->ptr;
1027
+ }
1028
+ op_mask[out_i] = 1;
1029
+ op[out_i++] = (double) 0.0;
1030
+ } else {
1031
+ double mean = sum / (double) n_valid;
1032
+ /* Pass 2: M2 = Σ (x - mean)² */
1033
+ double M2 = 0.0;
1034
+ ca_size_t __unused_mc = 0;
1035
+ CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, M2, 0.0, __variance_i8_P2EXPR, __unused_mc);
1036
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1037
+ }
1038
+ }
1039
+ ca_iter_state_finish(&st);
1040
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1041
+ keep_axis means the output is a 1-element CArray; unwrap it to
1042
+ the ruby scalar (matches the standard reduce emit path lines
1043
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1044
+ if ( naxes == ca->ndim && !keep_axis ) {
1045
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1046
+ return rb_float_new(op[0]);
1047
+ }
1048
+ return vout;
1049
+ }
1050
+ #undef __variance_i8_P1EXPR
1051
+ #undef __variance_i8_P2EXPR
1052
+
1053
+ #define __variance_u8_P1EXPR(__x) ((double)(__x))
1054
+ #define __variance_u8_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1055
+ static VALUE
1056
+ variance_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1057
+ {
1058
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1059
+ CArray *co;
1060
+ GetCArray(vout, co);
1061
+ double *op = (double *) co->ptr;
1062
+ ca_iter_state st;
1063
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1064
+ slab_axes, naxes, 0);
1065
+ if ( rc != CA_ITER_OK ) {
1066
+ rb_raise(rb_eRuntimeError,
1067
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1068
+ }
1069
+ char *p;
1070
+ boolean8_t *m;
1071
+ ca_size_t out_i = 0;
1072
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1073
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1074
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1075
+ double sum = 0.0;
1076
+ ca_size_t masked_cnt = 0;
1077
+ CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, sum, 0.0, __variance_u8_P1EXPR, masked_cnt);
1078
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1079
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1080
+ if ( __trigger ) {
1081
+ if ( ! op_mask ) {
1082
+ ca_create_mask(co);
1083
+ op_mask = (boolean8_t *) co->mask->ptr;
1084
+ }
1085
+ op_mask[out_i] = 1;
1086
+ op[out_i++] = (double) 0.0;
1087
+ } else {
1088
+ double mean = sum / (double) n_valid;
1089
+ /* Pass 2: M2 = Σ (x - mean)² */
1090
+ double M2 = 0.0;
1091
+ ca_size_t __unused_mc = 0;
1092
+ CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, M2, 0.0, __variance_u8_P2EXPR, __unused_mc);
1093
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1094
+ }
1095
+ }
1096
+ ca_iter_state_finish(&st);
1097
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1098
+ keep_axis means the output is a 1-element CArray; unwrap it to
1099
+ the ruby scalar (matches the standard reduce emit path lines
1100
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1101
+ if ( naxes == ca->ndim && !keep_axis ) {
1102
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1103
+ return rb_float_new(op[0]);
1104
+ }
1105
+ return vout;
1106
+ }
1107
+ #undef __variance_u8_P1EXPR
1108
+ #undef __variance_u8_P2EXPR
1109
+
1110
+ #define __variance_i16_P1EXPR(__x) ((double)(__x))
1111
+ #define __variance_i16_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1112
+ static VALUE
1113
+ variance_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1114
+ {
1115
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1116
+ CArray *co;
1117
+ GetCArray(vout, co);
1118
+ double *op = (double *) co->ptr;
1119
+ ca_iter_state st;
1120
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1121
+ slab_axes, naxes, 0);
1122
+ if ( rc != CA_ITER_OK ) {
1123
+ rb_raise(rb_eRuntimeError,
1124
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1125
+ }
1126
+ char *p;
1127
+ boolean8_t *m;
1128
+ ca_size_t out_i = 0;
1129
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1130
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1131
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1132
+ double sum = 0.0;
1133
+ ca_size_t masked_cnt = 0;
1134
+ CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, sum, 0.0, __variance_i16_P1EXPR, masked_cnt);
1135
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1136
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1137
+ if ( __trigger ) {
1138
+ if ( ! op_mask ) {
1139
+ ca_create_mask(co);
1140
+ op_mask = (boolean8_t *) co->mask->ptr;
1141
+ }
1142
+ op_mask[out_i] = 1;
1143
+ op[out_i++] = (double) 0.0;
1144
+ } else {
1145
+ double mean = sum / (double) n_valid;
1146
+ /* Pass 2: M2 = Σ (x - mean)² */
1147
+ double M2 = 0.0;
1148
+ ca_size_t __unused_mc = 0;
1149
+ CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, M2, 0.0, __variance_i16_P2EXPR, __unused_mc);
1150
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1151
+ }
1152
+ }
1153
+ ca_iter_state_finish(&st);
1154
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1155
+ keep_axis means the output is a 1-element CArray; unwrap it to
1156
+ the ruby scalar (matches the standard reduce emit path lines
1157
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1158
+ if ( naxes == ca->ndim && !keep_axis ) {
1159
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1160
+ return rb_float_new(op[0]);
1161
+ }
1162
+ return vout;
1163
+ }
1164
+ #undef __variance_i16_P1EXPR
1165
+ #undef __variance_i16_P2EXPR
1166
+
1167
+ #define __variance_u16_P1EXPR(__x) ((double)(__x))
1168
+ #define __variance_u16_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1169
+ static VALUE
1170
+ variance_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1171
+ {
1172
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1173
+ CArray *co;
1174
+ GetCArray(vout, co);
1175
+ double *op = (double *) co->ptr;
1176
+ ca_iter_state st;
1177
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1178
+ slab_axes, naxes, 0);
1179
+ if ( rc != CA_ITER_OK ) {
1180
+ rb_raise(rb_eRuntimeError,
1181
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1182
+ }
1183
+ char *p;
1184
+ boolean8_t *m;
1185
+ ca_size_t out_i = 0;
1186
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1187
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1188
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1189
+ double sum = 0.0;
1190
+ ca_size_t masked_cnt = 0;
1191
+ CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, sum, 0.0, __variance_u16_P1EXPR, masked_cnt);
1192
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1193
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1194
+ if ( __trigger ) {
1195
+ if ( ! op_mask ) {
1196
+ ca_create_mask(co);
1197
+ op_mask = (boolean8_t *) co->mask->ptr;
1198
+ }
1199
+ op_mask[out_i] = 1;
1200
+ op[out_i++] = (double) 0.0;
1201
+ } else {
1202
+ double mean = sum / (double) n_valid;
1203
+ /* Pass 2: M2 = Σ (x - mean)² */
1204
+ double M2 = 0.0;
1205
+ ca_size_t __unused_mc = 0;
1206
+ CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, M2, 0.0, __variance_u16_P2EXPR, __unused_mc);
1207
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1208
+ }
1209
+ }
1210
+ ca_iter_state_finish(&st);
1211
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1212
+ keep_axis means the output is a 1-element CArray; unwrap it to
1213
+ the ruby scalar (matches the standard reduce emit path lines
1214
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1215
+ if ( naxes == ca->ndim && !keep_axis ) {
1216
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1217
+ return rb_float_new(op[0]);
1218
+ }
1219
+ return vout;
1220
+ }
1221
+ #undef __variance_u16_P1EXPR
1222
+ #undef __variance_u16_P2EXPR
1223
+
1224
+ #define __variance_i32_P1EXPR(__x) ((double)(__x))
1225
+ #define __variance_i32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1226
+ static VALUE
1227
+ variance_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1228
+ {
1229
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1230
+ CArray *co;
1231
+ GetCArray(vout, co);
1232
+ double *op = (double *) co->ptr;
1233
+ ca_iter_state st;
1234
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1235
+ slab_axes, naxes, 0);
1236
+ if ( rc != CA_ITER_OK ) {
1237
+ rb_raise(rb_eRuntimeError,
1238
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1239
+ }
1240
+ char *p;
1241
+ boolean8_t *m;
1242
+ ca_size_t out_i = 0;
1243
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1244
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1245
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1246
+ double sum = 0.0;
1247
+ ca_size_t masked_cnt = 0;
1248
+ CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, sum, 0.0, __variance_i32_P1EXPR, masked_cnt);
1249
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1250
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1251
+ if ( __trigger ) {
1252
+ if ( ! op_mask ) {
1253
+ ca_create_mask(co);
1254
+ op_mask = (boolean8_t *) co->mask->ptr;
1255
+ }
1256
+ op_mask[out_i] = 1;
1257
+ op[out_i++] = (double) 0.0;
1258
+ } else {
1259
+ double mean = sum / (double) n_valid;
1260
+ /* Pass 2: M2 = Σ (x - mean)² */
1261
+ double M2 = 0.0;
1262
+ ca_size_t __unused_mc = 0;
1263
+ CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, M2, 0.0, __variance_i32_P2EXPR, __unused_mc);
1264
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1265
+ }
1266
+ }
1267
+ ca_iter_state_finish(&st);
1268
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1269
+ keep_axis means the output is a 1-element CArray; unwrap it to
1270
+ the ruby scalar (matches the standard reduce emit path lines
1271
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1272
+ if ( naxes == ca->ndim && !keep_axis ) {
1273
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1274
+ return rb_float_new(op[0]);
1275
+ }
1276
+ return vout;
1277
+ }
1278
+ #undef __variance_i32_P1EXPR
1279
+ #undef __variance_i32_P2EXPR
1280
+
1281
+ #define __variance_u32_P1EXPR(__x) ((double)(__x))
1282
+ #define __variance_u32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1283
+ static VALUE
1284
+ variance_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1285
+ {
1286
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1287
+ CArray *co;
1288
+ GetCArray(vout, co);
1289
+ double *op = (double *) co->ptr;
1290
+ ca_iter_state st;
1291
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1292
+ slab_axes, naxes, 0);
1293
+ if ( rc != CA_ITER_OK ) {
1294
+ rb_raise(rb_eRuntimeError,
1295
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1296
+ }
1297
+ char *p;
1298
+ boolean8_t *m;
1299
+ ca_size_t out_i = 0;
1300
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1301
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1302
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1303
+ double sum = 0.0;
1304
+ ca_size_t masked_cnt = 0;
1305
+ CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, sum, 0.0, __variance_u32_P1EXPR, masked_cnt);
1306
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1307
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1308
+ if ( __trigger ) {
1309
+ if ( ! op_mask ) {
1310
+ ca_create_mask(co);
1311
+ op_mask = (boolean8_t *) co->mask->ptr;
1312
+ }
1313
+ op_mask[out_i] = 1;
1314
+ op[out_i++] = (double) 0.0;
1315
+ } else {
1316
+ double mean = sum / (double) n_valid;
1317
+ /* Pass 2: M2 = Σ (x - mean)² */
1318
+ double M2 = 0.0;
1319
+ ca_size_t __unused_mc = 0;
1320
+ CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, M2, 0.0, __variance_u32_P2EXPR, __unused_mc);
1321
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1322
+ }
1323
+ }
1324
+ ca_iter_state_finish(&st);
1325
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1326
+ keep_axis means the output is a 1-element CArray; unwrap it to
1327
+ the ruby scalar (matches the standard reduce emit path lines
1328
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1329
+ if ( naxes == ca->ndim && !keep_axis ) {
1330
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1331
+ return rb_float_new(op[0]);
1332
+ }
1333
+ return vout;
1334
+ }
1335
+ #undef __variance_u32_P1EXPR
1336
+ #undef __variance_u32_P2EXPR
1337
+
1338
+ #define __variance_i64_P1EXPR(__x) ((double)(__x))
1339
+ #define __variance_i64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1340
+ static VALUE
1341
+ variance_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1342
+ {
1343
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1344
+ CArray *co;
1345
+ GetCArray(vout, co);
1346
+ double *op = (double *) co->ptr;
1347
+ ca_iter_state st;
1348
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1349
+ slab_axes, naxes, 0);
1350
+ if ( rc != CA_ITER_OK ) {
1351
+ rb_raise(rb_eRuntimeError,
1352
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1353
+ }
1354
+ char *p;
1355
+ boolean8_t *m;
1356
+ ca_size_t out_i = 0;
1357
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1358
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1359
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1360
+ double sum = 0.0;
1361
+ ca_size_t masked_cnt = 0;
1362
+ CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, sum, 0.0, __variance_i64_P1EXPR, masked_cnt);
1363
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1364
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1365
+ if ( __trigger ) {
1366
+ if ( ! op_mask ) {
1367
+ ca_create_mask(co);
1368
+ op_mask = (boolean8_t *) co->mask->ptr;
1369
+ }
1370
+ op_mask[out_i] = 1;
1371
+ op[out_i++] = (double) 0.0;
1372
+ } else {
1373
+ double mean = sum / (double) n_valid;
1374
+ /* Pass 2: M2 = Σ (x - mean)² */
1375
+ double M2 = 0.0;
1376
+ ca_size_t __unused_mc = 0;
1377
+ CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, M2, 0.0, __variance_i64_P2EXPR, __unused_mc);
1378
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1379
+ }
1380
+ }
1381
+ ca_iter_state_finish(&st);
1382
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1383
+ keep_axis means the output is a 1-element CArray; unwrap it to
1384
+ the ruby scalar (matches the standard reduce emit path lines
1385
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1386
+ if ( naxes == ca->ndim && !keep_axis ) {
1387
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1388
+ return rb_float_new(op[0]);
1389
+ }
1390
+ return vout;
1391
+ }
1392
+ #undef __variance_i64_P1EXPR
1393
+ #undef __variance_i64_P2EXPR
1394
+
1395
+ #define __variance_u64_P1EXPR(__x) ((double)(__x))
1396
+ #define __variance_u64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1397
+ static VALUE
1398
+ variance_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1399
+ {
1400
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1401
+ CArray *co;
1402
+ GetCArray(vout, co);
1403
+ double *op = (double *) co->ptr;
1404
+ ca_iter_state st;
1405
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1406
+ slab_axes, naxes, 0);
1407
+ if ( rc != CA_ITER_OK ) {
1408
+ rb_raise(rb_eRuntimeError,
1409
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1410
+ }
1411
+ char *p;
1412
+ boolean8_t *m;
1413
+ ca_size_t out_i = 0;
1414
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1415
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1416
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1417
+ double sum = 0.0;
1418
+ ca_size_t masked_cnt = 0;
1419
+ CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, sum, 0.0, __variance_u64_P1EXPR, masked_cnt);
1420
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1421
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1422
+ if ( __trigger ) {
1423
+ if ( ! op_mask ) {
1424
+ ca_create_mask(co);
1425
+ op_mask = (boolean8_t *) co->mask->ptr;
1426
+ }
1427
+ op_mask[out_i] = 1;
1428
+ op[out_i++] = (double) 0.0;
1429
+ } else {
1430
+ double mean = sum / (double) n_valid;
1431
+ /* Pass 2: M2 = Σ (x - mean)² */
1432
+ double M2 = 0.0;
1433
+ ca_size_t __unused_mc = 0;
1434
+ CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, M2, 0.0, __variance_u64_P2EXPR, __unused_mc);
1435
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1436
+ }
1437
+ }
1438
+ ca_iter_state_finish(&st);
1439
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1440
+ keep_axis means the output is a 1-element CArray; unwrap it to
1441
+ the ruby scalar (matches the standard reduce emit path lines
1442
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1443
+ if ( naxes == ca->ndim && !keep_axis ) {
1444
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1445
+ return rb_float_new(op[0]);
1446
+ }
1447
+ return vout;
1448
+ }
1449
+ #undef __variance_u64_P1EXPR
1450
+ #undef __variance_u64_P2EXPR
1451
+
1452
+ #define __variance_f32_P1EXPR(__x) ((double)(__x))
1453
+ #define __variance_f32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1454
+ static VALUE
1455
+ variance_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1456
+ {
1457
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1458
+ CArray *co;
1459
+ GetCArray(vout, co);
1460
+ double *op = (double *) co->ptr;
1461
+ ca_iter_state st;
1462
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1463
+ slab_axes, naxes, 0);
1464
+ if ( rc != CA_ITER_OK ) {
1465
+ rb_raise(rb_eRuntimeError,
1466
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1467
+ }
1468
+ char *p;
1469
+ boolean8_t *m;
1470
+ ca_size_t out_i = 0;
1471
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1472
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1473
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1474
+ double sum = 0.0;
1475
+ ca_size_t masked_cnt = 0;
1476
+ CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, sum, 0.0, __variance_f32_P1EXPR, masked_cnt);
1477
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1478
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1479
+ if ( __trigger ) {
1480
+ if ( ! op_mask ) {
1481
+ ca_create_mask(co);
1482
+ op_mask = (boolean8_t *) co->mask->ptr;
1483
+ }
1484
+ op_mask[out_i] = 1;
1485
+ op[out_i++] = (double) 0.0;
1486
+ } else {
1487
+ double mean = sum / (double) n_valid;
1488
+ /* Pass 2: M2 = Σ (x - mean)² */
1489
+ double M2 = 0.0;
1490
+ ca_size_t __unused_mc = 0;
1491
+ CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, M2, 0.0, __variance_f32_P2EXPR, __unused_mc);
1492
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1493
+ }
1494
+ }
1495
+ ca_iter_state_finish(&st);
1496
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1497
+ keep_axis means the output is a 1-element CArray; unwrap it to
1498
+ the ruby scalar (matches the standard reduce emit path lines
1499
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1500
+ if ( naxes == ca->ndim && !keep_axis ) {
1501
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1502
+ return rb_float_new(op[0]);
1503
+ }
1504
+ return vout;
1505
+ }
1506
+ #undef __variance_f32_P1EXPR
1507
+ #undef __variance_f32_P2EXPR
1508
+
1509
+ #define __variance_f64_P1EXPR(__x) ((double)(__x))
1510
+ #define __variance_f64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1511
+ static VALUE
1512
+ variance_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1513
+ {
1514
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1515
+ CArray *co;
1516
+ GetCArray(vout, co);
1517
+ double *op = (double *) co->ptr;
1518
+ ca_iter_state st;
1519
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1520
+ slab_axes, naxes, 0);
1521
+ if ( rc != CA_ITER_OK ) {
1522
+ rb_raise(rb_eRuntimeError,
1523
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1524
+ }
1525
+ char *p;
1526
+ boolean8_t *m;
1527
+ ca_size_t out_i = 0;
1528
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1529
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1530
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1531
+ double sum = 0.0;
1532
+ ca_size_t masked_cnt = 0;
1533
+ CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, sum, 0.0, __variance_f64_P1EXPR, masked_cnt);
1534
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1535
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1536
+ if ( __trigger ) {
1537
+ if ( ! op_mask ) {
1538
+ ca_create_mask(co);
1539
+ op_mask = (boolean8_t *) co->mask->ptr;
1540
+ }
1541
+ op_mask[out_i] = 1;
1542
+ op[out_i++] = (double) 0.0;
1543
+ } else {
1544
+ double mean = sum / (double) n_valid;
1545
+ /* Pass 2: M2 = Σ (x - mean)² */
1546
+ double M2 = 0.0;
1547
+ ca_size_t __unused_mc = 0;
1548
+ CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, M2, 0.0, __variance_f64_P2EXPR, __unused_mc);
1549
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1550
+ }
1551
+ }
1552
+ ca_iter_state_finish(&st);
1553
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1554
+ keep_axis means the output is a 1-element CArray; unwrap it to
1555
+ the ruby scalar (matches the standard reduce emit path lines
1556
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1557
+ if ( naxes == ca->ndim && !keep_axis ) {
1558
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1559
+ return rb_float_new(op[0]);
1560
+ }
1561
+ return vout;
1562
+ }
1563
+ #undef __variance_f64_P1EXPR
1564
+ #undef __variance_f64_P2EXPR
1565
+
1566
+ #define __variance_cmplx64_P1EXPR(__x) (__x)
1567
+ #define __variance_cmplx64_P2EXPR(__x) (creal((__x) - mean) * creal((__x) - mean) + cimag((__x) - mean) * cimag((__x) - mean))
1568
+ static VALUE
1569
+ variance_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1570
+ {
1571
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1572
+ CArray *co;
1573
+ GetCArray(vout, co);
1574
+ double *op = (double *) co->ptr;
1575
+ ca_iter_state st;
1576
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1577
+ slab_axes, naxes, 0);
1578
+ if ( rc != CA_ITER_OK ) {
1579
+ rb_raise(rb_eRuntimeError,
1580
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1581
+ }
1582
+ char *p;
1583
+ boolean8_t *m;
1584
+ ca_size_t out_i = 0;
1585
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1586
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1587
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1588
+ cmplx64_t sum = 0;
1589
+ ca_size_t masked_cnt = 0;
1590
+ CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, cmplx64_t, st, p, m, sum, 0, __variance_cmplx64_P1EXPR, masked_cnt);
1591
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1592
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1593
+ if ( __trigger ) {
1594
+ if ( ! op_mask ) {
1595
+ ca_create_mask(co);
1596
+ op_mask = (boolean8_t *) co->mask->ptr;
1597
+ }
1598
+ op_mask[out_i] = 1;
1599
+ op[out_i++] = (double) 0.0;
1600
+ } else {
1601
+ cmplx64_t mean = sum / (cmplx64_t)(double) n_valid;
1602
+ /* Pass 2: M2 = Σ (x - mean)² */
1603
+ double M2 = 0.0;
1604
+ ca_size_t __unused_mc = 0;
1605
+ CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, double, st, p, m, M2, 0.0, __variance_cmplx64_P2EXPR, __unused_mc);
1606
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1607
+ }
1608
+ }
1609
+ ca_iter_state_finish(&st);
1610
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1611
+ keep_axis means the output is a 1-element CArray; unwrap it to
1612
+ the ruby scalar (matches the standard reduce emit path lines
1613
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1614
+ if ( naxes == ca->ndim && !keep_axis ) {
1615
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1616
+ return rb_float_new(op[0]);
1617
+ }
1618
+ return vout;
1619
+ }
1620
+ #undef __variance_cmplx64_P1EXPR
1621
+ #undef __variance_cmplx64_P2EXPR
1622
+
1623
+ #define __variance_cmplx128_P1EXPR(__x) (__x)
1624
+ #define __variance_cmplx128_P2EXPR(__x) (creal((__x) - mean) * creal((__x) - mean) + cimag((__x) - mean) * cimag((__x) - mean))
1625
+ static VALUE
1626
+ variance_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1627
+ {
1628
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1629
+ CArray *co;
1630
+ GetCArray(vout, co);
1631
+ double *op = (double *) co->ptr;
1632
+ ca_iter_state st;
1633
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1634
+ slab_axes, naxes, 0);
1635
+ if ( rc != CA_ITER_OK ) {
1636
+ rb_raise(rb_eRuntimeError,
1637
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1638
+ }
1639
+ char *p;
1640
+ boolean8_t *m;
1641
+ ca_size_t out_i = 0;
1642
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1643
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1644
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1645
+ cmplx128_t sum = 0;
1646
+ ca_size_t masked_cnt = 0;
1647
+ CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, cmplx128_t, st, p, m, sum, 0, __variance_cmplx128_P1EXPR, masked_cnt);
1648
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1649
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1650
+ if ( __trigger ) {
1651
+ if ( ! op_mask ) {
1652
+ ca_create_mask(co);
1653
+ op_mask = (boolean8_t *) co->mask->ptr;
1654
+ }
1655
+ op_mask[out_i] = 1;
1656
+ op[out_i++] = (double) 0.0;
1657
+ } else {
1658
+ cmplx128_t mean = sum / (cmplx128_t)(double) n_valid;
1659
+ /* Pass 2: M2 = Σ (x - mean)² */
1660
+ double M2 = 0.0;
1661
+ ca_size_t __unused_mc = 0;
1662
+ CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, double, st, p, m, M2, 0.0, __variance_cmplx128_P2EXPR, __unused_mc);
1663
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1664
+ }
1665
+ }
1666
+ ca_iter_state_finish(&st);
1667
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1668
+ keep_axis means the output is a 1-element CArray; unwrap it to
1669
+ the ruby scalar (matches the standard reduce emit path lines
1670
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1671
+ if ( naxes == ca->ndim && !keep_axis ) {
1672
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1673
+ return rb_float_new(op[0]);
1674
+ }
1675
+ return vout;
1676
+ }
1677
+ #undef __variance_cmplx128_P1EXPR
1678
+ #undef __variance_cmplx128_P2EXPR
1679
+
1680
+ #define __variance_bool_P1EXPR(__x) ((double)(__x))
1681
+ #define __variance_bool_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1682
+ static VALUE
1683
+ variance_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1684
+ {
1685
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1686
+ CArray *co;
1687
+ GetCArray(vout, co);
1688
+ double *op = (double *) co->ptr;
1689
+ ca_iter_state st;
1690
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1691
+ slab_axes, naxes, 0);
1692
+ if ( rc != CA_ITER_OK ) {
1693
+ rb_raise(rb_eRuntimeError,
1694
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1695
+ }
1696
+ char *p;
1697
+ boolean8_t *m;
1698
+ ca_size_t out_i = 0;
1699
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1700
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1701
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1702
+ double sum = 0.0;
1703
+ ca_size_t masked_cnt = 0;
1704
+ CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, sum, 0.0, __variance_bool_P1EXPR, masked_cnt);
1705
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1706
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1707
+ if ( __trigger ) {
1708
+ if ( ! op_mask ) {
1709
+ ca_create_mask(co);
1710
+ op_mask = (boolean8_t *) co->mask->ptr;
1711
+ }
1712
+ op_mask[out_i] = 1;
1713
+ op[out_i++] = (double) 0.0;
1714
+ } else {
1715
+ double mean = sum / (double) n_valid;
1716
+ /* Pass 2: M2 = Σ (x - mean)² */
1717
+ double M2 = 0.0;
1718
+ ca_size_t __unused_mc = 0;
1719
+ CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, M2, 0.0, __variance_bool_P2EXPR, __unused_mc);
1720
+ op[out_i++] = (double)(M2 / (double)(n_valid - 1));
1721
+ }
1722
+ }
1723
+ ca_iter_state_finish(&st);
1724
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1725
+ keep_axis means the output is a 1-element CArray; unwrap it to
1726
+ the ruby scalar (matches the standard reduce emit path lines
1727
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1728
+ if ( naxes == ca->ndim && !keep_axis ) {
1729
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1730
+ return rb_float_new(op[0]);
1731
+ }
1732
+ return vout;
1733
+ }
1734
+ #undef __variance_bool_P1EXPR
1735
+ #undef __variance_bool_P2EXPR
1736
+
1737
+ /* The object lane calls Ruby for every cell, so a walk can raise part
1738
+ way. Its states live in the caller's frame, and ca_iter_ensure
1739
+ finishes them however the body leaves. */
1740
+ typedef struct {
1741
+ ca_iter_state states[1];
1742
+ VALUE self;
1743
+ CArray *ca;
1744
+ int8_t *slab_axes;
1745
+ int8_t naxes;
1746
+ int keep_axis;
1747
+ ca_size_t min_count;
1748
+ } variance_ki_native_object_frame;
1749
+
1750
+ static VALUE
1751
+ variance_ki_native_object_body (VALUE arg)
1752
+ {
1753
+ variance_ki_native_object_frame *fr = (variance_ki_native_object_frame *) arg;
1754
+ VALUE self = fr->self;
1755
+ (void) self;
1756
+ CArray *ca = fr->ca;
1757
+ (void) ca;
1758
+ int8_t *slab_axes = fr->slab_axes;
1759
+ (void) slab_axes;
1760
+ int8_t naxes = fr->naxes;
1761
+ (void) naxes;
1762
+ int keep_axis = fr->keep_axis;
1763
+ (void) keep_axis;
1764
+ ca_size_t min_count = fr->min_count;
1765
+ (void) min_count;
1766
+ #define st (fr->states[0])
1767
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_OBJECT, keep_axis);
1768
+ CArray *co;
1769
+ GetCArray(vout, co);
1770
+ VALUE *op = (VALUE *) co->ptr;
1771
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1772
+ slab_axes, naxes, 0);
1773
+ if ( rc != CA_ITER_OK ) {
1774
+ rb_raise(rb_eRuntimeError,
1775
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1776
+ }
1777
+ char *p;
1778
+ boolean8_t *m;
1779
+ ca_size_t out_i = 0;
1780
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1781
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1782
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1783
+ VALUE sum = INT2FIX(0);
1784
+ ca_size_t masked_cnt = 0;
1785
+ CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, sum, INT2FIX(0), sum = rb_funcall(sum, rb_intern("+"), 1, v), masked_cnt);
1786
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1787
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1788
+ if ( __trigger ) {
1789
+ if ( ! op_mask ) {
1790
+ ca_create_mask(co);
1791
+ op_mask = (boolean8_t *) co->mask->ptr;
1792
+ }
1793
+ op_mask[out_i] = 1;
1794
+ op[out_i++] = (VALUE) INT2FIX(0);
1795
+ } else {
1796
+ VALUE mean = rb_funcall(sum, rb_intern("/"), 1, LONG2NUM(n_valid));
1797
+ /* Pass 2: M2 = Σ (x - mean)² */
1798
+ VALUE M2 = INT2FIX(0);
1799
+ ca_size_t __unused_mc = 0;
1800
+ CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, M2, INT2FIX(0), VALUE __d = rb_funcall(v, rb_intern("-"), 1, mean);
1801
+ M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)), __unused_mc);
1802
+ op[out_i++] = (VALUE)(rb_funcall(M2, rb_intern("/"), 1, LONG2NUM(n_valid - 1)));
1803
+ }
1804
+ }
1805
+ ca_iter_state_finish(&st);
1806
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1807
+ keep_axis means the output is a 1-element CArray; unwrap it to
1808
+ the ruby scalar (matches the standard reduce emit path lines
1809
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1810
+ if ( naxes == ca->ndim && !keep_axis ) {
1811
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1812
+ return (VALUE)(op[0]);
1813
+ }
1814
+ return vout;
1815
+ #undef st
1816
+ }
1817
+
1818
+ static VALUE
1819
+ variance_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1820
+ {
1821
+ variance_ki_native_object_frame fr;
1822
+ fr.self = self;
1823
+ fr.ca = ca;
1824
+ fr.slab_axes = slab_axes;
1825
+ fr.naxes = naxes;
1826
+ fr.keep_axis = keep_axis;
1827
+ fr.min_count = min_count;
1828
+ return ca_iter_ensure(1, fr.states, variance_ki_native_object_body, (VALUE) &fr);
1829
+ }
1830
+
1831
+
1832
+ VALUE
1833
+ rb_ca_variance_ki (int argc, VALUE *argv, VALUE self)
1834
+ {
1835
+ CArray *src;
1836
+ GetCArray(self, src);
1837
+ volatile VALUE ropt = rb_pop_options(&argc, &argv);
1838
+ volatile VALUE raxis = Qnil, rkeep_axis = Qnil, rmin_count = Qnil, rfval = Qnil;
1839
+ rb_scan_options(ropt, "axis,keep_axis,min_count,fill_value", &raxis, &rkeep_axis, &rmin_count, &rfval);
1840
+ int keep_axis = RTEST(rkeep_axis);
1841
+ int8_t slab_axes[CA_RANK_MAX];
1842
+ int8_t naxes = rb_ca_parse_reduce_axes_kw(raxis, src, slab_axes);
1843
+ if ( ca_is_lazy_view(src) ) {
1844
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
1845
+ if ( ! NIL_P(evaluated) ) {
1846
+ self = evaluated;
1847
+ GetCArray(self, src);
1848
+ }
1849
+ }
1850
+ ca_size_t min_count = ca_min_count_arg(rmin_count, NULL);
1851
+ if ( rfval == CA_UNDEF ) rfval = Qnil;
1852
+ int fill_whole = 0;
1853
+ if ( ! NIL_P(rfval) && naxes == src->ndim && ! keep_axis ) {
1854
+ keep_axis = 1;
1855
+ fill_whole = 1;
1856
+ }
1857
+ VALUE result;
1858
+ if ( argc > 0 ) {
1859
+ rb_raise(rb_eArgError, "%s: positional axis arguments are no longer accepted (got %d); use the axis: keyword, e.g. a.%s(axis: 0) or a.%s(axis: [0, 1])", ca_calling_method_name(), argc, ca_calling_method_name(), ca_calling_method_name());
1860
+ }
1861
+ switch ( src->data_type ) {
1862
+ case CA_INT8: {
1863
+ result = variance_ki_native_i8(self, src, slab_axes, naxes, keep_axis, min_count);
1864
+ RB_GC_GUARD(self);
1865
+ break;
1866
+ }
1867
+ case CA_UINT8: {
1868
+ result = variance_ki_native_u8(self, src, slab_axes, naxes, keep_axis, min_count);
1869
+ RB_GC_GUARD(self);
1870
+ break;
1871
+ }
1872
+ case CA_INT16: {
1873
+ result = variance_ki_native_i16(self, src, slab_axes, naxes, keep_axis, min_count);
1874
+ RB_GC_GUARD(self);
1875
+ break;
1876
+ }
1877
+ case CA_UINT16: {
1878
+ result = variance_ki_native_u16(self, src, slab_axes, naxes, keep_axis, min_count);
1879
+ RB_GC_GUARD(self);
1880
+ break;
1881
+ }
1882
+ case CA_INT32: {
1883
+ result = variance_ki_native_i32(self, src, slab_axes, naxes, keep_axis, min_count);
1884
+ RB_GC_GUARD(self);
1885
+ break;
1886
+ }
1887
+ case CA_UINT32: {
1888
+ result = variance_ki_native_u32(self, src, slab_axes, naxes, keep_axis, min_count);
1889
+ RB_GC_GUARD(self);
1890
+ break;
1891
+ }
1892
+ case CA_INT64: {
1893
+ result = variance_ki_native_i64(self, src, slab_axes, naxes, keep_axis, min_count);
1894
+ RB_GC_GUARD(self);
1895
+ break;
1896
+ }
1897
+ case CA_UINT64: {
1898
+ result = variance_ki_native_u64(self, src, slab_axes, naxes, keep_axis, min_count);
1899
+ RB_GC_GUARD(self);
1900
+ break;
1901
+ }
1902
+ case CA_FLOAT32: {
1903
+ result = variance_ki_native_f32(self, src, slab_axes, naxes, keep_axis, min_count);
1904
+ RB_GC_GUARD(self);
1905
+ break;
1906
+ }
1907
+ case CA_FLOAT64: {
1908
+ result = variance_ki_native_f64(self, src, slab_axes, naxes, keep_axis, min_count);
1909
+ RB_GC_GUARD(self);
1910
+ break;
1911
+ }
1912
+ case CA_CMPLX64: {
1913
+ result = variance_ki_native_cmplx64(self, src, slab_axes, naxes, keep_axis, min_count);
1914
+ RB_GC_GUARD(self);
1915
+ break;
1916
+ }
1917
+ case CA_CMPLX128: {
1918
+ result = variance_ki_native_cmplx128(self, src, slab_axes, naxes, keep_axis, min_count);
1919
+ RB_GC_GUARD(self);
1920
+ break;
1921
+ }
1922
+ case CA_BOOLEAN: {
1923
+ result = variance_ki_native_bool(self, src, slab_axes, naxes, keep_axis, min_count);
1924
+ RB_GC_GUARD(self);
1925
+ break;
1926
+ }
1927
+ case CA_OBJECT: {
1928
+ result = variance_ki_native_object(self, src, slab_axes, naxes, keep_axis, min_count);
1929
+ RB_GC_GUARD(self);
1930
+ break;
1931
+ }
1932
+ default:
1933
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
1934
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, cmplx64, cmplx128, boolean, object)", ca_calling_method_name(), ca_type_name[src->data_type]);
1935
+ }
1936
+ if ( ! NIL_P(rfval) ) {
1937
+ result = ca_reduce_fill(result, rfval, fill_whole);
1938
+ }
1939
+ return result;
1940
+ }
1941
+
1942
+ /* ===== stddevp_ki ============================================ */
1943
+
1944
+ #define __stddevp_i8_P1EXPR(__x) ((double)(__x))
1945
+ #define __stddevp_i8_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
1946
+ static VALUE
1947
+ stddevp_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1948
+ {
1949
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
1950
+ CArray *co;
1951
+ GetCArray(vout, co);
1952
+ double *op = (double *) co->ptr;
1953
+ ca_iter_state st;
1954
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1955
+ slab_axes, naxes, 0);
1956
+ if ( rc != CA_ITER_OK ) {
1957
+ rb_raise(rb_eRuntimeError,
1958
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1959
+ }
1960
+ char *p;
1961
+ boolean8_t *m;
1962
+ ca_size_t out_i = 0;
1963
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
1964
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
1965
+ /* Pass 1: sum = Σx (+ masked_cnt) */
1966
+ double sum = 0.0;
1967
+ ca_size_t masked_cnt = 0;
1968
+ CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, sum, 0.0, __stddevp_i8_P1EXPR, masked_cnt);
1969
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
1970
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
1971
+ if ( __trigger ) {
1972
+ if ( ! op_mask ) {
1973
+ ca_create_mask(co);
1974
+ op_mask = (boolean8_t *) co->mask->ptr;
1975
+ }
1976
+ op_mask[out_i] = 1;
1977
+ op[out_i++] = (double) 0.0;
1978
+ } else {
1979
+ double mean = sum / (double) n_valid;
1980
+ /* Pass 2: M2 = Σ (x - mean)² */
1981
+ double M2 = 0.0;
1982
+ ca_size_t __unused_mc = 0;
1983
+ CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, M2, 0.0, __stddevp_i8_P2EXPR, __unused_mc);
1984
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
1985
+ }
1986
+ }
1987
+ ca_iter_state_finish(&st);
1988
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
1989
+ keep_axis means the output is a 1-element CArray; unwrap it to
1990
+ the ruby scalar (matches the standard reduce emit path lines
1991
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
1992
+ if ( naxes == ca->ndim && !keep_axis ) {
1993
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
1994
+ return rb_float_new(op[0]);
1995
+ }
1996
+ return vout;
1997
+ }
1998
+ #undef __stddevp_i8_P1EXPR
1999
+ #undef __stddevp_i8_P2EXPR
2000
+
2001
+ #define __stddevp_u8_P1EXPR(__x) ((double)(__x))
2002
+ #define __stddevp_u8_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2003
+ static VALUE
2004
+ stddevp_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2005
+ {
2006
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2007
+ CArray *co;
2008
+ GetCArray(vout, co);
2009
+ double *op = (double *) co->ptr;
2010
+ ca_iter_state st;
2011
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2012
+ slab_axes, naxes, 0);
2013
+ if ( rc != CA_ITER_OK ) {
2014
+ rb_raise(rb_eRuntimeError,
2015
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2016
+ }
2017
+ char *p;
2018
+ boolean8_t *m;
2019
+ ca_size_t out_i = 0;
2020
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2021
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2022
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2023
+ double sum = 0.0;
2024
+ ca_size_t masked_cnt = 0;
2025
+ CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, sum, 0.0, __stddevp_u8_P1EXPR, masked_cnt);
2026
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2027
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2028
+ if ( __trigger ) {
2029
+ if ( ! op_mask ) {
2030
+ ca_create_mask(co);
2031
+ op_mask = (boolean8_t *) co->mask->ptr;
2032
+ }
2033
+ op_mask[out_i] = 1;
2034
+ op[out_i++] = (double) 0.0;
2035
+ } else {
2036
+ double mean = sum / (double) n_valid;
2037
+ /* Pass 2: M2 = Σ (x - mean)² */
2038
+ double M2 = 0.0;
2039
+ ca_size_t __unused_mc = 0;
2040
+ CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, M2, 0.0, __stddevp_u8_P2EXPR, __unused_mc);
2041
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2042
+ }
2043
+ }
2044
+ ca_iter_state_finish(&st);
2045
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2046
+ keep_axis means the output is a 1-element CArray; unwrap it to
2047
+ the ruby scalar (matches the standard reduce emit path lines
2048
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2049
+ if ( naxes == ca->ndim && !keep_axis ) {
2050
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2051
+ return rb_float_new(op[0]);
2052
+ }
2053
+ return vout;
2054
+ }
2055
+ #undef __stddevp_u8_P1EXPR
2056
+ #undef __stddevp_u8_P2EXPR
2057
+
2058
+ #define __stddevp_i16_P1EXPR(__x) ((double)(__x))
2059
+ #define __stddevp_i16_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2060
+ static VALUE
2061
+ stddevp_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2062
+ {
2063
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2064
+ CArray *co;
2065
+ GetCArray(vout, co);
2066
+ double *op = (double *) co->ptr;
2067
+ ca_iter_state st;
2068
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2069
+ slab_axes, naxes, 0);
2070
+ if ( rc != CA_ITER_OK ) {
2071
+ rb_raise(rb_eRuntimeError,
2072
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2073
+ }
2074
+ char *p;
2075
+ boolean8_t *m;
2076
+ ca_size_t out_i = 0;
2077
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2078
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2079
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2080
+ double sum = 0.0;
2081
+ ca_size_t masked_cnt = 0;
2082
+ CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, sum, 0.0, __stddevp_i16_P1EXPR, masked_cnt);
2083
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2084
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2085
+ if ( __trigger ) {
2086
+ if ( ! op_mask ) {
2087
+ ca_create_mask(co);
2088
+ op_mask = (boolean8_t *) co->mask->ptr;
2089
+ }
2090
+ op_mask[out_i] = 1;
2091
+ op[out_i++] = (double) 0.0;
2092
+ } else {
2093
+ double mean = sum / (double) n_valid;
2094
+ /* Pass 2: M2 = Σ (x - mean)² */
2095
+ double M2 = 0.0;
2096
+ ca_size_t __unused_mc = 0;
2097
+ CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, M2, 0.0, __stddevp_i16_P2EXPR, __unused_mc);
2098
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2099
+ }
2100
+ }
2101
+ ca_iter_state_finish(&st);
2102
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2103
+ keep_axis means the output is a 1-element CArray; unwrap it to
2104
+ the ruby scalar (matches the standard reduce emit path lines
2105
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2106
+ if ( naxes == ca->ndim && !keep_axis ) {
2107
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2108
+ return rb_float_new(op[0]);
2109
+ }
2110
+ return vout;
2111
+ }
2112
+ #undef __stddevp_i16_P1EXPR
2113
+ #undef __stddevp_i16_P2EXPR
2114
+
2115
+ #define __stddevp_u16_P1EXPR(__x) ((double)(__x))
2116
+ #define __stddevp_u16_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2117
+ static VALUE
2118
+ stddevp_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2119
+ {
2120
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2121
+ CArray *co;
2122
+ GetCArray(vout, co);
2123
+ double *op = (double *) co->ptr;
2124
+ ca_iter_state st;
2125
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2126
+ slab_axes, naxes, 0);
2127
+ if ( rc != CA_ITER_OK ) {
2128
+ rb_raise(rb_eRuntimeError,
2129
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2130
+ }
2131
+ char *p;
2132
+ boolean8_t *m;
2133
+ ca_size_t out_i = 0;
2134
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2135
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2136
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2137
+ double sum = 0.0;
2138
+ ca_size_t masked_cnt = 0;
2139
+ CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, sum, 0.0, __stddevp_u16_P1EXPR, masked_cnt);
2140
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2141
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2142
+ if ( __trigger ) {
2143
+ if ( ! op_mask ) {
2144
+ ca_create_mask(co);
2145
+ op_mask = (boolean8_t *) co->mask->ptr;
2146
+ }
2147
+ op_mask[out_i] = 1;
2148
+ op[out_i++] = (double) 0.0;
2149
+ } else {
2150
+ double mean = sum / (double) n_valid;
2151
+ /* Pass 2: M2 = Σ (x - mean)² */
2152
+ double M2 = 0.0;
2153
+ ca_size_t __unused_mc = 0;
2154
+ CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, M2, 0.0, __stddevp_u16_P2EXPR, __unused_mc);
2155
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2156
+ }
2157
+ }
2158
+ ca_iter_state_finish(&st);
2159
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2160
+ keep_axis means the output is a 1-element CArray; unwrap it to
2161
+ the ruby scalar (matches the standard reduce emit path lines
2162
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2163
+ if ( naxes == ca->ndim && !keep_axis ) {
2164
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2165
+ return rb_float_new(op[0]);
2166
+ }
2167
+ return vout;
2168
+ }
2169
+ #undef __stddevp_u16_P1EXPR
2170
+ #undef __stddevp_u16_P2EXPR
2171
+
2172
+ #define __stddevp_i32_P1EXPR(__x) ((double)(__x))
2173
+ #define __stddevp_i32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2174
+ static VALUE
2175
+ stddevp_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2176
+ {
2177
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2178
+ CArray *co;
2179
+ GetCArray(vout, co);
2180
+ double *op = (double *) co->ptr;
2181
+ ca_iter_state st;
2182
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2183
+ slab_axes, naxes, 0);
2184
+ if ( rc != CA_ITER_OK ) {
2185
+ rb_raise(rb_eRuntimeError,
2186
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2187
+ }
2188
+ char *p;
2189
+ boolean8_t *m;
2190
+ ca_size_t out_i = 0;
2191
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2192
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2193
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2194
+ double sum = 0.0;
2195
+ ca_size_t masked_cnt = 0;
2196
+ CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, sum, 0.0, __stddevp_i32_P1EXPR, masked_cnt);
2197
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2198
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2199
+ if ( __trigger ) {
2200
+ if ( ! op_mask ) {
2201
+ ca_create_mask(co);
2202
+ op_mask = (boolean8_t *) co->mask->ptr;
2203
+ }
2204
+ op_mask[out_i] = 1;
2205
+ op[out_i++] = (double) 0.0;
2206
+ } else {
2207
+ double mean = sum / (double) n_valid;
2208
+ /* Pass 2: M2 = Σ (x - mean)² */
2209
+ double M2 = 0.0;
2210
+ ca_size_t __unused_mc = 0;
2211
+ CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, M2, 0.0, __stddevp_i32_P2EXPR, __unused_mc);
2212
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2213
+ }
2214
+ }
2215
+ ca_iter_state_finish(&st);
2216
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2217
+ keep_axis means the output is a 1-element CArray; unwrap it to
2218
+ the ruby scalar (matches the standard reduce emit path lines
2219
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2220
+ if ( naxes == ca->ndim && !keep_axis ) {
2221
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2222
+ return rb_float_new(op[0]);
2223
+ }
2224
+ return vout;
2225
+ }
2226
+ #undef __stddevp_i32_P1EXPR
2227
+ #undef __stddevp_i32_P2EXPR
2228
+
2229
+ #define __stddevp_u32_P1EXPR(__x) ((double)(__x))
2230
+ #define __stddevp_u32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2231
+ static VALUE
2232
+ stddevp_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2233
+ {
2234
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2235
+ CArray *co;
2236
+ GetCArray(vout, co);
2237
+ double *op = (double *) co->ptr;
2238
+ ca_iter_state st;
2239
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2240
+ slab_axes, naxes, 0);
2241
+ if ( rc != CA_ITER_OK ) {
2242
+ rb_raise(rb_eRuntimeError,
2243
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2244
+ }
2245
+ char *p;
2246
+ boolean8_t *m;
2247
+ ca_size_t out_i = 0;
2248
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2249
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2250
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2251
+ double sum = 0.0;
2252
+ ca_size_t masked_cnt = 0;
2253
+ CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, sum, 0.0, __stddevp_u32_P1EXPR, masked_cnt);
2254
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2255
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2256
+ if ( __trigger ) {
2257
+ if ( ! op_mask ) {
2258
+ ca_create_mask(co);
2259
+ op_mask = (boolean8_t *) co->mask->ptr;
2260
+ }
2261
+ op_mask[out_i] = 1;
2262
+ op[out_i++] = (double) 0.0;
2263
+ } else {
2264
+ double mean = sum / (double) n_valid;
2265
+ /* Pass 2: M2 = Σ (x - mean)² */
2266
+ double M2 = 0.0;
2267
+ ca_size_t __unused_mc = 0;
2268
+ CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, M2, 0.0, __stddevp_u32_P2EXPR, __unused_mc);
2269
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2270
+ }
2271
+ }
2272
+ ca_iter_state_finish(&st);
2273
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2274
+ keep_axis means the output is a 1-element CArray; unwrap it to
2275
+ the ruby scalar (matches the standard reduce emit path lines
2276
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2277
+ if ( naxes == ca->ndim && !keep_axis ) {
2278
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2279
+ return rb_float_new(op[0]);
2280
+ }
2281
+ return vout;
2282
+ }
2283
+ #undef __stddevp_u32_P1EXPR
2284
+ #undef __stddevp_u32_P2EXPR
2285
+
2286
+ #define __stddevp_i64_P1EXPR(__x) ((double)(__x))
2287
+ #define __stddevp_i64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2288
+ static VALUE
2289
+ stddevp_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2290
+ {
2291
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2292
+ CArray *co;
2293
+ GetCArray(vout, co);
2294
+ double *op = (double *) co->ptr;
2295
+ ca_iter_state st;
2296
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2297
+ slab_axes, naxes, 0);
2298
+ if ( rc != CA_ITER_OK ) {
2299
+ rb_raise(rb_eRuntimeError,
2300
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2301
+ }
2302
+ char *p;
2303
+ boolean8_t *m;
2304
+ ca_size_t out_i = 0;
2305
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2306
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2307
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2308
+ double sum = 0.0;
2309
+ ca_size_t masked_cnt = 0;
2310
+ CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, sum, 0.0, __stddevp_i64_P1EXPR, masked_cnt);
2311
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2312
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2313
+ if ( __trigger ) {
2314
+ if ( ! op_mask ) {
2315
+ ca_create_mask(co);
2316
+ op_mask = (boolean8_t *) co->mask->ptr;
2317
+ }
2318
+ op_mask[out_i] = 1;
2319
+ op[out_i++] = (double) 0.0;
2320
+ } else {
2321
+ double mean = sum / (double) n_valid;
2322
+ /* Pass 2: M2 = Σ (x - mean)² */
2323
+ double M2 = 0.0;
2324
+ ca_size_t __unused_mc = 0;
2325
+ CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, M2, 0.0, __stddevp_i64_P2EXPR, __unused_mc);
2326
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2327
+ }
2328
+ }
2329
+ ca_iter_state_finish(&st);
2330
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2331
+ keep_axis means the output is a 1-element CArray; unwrap it to
2332
+ the ruby scalar (matches the standard reduce emit path lines
2333
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2334
+ if ( naxes == ca->ndim && !keep_axis ) {
2335
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2336
+ return rb_float_new(op[0]);
2337
+ }
2338
+ return vout;
2339
+ }
2340
+ #undef __stddevp_i64_P1EXPR
2341
+ #undef __stddevp_i64_P2EXPR
2342
+
2343
+ #define __stddevp_u64_P1EXPR(__x) ((double)(__x))
2344
+ #define __stddevp_u64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2345
+ static VALUE
2346
+ stddevp_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2347
+ {
2348
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2349
+ CArray *co;
2350
+ GetCArray(vout, co);
2351
+ double *op = (double *) co->ptr;
2352
+ ca_iter_state st;
2353
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2354
+ slab_axes, naxes, 0);
2355
+ if ( rc != CA_ITER_OK ) {
2356
+ rb_raise(rb_eRuntimeError,
2357
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2358
+ }
2359
+ char *p;
2360
+ boolean8_t *m;
2361
+ ca_size_t out_i = 0;
2362
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2363
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2364
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2365
+ double sum = 0.0;
2366
+ ca_size_t masked_cnt = 0;
2367
+ CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, sum, 0.0, __stddevp_u64_P1EXPR, masked_cnt);
2368
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2369
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2370
+ if ( __trigger ) {
2371
+ if ( ! op_mask ) {
2372
+ ca_create_mask(co);
2373
+ op_mask = (boolean8_t *) co->mask->ptr;
2374
+ }
2375
+ op_mask[out_i] = 1;
2376
+ op[out_i++] = (double) 0.0;
2377
+ } else {
2378
+ double mean = sum / (double) n_valid;
2379
+ /* Pass 2: M2 = Σ (x - mean)² */
2380
+ double M2 = 0.0;
2381
+ ca_size_t __unused_mc = 0;
2382
+ CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, M2, 0.0, __stddevp_u64_P2EXPR, __unused_mc);
2383
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2384
+ }
2385
+ }
2386
+ ca_iter_state_finish(&st);
2387
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2388
+ keep_axis means the output is a 1-element CArray; unwrap it to
2389
+ the ruby scalar (matches the standard reduce emit path lines
2390
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2391
+ if ( naxes == ca->ndim && !keep_axis ) {
2392
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2393
+ return rb_float_new(op[0]);
2394
+ }
2395
+ return vout;
2396
+ }
2397
+ #undef __stddevp_u64_P1EXPR
2398
+ #undef __stddevp_u64_P2EXPR
2399
+
2400
+ #define __stddevp_f32_P1EXPR(__x) ((double)(__x))
2401
+ #define __stddevp_f32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2402
+ static VALUE
2403
+ stddevp_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2404
+ {
2405
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2406
+ CArray *co;
2407
+ GetCArray(vout, co);
2408
+ double *op = (double *) co->ptr;
2409
+ ca_iter_state st;
2410
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2411
+ slab_axes, naxes, 0);
2412
+ if ( rc != CA_ITER_OK ) {
2413
+ rb_raise(rb_eRuntimeError,
2414
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2415
+ }
2416
+ char *p;
2417
+ boolean8_t *m;
2418
+ ca_size_t out_i = 0;
2419
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2420
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2421
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2422
+ double sum = 0.0;
2423
+ ca_size_t masked_cnt = 0;
2424
+ CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, sum, 0.0, __stddevp_f32_P1EXPR, masked_cnt);
2425
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2426
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2427
+ if ( __trigger ) {
2428
+ if ( ! op_mask ) {
2429
+ ca_create_mask(co);
2430
+ op_mask = (boolean8_t *) co->mask->ptr;
2431
+ }
2432
+ op_mask[out_i] = 1;
2433
+ op[out_i++] = (double) 0.0;
2434
+ } else {
2435
+ double mean = sum / (double) n_valid;
2436
+ /* Pass 2: M2 = Σ (x - mean)² */
2437
+ double M2 = 0.0;
2438
+ ca_size_t __unused_mc = 0;
2439
+ CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, M2, 0.0, __stddevp_f32_P2EXPR, __unused_mc);
2440
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2441
+ }
2442
+ }
2443
+ ca_iter_state_finish(&st);
2444
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2445
+ keep_axis means the output is a 1-element CArray; unwrap it to
2446
+ the ruby scalar (matches the standard reduce emit path lines
2447
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2448
+ if ( naxes == ca->ndim && !keep_axis ) {
2449
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2450
+ return rb_float_new(op[0]);
2451
+ }
2452
+ return vout;
2453
+ }
2454
+ #undef __stddevp_f32_P1EXPR
2455
+ #undef __stddevp_f32_P2EXPR
2456
+
2457
+ #define __stddevp_f64_P1EXPR(__x) ((double)(__x))
2458
+ #define __stddevp_f64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2459
+ static VALUE
2460
+ stddevp_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2461
+ {
2462
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2463
+ CArray *co;
2464
+ GetCArray(vout, co);
2465
+ double *op = (double *) co->ptr;
2466
+ ca_iter_state st;
2467
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2468
+ slab_axes, naxes, 0);
2469
+ if ( rc != CA_ITER_OK ) {
2470
+ rb_raise(rb_eRuntimeError,
2471
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2472
+ }
2473
+ char *p;
2474
+ boolean8_t *m;
2475
+ ca_size_t out_i = 0;
2476
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2477
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2478
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2479
+ double sum = 0.0;
2480
+ ca_size_t masked_cnt = 0;
2481
+ CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, sum, 0.0, __stddevp_f64_P1EXPR, masked_cnt);
2482
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2483
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2484
+ if ( __trigger ) {
2485
+ if ( ! op_mask ) {
2486
+ ca_create_mask(co);
2487
+ op_mask = (boolean8_t *) co->mask->ptr;
2488
+ }
2489
+ op_mask[out_i] = 1;
2490
+ op[out_i++] = (double) 0.0;
2491
+ } else {
2492
+ double mean = sum / (double) n_valid;
2493
+ /* Pass 2: M2 = Σ (x - mean)² */
2494
+ double M2 = 0.0;
2495
+ ca_size_t __unused_mc = 0;
2496
+ CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, M2, 0.0, __stddevp_f64_P2EXPR, __unused_mc);
2497
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2498
+ }
2499
+ }
2500
+ ca_iter_state_finish(&st);
2501
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2502
+ keep_axis means the output is a 1-element CArray; unwrap it to
2503
+ the ruby scalar (matches the standard reduce emit path lines
2504
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2505
+ if ( naxes == ca->ndim && !keep_axis ) {
2506
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2507
+ return rb_float_new(op[0]);
2508
+ }
2509
+ return vout;
2510
+ }
2511
+ #undef __stddevp_f64_P1EXPR
2512
+ #undef __stddevp_f64_P2EXPR
2513
+
2514
+ #define __stddevp_cmplx64_P1EXPR(__x) (__x)
2515
+ #define __stddevp_cmplx64_P2EXPR(__x) (creal((__x) - mean) * creal((__x) - mean) + cimag((__x) - mean) * cimag((__x) - mean))
2516
+ static VALUE
2517
+ stddevp_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2518
+ {
2519
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2520
+ CArray *co;
2521
+ GetCArray(vout, co);
2522
+ double *op = (double *) co->ptr;
2523
+ ca_iter_state st;
2524
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2525
+ slab_axes, naxes, 0);
2526
+ if ( rc != CA_ITER_OK ) {
2527
+ rb_raise(rb_eRuntimeError,
2528
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2529
+ }
2530
+ char *p;
2531
+ boolean8_t *m;
2532
+ ca_size_t out_i = 0;
2533
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2534
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2535
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2536
+ cmplx64_t sum = 0;
2537
+ ca_size_t masked_cnt = 0;
2538
+ CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, cmplx64_t, st, p, m, sum, 0, __stddevp_cmplx64_P1EXPR, masked_cnt);
2539
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2540
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2541
+ if ( __trigger ) {
2542
+ if ( ! op_mask ) {
2543
+ ca_create_mask(co);
2544
+ op_mask = (boolean8_t *) co->mask->ptr;
2545
+ }
2546
+ op_mask[out_i] = 1;
2547
+ op[out_i++] = (double) 0.0;
2548
+ } else {
2549
+ cmplx64_t mean = sum / (cmplx64_t)(double) n_valid;
2550
+ /* Pass 2: M2 = Σ (x - mean)² */
2551
+ double M2 = 0.0;
2552
+ ca_size_t __unused_mc = 0;
2553
+ CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, double, st, p, m, M2, 0.0, __stddevp_cmplx64_P2EXPR, __unused_mc);
2554
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2555
+ }
2556
+ }
2557
+ ca_iter_state_finish(&st);
2558
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2559
+ keep_axis means the output is a 1-element CArray; unwrap it to
2560
+ the ruby scalar (matches the standard reduce emit path lines
2561
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2562
+ if ( naxes == ca->ndim && !keep_axis ) {
2563
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2564
+ return rb_float_new(op[0]);
2565
+ }
2566
+ return vout;
2567
+ }
2568
+ #undef __stddevp_cmplx64_P1EXPR
2569
+ #undef __stddevp_cmplx64_P2EXPR
2570
+
2571
+ #define __stddevp_cmplx128_P1EXPR(__x) (__x)
2572
+ #define __stddevp_cmplx128_P2EXPR(__x) (creal((__x) - mean) * creal((__x) - mean) + cimag((__x) - mean) * cimag((__x) - mean))
2573
+ static VALUE
2574
+ stddevp_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2575
+ {
2576
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2577
+ CArray *co;
2578
+ GetCArray(vout, co);
2579
+ double *op = (double *) co->ptr;
2580
+ ca_iter_state st;
2581
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2582
+ slab_axes, naxes, 0);
2583
+ if ( rc != CA_ITER_OK ) {
2584
+ rb_raise(rb_eRuntimeError,
2585
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2586
+ }
2587
+ char *p;
2588
+ boolean8_t *m;
2589
+ ca_size_t out_i = 0;
2590
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2591
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2592
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2593
+ cmplx128_t sum = 0;
2594
+ ca_size_t masked_cnt = 0;
2595
+ CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, cmplx128_t, st, p, m, sum, 0, __stddevp_cmplx128_P1EXPR, masked_cnt);
2596
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2597
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2598
+ if ( __trigger ) {
2599
+ if ( ! op_mask ) {
2600
+ ca_create_mask(co);
2601
+ op_mask = (boolean8_t *) co->mask->ptr;
2602
+ }
2603
+ op_mask[out_i] = 1;
2604
+ op[out_i++] = (double) 0.0;
2605
+ } else {
2606
+ cmplx128_t mean = sum / (cmplx128_t)(double) n_valid;
2607
+ /* Pass 2: M2 = Σ (x - mean)² */
2608
+ double M2 = 0.0;
2609
+ ca_size_t __unused_mc = 0;
2610
+ CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, double, st, p, m, M2, 0.0, __stddevp_cmplx128_P2EXPR, __unused_mc);
2611
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2612
+ }
2613
+ }
2614
+ ca_iter_state_finish(&st);
2615
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2616
+ keep_axis means the output is a 1-element CArray; unwrap it to
2617
+ the ruby scalar (matches the standard reduce emit path lines
2618
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2619
+ if ( naxes == ca->ndim && !keep_axis ) {
2620
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2621
+ return rb_float_new(op[0]);
2622
+ }
2623
+ return vout;
2624
+ }
2625
+ #undef __stddevp_cmplx128_P1EXPR
2626
+ #undef __stddevp_cmplx128_P2EXPR
2627
+
2628
+ #define __stddevp_bool_P1EXPR(__x) ((double)(__x))
2629
+ #define __stddevp_bool_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2630
+ static VALUE
2631
+ stddevp_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2632
+ {
2633
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2634
+ CArray *co;
2635
+ GetCArray(vout, co);
2636
+ double *op = (double *) co->ptr;
2637
+ ca_iter_state st;
2638
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2639
+ slab_axes, naxes, 0);
2640
+ if ( rc != CA_ITER_OK ) {
2641
+ rb_raise(rb_eRuntimeError,
2642
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2643
+ }
2644
+ char *p;
2645
+ boolean8_t *m;
2646
+ ca_size_t out_i = 0;
2647
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2648
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2649
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2650
+ double sum = 0.0;
2651
+ ca_size_t masked_cnt = 0;
2652
+ CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, sum, 0.0, __stddevp_bool_P1EXPR, masked_cnt);
2653
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2654
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2655
+ if ( __trigger ) {
2656
+ if ( ! op_mask ) {
2657
+ ca_create_mask(co);
2658
+ op_mask = (boolean8_t *) co->mask->ptr;
2659
+ }
2660
+ op_mask[out_i] = 1;
2661
+ op[out_i++] = (double) 0.0;
2662
+ } else {
2663
+ double mean = sum / (double) n_valid;
2664
+ /* Pass 2: M2 = Σ (x - mean)² */
2665
+ double M2 = 0.0;
2666
+ ca_size_t __unused_mc = 0;
2667
+ CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, M2, 0.0, __stddevp_bool_P2EXPR, __unused_mc);
2668
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2669
+ }
2670
+ }
2671
+ ca_iter_state_finish(&st);
2672
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2673
+ keep_axis means the output is a 1-element CArray; unwrap it to
2674
+ the ruby scalar (matches the standard reduce emit path lines
2675
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2676
+ if ( naxes == ca->ndim && !keep_axis ) {
2677
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2678
+ return rb_float_new(op[0]);
2679
+ }
2680
+ return vout;
2681
+ }
2682
+ #undef __stddevp_bool_P1EXPR
2683
+ #undef __stddevp_bool_P2EXPR
2684
+
2685
+ /* The object lane calls Ruby for every cell, so a walk can raise part
2686
+ way. Its states live in the caller's frame, and ca_iter_ensure
2687
+ finishes them however the body leaves. */
2688
+ typedef struct {
2689
+ ca_iter_state states[1];
2690
+ VALUE self;
2691
+ CArray *ca;
2692
+ int8_t *slab_axes;
2693
+ int8_t naxes;
2694
+ int keep_axis;
2695
+ ca_size_t min_count;
2696
+ } stddevp_ki_native_object_frame;
2697
+
2698
+ static VALUE
2699
+ stddevp_ki_native_object_body (VALUE arg)
2700
+ {
2701
+ stddevp_ki_native_object_frame *fr = (stddevp_ki_native_object_frame *) arg;
2702
+ VALUE self = fr->self;
2703
+ (void) self;
2704
+ CArray *ca = fr->ca;
2705
+ (void) ca;
2706
+ int8_t *slab_axes = fr->slab_axes;
2707
+ (void) slab_axes;
2708
+ int8_t naxes = fr->naxes;
2709
+ (void) naxes;
2710
+ int keep_axis = fr->keep_axis;
2711
+ (void) keep_axis;
2712
+ ca_size_t min_count = fr->min_count;
2713
+ (void) min_count;
2714
+ #define st (fr->states[0])
2715
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2716
+ CArray *co;
2717
+ GetCArray(vout, co);
2718
+ double *op = (double *) co->ptr;
2719
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2720
+ slab_axes, naxes, 0);
2721
+ if ( rc != CA_ITER_OK ) {
2722
+ rb_raise(rb_eRuntimeError,
2723
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2724
+ }
2725
+ char *p;
2726
+ boolean8_t *m;
2727
+ ca_size_t out_i = 0;
2728
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2729
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2730
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2731
+ VALUE sum = INT2FIX(0);
2732
+ ca_size_t masked_cnt = 0;
2733
+ CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, sum, INT2FIX(0), sum = rb_funcall(sum, rb_intern("+"), 1, v), masked_cnt);
2734
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2735
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2736
+ if ( __trigger ) {
2737
+ if ( ! op_mask ) {
2738
+ ca_create_mask(co);
2739
+ op_mask = (boolean8_t *) co->mask->ptr;
2740
+ }
2741
+ op_mask[out_i] = 1;
2742
+ op[out_i++] = (double) 0.0;
2743
+ } else {
2744
+ VALUE mean = rb_funcall(sum, rb_intern("/"), 1, LONG2NUM(n_valid));
2745
+ /* Pass 2: M2 = Σ (x - mean)² */
2746
+ VALUE M2 = INT2FIX(0);
2747
+ ca_size_t __unused_mc = 0;
2748
+ CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, M2, INT2FIX(0), VALUE __d = rb_funcall(v, rb_intern("-"), 1, mean);
2749
+ M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)), __unused_mc);
2750
+ op[out_i++] = (double)(ca_sqrt_clamp(NUM2DBL(rb_funcall(M2, rb_intern("/"), 1, LONG2NUM(n_valid)))));
2751
+ }
2752
+ }
2753
+ ca_iter_state_finish(&st);
2754
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2755
+ keep_axis means the output is a 1-element CArray; unwrap it to
2756
+ the ruby scalar (matches the standard reduce emit path lines
2757
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2758
+ if ( naxes == ca->ndim && !keep_axis ) {
2759
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2760
+ return rb_float_new(op[0]);
2761
+ }
2762
+ return vout;
2763
+ #undef st
2764
+ }
2765
+
2766
+ static VALUE
2767
+ stddevp_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2768
+ {
2769
+ stddevp_ki_native_object_frame fr;
2770
+ fr.self = self;
2771
+ fr.ca = ca;
2772
+ fr.slab_axes = slab_axes;
2773
+ fr.naxes = naxes;
2774
+ fr.keep_axis = keep_axis;
2775
+ fr.min_count = min_count;
2776
+ return ca_iter_ensure(1, fr.states, stddevp_ki_native_object_body, (VALUE) &fr);
2777
+ }
2778
+
2779
+
2780
+ VALUE
2781
+ rb_ca_stddevp_ki (int argc, VALUE *argv, VALUE self)
2782
+ {
2783
+ CArray *src;
2784
+ GetCArray(self, src);
2785
+ volatile VALUE ropt = rb_pop_options(&argc, &argv);
2786
+ volatile VALUE raxis = Qnil, rkeep_axis = Qnil, rmin_count = Qnil, rfval = Qnil;
2787
+ rb_scan_options(ropt, "axis,keep_axis,min_count,fill_value", &raxis, &rkeep_axis, &rmin_count, &rfval);
2788
+ int keep_axis = RTEST(rkeep_axis);
2789
+ int8_t slab_axes[CA_RANK_MAX];
2790
+ int8_t naxes = rb_ca_parse_reduce_axes_kw(raxis, src, slab_axes);
2791
+ if ( ca_is_lazy_view(src) ) {
2792
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
2793
+ if ( ! NIL_P(evaluated) ) {
2794
+ self = evaluated;
2795
+ GetCArray(self, src);
2796
+ }
2797
+ }
2798
+ ca_size_t min_count = ca_min_count_arg(rmin_count, NULL);
2799
+ if ( rfval == CA_UNDEF ) rfval = Qnil;
2800
+ int fill_whole = 0;
2801
+ if ( ! NIL_P(rfval) && naxes == src->ndim && ! keep_axis ) {
2802
+ keep_axis = 1;
2803
+ fill_whole = 1;
2804
+ }
2805
+ VALUE result;
2806
+ if ( argc > 0 ) {
2807
+ rb_raise(rb_eArgError, "%s: positional axis arguments are no longer accepted (got %d); use the axis: keyword, e.g. a.%s(axis: 0) or a.%s(axis: [0, 1])", ca_calling_method_name(), argc, ca_calling_method_name(), ca_calling_method_name());
2808
+ }
2809
+ switch ( src->data_type ) {
2810
+ case CA_INT8: {
2811
+ result = stddevp_ki_native_i8(self, src, slab_axes, naxes, keep_axis, min_count);
2812
+ RB_GC_GUARD(self);
2813
+ break;
2814
+ }
2815
+ case CA_UINT8: {
2816
+ result = stddevp_ki_native_u8(self, src, slab_axes, naxes, keep_axis, min_count);
2817
+ RB_GC_GUARD(self);
2818
+ break;
2819
+ }
2820
+ case CA_INT16: {
2821
+ result = stddevp_ki_native_i16(self, src, slab_axes, naxes, keep_axis, min_count);
2822
+ RB_GC_GUARD(self);
2823
+ break;
2824
+ }
2825
+ case CA_UINT16: {
2826
+ result = stddevp_ki_native_u16(self, src, slab_axes, naxes, keep_axis, min_count);
2827
+ RB_GC_GUARD(self);
2828
+ break;
2829
+ }
2830
+ case CA_INT32: {
2831
+ result = stddevp_ki_native_i32(self, src, slab_axes, naxes, keep_axis, min_count);
2832
+ RB_GC_GUARD(self);
2833
+ break;
2834
+ }
2835
+ case CA_UINT32: {
2836
+ result = stddevp_ki_native_u32(self, src, slab_axes, naxes, keep_axis, min_count);
2837
+ RB_GC_GUARD(self);
2838
+ break;
2839
+ }
2840
+ case CA_INT64: {
2841
+ result = stddevp_ki_native_i64(self, src, slab_axes, naxes, keep_axis, min_count);
2842
+ RB_GC_GUARD(self);
2843
+ break;
2844
+ }
2845
+ case CA_UINT64: {
2846
+ result = stddevp_ki_native_u64(self, src, slab_axes, naxes, keep_axis, min_count);
2847
+ RB_GC_GUARD(self);
2848
+ break;
2849
+ }
2850
+ case CA_FLOAT32: {
2851
+ result = stddevp_ki_native_f32(self, src, slab_axes, naxes, keep_axis, min_count);
2852
+ RB_GC_GUARD(self);
2853
+ break;
2854
+ }
2855
+ case CA_FLOAT64: {
2856
+ result = stddevp_ki_native_f64(self, src, slab_axes, naxes, keep_axis, min_count);
2857
+ RB_GC_GUARD(self);
2858
+ break;
2859
+ }
2860
+ case CA_CMPLX64: {
2861
+ result = stddevp_ki_native_cmplx64(self, src, slab_axes, naxes, keep_axis, min_count);
2862
+ RB_GC_GUARD(self);
2863
+ break;
2864
+ }
2865
+ case CA_CMPLX128: {
2866
+ result = stddevp_ki_native_cmplx128(self, src, slab_axes, naxes, keep_axis, min_count);
2867
+ RB_GC_GUARD(self);
2868
+ break;
2869
+ }
2870
+ case CA_BOOLEAN: {
2871
+ result = stddevp_ki_native_bool(self, src, slab_axes, naxes, keep_axis, min_count);
2872
+ RB_GC_GUARD(self);
2873
+ break;
2874
+ }
2875
+ case CA_OBJECT: {
2876
+ result = stddevp_ki_native_object(self, src, slab_axes, naxes, keep_axis, min_count);
2877
+ RB_GC_GUARD(self);
2878
+ break;
2879
+ }
2880
+ default:
2881
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
2882
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, cmplx64, cmplx128, boolean, object)", ca_calling_method_name(), ca_type_name[src->data_type]);
2883
+ }
2884
+ if ( ! NIL_P(rfval) ) {
2885
+ result = ca_reduce_fill(result, rfval, fill_whole);
2886
+ }
2887
+ return result;
2888
+ }
2889
+
2890
+ /* ===== stddev_ki ============================================ */
2891
+
2892
+ #define __stddev_i8_P1EXPR(__x) ((double)(__x))
2893
+ #define __stddev_i8_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2894
+ static VALUE
2895
+ stddev_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2896
+ {
2897
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2898
+ CArray *co;
2899
+ GetCArray(vout, co);
2900
+ double *op = (double *) co->ptr;
2901
+ ca_iter_state st;
2902
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2903
+ slab_axes, naxes, 0);
2904
+ if ( rc != CA_ITER_OK ) {
2905
+ rb_raise(rb_eRuntimeError,
2906
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2907
+ }
2908
+ char *p;
2909
+ boolean8_t *m;
2910
+ ca_size_t out_i = 0;
2911
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2912
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2913
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2914
+ double sum = 0.0;
2915
+ ca_size_t masked_cnt = 0;
2916
+ CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, sum, 0.0, __stddev_i8_P1EXPR, masked_cnt);
2917
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2918
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
2919
+ if ( __trigger ) {
2920
+ if ( ! op_mask ) {
2921
+ ca_create_mask(co);
2922
+ op_mask = (boolean8_t *) co->mask->ptr;
2923
+ }
2924
+ op_mask[out_i] = 1;
2925
+ op[out_i++] = (double) 0.0;
2926
+ } else {
2927
+ double mean = sum / (double) n_valid;
2928
+ /* Pass 2: M2 = Σ (x - mean)² */
2929
+ double M2 = 0.0;
2930
+ ca_size_t __unused_mc = 0;
2931
+ CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, M2, 0.0, __stddev_i8_P2EXPR, __unused_mc);
2932
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
2933
+ }
2934
+ }
2935
+ ca_iter_state_finish(&st);
2936
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2937
+ keep_axis means the output is a 1-element CArray; unwrap it to
2938
+ the ruby scalar (matches the standard reduce emit path lines
2939
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2940
+ if ( naxes == ca->ndim && !keep_axis ) {
2941
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2942
+ return rb_float_new(op[0]);
2943
+ }
2944
+ return vout;
2945
+ }
2946
+ #undef __stddev_i8_P1EXPR
2947
+ #undef __stddev_i8_P2EXPR
2948
+
2949
+ #define __stddev_u8_P1EXPR(__x) ((double)(__x))
2950
+ #define __stddev_u8_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
2951
+ static VALUE
2952
+ stddev_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2953
+ {
2954
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2955
+ CArray *co;
2956
+ GetCArray(vout, co);
2957
+ double *op = (double *) co->ptr;
2958
+ ca_iter_state st;
2959
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2960
+ slab_axes, naxes, 0);
2961
+ if ( rc != CA_ITER_OK ) {
2962
+ rb_raise(rb_eRuntimeError,
2963
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2964
+ }
2965
+ char *p;
2966
+ boolean8_t *m;
2967
+ ca_size_t out_i = 0;
2968
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
2969
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2970
+ /* Pass 1: sum = Σx (+ masked_cnt) */
2971
+ double sum = 0.0;
2972
+ ca_size_t masked_cnt = 0;
2973
+ CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, sum, 0.0, __stddev_u8_P1EXPR, masked_cnt);
2974
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
2975
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
2976
+ if ( __trigger ) {
2977
+ if ( ! op_mask ) {
2978
+ ca_create_mask(co);
2979
+ op_mask = (boolean8_t *) co->mask->ptr;
2980
+ }
2981
+ op_mask[out_i] = 1;
2982
+ op[out_i++] = (double) 0.0;
2983
+ } else {
2984
+ double mean = sum / (double) n_valid;
2985
+ /* Pass 2: M2 = Σ (x - mean)² */
2986
+ double M2 = 0.0;
2987
+ ca_size_t __unused_mc = 0;
2988
+ CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, M2, 0.0, __stddev_u8_P2EXPR, __unused_mc);
2989
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
2990
+ }
2991
+ }
2992
+ ca_iter_state_finish(&st);
2993
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
2994
+ keep_axis means the output is a 1-element CArray; unwrap it to
2995
+ the ruby scalar (matches the standard reduce emit path lines
2996
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
2997
+ if ( naxes == ca->ndim && !keep_axis ) {
2998
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
2999
+ return rb_float_new(op[0]);
3000
+ }
3001
+ return vout;
3002
+ }
3003
+ #undef __stddev_u8_P1EXPR
3004
+ #undef __stddev_u8_P2EXPR
3005
+
3006
+ #define __stddev_i16_P1EXPR(__x) ((double)(__x))
3007
+ #define __stddev_i16_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3008
+ static VALUE
3009
+ stddev_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3010
+ {
3011
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3012
+ CArray *co;
3013
+ GetCArray(vout, co);
3014
+ double *op = (double *) co->ptr;
3015
+ ca_iter_state st;
3016
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3017
+ slab_axes, naxes, 0);
3018
+ if ( rc != CA_ITER_OK ) {
3019
+ rb_raise(rb_eRuntimeError,
3020
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3021
+ }
3022
+ char *p;
3023
+ boolean8_t *m;
3024
+ ca_size_t out_i = 0;
3025
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3026
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3027
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3028
+ double sum = 0.0;
3029
+ ca_size_t masked_cnt = 0;
3030
+ CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, sum, 0.0, __stddev_i16_P1EXPR, masked_cnt);
3031
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3032
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3033
+ if ( __trigger ) {
3034
+ if ( ! op_mask ) {
3035
+ ca_create_mask(co);
3036
+ op_mask = (boolean8_t *) co->mask->ptr;
3037
+ }
3038
+ op_mask[out_i] = 1;
3039
+ op[out_i++] = (double) 0.0;
3040
+ } else {
3041
+ double mean = sum / (double) n_valid;
3042
+ /* Pass 2: M2 = Σ (x - mean)² */
3043
+ double M2 = 0.0;
3044
+ ca_size_t __unused_mc = 0;
3045
+ CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, M2, 0.0, __stddev_i16_P2EXPR, __unused_mc);
3046
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3047
+ }
3048
+ }
3049
+ ca_iter_state_finish(&st);
3050
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3051
+ keep_axis means the output is a 1-element CArray; unwrap it to
3052
+ the ruby scalar (matches the standard reduce emit path lines
3053
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3054
+ if ( naxes == ca->ndim && !keep_axis ) {
3055
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3056
+ return rb_float_new(op[0]);
3057
+ }
3058
+ return vout;
3059
+ }
3060
+ #undef __stddev_i16_P1EXPR
3061
+ #undef __stddev_i16_P2EXPR
3062
+
3063
+ #define __stddev_u16_P1EXPR(__x) ((double)(__x))
3064
+ #define __stddev_u16_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3065
+ static VALUE
3066
+ stddev_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3067
+ {
3068
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3069
+ CArray *co;
3070
+ GetCArray(vout, co);
3071
+ double *op = (double *) co->ptr;
3072
+ ca_iter_state st;
3073
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3074
+ slab_axes, naxes, 0);
3075
+ if ( rc != CA_ITER_OK ) {
3076
+ rb_raise(rb_eRuntimeError,
3077
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3078
+ }
3079
+ char *p;
3080
+ boolean8_t *m;
3081
+ ca_size_t out_i = 0;
3082
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3083
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3084
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3085
+ double sum = 0.0;
3086
+ ca_size_t masked_cnt = 0;
3087
+ CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, sum, 0.0, __stddev_u16_P1EXPR, masked_cnt);
3088
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3089
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3090
+ if ( __trigger ) {
3091
+ if ( ! op_mask ) {
3092
+ ca_create_mask(co);
3093
+ op_mask = (boolean8_t *) co->mask->ptr;
3094
+ }
3095
+ op_mask[out_i] = 1;
3096
+ op[out_i++] = (double) 0.0;
3097
+ } else {
3098
+ double mean = sum / (double) n_valid;
3099
+ /* Pass 2: M2 = Σ (x - mean)² */
3100
+ double M2 = 0.0;
3101
+ ca_size_t __unused_mc = 0;
3102
+ CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, M2, 0.0, __stddev_u16_P2EXPR, __unused_mc);
3103
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3104
+ }
3105
+ }
3106
+ ca_iter_state_finish(&st);
3107
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3108
+ keep_axis means the output is a 1-element CArray; unwrap it to
3109
+ the ruby scalar (matches the standard reduce emit path lines
3110
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3111
+ if ( naxes == ca->ndim && !keep_axis ) {
3112
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3113
+ return rb_float_new(op[0]);
3114
+ }
3115
+ return vout;
3116
+ }
3117
+ #undef __stddev_u16_P1EXPR
3118
+ #undef __stddev_u16_P2EXPR
3119
+
3120
+ #define __stddev_i32_P1EXPR(__x) ((double)(__x))
3121
+ #define __stddev_i32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3122
+ static VALUE
3123
+ stddev_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3124
+ {
3125
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3126
+ CArray *co;
3127
+ GetCArray(vout, co);
3128
+ double *op = (double *) co->ptr;
3129
+ ca_iter_state st;
3130
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3131
+ slab_axes, naxes, 0);
3132
+ if ( rc != CA_ITER_OK ) {
3133
+ rb_raise(rb_eRuntimeError,
3134
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3135
+ }
3136
+ char *p;
3137
+ boolean8_t *m;
3138
+ ca_size_t out_i = 0;
3139
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3140
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3141
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3142
+ double sum = 0.0;
3143
+ ca_size_t masked_cnt = 0;
3144
+ CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, sum, 0.0, __stddev_i32_P1EXPR, masked_cnt);
3145
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3146
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3147
+ if ( __trigger ) {
3148
+ if ( ! op_mask ) {
3149
+ ca_create_mask(co);
3150
+ op_mask = (boolean8_t *) co->mask->ptr;
3151
+ }
3152
+ op_mask[out_i] = 1;
3153
+ op[out_i++] = (double) 0.0;
3154
+ } else {
3155
+ double mean = sum / (double) n_valid;
3156
+ /* Pass 2: M2 = Σ (x - mean)² */
3157
+ double M2 = 0.0;
3158
+ ca_size_t __unused_mc = 0;
3159
+ CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, M2, 0.0, __stddev_i32_P2EXPR, __unused_mc);
3160
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3161
+ }
3162
+ }
3163
+ ca_iter_state_finish(&st);
3164
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3165
+ keep_axis means the output is a 1-element CArray; unwrap it to
3166
+ the ruby scalar (matches the standard reduce emit path lines
3167
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3168
+ if ( naxes == ca->ndim && !keep_axis ) {
3169
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3170
+ return rb_float_new(op[0]);
3171
+ }
3172
+ return vout;
3173
+ }
3174
+ #undef __stddev_i32_P1EXPR
3175
+ #undef __stddev_i32_P2EXPR
3176
+
3177
+ #define __stddev_u32_P1EXPR(__x) ((double)(__x))
3178
+ #define __stddev_u32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3179
+ static VALUE
3180
+ stddev_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3181
+ {
3182
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3183
+ CArray *co;
3184
+ GetCArray(vout, co);
3185
+ double *op = (double *) co->ptr;
3186
+ ca_iter_state st;
3187
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3188
+ slab_axes, naxes, 0);
3189
+ if ( rc != CA_ITER_OK ) {
3190
+ rb_raise(rb_eRuntimeError,
3191
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3192
+ }
3193
+ char *p;
3194
+ boolean8_t *m;
3195
+ ca_size_t out_i = 0;
3196
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3197
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3198
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3199
+ double sum = 0.0;
3200
+ ca_size_t masked_cnt = 0;
3201
+ CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, sum, 0.0, __stddev_u32_P1EXPR, masked_cnt);
3202
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3203
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3204
+ if ( __trigger ) {
3205
+ if ( ! op_mask ) {
3206
+ ca_create_mask(co);
3207
+ op_mask = (boolean8_t *) co->mask->ptr;
3208
+ }
3209
+ op_mask[out_i] = 1;
3210
+ op[out_i++] = (double) 0.0;
3211
+ } else {
3212
+ double mean = sum / (double) n_valid;
3213
+ /* Pass 2: M2 = Σ (x - mean)² */
3214
+ double M2 = 0.0;
3215
+ ca_size_t __unused_mc = 0;
3216
+ CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, M2, 0.0, __stddev_u32_P2EXPR, __unused_mc);
3217
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3218
+ }
3219
+ }
3220
+ ca_iter_state_finish(&st);
3221
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3222
+ keep_axis means the output is a 1-element CArray; unwrap it to
3223
+ the ruby scalar (matches the standard reduce emit path lines
3224
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3225
+ if ( naxes == ca->ndim && !keep_axis ) {
3226
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3227
+ return rb_float_new(op[0]);
3228
+ }
3229
+ return vout;
3230
+ }
3231
+ #undef __stddev_u32_P1EXPR
3232
+ #undef __stddev_u32_P2EXPR
3233
+
3234
+ #define __stddev_i64_P1EXPR(__x) ((double)(__x))
3235
+ #define __stddev_i64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3236
+ static VALUE
3237
+ stddev_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3238
+ {
3239
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3240
+ CArray *co;
3241
+ GetCArray(vout, co);
3242
+ double *op = (double *) co->ptr;
3243
+ ca_iter_state st;
3244
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3245
+ slab_axes, naxes, 0);
3246
+ if ( rc != CA_ITER_OK ) {
3247
+ rb_raise(rb_eRuntimeError,
3248
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3249
+ }
3250
+ char *p;
3251
+ boolean8_t *m;
3252
+ ca_size_t out_i = 0;
3253
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3254
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3255
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3256
+ double sum = 0.0;
3257
+ ca_size_t masked_cnt = 0;
3258
+ CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, sum, 0.0, __stddev_i64_P1EXPR, masked_cnt);
3259
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3260
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3261
+ if ( __trigger ) {
3262
+ if ( ! op_mask ) {
3263
+ ca_create_mask(co);
3264
+ op_mask = (boolean8_t *) co->mask->ptr;
3265
+ }
3266
+ op_mask[out_i] = 1;
3267
+ op[out_i++] = (double) 0.0;
3268
+ } else {
3269
+ double mean = sum / (double) n_valid;
3270
+ /* Pass 2: M2 = Σ (x - mean)² */
3271
+ double M2 = 0.0;
3272
+ ca_size_t __unused_mc = 0;
3273
+ CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, M2, 0.0, __stddev_i64_P2EXPR, __unused_mc);
3274
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3275
+ }
3276
+ }
3277
+ ca_iter_state_finish(&st);
3278
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3279
+ keep_axis means the output is a 1-element CArray; unwrap it to
3280
+ the ruby scalar (matches the standard reduce emit path lines
3281
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3282
+ if ( naxes == ca->ndim && !keep_axis ) {
3283
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3284
+ return rb_float_new(op[0]);
3285
+ }
3286
+ return vout;
3287
+ }
3288
+ #undef __stddev_i64_P1EXPR
3289
+ #undef __stddev_i64_P2EXPR
3290
+
3291
+ #define __stddev_u64_P1EXPR(__x) ((double)(__x))
3292
+ #define __stddev_u64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3293
+ static VALUE
3294
+ stddev_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3295
+ {
3296
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3297
+ CArray *co;
3298
+ GetCArray(vout, co);
3299
+ double *op = (double *) co->ptr;
3300
+ ca_iter_state st;
3301
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3302
+ slab_axes, naxes, 0);
3303
+ if ( rc != CA_ITER_OK ) {
3304
+ rb_raise(rb_eRuntimeError,
3305
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3306
+ }
3307
+ char *p;
3308
+ boolean8_t *m;
3309
+ ca_size_t out_i = 0;
3310
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3311
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3312
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3313
+ double sum = 0.0;
3314
+ ca_size_t masked_cnt = 0;
3315
+ CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, sum, 0.0, __stddev_u64_P1EXPR, masked_cnt);
3316
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3317
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3318
+ if ( __trigger ) {
3319
+ if ( ! op_mask ) {
3320
+ ca_create_mask(co);
3321
+ op_mask = (boolean8_t *) co->mask->ptr;
3322
+ }
3323
+ op_mask[out_i] = 1;
3324
+ op[out_i++] = (double) 0.0;
3325
+ } else {
3326
+ double mean = sum / (double) n_valid;
3327
+ /* Pass 2: M2 = Σ (x - mean)² */
3328
+ double M2 = 0.0;
3329
+ ca_size_t __unused_mc = 0;
3330
+ CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, M2, 0.0, __stddev_u64_P2EXPR, __unused_mc);
3331
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3332
+ }
3333
+ }
3334
+ ca_iter_state_finish(&st);
3335
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3336
+ keep_axis means the output is a 1-element CArray; unwrap it to
3337
+ the ruby scalar (matches the standard reduce emit path lines
3338
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3339
+ if ( naxes == ca->ndim && !keep_axis ) {
3340
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3341
+ return rb_float_new(op[0]);
3342
+ }
3343
+ return vout;
3344
+ }
3345
+ #undef __stddev_u64_P1EXPR
3346
+ #undef __stddev_u64_P2EXPR
3347
+
3348
+ #define __stddev_f32_P1EXPR(__x) ((double)(__x))
3349
+ #define __stddev_f32_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3350
+ static VALUE
3351
+ stddev_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3352
+ {
3353
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3354
+ CArray *co;
3355
+ GetCArray(vout, co);
3356
+ double *op = (double *) co->ptr;
3357
+ ca_iter_state st;
3358
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3359
+ slab_axes, naxes, 0);
3360
+ if ( rc != CA_ITER_OK ) {
3361
+ rb_raise(rb_eRuntimeError,
3362
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3363
+ }
3364
+ char *p;
3365
+ boolean8_t *m;
3366
+ ca_size_t out_i = 0;
3367
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3368
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3369
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3370
+ double sum = 0.0;
3371
+ ca_size_t masked_cnt = 0;
3372
+ CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, sum, 0.0, __stddev_f32_P1EXPR, masked_cnt);
3373
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3374
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3375
+ if ( __trigger ) {
3376
+ if ( ! op_mask ) {
3377
+ ca_create_mask(co);
3378
+ op_mask = (boolean8_t *) co->mask->ptr;
3379
+ }
3380
+ op_mask[out_i] = 1;
3381
+ op[out_i++] = (double) 0.0;
3382
+ } else {
3383
+ double mean = sum / (double) n_valid;
3384
+ /* Pass 2: M2 = Σ (x - mean)² */
3385
+ double M2 = 0.0;
3386
+ ca_size_t __unused_mc = 0;
3387
+ CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, M2, 0.0, __stddev_f32_P2EXPR, __unused_mc);
3388
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3389
+ }
3390
+ }
3391
+ ca_iter_state_finish(&st);
3392
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3393
+ keep_axis means the output is a 1-element CArray; unwrap it to
3394
+ the ruby scalar (matches the standard reduce emit path lines
3395
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3396
+ if ( naxes == ca->ndim && !keep_axis ) {
3397
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3398
+ return rb_float_new(op[0]);
3399
+ }
3400
+ return vout;
3401
+ }
3402
+ #undef __stddev_f32_P1EXPR
3403
+ #undef __stddev_f32_P2EXPR
3404
+
3405
+ #define __stddev_f64_P1EXPR(__x) ((double)(__x))
3406
+ #define __stddev_f64_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3407
+ static VALUE
3408
+ stddev_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3409
+ {
3410
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3411
+ CArray *co;
3412
+ GetCArray(vout, co);
3413
+ double *op = (double *) co->ptr;
3414
+ ca_iter_state st;
3415
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3416
+ slab_axes, naxes, 0);
3417
+ if ( rc != CA_ITER_OK ) {
3418
+ rb_raise(rb_eRuntimeError,
3419
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3420
+ }
3421
+ char *p;
3422
+ boolean8_t *m;
3423
+ ca_size_t out_i = 0;
3424
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3425
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3426
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3427
+ double sum = 0.0;
3428
+ ca_size_t masked_cnt = 0;
3429
+ CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, sum, 0.0, __stddev_f64_P1EXPR, masked_cnt);
3430
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3431
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3432
+ if ( __trigger ) {
3433
+ if ( ! op_mask ) {
3434
+ ca_create_mask(co);
3435
+ op_mask = (boolean8_t *) co->mask->ptr;
3436
+ }
3437
+ op_mask[out_i] = 1;
3438
+ op[out_i++] = (double) 0.0;
3439
+ } else {
3440
+ double mean = sum / (double) n_valid;
3441
+ /* Pass 2: M2 = Σ (x - mean)² */
3442
+ double M2 = 0.0;
3443
+ ca_size_t __unused_mc = 0;
3444
+ CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, M2, 0.0, __stddev_f64_P2EXPR, __unused_mc);
3445
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3446
+ }
3447
+ }
3448
+ ca_iter_state_finish(&st);
3449
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3450
+ keep_axis means the output is a 1-element CArray; unwrap it to
3451
+ the ruby scalar (matches the standard reduce emit path lines
3452
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3453
+ if ( naxes == ca->ndim && !keep_axis ) {
3454
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3455
+ return rb_float_new(op[0]);
3456
+ }
3457
+ return vout;
3458
+ }
3459
+ #undef __stddev_f64_P1EXPR
3460
+ #undef __stddev_f64_P2EXPR
3461
+
3462
+ #define __stddev_cmplx64_P1EXPR(__x) (__x)
3463
+ #define __stddev_cmplx64_P2EXPR(__x) (creal((__x) - mean) * creal((__x) - mean) + cimag((__x) - mean) * cimag((__x) - mean))
3464
+ static VALUE
3465
+ stddev_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3466
+ {
3467
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3468
+ CArray *co;
3469
+ GetCArray(vout, co);
3470
+ double *op = (double *) co->ptr;
3471
+ ca_iter_state st;
3472
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3473
+ slab_axes, naxes, 0);
3474
+ if ( rc != CA_ITER_OK ) {
3475
+ rb_raise(rb_eRuntimeError,
3476
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3477
+ }
3478
+ char *p;
3479
+ boolean8_t *m;
3480
+ ca_size_t out_i = 0;
3481
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3482
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3483
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3484
+ cmplx64_t sum = 0;
3485
+ ca_size_t masked_cnt = 0;
3486
+ CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, cmplx64_t, st, p, m, sum, 0, __stddev_cmplx64_P1EXPR, masked_cnt);
3487
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3488
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3489
+ if ( __trigger ) {
3490
+ if ( ! op_mask ) {
3491
+ ca_create_mask(co);
3492
+ op_mask = (boolean8_t *) co->mask->ptr;
3493
+ }
3494
+ op_mask[out_i] = 1;
3495
+ op[out_i++] = (double) 0.0;
3496
+ } else {
3497
+ cmplx64_t mean = sum / (cmplx64_t)(double) n_valid;
3498
+ /* Pass 2: M2 = Σ (x - mean)² */
3499
+ double M2 = 0.0;
3500
+ ca_size_t __unused_mc = 0;
3501
+ CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, double, st, p, m, M2, 0.0, __stddev_cmplx64_P2EXPR, __unused_mc);
3502
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3503
+ }
3504
+ }
3505
+ ca_iter_state_finish(&st);
3506
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3507
+ keep_axis means the output is a 1-element CArray; unwrap it to
3508
+ the ruby scalar (matches the standard reduce emit path lines
3509
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3510
+ if ( naxes == ca->ndim && !keep_axis ) {
3511
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3512
+ return rb_float_new(op[0]);
3513
+ }
3514
+ return vout;
3515
+ }
3516
+ #undef __stddev_cmplx64_P1EXPR
3517
+ #undef __stddev_cmplx64_P2EXPR
3518
+
3519
+ #define __stddev_cmplx128_P1EXPR(__x) (__x)
3520
+ #define __stddev_cmplx128_P2EXPR(__x) (creal((__x) - mean) * creal((__x) - mean) + cimag((__x) - mean) * cimag((__x) - mean))
3521
+ static VALUE
3522
+ stddev_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3523
+ {
3524
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3525
+ CArray *co;
3526
+ GetCArray(vout, co);
3527
+ double *op = (double *) co->ptr;
3528
+ ca_iter_state st;
3529
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3530
+ slab_axes, naxes, 0);
3531
+ if ( rc != CA_ITER_OK ) {
3532
+ rb_raise(rb_eRuntimeError,
3533
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3534
+ }
3535
+ char *p;
3536
+ boolean8_t *m;
3537
+ ca_size_t out_i = 0;
3538
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3539
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3540
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3541
+ cmplx128_t sum = 0;
3542
+ ca_size_t masked_cnt = 0;
3543
+ CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, cmplx128_t, st, p, m, sum, 0, __stddev_cmplx128_P1EXPR, masked_cnt);
3544
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3545
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3546
+ if ( __trigger ) {
3547
+ if ( ! op_mask ) {
3548
+ ca_create_mask(co);
3549
+ op_mask = (boolean8_t *) co->mask->ptr;
3550
+ }
3551
+ op_mask[out_i] = 1;
3552
+ op[out_i++] = (double) 0.0;
3553
+ } else {
3554
+ cmplx128_t mean = sum / (cmplx128_t)(double) n_valid;
3555
+ /* Pass 2: M2 = Σ (x - mean)² */
3556
+ double M2 = 0.0;
3557
+ ca_size_t __unused_mc = 0;
3558
+ CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, double, st, p, m, M2, 0.0, __stddev_cmplx128_P2EXPR, __unused_mc);
3559
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3560
+ }
3561
+ }
3562
+ ca_iter_state_finish(&st);
3563
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3564
+ keep_axis means the output is a 1-element CArray; unwrap it to
3565
+ the ruby scalar (matches the standard reduce emit path lines
3566
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3567
+ if ( naxes == ca->ndim && !keep_axis ) {
3568
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3569
+ return rb_float_new(op[0]);
3570
+ }
3571
+ return vout;
3572
+ }
3573
+ #undef __stddev_cmplx128_P1EXPR
3574
+ #undef __stddev_cmplx128_P2EXPR
3575
+
3576
+ #define __stddev_bool_P1EXPR(__x) ((double)(__x))
3577
+ #define __stddev_bool_P2EXPR(__x) (((double)(__x) - mean) * ((double)(__x) - mean))
3578
+ static VALUE
3579
+ stddev_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3580
+ {
3581
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3582
+ CArray *co;
3583
+ GetCArray(vout, co);
3584
+ double *op = (double *) co->ptr;
3585
+ ca_iter_state st;
3586
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3587
+ slab_axes, naxes, 0);
3588
+ if ( rc != CA_ITER_OK ) {
3589
+ rb_raise(rb_eRuntimeError,
3590
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3591
+ }
3592
+ char *p;
3593
+ boolean8_t *m;
3594
+ ca_size_t out_i = 0;
3595
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3596
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3597
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3598
+ double sum = 0.0;
3599
+ ca_size_t masked_cnt = 0;
3600
+ CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, sum, 0.0, __stddev_bool_P1EXPR, masked_cnt);
3601
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3602
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3603
+ if ( __trigger ) {
3604
+ if ( ! op_mask ) {
3605
+ ca_create_mask(co);
3606
+ op_mask = (boolean8_t *) co->mask->ptr;
3607
+ }
3608
+ op_mask[out_i] = 1;
3609
+ op[out_i++] = (double) 0.0;
3610
+ } else {
3611
+ double mean = sum / (double) n_valid;
3612
+ /* Pass 2: M2 = Σ (x - mean)² */
3613
+ double M2 = 0.0;
3614
+ ca_size_t __unused_mc = 0;
3615
+ CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, M2, 0.0, __stddev_bool_P2EXPR, __unused_mc);
3616
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3617
+ }
3618
+ }
3619
+ ca_iter_state_finish(&st);
3620
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3621
+ keep_axis means the output is a 1-element CArray; unwrap it to
3622
+ the ruby scalar (matches the standard reduce emit path lines
3623
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3624
+ if ( naxes == ca->ndim && !keep_axis ) {
3625
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3626
+ return rb_float_new(op[0]);
3627
+ }
3628
+ return vout;
3629
+ }
3630
+ #undef __stddev_bool_P1EXPR
3631
+ #undef __stddev_bool_P2EXPR
3632
+
3633
+ /* The object lane calls Ruby for every cell, so a walk can raise part
3634
+ way. Its states live in the caller's frame, and ca_iter_ensure
3635
+ finishes them however the body leaves. */
3636
+ typedef struct {
3637
+ ca_iter_state states[1];
3638
+ VALUE self;
3639
+ CArray *ca;
3640
+ int8_t *slab_axes;
3641
+ int8_t naxes;
3642
+ int keep_axis;
3643
+ ca_size_t min_count;
3644
+ } stddev_ki_native_object_frame;
3645
+
3646
+ static VALUE
3647
+ stddev_ki_native_object_body (VALUE arg)
3648
+ {
3649
+ stddev_ki_native_object_frame *fr = (stddev_ki_native_object_frame *) arg;
3650
+ VALUE self = fr->self;
3651
+ (void) self;
3652
+ CArray *ca = fr->ca;
3653
+ (void) ca;
3654
+ int8_t *slab_axes = fr->slab_axes;
3655
+ (void) slab_axes;
3656
+ int8_t naxes = fr->naxes;
3657
+ (void) naxes;
3658
+ int keep_axis = fr->keep_axis;
3659
+ (void) keep_axis;
3660
+ ca_size_t min_count = fr->min_count;
3661
+ (void) min_count;
3662
+ #define st (fr->states[0])
3663
+ VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3664
+ CArray *co;
3665
+ GetCArray(vout, co);
3666
+ double *op = (double *) co->ptr;
3667
+ int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3668
+ slab_axes, naxes, 0);
3669
+ if ( rc != CA_ITER_OK ) {
3670
+ rb_raise(rb_eRuntimeError,
3671
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3672
+ }
3673
+ char *p;
3674
+ boolean8_t *m;
3675
+ ca_size_t out_i = 0;
3676
+ boolean8_t *op_mask = NULL; /* lazily allocated on first UNDEF */
3677
+ while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
3678
+ /* Pass 1: sum = Σx (+ masked_cnt) */
3679
+ VALUE sum = INT2FIX(0);
3680
+ ca_size_t masked_cnt = 0;
3681
+ CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, sum, INT2FIX(0), sum = rb_funcall(sum, rb_intern("+"), 1, v), masked_cnt);
3682
+ ca_size_t n_valid = st.slab_elements - masked_cnt;
3683
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3684
+ if ( __trigger ) {
3685
+ if ( ! op_mask ) {
3686
+ ca_create_mask(co);
3687
+ op_mask = (boolean8_t *) co->mask->ptr;
3688
+ }
3689
+ op_mask[out_i] = 1;
3690
+ op[out_i++] = (double) 0.0;
3691
+ } else {
3692
+ VALUE mean = rb_funcall(sum, rb_intern("/"), 1, LONG2NUM(n_valid));
3693
+ /* Pass 2: M2 = Σ (x - mean)² */
3694
+ VALUE M2 = INT2FIX(0);
3695
+ ca_size_t __unused_mc = 0;
3696
+ CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, M2, INT2FIX(0), VALUE __d = rb_funcall(v, rb_intern("-"), 1, mean);
3697
+ M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)), __unused_mc);
3698
+ op[out_i++] = (double)(ca_sqrt_clamp(NUM2DBL(rb_funcall(M2, rb_intern("/"), 1, LONG2NUM(n_valid - 1)))));
3699
+ }
3700
+ }
3701
+ ca_iter_state_finish(&st);
3702
+ /* Full-reduction scalar shortcut: naxes == ca->ndim and no
3703
+ keep_axis means the output is a 1-element CArray; unwrap it to
3704
+ the ruby scalar (matches the standard reduce emit path lines
3705
+ 1811-1817). Full reduction with all-masked returns CA_UNDEF. */
3706
+ if ( naxes == ca->ndim && !keep_axis ) {
3707
+ if ( op_mask && op_mask[0] ) return CA_UNDEF;
3708
+ return rb_float_new(op[0]);
3709
+ }
3710
+ return vout;
3711
+ #undef st
3712
+ }
3713
+
3714
+ static VALUE
3715
+ stddev_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3716
+ {
3717
+ stddev_ki_native_object_frame fr;
3718
+ fr.self = self;
3719
+ fr.ca = ca;
3720
+ fr.slab_axes = slab_axes;
3721
+ fr.naxes = naxes;
3722
+ fr.keep_axis = keep_axis;
3723
+ fr.min_count = min_count;
3724
+ return ca_iter_ensure(1, fr.states, stddev_ki_native_object_body, (VALUE) &fr);
3725
+ }
3726
+
3727
+
3728
+ VALUE
3729
+ rb_ca_stddev_ki (int argc, VALUE *argv, VALUE self)
3730
+ {
3731
+ CArray *src;
3732
+ GetCArray(self, src);
3733
+ volatile VALUE ropt = rb_pop_options(&argc, &argv);
3734
+ volatile VALUE raxis = Qnil, rkeep_axis = Qnil, rmin_count = Qnil, rfval = Qnil;
3735
+ rb_scan_options(ropt, "axis,keep_axis,min_count,fill_value", &raxis, &rkeep_axis, &rmin_count, &rfval);
3736
+ int keep_axis = RTEST(rkeep_axis);
3737
+ int8_t slab_axes[CA_RANK_MAX];
3738
+ int8_t naxes = rb_ca_parse_reduce_axes_kw(raxis, src, slab_axes);
3739
+ if ( ca_is_lazy_view(src) ) {
3740
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
3741
+ if ( ! NIL_P(evaluated) ) {
3742
+ self = evaluated;
3743
+ GetCArray(self, src);
3744
+ }
3745
+ }
3746
+ ca_size_t min_count = ca_min_count_arg(rmin_count, NULL);
3747
+ if ( rfval == CA_UNDEF ) rfval = Qnil;
3748
+ int fill_whole = 0;
3749
+ if ( ! NIL_P(rfval) && naxes == src->ndim && ! keep_axis ) {
3750
+ keep_axis = 1;
3751
+ fill_whole = 1;
3752
+ }
3753
+ VALUE result;
3754
+ if ( argc > 0 ) {
3755
+ rb_raise(rb_eArgError, "%s: positional axis arguments are no longer accepted (got %d); use the axis: keyword, e.g. a.%s(axis: 0) or a.%s(axis: [0, 1])", ca_calling_method_name(), argc, ca_calling_method_name(), ca_calling_method_name());
3756
+ }
3757
+ switch ( src->data_type ) {
3758
+ case CA_INT8: {
3759
+ result = stddev_ki_native_i8(self, src, slab_axes, naxes, keep_axis, min_count);
3760
+ RB_GC_GUARD(self);
3761
+ break;
3762
+ }
3763
+ case CA_UINT8: {
3764
+ result = stddev_ki_native_u8(self, src, slab_axes, naxes, keep_axis, min_count);
3765
+ RB_GC_GUARD(self);
3766
+ break;
3767
+ }
3768
+ case CA_INT16: {
3769
+ result = stddev_ki_native_i16(self, src, slab_axes, naxes, keep_axis, min_count);
3770
+ RB_GC_GUARD(self);
3771
+ break;
3772
+ }
3773
+ case CA_UINT16: {
3774
+ result = stddev_ki_native_u16(self, src, slab_axes, naxes, keep_axis, min_count);
3775
+ RB_GC_GUARD(self);
3776
+ break;
3777
+ }
3778
+ case CA_INT32: {
3779
+ result = stddev_ki_native_i32(self, src, slab_axes, naxes, keep_axis, min_count);
3780
+ RB_GC_GUARD(self);
3781
+ break;
3782
+ }
3783
+ case CA_UINT32: {
3784
+ result = stddev_ki_native_u32(self, src, slab_axes, naxes, keep_axis, min_count);
3785
+ RB_GC_GUARD(self);
3786
+ break;
3787
+ }
3788
+ case CA_INT64: {
3789
+ result = stddev_ki_native_i64(self, src, slab_axes, naxes, keep_axis, min_count);
3790
+ RB_GC_GUARD(self);
3791
+ break;
3792
+ }
3793
+ case CA_UINT64: {
3794
+ result = stddev_ki_native_u64(self, src, slab_axes, naxes, keep_axis, min_count);
3795
+ RB_GC_GUARD(self);
3796
+ break;
3797
+ }
3798
+ case CA_FLOAT32: {
3799
+ result = stddev_ki_native_f32(self, src, slab_axes, naxes, keep_axis, min_count);
3800
+ RB_GC_GUARD(self);
3801
+ break;
3802
+ }
3803
+ case CA_FLOAT64: {
3804
+ result = stddev_ki_native_f64(self, src, slab_axes, naxes, keep_axis, min_count);
3805
+ RB_GC_GUARD(self);
3806
+ break;
3807
+ }
3808
+ case CA_CMPLX64: {
3809
+ result = stddev_ki_native_cmplx64(self, src, slab_axes, naxes, keep_axis, min_count);
3810
+ RB_GC_GUARD(self);
3811
+ break;
3812
+ }
3813
+ case CA_CMPLX128: {
3814
+ result = stddev_ki_native_cmplx128(self, src, slab_axes, naxes, keep_axis, min_count);
3815
+ RB_GC_GUARD(self);
3816
+ break;
3817
+ }
3818
+ case CA_BOOLEAN: {
3819
+ result = stddev_ki_native_bool(self, src, slab_axes, naxes, keep_axis, min_count);
3820
+ RB_GC_GUARD(self);
3821
+ break;
3822
+ }
3823
+ case CA_OBJECT: {
3824
+ result = stddev_ki_native_object(self, src, slab_axes, naxes, keep_axis, min_count);
3825
+ RB_GC_GUARD(self);
3826
+ break;
3827
+ }
3828
+ default:
3829
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
3830
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, cmplx64, cmplx128, boolean, object)", ca_calling_method_name(), ca_type_name[src->data_type]);
3831
+ }
3832
+ if ( ! NIL_P(rfval) ) {
3833
+ result = ca_reduce_fill(result, rfval, fill_whole);
3834
+ }
3835
+ return result;
3836
+ }
3837
+
3838
+ void
3839
+ Init_carray_kernels_reduce_variance (void)
3840
+ {
3841
+ /* Phase E rewire: legacy 'variancep' now dispatches via variancep_ki */
3842
+ rb_define_method(rb_cCArray, "variancep", rb_ca_variancep_ki, -1);
3843
+ /* Phase E rewire: legacy 'variance' now dispatches via variance_ki */
3844
+ rb_define_method(rb_cCArray, "variance", rb_ca_variance_ki, -1);
3845
+ /* Phase E rewire: legacy 'stddevp' now dispatches via stddevp_ki */
3846
+ rb_define_method(rb_cCArray, "stddevp", rb_ca_stddevp_ki, -1);
3847
+ /* Phase E rewire: legacy 'stddev' now dispatches via stddev_ki */
3848
+ rb_define_method(rb_cCArray, "stddev", rb_ca_stddev_ki, -1);
3849
+ }