carray 3.0.2 → 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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
@@ -24,6 +24,13 @@
24
24
  probed by operation-table function pointer (obj_type is runtime-
25
25
  assigned, so no CA_OBJ_STACK compile-time constant exists). */
26
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
+
27
34
  /* BOOL2VAL: bool -> Ruby (Qtrue/Qfalse). Used as ruby_scalar wrapper
28
35
  for the all/any flat-reduction Ruby surface so `a.all` / `a.any`
29
36
  return real true/false, not Integer 0/1. (BOOL2OBJ already exists
@@ -52,7 +59,7 @@ variancep_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
52
59
  slab_axes, naxes, 0);
53
60
  if ( rc != CA_ITER_OK ) {
54
61
  rb_raise(rb_eRuntimeError,
55
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
62
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
56
63
  }
57
64
  char *p;
58
65
  boolean8_t *m;
@@ -64,7 +71,7 @@ variancep_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
64
71
  ca_size_t masked_cnt = 0;
65
72
  CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, sum, 0.0, __variancep_i8_P1EXPR, masked_cnt);
66
73
  ca_size_t n_valid = st.slab_elements - masked_cnt;
67
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
74
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
68
75
  if ( __trigger ) {
69
76
  if ( ! op_mask ) {
70
77
  ca_create_mask(co);
@@ -72,11 +79,6 @@ variancep_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
72
79
  }
73
80
  op_mask[out_i] = 1;
74
81
  op[out_i++] = (double) 0.0;
75
- } else if ( n_valid < 1 ) {
76
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
77
- cell): return zero rather than UNDEF, matching pre-migration
78
- behaviour of `cnt > 1 ? formula : 0`. */
79
- op[out_i++] = (double) 0.0;
80
82
  } else {
81
83
  double mean = sum / (double) n_valid;
82
84
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -114,7 +116,7 @@ variancep_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
114
116
  slab_axes, naxes, 0);
115
117
  if ( rc != CA_ITER_OK ) {
116
118
  rb_raise(rb_eRuntimeError,
117
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
119
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
118
120
  }
119
121
  char *p;
120
122
  boolean8_t *m;
@@ -126,7 +128,7 @@ variancep_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
126
128
  ca_size_t masked_cnt = 0;
127
129
  CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, sum, 0.0, __variancep_u8_P1EXPR, masked_cnt);
128
130
  ca_size_t n_valid = st.slab_elements - masked_cnt;
129
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
131
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
130
132
  if ( __trigger ) {
131
133
  if ( ! op_mask ) {
132
134
  ca_create_mask(co);
@@ -134,11 +136,6 @@ variancep_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
134
136
  }
135
137
  op_mask[out_i] = 1;
136
138
  op[out_i++] = (double) 0.0;
137
- } else if ( n_valid < 1 ) {
138
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
139
- cell): return zero rather than UNDEF, matching pre-migration
140
- behaviour of `cnt > 1 ? formula : 0`. */
141
- op[out_i++] = (double) 0.0;
142
139
  } else {
143
140
  double mean = sum / (double) n_valid;
144
141
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -176,7 +173,7 @@ variancep_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
176
173
  slab_axes, naxes, 0);
177
174
  if ( rc != CA_ITER_OK ) {
178
175
  rb_raise(rb_eRuntimeError,
179
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
176
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
180
177
  }
181
178
  char *p;
182
179
  boolean8_t *m;
@@ -188,7 +185,7 @@ variancep_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
188
185
  ca_size_t masked_cnt = 0;
189
186
  CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, sum, 0.0, __variancep_i16_P1EXPR, masked_cnt);
190
187
  ca_size_t n_valid = st.slab_elements - masked_cnt;
191
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
188
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
192
189
  if ( __trigger ) {
193
190
  if ( ! op_mask ) {
194
191
  ca_create_mask(co);
@@ -196,11 +193,6 @@ variancep_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
196
193
  }
197
194
  op_mask[out_i] = 1;
198
195
  op[out_i++] = (double) 0.0;
199
- } else if ( n_valid < 1 ) {
200
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
201
- cell): return zero rather than UNDEF, matching pre-migration
202
- behaviour of `cnt > 1 ? formula : 0`. */
203
- op[out_i++] = (double) 0.0;
204
196
  } else {
205
197
  double mean = sum / (double) n_valid;
206
198
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -238,7 +230,7 @@ variancep_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
238
230
  slab_axes, naxes, 0);
239
231
  if ( rc != CA_ITER_OK ) {
240
232
  rb_raise(rb_eRuntimeError,
241
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
233
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
242
234
  }
243
235
  char *p;
244
236
  boolean8_t *m;
@@ -250,7 +242,7 @@ variancep_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
250
242
  ca_size_t masked_cnt = 0;
251
243
  CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, sum, 0.0, __variancep_u16_P1EXPR, masked_cnt);
252
244
  ca_size_t n_valid = st.slab_elements - masked_cnt;
253
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
245
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
254
246
  if ( __trigger ) {
255
247
  if ( ! op_mask ) {
256
248
  ca_create_mask(co);
@@ -258,11 +250,6 @@ variancep_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
258
250
  }
259
251
  op_mask[out_i] = 1;
260
252
  op[out_i++] = (double) 0.0;
261
- } else if ( n_valid < 1 ) {
262
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
263
- cell): return zero rather than UNDEF, matching pre-migration
264
- behaviour of `cnt > 1 ? formula : 0`. */
265
- op[out_i++] = (double) 0.0;
266
253
  } else {
267
254
  double mean = sum / (double) n_valid;
268
255
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -300,7 +287,7 @@ variancep_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
300
287
  slab_axes, naxes, 0);
301
288
  if ( rc != CA_ITER_OK ) {
302
289
  rb_raise(rb_eRuntimeError,
303
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
290
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
304
291
  }
305
292
  char *p;
306
293
  boolean8_t *m;
@@ -312,7 +299,7 @@ variancep_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
312
299
  ca_size_t masked_cnt = 0;
313
300
  CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, sum, 0.0, __variancep_i32_P1EXPR, masked_cnt);
314
301
  ca_size_t n_valid = st.slab_elements - masked_cnt;
315
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
302
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
316
303
  if ( __trigger ) {
317
304
  if ( ! op_mask ) {
318
305
  ca_create_mask(co);
@@ -320,11 +307,6 @@ variancep_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
320
307
  }
321
308
  op_mask[out_i] = 1;
322
309
  op[out_i++] = (double) 0.0;
323
- } else if ( n_valid < 1 ) {
324
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
325
- cell): return zero rather than UNDEF, matching pre-migration
326
- behaviour of `cnt > 1 ? formula : 0`. */
327
- op[out_i++] = (double) 0.0;
328
310
  } else {
329
311
  double mean = sum / (double) n_valid;
330
312
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -362,7 +344,7 @@ variancep_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
362
344
  slab_axes, naxes, 0);
363
345
  if ( rc != CA_ITER_OK ) {
364
346
  rb_raise(rb_eRuntimeError,
365
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
347
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
366
348
  }
367
349
  char *p;
368
350
  boolean8_t *m;
@@ -374,7 +356,7 @@ variancep_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
374
356
  ca_size_t masked_cnt = 0;
375
357
  CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, sum, 0.0, __variancep_u32_P1EXPR, masked_cnt);
376
358
  ca_size_t n_valid = st.slab_elements - masked_cnt;
377
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
359
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
378
360
  if ( __trigger ) {
379
361
  if ( ! op_mask ) {
380
362
  ca_create_mask(co);
@@ -382,11 +364,6 @@ variancep_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
382
364
  }
383
365
  op_mask[out_i] = 1;
384
366
  op[out_i++] = (double) 0.0;
385
- } else if ( n_valid < 1 ) {
386
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
387
- cell): return zero rather than UNDEF, matching pre-migration
388
- behaviour of `cnt > 1 ? formula : 0`. */
389
- op[out_i++] = (double) 0.0;
390
367
  } else {
391
368
  double mean = sum / (double) n_valid;
392
369
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -424,7 +401,7 @@ variancep_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
424
401
  slab_axes, naxes, 0);
425
402
  if ( rc != CA_ITER_OK ) {
426
403
  rb_raise(rb_eRuntimeError,
427
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
404
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
428
405
  }
429
406
  char *p;
430
407
  boolean8_t *m;
@@ -436,7 +413,7 @@ variancep_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
436
413
  ca_size_t masked_cnt = 0;
437
414
  CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, sum, 0.0, __variancep_i64_P1EXPR, masked_cnt);
438
415
  ca_size_t n_valid = st.slab_elements - masked_cnt;
439
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
416
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
440
417
  if ( __trigger ) {
441
418
  if ( ! op_mask ) {
442
419
  ca_create_mask(co);
@@ -444,11 +421,6 @@ variancep_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
444
421
  }
445
422
  op_mask[out_i] = 1;
446
423
  op[out_i++] = (double) 0.0;
447
- } else if ( n_valid < 1 ) {
448
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
449
- cell): return zero rather than UNDEF, matching pre-migration
450
- behaviour of `cnt > 1 ? formula : 0`. */
451
- op[out_i++] = (double) 0.0;
452
424
  } else {
453
425
  double mean = sum / (double) n_valid;
454
426
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -486,7 +458,7 @@ variancep_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
486
458
  slab_axes, naxes, 0);
487
459
  if ( rc != CA_ITER_OK ) {
488
460
  rb_raise(rb_eRuntimeError,
489
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
461
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
490
462
  }
491
463
  char *p;
492
464
  boolean8_t *m;
@@ -498,7 +470,7 @@ variancep_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
498
470
  ca_size_t masked_cnt = 0;
499
471
  CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, sum, 0.0, __variancep_u64_P1EXPR, masked_cnt);
500
472
  ca_size_t n_valid = st.slab_elements - masked_cnt;
501
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
473
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
502
474
  if ( __trigger ) {
503
475
  if ( ! op_mask ) {
504
476
  ca_create_mask(co);
@@ -506,11 +478,6 @@ variancep_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
506
478
  }
507
479
  op_mask[out_i] = 1;
508
480
  op[out_i++] = (double) 0.0;
509
- } else if ( n_valid < 1 ) {
510
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
511
- cell): return zero rather than UNDEF, matching pre-migration
512
- behaviour of `cnt > 1 ? formula : 0`. */
513
- op[out_i++] = (double) 0.0;
514
481
  } else {
515
482
  double mean = sum / (double) n_valid;
516
483
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -548,7 +515,7 @@ variancep_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
548
515
  slab_axes, naxes, 0);
549
516
  if ( rc != CA_ITER_OK ) {
550
517
  rb_raise(rb_eRuntimeError,
551
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
518
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
552
519
  }
553
520
  char *p;
554
521
  boolean8_t *m;
@@ -560,7 +527,7 @@ variancep_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
560
527
  ca_size_t masked_cnt = 0;
561
528
  CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, sum, 0.0, __variancep_f32_P1EXPR, masked_cnt);
562
529
  ca_size_t n_valid = st.slab_elements - masked_cnt;
563
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
530
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
564
531
  if ( __trigger ) {
565
532
  if ( ! op_mask ) {
566
533
  ca_create_mask(co);
@@ -568,11 +535,6 @@ variancep_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
568
535
  }
569
536
  op_mask[out_i] = 1;
570
537
  op[out_i++] = (double) 0.0;
571
- } else if ( n_valid < 1 ) {
572
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
573
- cell): return zero rather than UNDEF, matching pre-migration
574
- behaviour of `cnt > 1 ? formula : 0`. */
575
- op[out_i++] = (double) 0.0;
576
538
  } else {
577
539
  double mean = sum / (double) n_valid;
578
540
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -610,7 +572,7 @@ variancep_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
610
572
  slab_axes, naxes, 0);
611
573
  if ( rc != CA_ITER_OK ) {
612
574
  rb_raise(rb_eRuntimeError,
613
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
575
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
614
576
  }
615
577
  char *p;
616
578
  boolean8_t *m;
@@ -622,7 +584,7 @@ variancep_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
622
584
  ca_size_t masked_cnt = 0;
623
585
  CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, sum, 0.0, __variancep_f64_P1EXPR, masked_cnt);
624
586
  ca_size_t n_valid = st.slab_elements - masked_cnt;
625
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
587
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
626
588
  if ( __trigger ) {
627
589
  if ( ! op_mask ) {
628
590
  ca_create_mask(co);
@@ -630,11 +592,6 @@ variancep_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
630
592
  }
631
593
  op_mask[out_i] = 1;
632
594
  op[out_i++] = (double) 0.0;
633
- } else if ( n_valid < 1 ) {
634
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
635
- cell): return zero rather than UNDEF, matching pre-migration
636
- behaviour of `cnt > 1 ? formula : 0`. */
637
- op[out_i++] = (double) 0.0;
638
595
  } else {
639
596
  double mean = sum / (double) n_valid;
640
597
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -672,7 +629,7 @@ variancep_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t n
672
629
  slab_axes, naxes, 0);
673
630
  if ( rc != CA_ITER_OK ) {
674
631
  rb_raise(rb_eRuntimeError,
675
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
632
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
676
633
  }
677
634
  char *p;
678
635
  boolean8_t *m;
@@ -684,7 +641,7 @@ variancep_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t n
684
641
  ca_size_t masked_cnt = 0;
685
642
  CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, cmplx64_t, st, p, m, sum, 0, __variancep_cmplx64_P1EXPR, masked_cnt);
686
643
  ca_size_t n_valid = st.slab_elements - masked_cnt;
687
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
644
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
688
645
  if ( __trigger ) {
689
646
  if ( ! op_mask ) {
690
647
  ca_create_mask(co);
@@ -692,11 +649,6 @@ variancep_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t n
692
649
  }
693
650
  op_mask[out_i] = 1;
694
651
  op[out_i++] = (double) 0.0;
695
- } else if ( n_valid < 1 ) {
696
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
697
- cell): return zero rather than UNDEF, matching pre-migration
698
- behaviour of `cnt > 1 ? formula : 0`. */
699
- op[out_i++] = (double) 0.0;
700
652
  } else {
701
653
  cmplx64_t mean = sum / (cmplx64_t)(double) n_valid;
702
654
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -734,7 +686,7 @@ variancep_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t
734
686
  slab_axes, naxes, 0);
735
687
  if ( rc != CA_ITER_OK ) {
736
688
  rb_raise(rb_eRuntimeError,
737
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
689
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
738
690
  }
739
691
  char *p;
740
692
  boolean8_t *m;
@@ -746,7 +698,7 @@ variancep_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t
746
698
  ca_size_t masked_cnt = 0;
747
699
  CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, cmplx128_t, st, p, m, sum, 0, __variancep_cmplx128_P1EXPR, masked_cnt);
748
700
  ca_size_t n_valid = st.slab_elements - masked_cnt;
749
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
701
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
750
702
  if ( __trigger ) {
751
703
  if ( ! op_mask ) {
752
704
  ca_create_mask(co);
@@ -754,11 +706,6 @@ variancep_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t
754
706
  }
755
707
  op_mask[out_i] = 1;
756
708
  op[out_i++] = (double) 0.0;
757
- } else if ( n_valid < 1 ) {
758
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
759
- cell): return zero rather than UNDEF, matching pre-migration
760
- behaviour of `cnt > 1 ? formula : 0`. */
761
- op[out_i++] = (double) 0.0;
762
709
  } else {
763
710
  cmplx128_t mean = sum / (cmplx128_t)(double) n_valid;
764
711
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -796,7 +743,7 @@ variancep_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
796
743
  slab_axes, naxes, 0);
797
744
  if ( rc != CA_ITER_OK ) {
798
745
  rb_raise(rb_eRuntimeError,
799
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
746
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
800
747
  }
801
748
  char *p;
802
749
  boolean8_t *m;
@@ -808,7 +755,7 @@ variancep_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
808
755
  ca_size_t masked_cnt = 0;
809
756
  CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, sum, 0.0, __variancep_bool_P1EXPR, masked_cnt);
810
757
  ca_size_t n_valid = st.slab_elements - masked_cnt;
811
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
758
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
812
759
  if ( __trigger ) {
813
760
  if ( ! op_mask ) {
814
761
  ca_create_mask(co);
@@ -816,11 +763,6 @@ variancep_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
816
763
  }
817
764
  op_mask[out_i] = 1;
818
765
  op[out_i++] = (double) 0.0;
819
- } else if ( n_valid < 1 ) {
820
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
821
- cell): return zero rather than UNDEF, matching pre-migration
822
- behaviour of `cnt > 1 ? formula : 0`. */
823
- op[out_i++] = (double) 0.0;
824
766
  } else {
825
767
  double mean = sum / (double) n_valid;
826
768
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -844,19 +786,45 @@ variancep_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
844
786
  #undef __variancep_bool_P1EXPR
845
787
  #undef __variancep_bool_P2EXPR
846
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
+
847
802
  static VALUE
848
- variancep_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
803
+ variancep_ki_native_object_body (VALUE arg)
849
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])
850
819
  VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_OBJECT, keep_axis);
851
820
  CArray *co;
852
821
  GetCArray(vout, co);
853
822
  VALUE *op = (VALUE *) co->ptr;
854
- ca_iter_state st;
855
823
  int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
856
824
  slab_axes, naxes, 0);
857
825
  if ( rc != CA_ITER_OK ) {
858
826
  rb_raise(rb_eRuntimeError,
859
- "variancep_ki: kernel_iterator init failed rc=%d", rc);
827
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
860
828
  }
861
829
  char *p;
862
830
  boolean8_t *m;
@@ -868,7 +836,7 @@ variancep_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t na
868
836
  ca_size_t masked_cnt = 0;
869
837
  CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, sum, INT2FIX(0), sum = rb_funcall(sum, rb_intern("+"), 1, v), masked_cnt);
870
838
  ca_size_t n_valid = st.slab_elements - masked_cnt;
871
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
839
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
872
840
  if ( __trigger ) {
873
841
  if ( ! op_mask ) {
874
842
  ca_create_mask(co);
@@ -876,11 +844,6 @@ variancep_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t na
876
844
  }
877
845
  op_mask[out_i] = 1;
878
846
  op[out_i++] = (VALUE) INT2FIX(0);
879
- } else if ( n_valid < 1 ) {
880
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
881
- cell): return zero rather than UNDEF, matching pre-migration
882
- behaviour of `cnt > 1 ? formula : 0`. */
883
- op[out_i++] = (VALUE) INT2FIX(0);
884
847
  } else {
885
848
  VALUE mean = rb_funcall(sum, rb_intern("/"), 1, LONG2NUM(n_valid));
886
849
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -901,8 +864,23 @@ M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)),
901
864
  return (VALUE)(op[0]);
902
865
  }
903
866
  return vout;
867
+ #undef st
904
868
  }
905
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
+
906
884
  VALUE
907
885
  rb_ca_variancep_ki (int argc, VALUE *argv, VALUE self)
908
886
  {
@@ -914,89 +892,101 @@ rb_ca_variancep_ki (int argc, VALUE *argv, VALUE self)
914
892
  int keep_axis = RTEST(rkeep_axis);
915
893
  int8_t slab_axes[CA_RANK_MAX];
916
894
  int8_t naxes = rb_ca_parse_reduce_axes_kw(raxis, src, slab_axes);
917
- ca_size_t min_count = -1;
918
- if ( ! NIL_P(rmin_count) ) {
919
- ca_size_t mc_user = NUM2SIZE(rmin_count);
920
- if ( mc_user < 0 ) {
921
- rb_raise(rb_eArgError, "variancep_ki: :min_count must be non-negative, got %lld", (long long) mc_user);
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);
922
900
  }
923
- /* :min_count = 0 collapses to legacy default (any 1 valid). */
924
- if ( mc_user > 0 ) min_count = mc_user;
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;
925
908
  }
926
909
  VALUE result;
927
910
  if ( argc > 0 ) {
928
- rb_raise(rb_eArgError, "variancep_ki: positional axis arguments are no longer accepted (got %d); use axis: kwarg, e.g. a.variancep(axis: 0) or a.variancep(axis: [0, 1])", argc);
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());
929
912
  }
930
913
  switch ( src->data_type ) {
931
914
  case CA_INT8: {
932
915
  result = variancep_ki_native_i8(self, src, slab_axes, naxes, keep_axis, min_count);
916
+ RB_GC_GUARD(self);
933
917
  break;
934
918
  }
935
919
  case CA_UINT8: {
936
920
  result = variancep_ki_native_u8(self, src, slab_axes, naxes, keep_axis, min_count);
921
+ RB_GC_GUARD(self);
937
922
  break;
938
923
  }
939
924
  case CA_INT16: {
940
925
  result = variancep_ki_native_i16(self, src, slab_axes, naxes, keep_axis, min_count);
926
+ RB_GC_GUARD(self);
941
927
  break;
942
928
  }
943
929
  case CA_UINT16: {
944
930
  result = variancep_ki_native_u16(self, src, slab_axes, naxes, keep_axis, min_count);
931
+ RB_GC_GUARD(self);
945
932
  break;
946
933
  }
947
934
  case CA_INT32: {
948
935
  result = variancep_ki_native_i32(self, src, slab_axes, naxes, keep_axis, min_count);
936
+ RB_GC_GUARD(self);
949
937
  break;
950
938
  }
951
939
  case CA_UINT32: {
952
940
  result = variancep_ki_native_u32(self, src, slab_axes, naxes, keep_axis, min_count);
941
+ RB_GC_GUARD(self);
953
942
  break;
954
943
  }
955
944
  case CA_INT64: {
956
945
  result = variancep_ki_native_i64(self, src, slab_axes, naxes, keep_axis, min_count);
946
+ RB_GC_GUARD(self);
957
947
  break;
958
948
  }
959
949
  case CA_UINT64: {
960
950
  result = variancep_ki_native_u64(self, src, slab_axes, naxes, keep_axis, min_count);
951
+ RB_GC_GUARD(self);
961
952
  break;
962
953
  }
963
954
  case CA_FLOAT32: {
964
955
  result = variancep_ki_native_f32(self, src, slab_axes, naxes, keep_axis, min_count);
956
+ RB_GC_GUARD(self);
965
957
  break;
966
958
  }
967
959
  case CA_FLOAT64: {
968
960
  result = variancep_ki_native_f64(self, src, slab_axes, naxes, keep_axis, min_count);
961
+ RB_GC_GUARD(self);
969
962
  break;
970
963
  }
971
964
  case CA_CMPLX64: {
972
965
  result = variancep_ki_native_cmplx64(self, src, slab_axes, naxes, keep_axis, min_count);
966
+ RB_GC_GUARD(self);
973
967
  break;
974
968
  }
975
969
  case CA_CMPLX128: {
976
970
  result = variancep_ki_native_cmplx128(self, src, slab_axes, naxes, keep_axis, min_count);
971
+ RB_GC_GUARD(self);
977
972
  break;
978
973
  }
979
974
  case CA_BOOLEAN: {
980
975
  result = variancep_ki_native_bool(self, src, slab_axes, naxes, keep_axis, min_count);
976
+ RB_GC_GUARD(self);
981
977
  break;
982
978
  }
983
979
  case CA_OBJECT: {
984
980
  result = variancep_ki_native_object(self, src, slab_axes, naxes, keep_axis, min_count);
981
+ RB_GC_GUARD(self);
985
982
  break;
986
983
  }
987
984
  default:
988
- rb_raise(rb_eCADataTypeError, "variancep_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, cmplx64, cmplx128, bool, object)", ca_type_name[src->data_type]);
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]);
989
987
  }
990
988
  if ( ! NIL_P(rfval) ) {
991
- if ( result == CA_UNDEF ) {
992
- result = rfval;
993
- } else if ( TYPE(result) != T_FLOAT && rb_obj_is_kind_of(result, rb_cCArray) ) {
994
- CArray *cr;
995
- GetCArray(result, cr);
996
- if ( ca_has_mask(cr) ) {
997
- result = rb_ca_mask_fill_copy(result, rfval);
998
- }
999
- }
989
+ result = ca_reduce_fill(result, rfval, fill_whole);
1000
990
  }
1001
991
  return result;
1002
992
  }
@@ -1017,7 +1007,7 @@ variance_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1017
1007
  slab_axes, naxes, 0);
1018
1008
  if ( rc != CA_ITER_OK ) {
1019
1009
  rb_raise(rb_eRuntimeError,
1020
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1010
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1021
1011
  }
1022
1012
  char *p;
1023
1013
  boolean8_t *m;
@@ -1029,7 +1019,7 @@ variance_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1029
1019
  ca_size_t masked_cnt = 0;
1030
1020
  CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, sum, 0.0, __variance_i8_P1EXPR, masked_cnt);
1031
1021
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1032
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1022
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1033
1023
  if ( __trigger ) {
1034
1024
  if ( ! op_mask ) {
1035
1025
  ca_create_mask(co);
@@ -1037,11 +1027,6 @@ variance_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1037
1027
  }
1038
1028
  op_mask[out_i] = 1;
1039
1029
  op[out_i++] = (double) 0.0;
1040
- } else if ( n_valid < 2 ) {
1041
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1042
- cell): return zero rather than UNDEF, matching pre-migration
1043
- behaviour of `cnt > 1 ? formula : 0`. */
1044
- op[out_i++] = (double) 0.0;
1045
1030
  } else {
1046
1031
  double mean = sum / (double) n_valid;
1047
1032
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1079,7 +1064,7 @@ variance_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1079
1064
  slab_axes, naxes, 0);
1080
1065
  if ( rc != CA_ITER_OK ) {
1081
1066
  rb_raise(rb_eRuntimeError,
1082
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1067
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1083
1068
  }
1084
1069
  char *p;
1085
1070
  boolean8_t *m;
@@ -1091,7 +1076,7 @@ variance_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1091
1076
  ca_size_t masked_cnt = 0;
1092
1077
  CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, sum, 0.0, __variance_u8_P1EXPR, masked_cnt);
1093
1078
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1094
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1079
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1095
1080
  if ( __trigger ) {
1096
1081
  if ( ! op_mask ) {
1097
1082
  ca_create_mask(co);
@@ -1099,11 +1084,6 @@ variance_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1099
1084
  }
1100
1085
  op_mask[out_i] = 1;
1101
1086
  op[out_i++] = (double) 0.0;
1102
- } else if ( n_valid < 2 ) {
1103
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1104
- cell): return zero rather than UNDEF, matching pre-migration
1105
- behaviour of `cnt > 1 ? formula : 0`. */
1106
- op[out_i++] = (double) 0.0;
1107
1087
  } else {
1108
1088
  double mean = sum / (double) n_valid;
1109
1089
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1141,7 +1121,7 @@ variance_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1141
1121
  slab_axes, naxes, 0);
1142
1122
  if ( rc != CA_ITER_OK ) {
1143
1123
  rb_raise(rb_eRuntimeError,
1144
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1124
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1145
1125
  }
1146
1126
  char *p;
1147
1127
  boolean8_t *m;
@@ -1153,7 +1133,7 @@ variance_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1153
1133
  ca_size_t masked_cnt = 0;
1154
1134
  CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, sum, 0.0, __variance_i16_P1EXPR, masked_cnt);
1155
1135
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1156
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1136
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1157
1137
  if ( __trigger ) {
1158
1138
  if ( ! op_mask ) {
1159
1139
  ca_create_mask(co);
@@ -1161,11 +1141,6 @@ variance_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1161
1141
  }
1162
1142
  op_mask[out_i] = 1;
1163
1143
  op[out_i++] = (double) 0.0;
1164
- } else if ( n_valid < 2 ) {
1165
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1166
- cell): return zero rather than UNDEF, matching pre-migration
1167
- behaviour of `cnt > 1 ? formula : 0`. */
1168
- op[out_i++] = (double) 0.0;
1169
1144
  } else {
1170
1145
  double mean = sum / (double) n_valid;
1171
1146
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1203,7 +1178,7 @@ variance_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1203
1178
  slab_axes, naxes, 0);
1204
1179
  if ( rc != CA_ITER_OK ) {
1205
1180
  rb_raise(rb_eRuntimeError,
1206
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1181
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1207
1182
  }
1208
1183
  char *p;
1209
1184
  boolean8_t *m;
@@ -1215,7 +1190,7 @@ variance_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1215
1190
  ca_size_t masked_cnt = 0;
1216
1191
  CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, sum, 0.0, __variance_u16_P1EXPR, masked_cnt);
1217
1192
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1218
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1193
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1219
1194
  if ( __trigger ) {
1220
1195
  if ( ! op_mask ) {
1221
1196
  ca_create_mask(co);
@@ -1223,11 +1198,6 @@ variance_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1223
1198
  }
1224
1199
  op_mask[out_i] = 1;
1225
1200
  op[out_i++] = (double) 0.0;
1226
- } else if ( n_valid < 2 ) {
1227
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1228
- cell): return zero rather than UNDEF, matching pre-migration
1229
- behaviour of `cnt > 1 ? formula : 0`. */
1230
- op[out_i++] = (double) 0.0;
1231
1201
  } else {
1232
1202
  double mean = sum / (double) n_valid;
1233
1203
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1265,7 +1235,7 @@ variance_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1265
1235
  slab_axes, naxes, 0);
1266
1236
  if ( rc != CA_ITER_OK ) {
1267
1237
  rb_raise(rb_eRuntimeError,
1268
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1238
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1269
1239
  }
1270
1240
  char *p;
1271
1241
  boolean8_t *m;
@@ -1277,7 +1247,7 @@ variance_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1277
1247
  ca_size_t masked_cnt = 0;
1278
1248
  CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, sum, 0.0, __variance_i32_P1EXPR, masked_cnt);
1279
1249
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1280
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1250
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1281
1251
  if ( __trigger ) {
1282
1252
  if ( ! op_mask ) {
1283
1253
  ca_create_mask(co);
@@ -1285,11 +1255,6 @@ variance_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1285
1255
  }
1286
1256
  op_mask[out_i] = 1;
1287
1257
  op[out_i++] = (double) 0.0;
1288
- } else if ( n_valid < 2 ) {
1289
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1290
- cell): return zero rather than UNDEF, matching pre-migration
1291
- behaviour of `cnt > 1 ? formula : 0`. */
1292
- op[out_i++] = (double) 0.0;
1293
1258
  } else {
1294
1259
  double mean = sum / (double) n_valid;
1295
1260
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1327,7 +1292,7 @@ variance_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1327
1292
  slab_axes, naxes, 0);
1328
1293
  if ( rc != CA_ITER_OK ) {
1329
1294
  rb_raise(rb_eRuntimeError,
1330
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1295
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1331
1296
  }
1332
1297
  char *p;
1333
1298
  boolean8_t *m;
@@ -1339,7 +1304,7 @@ variance_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1339
1304
  ca_size_t masked_cnt = 0;
1340
1305
  CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, sum, 0.0, __variance_u32_P1EXPR, masked_cnt);
1341
1306
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1342
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1307
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1343
1308
  if ( __trigger ) {
1344
1309
  if ( ! op_mask ) {
1345
1310
  ca_create_mask(co);
@@ -1347,11 +1312,6 @@ variance_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1347
1312
  }
1348
1313
  op_mask[out_i] = 1;
1349
1314
  op[out_i++] = (double) 0.0;
1350
- } else if ( n_valid < 2 ) {
1351
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1352
- cell): return zero rather than UNDEF, matching pre-migration
1353
- behaviour of `cnt > 1 ? formula : 0`. */
1354
- op[out_i++] = (double) 0.0;
1355
1315
  } else {
1356
1316
  double mean = sum / (double) n_valid;
1357
1317
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1389,7 +1349,7 @@ variance_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1389
1349
  slab_axes, naxes, 0);
1390
1350
  if ( rc != CA_ITER_OK ) {
1391
1351
  rb_raise(rb_eRuntimeError,
1392
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1352
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1393
1353
  }
1394
1354
  char *p;
1395
1355
  boolean8_t *m;
@@ -1401,7 +1361,7 @@ variance_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1401
1361
  ca_size_t masked_cnt = 0;
1402
1362
  CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, sum, 0.0, __variance_i64_P1EXPR, masked_cnt);
1403
1363
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1404
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1364
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1405
1365
  if ( __trigger ) {
1406
1366
  if ( ! op_mask ) {
1407
1367
  ca_create_mask(co);
@@ -1409,11 +1369,6 @@ variance_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1409
1369
  }
1410
1370
  op_mask[out_i] = 1;
1411
1371
  op[out_i++] = (double) 0.0;
1412
- } else if ( n_valid < 2 ) {
1413
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1414
- cell): return zero rather than UNDEF, matching pre-migration
1415
- behaviour of `cnt > 1 ? formula : 0`. */
1416
- op[out_i++] = (double) 0.0;
1417
1372
  } else {
1418
1373
  double mean = sum / (double) n_valid;
1419
1374
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1451,7 +1406,7 @@ variance_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1451
1406
  slab_axes, naxes, 0);
1452
1407
  if ( rc != CA_ITER_OK ) {
1453
1408
  rb_raise(rb_eRuntimeError,
1454
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1409
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1455
1410
  }
1456
1411
  char *p;
1457
1412
  boolean8_t *m;
@@ -1463,7 +1418,7 @@ variance_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1463
1418
  ca_size_t masked_cnt = 0;
1464
1419
  CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, sum, 0.0, __variance_u64_P1EXPR, masked_cnt);
1465
1420
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1466
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1421
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1467
1422
  if ( __trigger ) {
1468
1423
  if ( ! op_mask ) {
1469
1424
  ca_create_mask(co);
@@ -1471,11 +1426,6 @@ variance_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1471
1426
  }
1472
1427
  op_mask[out_i] = 1;
1473
1428
  op[out_i++] = (double) 0.0;
1474
- } else if ( n_valid < 2 ) {
1475
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1476
- cell): return zero rather than UNDEF, matching pre-migration
1477
- behaviour of `cnt > 1 ? formula : 0`. */
1478
- op[out_i++] = (double) 0.0;
1479
1429
  } else {
1480
1430
  double mean = sum / (double) n_valid;
1481
1431
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1513,7 +1463,7 @@ variance_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1513
1463
  slab_axes, naxes, 0);
1514
1464
  if ( rc != CA_ITER_OK ) {
1515
1465
  rb_raise(rb_eRuntimeError,
1516
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1466
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1517
1467
  }
1518
1468
  char *p;
1519
1469
  boolean8_t *m;
@@ -1525,7 +1475,7 @@ variance_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1525
1475
  ca_size_t masked_cnt = 0;
1526
1476
  CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, sum, 0.0, __variance_f32_P1EXPR, masked_cnt);
1527
1477
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1528
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1478
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1529
1479
  if ( __trigger ) {
1530
1480
  if ( ! op_mask ) {
1531
1481
  ca_create_mask(co);
@@ -1533,11 +1483,6 @@ variance_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1533
1483
  }
1534
1484
  op_mask[out_i] = 1;
1535
1485
  op[out_i++] = (double) 0.0;
1536
- } else if ( n_valid < 2 ) {
1537
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1538
- cell): return zero rather than UNDEF, matching pre-migration
1539
- behaviour of `cnt > 1 ? formula : 0`. */
1540
- op[out_i++] = (double) 0.0;
1541
1486
  } else {
1542
1487
  double mean = sum / (double) n_valid;
1543
1488
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1575,7 +1520,7 @@ variance_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1575
1520
  slab_axes, naxes, 0);
1576
1521
  if ( rc != CA_ITER_OK ) {
1577
1522
  rb_raise(rb_eRuntimeError,
1578
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1523
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1579
1524
  }
1580
1525
  char *p;
1581
1526
  boolean8_t *m;
@@ -1587,7 +1532,7 @@ variance_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1587
1532
  ca_size_t masked_cnt = 0;
1588
1533
  CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, sum, 0.0, __variance_f64_P1EXPR, masked_cnt);
1589
1534
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1590
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1535
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1591
1536
  if ( __trigger ) {
1592
1537
  if ( ! op_mask ) {
1593
1538
  ca_create_mask(co);
@@ -1595,11 +1540,6 @@ variance_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
1595
1540
  }
1596
1541
  op_mask[out_i] = 1;
1597
1542
  op[out_i++] = (double) 0.0;
1598
- } else if ( n_valid < 2 ) {
1599
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1600
- cell): return zero rather than UNDEF, matching pre-migration
1601
- behaviour of `cnt > 1 ? formula : 0`. */
1602
- op[out_i++] = (double) 0.0;
1603
1543
  } else {
1604
1544
  double mean = sum / (double) n_valid;
1605
1545
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1637,7 +1577,7 @@ variance_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t na
1637
1577
  slab_axes, naxes, 0);
1638
1578
  if ( rc != CA_ITER_OK ) {
1639
1579
  rb_raise(rb_eRuntimeError,
1640
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1580
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1641
1581
  }
1642
1582
  char *p;
1643
1583
  boolean8_t *m;
@@ -1649,7 +1589,7 @@ variance_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t na
1649
1589
  ca_size_t masked_cnt = 0;
1650
1590
  CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, cmplx64_t, st, p, m, sum, 0, __variance_cmplx64_P1EXPR, masked_cnt);
1651
1591
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1652
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1592
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1653
1593
  if ( __trigger ) {
1654
1594
  if ( ! op_mask ) {
1655
1595
  ca_create_mask(co);
@@ -1657,11 +1597,6 @@ variance_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t na
1657
1597
  }
1658
1598
  op_mask[out_i] = 1;
1659
1599
  op[out_i++] = (double) 0.0;
1660
- } else if ( n_valid < 2 ) {
1661
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1662
- cell): return zero rather than UNDEF, matching pre-migration
1663
- behaviour of `cnt > 1 ? formula : 0`. */
1664
- op[out_i++] = (double) 0.0;
1665
1600
  } else {
1666
1601
  cmplx64_t mean = sum / (cmplx64_t)(double) n_valid;
1667
1602
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1699,7 +1634,7 @@ variance_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t n
1699
1634
  slab_axes, naxes, 0);
1700
1635
  if ( rc != CA_ITER_OK ) {
1701
1636
  rb_raise(rb_eRuntimeError,
1702
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1637
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1703
1638
  }
1704
1639
  char *p;
1705
1640
  boolean8_t *m;
@@ -1711,7 +1646,7 @@ variance_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t n
1711
1646
  ca_size_t masked_cnt = 0;
1712
1647
  CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, cmplx128_t, st, p, m, sum, 0, __variance_cmplx128_P1EXPR, masked_cnt);
1713
1648
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1714
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1649
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1715
1650
  if ( __trigger ) {
1716
1651
  if ( ! op_mask ) {
1717
1652
  ca_create_mask(co);
@@ -1719,11 +1654,6 @@ variance_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t n
1719
1654
  }
1720
1655
  op_mask[out_i] = 1;
1721
1656
  op[out_i++] = (double) 0.0;
1722
- } else if ( n_valid < 2 ) {
1723
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1724
- cell): return zero rather than UNDEF, matching pre-migration
1725
- behaviour of `cnt > 1 ? formula : 0`. */
1726
- op[out_i++] = (double) 0.0;
1727
1657
  } else {
1728
1658
  cmplx128_t mean = sum / (cmplx128_t)(double) n_valid;
1729
1659
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1761,7 +1691,7 @@ variance_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
1761
1691
  slab_axes, naxes, 0);
1762
1692
  if ( rc != CA_ITER_OK ) {
1763
1693
  rb_raise(rb_eRuntimeError,
1764
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1694
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1765
1695
  }
1766
1696
  char *p;
1767
1697
  boolean8_t *m;
@@ -1773,7 +1703,7 @@ variance_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
1773
1703
  ca_size_t masked_cnt = 0;
1774
1704
  CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, sum, 0.0, __variance_bool_P1EXPR, masked_cnt);
1775
1705
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1776
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1706
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1777
1707
  if ( __trigger ) {
1778
1708
  if ( ! op_mask ) {
1779
1709
  ca_create_mask(co);
@@ -1781,11 +1711,6 @@ variance_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
1781
1711
  }
1782
1712
  op_mask[out_i] = 1;
1783
1713
  op[out_i++] = (double) 0.0;
1784
- } else if ( n_valid < 2 ) {
1785
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1786
- cell): return zero rather than UNDEF, matching pre-migration
1787
- behaviour of `cnt > 1 ? formula : 0`. */
1788
- op[out_i++] = (double) 0.0;
1789
1714
  } else {
1790
1715
  double mean = sum / (double) n_valid;
1791
1716
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1809,19 +1734,45 @@ variance_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
1809
1734
  #undef __variance_bool_P1EXPR
1810
1735
  #undef __variance_bool_P2EXPR
1811
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
+
1812
1750
  static VALUE
1813
- variance_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
1751
+ variance_ki_native_object_body (VALUE arg)
1814
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])
1815
1767
  VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_OBJECT, keep_axis);
1816
1768
  CArray *co;
1817
1769
  GetCArray(vout, co);
1818
1770
  VALUE *op = (VALUE *) co->ptr;
1819
- ca_iter_state st;
1820
1771
  int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
1821
1772
  slab_axes, naxes, 0);
1822
1773
  if ( rc != CA_ITER_OK ) {
1823
1774
  rb_raise(rb_eRuntimeError,
1824
- "variance_ki: kernel_iterator init failed rc=%d", rc);
1775
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1825
1776
  }
1826
1777
  char *p;
1827
1778
  boolean8_t *m;
@@ -1833,7 +1784,7 @@ variance_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t nax
1833
1784
  ca_size_t masked_cnt = 0;
1834
1785
  CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, sum, INT2FIX(0), sum = rb_funcall(sum, rb_intern("+"), 1, v), masked_cnt);
1835
1786
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1836
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1787
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
1837
1788
  if ( __trigger ) {
1838
1789
  if ( ! op_mask ) {
1839
1790
  ca_create_mask(co);
@@ -1841,11 +1792,6 @@ variance_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t nax
1841
1792
  }
1842
1793
  op_mask[out_i] = 1;
1843
1794
  op[out_i++] = (VALUE) INT2FIX(0);
1844
- } else if ( n_valid < 2 ) {
1845
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
1846
- cell): return zero rather than UNDEF, matching pre-migration
1847
- behaviour of `cnt > 1 ? formula : 0`. */
1848
- op[out_i++] = (VALUE) INT2FIX(0);
1849
1795
  } else {
1850
1796
  VALUE mean = rb_funcall(sum, rb_intern("/"), 1, LONG2NUM(n_valid));
1851
1797
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -1866,8 +1812,23 @@ M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)),
1866
1812
  return (VALUE)(op[0]);
1867
1813
  }
1868
1814
  return vout;
1815
+ #undef st
1869
1816
  }
1870
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
+
1871
1832
  VALUE
1872
1833
  rb_ca_variance_ki (int argc, VALUE *argv, VALUE self)
1873
1834
  {
@@ -1879,89 +1840,101 @@ rb_ca_variance_ki (int argc, VALUE *argv, VALUE self)
1879
1840
  int keep_axis = RTEST(rkeep_axis);
1880
1841
  int8_t slab_axes[CA_RANK_MAX];
1881
1842
  int8_t naxes = rb_ca_parse_reduce_axes_kw(raxis, src, slab_axes);
1882
- ca_size_t min_count = -1;
1883
- if ( ! NIL_P(rmin_count) ) {
1884
- ca_size_t mc_user = NUM2SIZE(rmin_count);
1885
- if ( mc_user < 0 ) {
1886
- rb_raise(rb_eArgError, "variance_ki: :min_count must be non-negative, got %lld", (long long) mc_user);
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);
1887
1848
  }
1888
- /* :min_count = 0 collapses to legacy default (any 1 valid). */
1889
- if ( mc_user > 0 ) min_count = mc_user;
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;
1890
1856
  }
1891
1857
  VALUE result;
1892
1858
  if ( argc > 0 ) {
1893
- rb_raise(rb_eArgError, "variance_ki: positional axis arguments are no longer accepted (got %d); use axis: kwarg, e.g. a.variance(axis: 0) or a.variance(axis: [0, 1])", argc);
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());
1894
1860
  }
1895
1861
  switch ( src->data_type ) {
1896
1862
  case CA_INT8: {
1897
1863
  result = variance_ki_native_i8(self, src, slab_axes, naxes, keep_axis, min_count);
1864
+ RB_GC_GUARD(self);
1898
1865
  break;
1899
1866
  }
1900
1867
  case CA_UINT8: {
1901
1868
  result = variance_ki_native_u8(self, src, slab_axes, naxes, keep_axis, min_count);
1869
+ RB_GC_GUARD(self);
1902
1870
  break;
1903
1871
  }
1904
1872
  case CA_INT16: {
1905
1873
  result = variance_ki_native_i16(self, src, slab_axes, naxes, keep_axis, min_count);
1874
+ RB_GC_GUARD(self);
1906
1875
  break;
1907
1876
  }
1908
1877
  case CA_UINT16: {
1909
1878
  result = variance_ki_native_u16(self, src, slab_axes, naxes, keep_axis, min_count);
1879
+ RB_GC_GUARD(self);
1910
1880
  break;
1911
1881
  }
1912
1882
  case CA_INT32: {
1913
1883
  result = variance_ki_native_i32(self, src, slab_axes, naxes, keep_axis, min_count);
1884
+ RB_GC_GUARD(self);
1914
1885
  break;
1915
1886
  }
1916
1887
  case CA_UINT32: {
1917
1888
  result = variance_ki_native_u32(self, src, slab_axes, naxes, keep_axis, min_count);
1889
+ RB_GC_GUARD(self);
1918
1890
  break;
1919
1891
  }
1920
1892
  case CA_INT64: {
1921
1893
  result = variance_ki_native_i64(self, src, slab_axes, naxes, keep_axis, min_count);
1894
+ RB_GC_GUARD(self);
1922
1895
  break;
1923
1896
  }
1924
1897
  case CA_UINT64: {
1925
1898
  result = variance_ki_native_u64(self, src, slab_axes, naxes, keep_axis, min_count);
1899
+ RB_GC_GUARD(self);
1926
1900
  break;
1927
1901
  }
1928
1902
  case CA_FLOAT32: {
1929
1903
  result = variance_ki_native_f32(self, src, slab_axes, naxes, keep_axis, min_count);
1904
+ RB_GC_GUARD(self);
1930
1905
  break;
1931
1906
  }
1932
1907
  case CA_FLOAT64: {
1933
1908
  result = variance_ki_native_f64(self, src, slab_axes, naxes, keep_axis, min_count);
1909
+ RB_GC_GUARD(self);
1934
1910
  break;
1935
1911
  }
1936
1912
  case CA_CMPLX64: {
1937
1913
  result = variance_ki_native_cmplx64(self, src, slab_axes, naxes, keep_axis, min_count);
1914
+ RB_GC_GUARD(self);
1938
1915
  break;
1939
1916
  }
1940
1917
  case CA_CMPLX128: {
1941
1918
  result = variance_ki_native_cmplx128(self, src, slab_axes, naxes, keep_axis, min_count);
1919
+ RB_GC_GUARD(self);
1942
1920
  break;
1943
1921
  }
1944
1922
  case CA_BOOLEAN: {
1945
1923
  result = variance_ki_native_bool(self, src, slab_axes, naxes, keep_axis, min_count);
1924
+ RB_GC_GUARD(self);
1946
1925
  break;
1947
1926
  }
1948
1927
  case CA_OBJECT: {
1949
1928
  result = variance_ki_native_object(self, src, slab_axes, naxes, keep_axis, min_count);
1929
+ RB_GC_GUARD(self);
1950
1930
  break;
1951
1931
  }
1952
1932
  default:
1953
- rb_raise(rb_eCADataTypeError, "variance_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, cmplx64, cmplx128, bool, object)", ca_type_name[src->data_type]);
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]);
1954
1935
  }
1955
1936
  if ( ! NIL_P(rfval) ) {
1956
- if ( result == CA_UNDEF ) {
1957
- result = rfval;
1958
- } else if ( TYPE(result) != T_FLOAT && rb_obj_is_kind_of(result, rb_cCArray) ) {
1959
- CArray *cr;
1960
- GetCArray(result, cr);
1961
- if ( ca_has_mask(cr) ) {
1962
- result = rb_ca_mask_fill_copy(result, rfval);
1963
- }
1964
- }
1937
+ result = ca_reduce_fill(result, rfval, fill_whole);
1965
1938
  }
1966
1939
  return result;
1967
1940
  }
@@ -1982,7 +1955,7 @@ stddevp_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
1982
1955
  slab_axes, naxes, 0);
1983
1956
  if ( rc != CA_ITER_OK ) {
1984
1957
  rb_raise(rb_eRuntimeError,
1985
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
1958
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
1986
1959
  }
1987
1960
  char *p;
1988
1961
  boolean8_t *m;
@@ -1994,7 +1967,7 @@ stddevp_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
1994
1967
  ca_size_t masked_cnt = 0;
1995
1968
  CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, sum, 0.0, __stddevp_i8_P1EXPR, masked_cnt);
1996
1969
  ca_size_t n_valid = st.slab_elements - masked_cnt;
1997
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
1970
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
1998
1971
  if ( __trigger ) {
1999
1972
  if ( ! op_mask ) {
2000
1973
  ca_create_mask(co);
@@ -2002,18 +1975,13 @@ stddevp_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
2002
1975
  }
2003
1976
  op_mask[out_i] = 1;
2004
1977
  op[out_i++] = (double) 0.0;
2005
- } else if ( n_valid < 1 ) {
2006
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2007
- cell): return zero rather than UNDEF, matching pre-migration
2008
- behaviour of `cnt > 1 ? formula : 0`. */
2009
- op[out_i++] = (double) 0.0;
2010
1978
  } else {
2011
1979
  double mean = sum / (double) n_valid;
2012
1980
  /* Pass 2: M2 = Σ (x - mean)² */
2013
1981
  double M2 = 0.0;
2014
1982
  ca_size_t __unused_mc = 0;
2015
1983
  CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, M2, 0.0, __stddevp_i8_P2EXPR, __unused_mc);
2016
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
1984
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2017
1985
  }
2018
1986
  }
2019
1987
  ca_iter_state_finish(&st);
@@ -2044,7 +2012,7 @@ stddevp_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
2044
2012
  slab_axes, naxes, 0);
2045
2013
  if ( rc != CA_ITER_OK ) {
2046
2014
  rb_raise(rb_eRuntimeError,
2047
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2015
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2048
2016
  }
2049
2017
  char *p;
2050
2018
  boolean8_t *m;
@@ -2056,7 +2024,7 @@ stddevp_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
2056
2024
  ca_size_t masked_cnt = 0;
2057
2025
  CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, sum, 0.0, __stddevp_u8_P1EXPR, masked_cnt);
2058
2026
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2059
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2027
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2060
2028
  if ( __trigger ) {
2061
2029
  if ( ! op_mask ) {
2062
2030
  ca_create_mask(co);
@@ -2064,18 +2032,13 @@ stddevp_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
2064
2032
  }
2065
2033
  op_mask[out_i] = 1;
2066
2034
  op[out_i++] = (double) 0.0;
2067
- } else if ( n_valid < 1 ) {
2068
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2069
- cell): return zero rather than UNDEF, matching pre-migration
2070
- behaviour of `cnt > 1 ? formula : 0`. */
2071
- op[out_i++] = (double) 0.0;
2072
2035
  } else {
2073
2036
  double mean = sum / (double) n_valid;
2074
2037
  /* Pass 2: M2 = Σ (x - mean)² */
2075
2038
  double M2 = 0.0;
2076
2039
  ca_size_t __unused_mc = 0;
2077
2040
  CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, M2, 0.0, __stddevp_u8_P2EXPR, __unused_mc);
2078
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2041
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2079
2042
  }
2080
2043
  }
2081
2044
  ca_iter_state_finish(&st);
@@ -2106,7 +2069,7 @@ stddevp_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2106
2069
  slab_axes, naxes, 0);
2107
2070
  if ( rc != CA_ITER_OK ) {
2108
2071
  rb_raise(rb_eRuntimeError,
2109
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2072
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2110
2073
  }
2111
2074
  char *p;
2112
2075
  boolean8_t *m;
@@ -2118,7 +2081,7 @@ stddevp_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2118
2081
  ca_size_t masked_cnt = 0;
2119
2082
  CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, sum, 0.0, __stddevp_i16_P1EXPR, masked_cnt);
2120
2083
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2121
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2084
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2122
2085
  if ( __trigger ) {
2123
2086
  if ( ! op_mask ) {
2124
2087
  ca_create_mask(co);
@@ -2126,18 +2089,13 @@ stddevp_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2126
2089
  }
2127
2090
  op_mask[out_i] = 1;
2128
2091
  op[out_i++] = (double) 0.0;
2129
- } else if ( n_valid < 1 ) {
2130
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2131
- cell): return zero rather than UNDEF, matching pre-migration
2132
- behaviour of `cnt > 1 ? formula : 0`. */
2133
- op[out_i++] = (double) 0.0;
2134
2092
  } else {
2135
2093
  double mean = sum / (double) n_valid;
2136
2094
  /* Pass 2: M2 = Σ (x - mean)² */
2137
2095
  double M2 = 0.0;
2138
2096
  ca_size_t __unused_mc = 0;
2139
2097
  CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, M2, 0.0, __stddevp_i16_P2EXPR, __unused_mc);
2140
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2098
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2141
2099
  }
2142
2100
  }
2143
2101
  ca_iter_state_finish(&st);
@@ -2168,7 +2126,7 @@ stddevp_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2168
2126
  slab_axes, naxes, 0);
2169
2127
  if ( rc != CA_ITER_OK ) {
2170
2128
  rb_raise(rb_eRuntimeError,
2171
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2129
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2172
2130
  }
2173
2131
  char *p;
2174
2132
  boolean8_t *m;
@@ -2180,7 +2138,7 @@ stddevp_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2180
2138
  ca_size_t masked_cnt = 0;
2181
2139
  CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, sum, 0.0, __stddevp_u16_P1EXPR, masked_cnt);
2182
2140
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2183
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2141
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2184
2142
  if ( __trigger ) {
2185
2143
  if ( ! op_mask ) {
2186
2144
  ca_create_mask(co);
@@ -2188,18 +2146,13 @@ stddevp_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2188
2146
  }
2189
2147
  op_mask[out_i] = 1;
2190
2148
  op[out_i++] = (double) 0.0;
2191
- } else if ( n_valid < 1 ) {
2192
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2193
- cell): return zero rather than UNDEF, matching pre-migration
2194
- behaviour of `cnt > 1 ? formula : 0`. */
2195
- op[out_i++] = (double) 0.0;
2196
2149
  } else {
2197
2150
  double mean = sum / (double) n_valid;
2198
2151
  /* Pass 2: M2 = Σ (x - mean)² */
2199
2152
  double M2 = 0.0;
2200
2153
  ca_size_t __unused_mc = 0;
2201
2154
  CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, M2, 0.0, __stddevp_u16_P2EXPR, __unused_mc);
2202
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2155
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2203
2156
  }
2204
2157
  }
2205
2158
  ca_iter_state_finish(&st);
@@ -2230,7 +2183,7 @@ stddevp_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2230
2183
  slab_axes, naxes, 0);
2231
2184
  if ( rc != CA_ITER_OK ) {
2232
2185
  rb_raise(rb_eRuntimeError,
2233
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2186
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2234
2187
  }
2235
2188
  char *p;
2236
2189
  boolean8_t *m;
@@ -2242,7 +2195,7 @@ stddevp_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2242
2195
  ca_size_t masked_cnt = 0;
2243
2196
  CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, sum, 0.0, __stddevp_i32_P1EXPR, masked_cnt);
2244
2197
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2245
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2198
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2246
2199
  if ( __trigger ) {
2247
2200
  if ( ! op_mask ) {
2248
2201
  ca_create_mask(co);
@@ -2250,18 +2203,13 @@ stddevp_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2250
2203
  }
2251
2204
  op_mask[out_i] = 1;
2252
2205
  op[out_i++] = (double) 0.0;
2253
- } else if ( n_valid < 1 ) {
2254
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2255
- cell): return zero rather than UNDEF, matching pre-migration
2256
- behaviour of `cnt > 1 ? formula : 0`. */
2257
- op[out_i++] = (double) 0.0;
2258
2206
  } else {
2259
2207
  double mean = sum / (double) n_valid;
2260
2208
  /* Pass 2: M2 = Σ (x - mean)² */
2261
2209
  double M2 = 0.0;
2262
2210
  ca_size_t __unused_mc = 0;
2263
2211
  CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, M2, 0.0, __stddevp_i32_P2EXPR, __unused_mc);
2264
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2212
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2265
2213
  }
2266
2214
  }
2267
2215
  ca_iter_state_finish(&st);
@@ -2292,7 +2240,7 @@ stddevp_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2292
2240
  slab_axes, naxes, 0);
2293
2241
  if ( rc != CA_ITER_OK ) {
2294
2242
  rb_raise(rb_eRuntimeError,
2295
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2243
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2296
2244
  }
2297
2245
  char *p;
2298
2246
  boolean8_t *m;
@@ -2304,7 +2252,7 @@ stddevp_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2304
2252
  ca_size_t masked_cnt = 0;
2305
2253
  CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, sum, 0.0, __stddevp_u32_P1EXPR, masked_cnt);
2306
2254
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2307
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2255
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2308
2256
  if ( __trigger ) {
2309
2257
  if ( ! op_mask ) {
2310
2258
  ca_create_mask(co);
@@ -2312,18 +2260,13 @@ stddevp_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2312
2260
  }
2313
2261
  op_mask[out_i] = 1;
2314
2262
  op[out_i++] = (double) 0.0;
2315
- } else if ( n_valid < 1 ) {
2316
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2317
- cell): return zero rather than UNDEF, matching pre-migration
2318
- behaviour of `cnt > 1 ? formula : 0`. */
2319
- op[out_i++] = (double) 0.0;
2320
2263
  } else {
2321
2264
  double mean = sum / (double) n_valid;
2322
2265
  /* Pass 2: M2 = Σ (x - mean)² */
2323
2266
  double M2 = 0.0;
2324
2267
  ca_size_t __unused_mc = 0;
2325
2268
  CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, M2, 0.0, __stddevp_u32_P2EXPR, __unused_mc);
2326
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2269
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2327
2270
  }
2328
2271
  }
2329
2272
  ca_iter_state_finish(&st);
@@ -2354,7 +2297,7 @@ stddevp_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2354
2297
  slab_axes, naxes, 0);
2355
2298
  if ( rc != CA_ITER_OK ) {
2356
2299
  rb_raise(rb_eRuntimeError,
2357
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2300
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2358
2301
  }
2359
2302
  char *p;
2360
2303
  boolean8_t *m;
@@ -2366,7 +2309,7 @@ stddevp_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2366
2309
  ca_size_t masked_cnt = 0;
2367
2310
  CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, sum, 0.0, __stddevp_i64_P1EXPR, masked_cnt);
2368
2311
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2369
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2312
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2370
2313
  if ( __trigger ) {
2371
2314
  if ( ! op_mask ) {
2372
2315
  ca_create_mask(co);
@@ -2374,18 +2317,13 @@ stddevp_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2374
2317
  }
2375
2318
  op_mask[out_i] = 1;
2376
2319
  op[out_i++] = (double) 0.0;
2377
- } else if ( n_valid < 1 ) {
2378
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2379
- cell): return zero rather than UNDEF, matching pre-migration
2380
- behaviour of `cnt > 1 ? formula : 0`. */
2381
- op[out_i++] = (double) 0.0;
2382
2320
  } else {
2383
2321
  double mean = sum / (double) n_valid;
2384
2322
  /* Pass 2: M2 = Σ (x - mean)² */
2385
2323
  double M2 = 0.0;
2386
2324
  ca_size_t __unused_mc = 0;
2387
2325
  CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, M2, 0.0, __stddevp_i64_P2EXPR, __unused_mc);
2388
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2326
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2389
2327
  }
2390
2328
  }
2391
2329
  ca_iter_state_finish(&st);
@@ -2416,7 +2354,7 @@ stddevp_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2416
2354
  slab_axes, naxes, 0);
2417
2355
  if ( rc != CA_ITER_OK ) {
2418
2356
  rb_raise(rb_eRuntimeError,
2419
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2357
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2420
2358
  }
2421
2359
  char *p;
2422
2360
  boolean8_t *m;
@@ -2428,7 +2366,7 @@ stddevp_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2428
2366
  ca_size_t masked_cnt = 0;
2429
2367
  CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, sum, 0.0, __stddevp_u64_P1EXPR, masked_cnt);
2430
2368
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2431
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2369
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2432
2370
  if ( __trigger ) {
2433
2371
  if ( ! op_mask ) {
2434
2372
  ca_create_mask(co);
@@ -2436,18 +2374,13 @@ stddevp_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2436
2374
  }
2437
2375
  op_mask[out_i] = 1;
2438
2376
  op[out_i++] = (double) 0.0;
2439
- } else if ( n_valid < 1 ) {
2440
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2441
- cell): return zero rather than UNDEF, matching pre-migration
2442
- behaviour of `cnt > 1 ? formula : 0`. */
2443
- op[out_i++] = (double) 0.0;
2444
2377
  } else {
2445
2378
  double mean = sum / (double) n_valid;
2446
2379
  /* Pass 2: M2 = Σ (x - mean)² */
2447
2380
  double M2 = 0.0;
2448
2381
  ca_size_t __unused_mc = 0;
2449
2382
  CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, M2, 0.0, __stddevp_u64_P2EXPR, __unused_mc);
2450
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2383
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2451
2384
  }
2452
2385
  }
2453
2386
  ca_iter_state_finish(&st);
@@ -2478,7 +2411,7 @@ stddevp_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2478
2411
  slab_axes, naxes, 0);
2479
2412
  if ( rc != CA_ITER_OK ) {
2480
2413
  rb_raise(rb_eRuntimeError,
2481
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2414
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2482
2415
  }
2483
2416
  char *p;
2484
2417
  boolean8_t *m;
@@ -2490,7 +2423,7 @@ stddevp_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2490
2423
  ca_size_t masked_cnt = 0;
2491
2424
  CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, sum, 0.0, __stddevp_f32_P1EXPR, masked_cnt);
2492
2425
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2493
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2426
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2494
2427
  if ( __trigger ) {
2495
2428
  if ( ! op_mask ) {
2496
2429
  ca_create_mask(co);
@@ -2498,18 +2431,13 @@ stddevp_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2498
2431
  }
2499
2432
  op_mask[out_i] = 1;
2500
2433
  op[out_i++] = (double) 0.0;
2501
- } else if ( n_valid < 1 ) {
2502
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2503
- cell): return zero rather than UNDEF, matching pre-migration
2504
- behaviour of `cnt > 1 ? formula : 0`. */
2505
- op[out_i++] = (double) 0.0;
2506
2434
  } else {
2507
2435
  double mean = sum / (double) n_valid;
2508
2436
  /* Pass 2: M2 = Σ (x - mean)² */
2509
2437
  double M2 = 0.0;
2510
2438
  ca_size_t __unused_mc = 0;
2511
2439
  CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, M2, 0.0, __stddevp_f32_P2EXPR, __unused_mc);
2512
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2440
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2513
2441
  }
2514
2442
  }
2515
2443
  ca_iter_state_finish(&st);
@@ -2540,7 +2468,7 @@ stddevp_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2540
2468
  slab_axes, naxes, 0);
2541
2469
  if ( rc != CA_ITER_OK ) {
2542
2470
  rb_raise(rb_eRuntimeError,
2543
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2471
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2544
2472
  }
2545
2473
  char *p;
2546
2474
  boolean8_t *m;
@@ -2552,7 +2480,7 @@ stddevp_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2552
2480
  ca_size_t masked_cnt = 0;
2553
2481
  CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, sum, 0.0, __stddevp_f64_P1EXPR, masked_cnt);
2554
2482
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2555
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2483
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2556
2484
  if ( __trigger ) {
2557
2485
  if ( ! op_mask ) {
2558
2486
  ca_create_mask(co);
@@ -2560,18 +2488,13 @@ stddevp_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2560
2488
  }
2561
2489
  op_mask[out_i] = 1;
2562
2490
  op[out_i++] = (double) 0.0;
2563
- } else if ( n_valid < 1 ) {
2564
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2565
- cell): return zero rather than UNDEF, matching pre-migration
2566
- behaviour of `cnt > 1 ? formula : 0`. */
2567
- op[out_i++] = (double) 0.0;
2568
2491
  } else {
2569
2492
  double mean = sum / (double) n_valid;
2570
2493
  /* Pass 2: M2 = Σ (x - mean)² */
2571
2494
  double M2 = 0.0;
2572
2495
  ca_size_t __unused_mc = 0;
2573
2496
  CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, M2, 0.0, __stddevp_f64_P2EXPR, __unused_mc);
2574
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2497
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2575
2498
  }
2576
2499
  }
2577
2500
  ca_iter_state_finish(&st);
@@ -2602,7 +2525,7 @@ stddevp_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t nax
2602
2525
  slab_axes, naxes, 0);
2603
2526
  if ( rc != CA_ITER_OK ) {
2604
2527
  rb_raise(rb_eRuntimeError,
2605
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2528
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2606
2529
  }
2607
2530
  char *p;
2608
2531
  boolean8_t *m;
@@ -2614,7 +2537,7 @@ stddevp_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t nax
2614
2537
  ca_size_t masked_cnt = 0;
2615
2538
  CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, cmplx64_t, st, p, m, sum, 0, __stddevp_cmplx64_P1EXPR, masked_cnt);
2616
2539
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2617
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2540
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2618
2541
  if ( __trigger ) {
2619
2542
  if ( ! op_mask ) {
2620
2543
  ca_create_mask(co);
@@ -2622,18 +2545,13 @@ stddevp_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t nax
2622
2545
  }
2623
2546
  op_mask[out_i] = 1;
2624
2547
  op[out_i++] = (double) 0.0;
2625
- } else if ( n_valid < 1 ) {
2626
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2627
- cell): return zero rather than UNDEF, matching pre-migration
2628
- behaviour of `cnt > 1 ? formula : 0`. */
2629
- op[out_i++] = (double) 0.0;
2630
2548
  } else {
2631
2549
  cmplx64_t mean = sum / (cmplx64_t)(double) n_valid;
2632
2550
  /* Pass 2: M2 = Σ (x - mean)² */
2633
2551
  double M2 = 0.0;
2634
2552
  ca_size_t __unused_mc = 0;
2635
2553
  CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, double, st, p, m, M2, 0.0, __stddevp_cmplx64_P2EXPR, __unused_mc);
2636
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2554
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2637
2555
  }
2638
2556
  }
2639
2557
  ca_iter_state_finish(&st);
@@ -2664,7 +2582,7 @@ stddevp_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t na
2664
2582
  slab_axes, naxes, 0);
2665
2583
  if ( rc != CA_ITER_OK ) {
2666
2584
  rb_raise(rb_eRuntimeError,
2667
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2585
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2668
2586
  }
2669
2587
  char *p;
2670
2588
  boolean8_t *m;
@@ -2676,7 +2594,7 @@ stddevp_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t na
2676
2594
  ca_size_t masked_cnt = 0;
2677
2595
  CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, cmplx128_t, st, p, m, sum, 0, __stddevp_cmplx128_P1EXPR, masked_cnt);
2678
2596
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2679
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2597
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2680
2598
  if ( __trigger ) {
2681
2599
  if ( ! op_mask ) {
2682
2600
  ca_create_mask(co);
@@ -2684,18 +2602,13 @@ stddevp_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t na
2684
2602
  }
2685
2603
  op_mask[out_i] = 1;
2686
2604
  op[out_i++] = (double) 0.0;
2687
- } else if ( n_valid < 1 ) {
2688
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2689
- cell): return zero rather than UNDEF, matching pre-migration
2690
- behaviour of `cnt > 1 ? formula : 0`. */
2691
- op[out_i++] = (double) 0.0;
2692
2605
  } else {
2693
2606
  cmplx128_t mean = sum / (cmplx128_t)(double) n_valid;
2694
2607
  /* Pass 2: M2 = Σ (x - mean)² */
2695
2608
  double M2 = 0.0;
2696
2609
  ca_size_t __unused_mc = 0;
2697
2610
  CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, double, st, p, m, M2, 0.0, __stddevp_cmplx128_P2EXPR, __unused_mc);
2698
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2611
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2699
2612
  }
2700
2613
  }
2701
2614
  ca_iter_state_finish(&st);
@@ -2726,7 +2639,7 @@ stddevp_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2726
2639
  slab_axes, naxes, 0);
2727
2640
  if ( rc != CA_ITER_OK ) {
2728
2641
  rb_raise(rb_eRuntimeError,
2729
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2642
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2730
2643
  }
2731
2644
  char *p;
2732
2645
  boolean8_t *m;
@@ -2738,7 +2651,7 @@ stddevp_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2738
2651
  ca_size_t masked_cnt = 0;
2739
2652
  CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, sum, 0.0, __stddevp_bool_P1EXPR, masked_cnt);
2740
2653
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2741
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2654
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2742
2655
  if ( __trigger ) {
2743
2656
  if ( ! op_mask ) {
2744
2657
  ca_create_mask(co);
@@ -2746,18 +2659,13 @@ stddevp_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2746
2659
  }
2747
2660
  op_mask[out_i] = 1;
2748
2661
  op[out_i++] = (double) 0.0;
2749
- } else if ( n_valid < 1 ) {
2750
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2751
- cell): return zero rather than UNDEF, matching pre-migration
2752
- behaviour of `cnt > 1 ? formula : 0`. */
2753
- op[out_i++] = (double) 0.0;
2754
2662
  } else {
2755
2663
  double mean = sum / (double) n_valid;
2756
2664
  /* Pass 2: M2 = Σ (x - mean)² */
2757
2665
  double M2 = 0.0;
2758
2666
  ca_size_t __unused_mc = 0;
2759
2667
  CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, M2, 0.0, __stddevp_bool_P2EXPR, __unused_mc);
2760
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid), 0.0)));
2668
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid)));
2761
2669
  }
2762
2670
  }
2763
2671
  ca_iter_state_finish(&st);
@@ -2774,19 +2682,45 @@ stddevp_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
2774
2682
  #undef __stddevp_bool_P1EXPR
2775
2683
  #undef __stddevp_bool_P2EXPR
2776
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
+
2777
2698
  static VALUE
2778
- stddevp_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
2699
+ stddevp_ki_native_object_body (VALUE arg)
2779
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])
2780
2715
  VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
2781
2716
  CArray *co;
2782
2717
  GetCArray(vout, co);
2783
2718
  double *op = (double *) co->ptr;
2784
- ca_iter_state st;
2785
2719
  int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
2786
2720
  slab_axes, naxes, 0);
2787
2721
  if ( rc != CA_ITER_OK ) {
2788
2722
  rb_raise(rb_eRuntimeError,
2789
- "stddevp_ki: kernel_iterator init failed rc=%d", rc);
2723
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2790
2724
  }
2791
2725
  char *p;
2792
2726
  boolean8_t *m;
@@ -2798,7 +2732,7 @@ stddevp_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
2798
2732
  ca_size_t masked_cnt = 0;
2799
2733
  CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, sum, INT2FIX(0), sum = rb_funcall(sum, rb_intern("+"), 1, v), masked_cnt);
2800
2734
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2801
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2735
+ int __trigger = ca_too_few_present(n_valid, min_count, 1);
2802
2736
  if ( __trigger ) {
2803
2737
  if ( ! op_mask ) {
2804
2738
  ca_create_mask(co);
@@ -2806,11 +2740,6 @@ stddevp_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
2806
2740
  }
2807
2741
  op_mask[out_i] = 1;
2808
2742
  op[out_i++] = (double) 0.0;
2809
- } else if ( n_valid < 1 ) {
2810
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2811
- cell): return zero rather than UNDEF, matching pre-migration
2812
- behaviour of `cnt > 1 ? formula : 0`. */
2813
- op[out_i++] = (double) 0.0;
2814
2743
  } else {
2815
2744
  VALUE mean = rb_funcall(sum, rb_intern("/"), 1, LONG2NUM(n_valid));
2816
2745
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -2818,7 +2747,7 @@ stddevp_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
2818
2747
  ca_size_t __unused_mc = 0;
2819
2748
  CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, M2, INT2FIX(0), VALUE __d = rb_funcall(v, rb_intern("-"), 1, mean);
2820
2749
  M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)), __unused_mc);
2821
- op[out_i++] = (double)(sqrt(fmax(NUM2DBL(rb_funcall(M2, rb_intern("/"), 1, LONG2NUM(n_valid))), 0.0)));
2750
+ op[out_i++] = (double)(ca_sqrt_clamp(NUM2DBL(rb_funcall(M2, rb_intern("/"), 1, LONG2NUM(n_valid)))));
2822
2751
  }
2823
2752
  }
2824
2753
  ca_iter_state_finish(&st);
@@ -2831,8 +2760,23 @@ M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)),
2831
2760
  return rb_float_new(op[0]);
2832
2761
  }
2833
2762
  return vout;
2763
+ #undef st
2834
2764
  }
2835
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
+
2836
2780
  VALUE
2837
2781
  rb_ca_stddevp_ki (int argc, VALUE *argv, VALUE self)
2838
2782
  {
@@ -2844,89 +2788,101 @@ rb_ca_stddevp_ki (int argc, VALUE *argv, VALUE self)
2844
2788
  int keep_axis = RTEST(rkeep_axis);
2845
2789
  int8_t slab_axes[CA_RANK_MAX];
2846
2790
  int8_t naxes = rb_ca_parse_reduce_axes_kw(raxis, src, slab_axes);
2847
- ca_size_t min_count = -1;
2848
- if ( ! NIL_P(rmin_count) ) {
2849
- ca_size_t mc_user = NUM2SIZE(rmin_count);
2850
- if ( mc_user < 0 ) {
2851
- rb_raise(rb_eArgError, "stddevp_ki: :min_count must be non-negative, got %lld", (long long) mc_user);
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);
2852
2796
  }
2853
- /* :min_count = 0 collapses to legacy default (any 1 valid). */
2854
- if ( mc_user > 0 ) min_count = mc_user;
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;
2855
2804
  }
2856
2805
  VALUE result;
2857
2806
  if ( argc > 0 ) {
2858
- rb_raise(rb_eArgError, "stddevp_ki: positional axis arguments are no longer accepted (got %d); use axis: kwarg, e.g. a.stddevp(axis: 0) or a.stddevp(axis: [0, 1])", argc);
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());
2859
2808
  }
2860
2809
  switch ( src->data_type ) {
2861
2810
  case CA_INT8: {
2862
2811
  result = stddevp_ki_native_i8(self, src, slab_axes, naxes, keep_axis, min_count);
2812
+ RB_GC_GUARD(self);
2863
2813
  break;
2864
2814
  }
2865
2815
  case CA_UINT8: {
2866
2816
  result = stddevp_ki_native_u8(self, src, slab_axes, naxes, keep_axis, min_count);
2817
+ RB_GC_GUARD(self);
2867
2818
  break;
2868
2819
  }
2869
2820
  case CA_INT16: {
2870
2821
  result = stddevp_ki_native_i16(self, src, slab_axes, naxes, keep_axis, min_count);
2822
+ RB_GC_GUARD(self);
2871
2823
  break;
2872
2824
  }
2873
2825
  case CA_UINT16: {
2874
2826
  result = stddevp_ki_native_u16(self, src, slab_axes, naxes, keep_axis, min_count);
2827
+ RB_GC_GUARD(self);
2875
2828
  break;
2876
2829
  }
2877
2830
  case CA_INT32: {
2878
2831
  result = stddevp_ki_native_i32(self, src, slab_axes, naxes, keep_axis, min_count);
2832
+ RB_GC_GUARD(self);
2879
2833
  break;
2880
2834
  }
2881
2835
  case CA_UINT32: {
2882
2836
  result = stddevp_ki_native_u32(self, src, slab_axes, naxes, keep_axis, min_count);
2837
+ RB_GC_GUARD(self);
2883
2838
  break;
2884
2839
  }
2885
2840
  case CA_INT64: {
2886
2841
  result = stddevp_ki_native_i64(self, src, slab_axes, naxes, keep_axis, min_count);
2842
+ RB_GC_GUARD(self);
2887
2843
  break;
2888
2844
  }
2889
2845
  case CA_UINT64: {
2890
2846
  result = stddevp_ki_native_u64(self, src, slab_axes, naxes, keep_axis, min_count);
2847
+ RB_GC_GUARD(self);
2891
2848
  break;
2892
2849
  }
2893
2850
  case CA_FLOAT32: {
2894
2851
  result = stddevp_ki_native_f32(self, src, slab_axes, naxes, keep_axis, min_count);
2852
+ RB_GC_GUARD(self);
2895
2853
  break;
2896
2854
  }
2897
2855
  case CA_FLOAT64: {
2898
2856
  result = stddevp_ki_native_f64(self, src, slab_axes, naxes, keep_axis, min_count);
2857
+ RB_GC_GUARD(self);
2899
2858
  break;
2900
2859
  }
2901
2860
  case CA_CMPLX64: {
2902
2861
  result = stddevp_ki_native_cmplx64(self, src, slab_axes, naxes, keep_axis, min_count);
2862
+ RB_GC_GUARD(self);
2903
2863
  break;
2904
2864
  }
2905
2865
  case CA_CMPLX128: {
2906
2866
  result = stddevp_ki_native_cmplx128(self, src, slab_axes, naxes, keep_axis, min_count);
2867
+ RB_GC_GUARD(self);
2907
2868
  break;
2908
2869
  }
2909
2870
  case CA_BOOLEAN: {
2910
2871
  result = stddevp_ki_native_bool(self, src, slab_axes, naxes, keep_axis, min_count);
2872
+ RB_GC_GUARD(self);
2911
2873
  break;
2912
2874
  }
2913
2875
  case CA_OBJECT: {
2914
2876
  result = stddevp_ki_native_object(self, src, slab_axes, naxes, keep_axis, min_count);
2877
+ RB_GC_GUARD(self);
2915
2878
  break;
2916
2879
  }
2917
2880
  default:
2918
- rb_raise(rb_eCADataTypeError, "stddevp_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, cmplx64, cmplx128, bool, object)", ca_type_name[src->data_type]);
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]);
2919
2883
  }
2920
2884
  if ( ! NIL_P(rfval) ) {
2921
- if ( result == CA_UNDEF ) {
2922
- result = rfval;
2923
- } else if ( TYPE(result) != T_FLOAT && rb_obj_is_kind_of(result, rb_cCArray) ) {
2924
- CArray *cr;
2925
- GetCArray(result, cr);
2926
- if ( ca_has_mask(cr) ) {
2927
- result = rb_ca_mask_fill_copy(result, rfval);
2928
- }
2929
- }
2885
+ result = ca_reduce_fill(result, rfval, fill_whole);
2930
2886
  }
2931
2887
  return result;
2932
2888
  }
@@ -2947,7 +2903,7 @@ stddev_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, in
2947
2903
  slab_axes, naxes, 0);
2948
2904
  if ( rc != CA_ITER_OK ) {
2949
2905
  rb_raise(rb_eRuntimeError,
2950
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
2906
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
2951
2907
  }
2952
2908
  char *p;
2953
2909
  boolean8_t *m;
@@ -2959,7 +2915,7 @@ stddev_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, in
2959
2915
  ca_size_t masked_cnt = 0;
2960
2916
  CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, sum, 0.0, __stddev_i8_P1EXPR, masked_cnt);
2961
2917
  ca_size_t n_valid = st.slab_elements - masked_cnt;
2962
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2918
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
2963
2919
  if ( __trigger ) {
2964
2920
  if ( ! op_mask ) {
2965
2921
  ca_create_mask(co);
@@ -2967,18 +2923,13 @@ stddev_ki_native_i8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, in
2967
2923
  }
2968
2924
  op_mask[out_i] = 1;
2969
2925
  op[out_i++] = (double) 0.0;
2970
- } else if ( n_valid < 2 ) {
2971
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
2972
- cell): return zero rather than UNDEF, matching pre-migration
2973
- behaviour of `cnt > 1 ? formula : 0`. */
2974
- op[out_i++] = (double) 0.0;
2975
2926
  } else {
2976
2927
  double mean = sum / (double) n_valid;
2977
2928
  /* Pass 2: M2 = Σ (x - mean)² */
2978
2929
  double M2 = 0.0;
2979
2930
  ca_size_t __unused_mc = 0;
2980
2931
  CA_SLAB_REDUCE_SUM8_EX(int8_t, double, st, p, m, M2, 0.0, __stddev_i8_P2EXPR, __unused_mc);
2981
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
2932
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
2982
2933
  }
2983
2934
  }
2984
2935
  ca_iter_state_finish(&st);
@@ -3009,7 +2960,7 @@ stddev_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, in
3009
2960
  slab_axes, naxes, 0);
3010
2961
  if ( rc != CA_ITER_OK ) {
3011
2962
  rb_raise(rb_eRuntimeError,
3012
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
2963
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3013
2964
  }
3014
2965
  char *p;
3015
2966
  boolean8_t *m;
@@ -3021,7 +2972,7 @@ stddev_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, in
3021
2972
  ca_size_t masked_cnt = 0;
3022
2973
  CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, sum, 0.0, __stddev_u8_P1EXPR, masked_cnt);
3023
2974
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3024
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
2975
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3025
2976
  if ( __trigger ) {
3026
2977
  if ( ! op_mask ) {
3027
2978
  ca_create_mask(co);
@@ -3029,18 +2980,13 @@ stddev_ki_native_u8 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, in
3029
2980
  }
3030
2981
  op_mask[out_i] = 1;
3031
2982
  op[out_i++] = (double) 0.0;
3032
- } else if ( n_valid < 2 ) {
3033
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3034
- cell): return zero rather than UNDEF, matching pre-migration
3035
- behaviour of `cnt > 1 ? formula : 0`. */
3036
- op[out_i++] = (double) 0.0;
3037
2983
  } else {
3038
2984
  double mean = sum / (double) n_valid;
3039
2985
  /* Pass 2: M2 = Σ (x - mean)² */
3040
2986
  double M2 = 0.0;
3041
2987
  ca_size_t __unused_mc = 0;
3042
2988
  CA_SLAB_REDUCE_SUM8_EX(uint8_t, double, st, p, m, M2, 0.0, __stddev_u8_P2EXPR, __unused_mc);
3043
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
2989
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3044
2990
  }
3045
2991
  }
3046
2992
  ca_iter_state_finish(&st);
@@ -3071,7 +3017,7 @@ stddev_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3071
3017
  slab_axes, naxes, 0);
3072
3018
  if ( rc != CA_ITER_OK ) {
3073
3019
  rb_raise(rb_eRuntimeError,
3074
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3020
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3075
3021
  }
3076
3022
  char *p;
3077
3023
  boolean8_t *m;
@@ -3083,7 +3029,7 @@ stddev_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3083
3029
  ca_size_t masked_cnt = 0;
3084
3030
  CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, sum, 0.0, __stddev_i16_P1EXPR, masked_cnt);
3085
3031
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3086
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3032
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3087
3033
  if ( __trigger ) {
3088
3034
  if ( ! op_mask ) {
3089
3035
  ca_create_mask(co);
@@ -3091,18 +3037,13 @@ stddev_ki_native_i16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3091
3037
  }
3092
3038
  op_mask[out_i] = 1;
3093
3039
  op[out_i++] = (double) 0.0;
3094
- } else if ( n_valid < 2 ) {
3095
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3096
- cell): return zero rather than UNDEF, matching pre-migration
3097
- behaviour of `cnt > 1 ? formula : 0`. */
3098
- op[out_i++] = (double) 0.0;
3099
3040
  } else {
3100
3041
  double mean = sum / (double) n_valid;
3101
3042
  /* Pass 2: M2 = Σ (x - mean)² */
3102
3043
  double M2 = 0.0;
3103
3044
  ca_size_t __unused_mc = 0;
3104
3045
  CA_SLAB_REDUCE_SUM8_EX(int16_t, double, st, p, m, M2, 0.0, __stddev_i16_P2EXPR, __unused_mc);
3105
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3046
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3106
3047
  }
3107
3048
  }
3108
3049
  ca_iter_state_finish(&st);
@@ -3133,7 +3074,7 @@ stddev_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3133
3074
  slab_axes, naxes, 0);
3134
3075
  if ( rc != CA_ITER_OK ) {
3135
3076
  rb_raise(rb_eRuntimeError,
3136
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3077
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3137
3078
  }
3138
3079
  char *p;
3139
3080
  boolean8_t *m;
@@ -3145,7 +3086,7 @@ stddev_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3145
3086
  ca_size_t masked_cnt = 0;
3146
3087
  CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, sum, 0.0, __stddev_u16_P1EXPR, masked_cnt);
3147
3088
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3148
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3089
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3149
3090
  if ( __trigger ) {
3150
3091
  if ( ! op_mask ) {
3151
3092
  ca_create_mask(co);
@@ -3153,18 +3094,13 @@ stddev_ki_native_u16 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3153
3094
  }
3154
3095
  op_mask[out_i] = 1;
3155
3096
  op[out_i++] = (double) 0.0;
3156
- } else if ( n_valid < 2 ) {
3157
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3158
- cell): return zero rather than UNDEF, matching pre-migration
3159
- behaviour of `cnt > 1 ? formula : 0`. */
3160
- op[out_i++] = (double) 0.0;
3161
3097
  } else {
3162
3098
  double mean = sum / (double) n_valid;
3163
3099
  /* Pass 2: M2 = Σ (x - mean)² */
3164
3100
  double M2 = 0.0;
3165
3101
  ca_size_t __unused_mc = 0;
3166
3102
  CA_SLAB_REDUCE_SUM8_EX(uint16_t, double, st, p, m, M2, 0.0, __stddev_u16_P2EXPR, __unused_mc);
3167
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3103
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3168
3104
  }
3169
3105
  }
3170
3106
  ca_iter_state_finish(&st);
@@ -3195,7 +3131,7 @@ stddev_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3195
3131
  slab_axes, naxes, 0);
3196
3132
  if ( rc != CA_ITER_OK ) {
3197
3133
  rb_raise(rb_eRuntimeError,
3198
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3134
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3199
3135
  }
3200
3136
  char *p;
3201
3137
  boolean8_t *m;
@@ -3207,7 +3143,7 @@ stddev_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3207
3143
  ca_size_t masked_cnt = 0;
3208
3144
  CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, sum, 0.0, __stddev_i32_P1EXPR, masked_cnt);
3209
3145
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3210
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3146
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3211
3147
  if ( __trigger ) {
3212
3148
  if ( ! op_mask ) {
3213
3149
  ca_create_mask(co);
@@ -3215,18 +3151,13 @@ stddev_ki_native_i32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3215
3151
  }
3216
3152
  op_mask[out_i] = 1;
3217
3153
  op[out_i++] = (double) 0.0;
3218
- } else if ( n_valid < 2 ) {
3219
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3220
- cell): return zero rather than UNDEF, matching pre-migration
3221
- behaviour of `cnt > 1 ? formula : 0`. */
3222
- op[out_i++] = (double) 0.0;
3223
3154
  } else {
3224
3155
  double mean = sum / (double) n_valid;
3225
3156
  /* Pass 2: M2 = Σ (x - mean)² */
3226
3157
  double M2 = 0.0;
3227
3158
  ca_size_t __unused_mc = 0;
3228
3159
  CA_SLAB_REDUCE_SUM8_EX(int32_t, double, st, p, m, M2, 0.0, __stddev_i32_P2EXPR, __unused_mc);
3229
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3160
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3230
3161
  }
3231
3162
  }
3232
3163
  ca_iter_state_finish(&st);
@@ -3257,7 +3188,7 @@ stddev_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3257
3188
  slab_axes, naxes, 0);
3258
3189
  if ( rc != CA_ITER_OK ) {
3259
3190
  rb_raise(rb_eRuntimeError,
3260
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3191
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3261
3192
  }
3262
3193
  char *p;
3263
3194
  boolean8_t *m;
@@ -3269,7 +3200,7 @@ stddev_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3269
3200
  ca_size_t masked_cnt = 0;
3270
3201
  CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, sum, 0.0, __stddev_u32_P1EXPR, masked_cnt);
3271
3202
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3272
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3203
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3273
3204
  if ( __trigger ) {
3274
3205
  if ( ! op_mask ) {
3275
3206
  ca_create_mask(co);
@@ -3277,18 +3208,13 @@ stddev_ki_native_u32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3277
3208
  }
3278
3209
  op_mask[out_i] = 1;
3279
3210
  op[out_i++] = (double) 0.0;
3280
- } else if ( n_valid < 2 ) {
3281
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3282
- cell): return zero rather than UNDEF, matching pre-migration
3283
- behaviour of `cnt > 1 ? formula : 0`. */
3284
- op[out_i++] = (double) 0.0;
3285
3211
  } else {
3286
3212
  double mean = sum / (double) n_valid;
3287
3213
  /* Pass 2: M2 = Σ (x - mean)² */
3288
3214
  double M2 = 0.0;
3289
3215
  ca_size_t __unused_mc = 0;
3290
3216
  CA_SLAB_REDUCE_SUM8_EX(uint32_t, double, st, p, m, M2, 0.0, __stddev_u32_P2EXPR, __unused_mc);
3291
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3217
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3292
3218
  }
3293
3219
  }
3294
3220
  ca_iter_state_finish(&st);
@@ -3319,7 +3245,7 @@ stddev_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3319
3245
  slab_axes, naxes, 0);
3320
3246
  if ( rc != CA_ITER_OK ) {
3321
3247
  rb_raise(rb_eRuntimeError,
3322
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3248
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3323
3249
  }
3324
3250
  char *p;
3325
3251
  boolean8_t *m;
@@ -3331,7 +3257,7 @@ stddev_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3331
3257
  ca_size_t masked_cnt = 0;
3332
3258
  CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, sum, 0.0, __stddev_i64_P1EXPR, masked_cnt);
3333
3259
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3334
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3260
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3335
3261
  if ( __trigger ) {
3336
3262
  if ( ! op_mask ) {
3337
3263
  ca_create_mask(co);
@@ -3339,18 +3265,13 @@ stddev_ki_native_i64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3339
3265
  }
3340
3266
  op_mask[out_i] = 1;
3341
3267
  op[out_i++] = (double) 0.0;
3342
- } else if ( n_valid < 2 ) {
3343
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3344
- cell): return zero rather than UNDEF, matching pre-migration
3345
- behaviour of `cnt > 1 ? formula : 0`. */
3346
- op[out_i++] = (double) 0.0;
3347
3268
  } else {
3348
3269
  double mean = sum / (double) n_valid;
3349
3270
  /* Pass 2: M2 = Σ (x - mean)² */
3350
3271
  double M2 = 0.0;
3351
3272
  ca_size_t __unused_mc = 0;
3352
3273
  CA_SLAB_REDUCE_SUM8_EX(int64_t, double, st, p, m, M2, 0.0, __stddev_i64_P2EXPR, __unused_mc);
3353
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3274
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3354
3275
  }
3355
3276
  }
3356
3277
  ca_iter_state_finish(&st);
@@ -3381,7 +3302,7 @@ stddev_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3381
3302
  slab_axes, naxes, 0);
3382
3303
  if ( rc != CA_ITER_OK ) {
3383
3304
  rb_raise(rb_eRuntimeError,
3384
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3305
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3385
3306
  }
3386
3307
  char *p;
3387
3308
  boolean8_t *m;
@@ -3393,7 +3314,7 @@ stddev_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3393
3314
  ca_size_t masked_cnt = 0;
3394
3315
  CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, sum, 0.0, __stddev_u64_P1EXPR, masked_cnt);
3395
3316
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3396
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3317
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3397
3318
  if ( __trigger ) {
3398
3319
  if ( ! op_mask ) {
3399
3320
  ca_create_mask(co);
@@ -3401,18 +3322,13 @@ stddev_ki_native_u64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3401
3322
  }
3402
3323
  op_mask[out_i] = 1;
3403
3324
  op[out_i++] = (double) 0.0;
3404
- } else if ( n_valid < 2 ) {
3405
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3406
- cell): return zero rather than UNDEF, matching pre-migration
3407
- behaviour of `cnt > 1 ? formula : 0`. */
3408
- op[out_i++] = (double) 0.0;
3409
3325
  } else {
3410
3326
  double mean = sum / (double) n_valid;
3411
3327
  /* Pass 2: M2 = Σ (x - mean)² */
3412
3328
  double M2 = 0.0;
3413
3329
  ca_size_t __unused_mc = 0;
3414
3330
  CA_SLAB_REDUCE_SUM8_EX(uint64_t, double, st, p, m, M2, 0.0, __stddev_u64_P2EXPR, __unused_mc);
3415
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3331
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3416
3332
  }
3417
3333
  }
3418
3334
  ca_iter_state_finish(&st);
@@ -3443,7 +3359,7 @@ stddev_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3443
3359
  slab_axes, naxes, 0);
3444
3360
  if ( rc != CA_ITER_OK ) {
3445
3361
  rb_raise(rb_eRuntimeError,
3446
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3362
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3447
3363
  }
3448
3364
  char *p;
3449
3365
  boolean8_t *m;
@@ -3455,7 +3371,7 @@ stddev_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3455
3371
  ca_size_t masked_cnt = 0;
3456
3372
  CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, sum, 0.0, __stddev_f32_P1EXPR, masked_cnt);
3457
3373
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3458
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3374
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3459
3375
  if ( __trigger ) {
3460
3376
  if ( ! op_mask ) {
3461
3377
  ca_create_mask(co);
@@ -3463,18 +3379,13 @@ stddev_ki_native_f32 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3463
3379
  }
3464
3380
  op_mask[out_i] = 1;
3465
3381
  op[out_i++] = (double) 0.0;
3466
- } else if ( n_valid < 2 ) {
3467
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3468
- cell): return zero rather than UNDEF, matching pre-migration
3469
- behaviour of `cnt > 1 ? formula : 0`. */
3470
- op[out_i++] = (double) 0.0;
3471
3382
  } else {
3472
3383
  double mean = sum / (double) n_valid;
3473
3384
  /* Pass 2: M2 = Σ (x - mean)² */
3474
3385
  double M2 = 0.0;
3475
3386
  ca_size_t __unused_mc = 0;
3476
3387
  CA_SLAB_REDUCE_SUM8_EX(float, double, st, p, m, M2, 0.0, __stddev_f32_P2EXPR, __unused_mc);
3477
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3388
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3478
3389
  }
3479
3390
  }
3480
3391
  ca_iter_state_finish(&st);
@@ -3505,7 +3416,7 @@ stddev_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3505
3416
  slab_axes, naxes, 0);
3506
3417
  if ( rc != CA_ITER_OK ) {
3507
3418
  rb_raise(rb_eRuntimeError,
3508
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3419
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3509
3420
  }
3510
3421
  char *p;
3511
3422
  boolean8_t *m;
@@ -3517,7 +3428,7 @@ stddev_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3517
3428
  ca_size_t masked_cnt = 0;
3518
3429
  CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, sum, 0.0, __stddev_f64_P1EXPR, masked_cnt);
3519
3430
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3520
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3431
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3521
3432
  if ( __trigger ) {
3522
3433
  if ( ! op_mask ) {
3523
3434
  ca_create_mask(co);
@@ -3525,18 +3436,13 @@ stddev_ki_native_f64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, i
3525
3436
  }
3526
3437
  op_mask[out_i] = 1;
3527
3438
  op[out_i++] = (double) 0.0;
3528
- } else if ( n_valid < 2 ) {
3529
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3530
- cell): return zero rather than UNDEF, matching pre-migration
3531
- behaviour of `cnt > 1 ? formula : 0`. */
3532
- op[out_i++] = (double) 0.0;
3533
3439
  } else {
3534
3440
  double mean = sum / (double) n_valid;
3535
3441
  /* Pass 2: M2 = Σ (x - mean)² */
3536
3442
  double M2 = 0.0;
3537
3443
  ca_size_t __unused_mc = 0;
3538
3444
  CA_SLAB_REDUCE_SUM8_EX(double, double, st, p, m, M2, 0.0, __stddev_f64_P2EXPR, __unused_mc);
3539
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3445
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3540
3446
  }
3541
3447
  }
3542
3448
  ca_iter_state_finish(&st);
@@ -3567,7 +3473,7 @@ stddev_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
3567
3473
  slab_axes, naxes, 0);
3568
3474
  if ( rc != CA_ITER_OK ) {
3569
3475
  rb_raise(rb_eRuntimeError,
3570
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3476
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3571
3477
  }
3572
3478
  char *p;
3573
3479
  boolean8_t *m;
@@ -3579,7 +3485,7 @@ stddev_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
3579
3485
  ca_size_t masked_cnt = 0;
3580
3486
  CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, cmplx64_t, st, p, m, sum, 0, __stddev_cmplx64_P1EXPR, masked_cnt);
3581
3487
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3582
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3488
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3583
3489
  if ( __trigger ) {
3584
3490
  if ( ! op_mask ) {
3585
3491
  ca_create_mask(co);
@@ -3587,18 +3493,13 @@ stddev_ki_native_cmplx64 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxe
3587
3493
  }
3588
3494
  op_mask[out_i] = 1;
3589
3495
  op[out_i++] = (double) 0.0;
3590
- } else if ( n_valid < 2 ) {
3591
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3592
- cell): return zero rather than UNDEF, matching pre-migration
3593
- behaviour of `cnt > 1 ? formula : 0`. */
3594
- op[out_i++] = (double) 0.0;
3595
3496
  } else {
3596
3497
  cmplx64_t mean = sum / (cmplx64_t)(double) n_valid;
3597
3498
  /* Pass 2: M2 = Σ (x - mean)² */
3598
3499
  double M2 = 0.0;
3599
3500
  ca_size_t __unused_mc = 0;
3600
3501
  CA_SLAB_REDUCE_SUM8_EX(cmplx64_t, double, st, p, m, M2, 0.0, __stddev_cmplx64_P2EXPR, __unused_mc);
3601
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3502
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3602
3503
  }
3603
3504
  }
3604
3505
  ca_iter_state_finish(&st);
@@ -3629,7 +3530,7 @@ stddev_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t nax
3629
3530
  slab_axes, naxes, 0);
3630
3531
  if ( rc != CA_ITER_OK ) {
3631
3532
  rb_raise(rb_eRuntimeError,
3632
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3533
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3633
3534
  }
3634
3535
  char *p;
3635
3536
  boolean8_t *m;
@@ -3641,7 +3542,7 @@ stddev_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t nax
3641
3542
  ca_size_t masked_cnt = 0;
3642
3543
  CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, cmplx128_t, st, p, m, sum, 0, __stddev_cmplx128_P1EXPR, masked_cnt);
3643
3544
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3644
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3545
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3645
3546
  if ( __trigger ) {
3646
3547
  if ( ! op_mask ) {
3647
3548
  ca_create_mask(co);
@@ -3649,18 +3550,13 @@ stddev_ki_native_cmplx128 (VALUE self, CArray *ca, int8_t *slab_axes, int8_t nax
3649
3550
  }
3650
3551
  op_mask[out_i] = 1;
3651
3552
  op[out_i++] = (double) 0.0;
3652
- } else if ( n_valid < 2 ) {
3653
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3654
- cell): return zero rather than UNDEF, matching pre-migration
3655
- behaviour of `cnt > 1 ? formula : 0`. */
3656
- op[out_i++] = (double) 0.0;
3657
3553
  } else {
3658
3554
  cmplx128_t mean = sum / (cmplx128_t)(double) n_valid;
3659
3555
  /* Pass 2: M2 = Σ (x - mean)² */
3660
3556
  double M2 = 0.0;
3661
3557
  ca_size_t __unused_mc = 0;
3662
3558
  CA_SLAB_REDUCE_SUM8_EX(cmplx128_t, double, st, p, m, M2, 0.0, __stddev_cmplx128_P2EXPR, __unused_mc);
3663
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3559
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3664
3560
  }
3665
3561
  }
3666
3562
  ca_iter_state_finish(&st);
@@ -3691,7 +3587,7 @@ stddev_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
3691
3587
  slab_axes, naxes, 0);
3692
3588
  if ( rc != CA_ITER_OK ) {
3693
3589
  rb_raise(rb_eRuntimeError,
3694
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3590
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3695
3591
  }
3696
3592
  char *p;
3697
3593
  boolean8_t *m;
@@ -3703,7 +3599,7 @@ stddev_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
3703
3599
  ca_size_t masked_cnt = 0;
3704
3600
  CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, sum, 0.0, __stddev_bool_P1EXPR, masked_cnt);
3705
3601
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3706
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3602
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3707
3603
  if ( __trigger ) {
3708
3604
  if ( ! op_mask ) {
3709
3605
  ca_create_mask(co);
@@ -3711,18 +3607,13 @@ stddev_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
3711
3607
  }
3712
3608
  op_mask[out_i] = 1;
3713
3609
  op[out_i++] = (double) 0.0;
3714
- } else if ( n_valid < 2 ) {
3715
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3716
- cell): return zero rather than UNDEF, matching pre-migration
3717
- behaviour of `cnt > 1 ? formula : 0`. */
3718
- op[out_i++] = (double) 0.0;
3719
3610
  } else {
3720
3611
  double mean = sum / (double) n_valid;
3721
3612
  /* Pass 2: M2 = Σ (x - mean)² */
3722
3613
  double M2 = 0.0;
3723
3614
  ca_size_t __unused_mc = 0;
3724
3615
  CA_SLAB_REDUCE_SUM8_EX(boolean8_t, double, st, p, m, M2, 0.0, __stddev_bool_P2EXPR, __unused_mc);
3725
- op[out_i++] = (double)(sqrt(fmax(M2 / (double)(n_valid - 1), 0.0)));
3616
+ op[out_i++] = (double)(ca_sqrt_clamp(M2 / (double)(n_valid - 1)));
3726
3617
  }
3727
3618
  }
3728
3619
  ca_iter_state_finish(&st);
@@ -3739,19 +3630,45 @@ stddev_ki_native_bool (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes,
3739
3630
  #undef __stddev_bool_P1EXPR
3740
3631
  #undef __stddev_bool_P2EXPR
3741
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
+
3742
3646
  static VALUE
3743
- stddev_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes, int keep_axis, ca_size_t min_count)
3647
+ stddev_ki_native_object_body (VALUE arg)
3744
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])
3745
3663
  VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, CA_FLOAT64, keep_axis);
3746
3664
  CArray *co;
3747
3665
  GetCArray(vout, co);
3748
3666
  double *op = (double *) co->ptr;
3749
- ca_iter_state st;
3750
3667
  int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
3751
3668
  slab_axes, naxes, 0);
3752
3669
  if ( rc != CA_ITER_OK ) {
3753
3670
  rb_raise(rb_eRuntimeError,
3754
- "stddev_ki: kernel_iterator init failed rc=%d", rc);
3671
+ "%s: kernel_iterator init failed rc=%d", ca_calling_method_name(), rc);
3755
3672
  }
3756
3673
  char *p;
3757
3674
  boolean8_t *m;
@@ -3763,7 +3680,7 @@ stddev_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
3763
3680
  ca_size_t masked_cnt = 0;
3764
3681
  CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, sum, INT2FIX(0), sum = rb_funcall(sum, rb_intern("+"), 1, v), masked_cnt);
3765
3682
  ca_size_t n_valid = st.slab_elements - masked_cnt;
3766
- int __trigger = (min_count < 0 ? masked_cnt == st.slab_elements : n_valid < min_count);
3683
+ int __trigger = ca_too_few_present(n_valid, min_count, 2);
3767
3684
  if ( __trigger ) {
3768
3685
  if ( ! op_mask ) {
3769
3686
  ca_create_mask(co);
@@ -3771,11 +3688,6 @@ stddev_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
3771
3688
  }
3772
3689
  op_mask[out_i] = 1;
3773
3690
  op[out_i++] = (double) 0.0;
3774
- } else if ( n_valid < 2 ) {
3775
- /* Legacy degenerate case (e.g. sample variance with 1 unmasked
3776
- cell): return zero rather than UNDEF, matching pre-migration
3777
- behaviour of `cnt > 1 ? formula : 0`. */
3778
- op[out_i++] = (double) 0.0;
3779
3691
  } else {
3780
3692
  VALUE mean = rb_funcall(sum, rb_intern("/"), 1, LONG2NUM(n_valid));
3781
3693
  /* Pass 2: M2 = Σ (x - mean)² */
@@ -3783,7 +3695,7 @@ stddev_ki_native_object (VALUE self, CArray *ca, int8_t *slab_axes, int8_t naxes
3783
3695
  ca_size_t __unused_mc = 0;
3784
3696
  CA_SLAB_REDUCE_T_EX(VALUE, st, p, m, M2, INT2FIX(0), VALUE __d = rb_funcall(v, rb_intern("-"), 1, mean);
3785
3697
  M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)), __unused_mc);
3786
- op[out_i++] = (double)(sqrt(fmax(NUM2DBL(rb_funcall(M2, rb_intern("/"), 1, LONG2NUM(n_valid - 1))), 0.0)));
3698
+ op[out_i++] = (double)(ca_sqrt_clamp(NUM2DBL(rb_funcall(M2, rb_intern("/"), 1, LONG2NUM(n_valid - 1)))));
3787
3699
  }
3788
3700
  }
3789
3701
  ca_iter_state_finish(&st);
@@ -3796,8 +3708,23 @@ M2 = rb_funcall(M2, rb_intern("+"), 1, rb_funcall(__d, rb_intern("*"), 1, __d)),
3796
3708
  return rb_float_new(op[0]);
3797
3709
  }
3798
3710
  return vout;
3711
+ #undef st
3799
3712
  }
3800
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
+
3801
3728
  VALUE
3802
3729
  rb_ca_stddev_ki (int argc, VALUE *argv, VALUE self)
3803
3730
  {
@@ -3809,89 +3736,101 @@ rb_ca_stddev_ki (int argc, VALUE *argv, VALUE self)
3809
3736
  int keep_axis = RTEST(rkeep_axis);
3810
3737
  int8_t slab_axes[CA_RANK_MAX];
3811
3738
  int8_t naxes = rb_ca_parse_reduce_axes_kw(raxis, src, slab_axes);
3812
- ca_size_t min_count = -1;
3813
- if ( ! NIL_P(rmin_count) ) {
3814
- ca_size_t mc_user = NUM2SIZE(rmin_count);
3815
- if ( mc_user < 0 ) {
3816
- rb_raise(rb_eArgError, "stddev_ki: :min_count must be non-negative, got %lld", (long long) mc_user);
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);
3817
3744
  }
3818
- /* :min_count = 0 collapses to legacy default (any 1 valid). */
3819
- if ( mc_user > 0 ) min_count = mc_user;
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;
3820
3752
  }
3821
3753
  VALUE result;
3822
3754
  if ( argc > 0 ) {
3823
- rb_raise(rb_eArgError, "stddev_ki: positional axis arguments are no longer accepted (got %d); use axis: kwarg, e.g. a.stddev(axis: 0) or a.stddev(axis: [0, 1])", argc);
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());
3824
3756
  }
3825
3757
  switch ( src->data_type ) {
3826
3758
  case CA_INT8: {
3827
3759
  result = stddev_ki_native_i8(self, src, slab_axes, naxes, keep_axis, min_count);
3760
+ RB_GC_GUARD(self);
3828
3761
  break;
3829
3762
  }
3830
3763
  case CA_UINT8: {
3831
3764
  result = stddev_ki_native_u8(self, src, slab_axes, naxes, keep_axis, min_count);
3765
+ RB_GC_GUARD(self);
3832
3766
  break;
3833
3767
  }
3834
3768
  case CA_INT16: {
3835
3769
  result = stddev_ki_native_i16(self, src, slab_axes, naxes, keep_axis, min_count);
3770
+ RB_GC_GUARD(self);
3836
3771
  break;
3837
3772
  }
3838
3773
  case CA_UINT16: {
3839
3774
  result = stddev_ki_native_u16(self, src, slab_axes, naxes, keep_axis, min_count);
3775
+ RB_GC_GUARD(self);
3840
3776
  break;
3841
3777
  }
3842
3778
  case CA_INT32: {
3843
3779
  result = stddev_ki_native_i32(self, src, slab_axes, naxes, keep_axis, min_count);
3780
+ RB_GC_GUARD(self);
3844
3781
  break;
3845
3782
  }
3846
3783
  case CA_UINT32: {
3847
3784
  result = stddev_ki_native_u32(self, src, slab_axes, naxes, keep_axis, min_count);
3785
+ RB_GC_GUARD(self);
3848
3786
  break;
3849
3787
  }
3850
3788
  case CA_INT64: {
3851
3789
  result = stddev_ki_native_i64(self, src, slab_axes, naxes, keep_axis, min_count);
3790
+ RB_GC_GUARD(self);
3852
3791
  break;
3853
3792
  }
3854
3793
  case CA_UINT64: {
3855
3794
  result = stddev_ki_native_u64(self, src, slab_axes, naxes, keep_axis, min_count);
3795
+ RB_GC_GUARD(self);
3856
3796
  break;
3857
3797
  }
3858
3798
  case CA_FLOAT32: {
3859
3799
  result = stddev_ki_native_f32(self, src, slab_axes, naxes, keep_axis, min_count);
3800
+ RB_GC_GUARD(self);
3860
3801
  break;
3861
3802
  }
3862
3803
  case CA_FLOAT64: {
3863
3804
  result = stddev_ki_native_f64(self, src, slab_axes, naxes, keep_axis, min_count);
3805
+ RB_GC_GUARD(self);
3864
3806
  break;
3865
3807
  }
3866
3808
  case CA_CMPLX64: {
3867
3809
  result = stddev_ki_native_cmplx64(self, src, slab_axes, naxes, keep_axis, min_count);
3810
+ RB_GC_GUARD(self);
3868
3811
  break;
3869
3812
  }
3870
3813
  case CA_CMPLX128: {
3871
3814
  result = stddev_ki_native_cmplx128(self, src, slab_axes, naxes, keep_axis, min_count);
3815
+ RB_GC_GUARD(self);
3872
3816
  break;
3873
3817
  }
3874
3818
  case CA_BOOLEAN: {
3875
3819
  result = stddev_ki_native_bool(self, src, slab_axes, naxes, keep_axis, min_count);
3820
+ RB_GC_GUARD(self);
3876
3821
  break;
3877
3822
  }
3878
3823
  case CA_OBJECT: {
3879
3824
  result = stddev_ki_native_object(self, src, slab_axes, naxes, keep_axis, min_count);
3825
+ RB_GC_GUARD(self);
3880
3826
  break;
3881
3827
  }
3882
3828
  default:
3883
- rb_raise(rb_eCADataTypeError, "stddev_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, cmplx64, cmplx128, bool, object)", ca_type_name[src->data_type]);
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]);
3884
3831
  }
3885
3832
  if ( ! NIL_P(rfval) ) {
3886
- if ( result == CA_UNDEF ) {
3887
- result = rfval;
3888
- } else if ( TYPE(result) != T_FLOAT && rb_obj_is_kind_of(result, rb_cCArray) ) {
3889
- CArray *cr;
3890
- GetCArray(result, cr);
3891
- if ( ca_has_mask(cr) ) {
3892
- result = rb_ca_mask_fill_copy(result, rfval);
3893
- }
3894
- }
3833
+ result = ca_reduce_fill(result, rfval, fill_whole);
3895
3834
  }
3896
3835
  return result;
3897
3836
  }