carray 3.0.2 → 3.0.3

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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +66 -71
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +132 -22
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +543 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +47 -49
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_random.c CHANGED
@@ -18,6 +18,7 @@
18
18
  ---------------------------------------------------------------------------- */
19
19
 
20
20
  #include "carray.h"
21
+ #include "carray_internal.h" /* ca_attach_window */
21
22
  #include <math.h>
22
23
  #include <string.h>
23
24
  #include <stdint.h>
@@ -47,12 +48,12 @@ enum {
47
48
  typedef struct {
48
49
  int kind;
49
50
  VALUE rng;
50
- CArray *state; /* attached for CA_RNG_OWN, else NULL */
51
- int64_t *cells;
51
+ CArray *state; /* the CArray::Rng state for CA_RNG_OWN, else NULL */
52
+ int64_t *cells; /* state->ptr, once the window has attached it */
52
53
  } ca_rng_t;
53
54
 
54
55
  static void
55
- ca_rng_open (VALUE rng, ca_rng_t *source)
56
+ ca_rng_resolve (VALUE rng, ca_rng_t *source)
56
57
  {
57
58
  source->rng = rng;
58
59
  source->state = NULL;
@@ -64,27 +65,46 @@ ca_rng_open (VALUE rng, ca_rng_t *source)
64
65
  VALUE state = rb_ivar_get(rng, rb_intern("@state"));
65
66
  CArray *ca;
66
67
  TypedData_Get_Struct(state, CArray, &carray_data_type, ca);
67
- ca_attach(ca);
68
68
  source->kind = CA_RNG_OWN;
69
69
  source->state = ca;
70
- source->cells = (int64_t *) ca->ptr;
71
70
  }
72
71
  else {
73
72
  source->kind = CA_RNG_RUBY;
74
73
  }
75
74
  }
76
75
 
77
- /* Writes the advanced state back where it came from. Safe to call twice,
78
- which is what lets an error path close before it raises. */
76
+ /* The fills below draw inside a window over the array and, for a
77
+ CArray::Rng, the generator's state. A Ruby generator (rng: a
78
+ ::Random or anything with #rand) is called per draw and can raise part
79
+ way; the window then pushes back the cells drawn so far, and the state
80
+ they advanced, as it does when the fill completes. */
81
+
82
+ typedef struct {
83
+ CArray *ca;
84
+ ca_rng_t source;
85
+ ca_size_t n;
86
+ int is_default;
87
+ double low_dbl, high_dbl;
88
+ long low_long;
89
+ unsigned long limit;
90
+ int axis; /* shuffle!: -1 = all cells */
91
+ } ca_random_ctx_t;
92
+
79
93
  static void
80
- ca_rng_close (ca_rng_t *source)
94
+ ca_random_run (ca_random_ctx_t *c, VALUE rng, VALUE (*fill)(VALUE))
81
95
  {
82
- if (source->state) {
83
- ca_sync(source->state);
84
- ca_detach(source->state);
85
- source->state = NULL;
86
- source->cells = NULL;
87
- }
96
+ CArray *list[2];
97
+ ca_rng_resolve(rng, &c->source);
98
+ list[0] = c->ca;
99
+ list[1] = c->source.state;
100
+ ca_attach_window(2, list, "ww", fill, (VALUE) c);
101
+ }
102
+
103
+ /* The state is attached once the window is open. */
104
+ static void
105
+ ca_rng_bind (ca_rng_t *source)
106
+ {
107
+ source->cells = source->state ? (int64_t *) source->state->ptr : NULL;
88
108
  }
89
109
 
90
110
  static inline double
@@ -129,117 +149,28 @@ ca_random_ulong_limited (ca_rng_t *source, unsigned long limit)
129
149
 
130
150
  /* ---- random! ----------------------------------------------------------- */
131
151
 
132
- /* CArray#random!([low,] [high], rng:) — fill self with uniform random
133
- * numbers in-place, returning self. Range surface mirrors Numo/NumPy
134
- * (half-open [low, high) as the default) plus Ruby idiom (`..` closed,
135
- * `...` half-open):
136
- *
137
- * random! float [0.0, 1.0); integer -> ArgumentError
138
- * random!(high) [0, high) (Ruby rand shorthand)
139
- * random!(low, high) [low, high) (Numo positional)
140
- * random!(a..b) [a, b] closed (integer: b included)
141
- * random!(a...b) [a, b) half-open
142
- * random!(..., rng: r) any of the above with a custom Random source
143
- *
144
- * For float, `..` and `...` return the same distribution (endpoint
145
- * probability ~2^-53); the closed form is honored syntactically but not
146
- * enforced at the mantissa level, matching NumPy/SciPy convention.
147
- * Boolean fills 0/1 with 50% probability each and ignores the range
148
- * argument. Rejects CA_OBJECT / CA_FIXLEN. */
149
152
  static VALUE
150
- rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
153
+ ca_random_bang_fill (VALUE arg)
151
154
  {
152
- CArray *ca;
153
- VALUE arg1 = Qnil, arg2 = Qnil, opts = Qnil;
154
- VALUE low_val = Qnil, high_val = Qnil;
155
- int is_default = 1;
156
- int high_is_closed = 0;
157
- double low_dbl = 0.0, high_dbl = 0.0;
158
- long low_long = 0, high_long = 0;
159
- unsigned long limit = 0;
160
- ca_size_t i, n;
161
- VALUE rng = Qnil;
162
- ca_rng_t source;
163
-
164
- rb_scan_args(argc, argv, "02:", &arg1, &arg2, &opts);
165
- rb_scan_options(opts, "rng", &rng);
166
-
167
- /* Parse positional args into (low_val, high_val) + high_is_closed. */
168
- if (rb_obj_is_kind_of(arg1, rb_cRange)) {
169
- if (!NIL_P(arg2)) {
170
- rb_raise(rb_eArgError,
171
- "random: cannot combine a Range with a second positional arg");
172
- }
173
- low_val = rb_funcall(arg1, rb_intern("begin"), 0);
174
- high_val = rb_funcall(arg1, rb_intern("end"), 0);
175
- if (NIL_P(low_val) || NIL_P(high_val)) {
176
- rb_raise(rb_eArgError,
177
- "random: Range must have finite begin and end");
178
- }
179
- high_is_closed = ! RTEST(rb_funcall(arg1, rb_intern("exclude_end?"), 0));
180
- is_default = 0;
181
- } else if (!NIL_P(arg1) && !NIL_P(arg2)) {
182
- low_val = arg1;
183
- high_val = arg2;
184
- is_default = 0;
185
- } else if (!NIL_P(arg1)) {
186
- low_val = INT2FIX(0);
187
- high_val = arg1;
188
- is_default = 0;
189
- }
190
-
191
- rb_ca_modify(self);
192
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
193
-
194
- if (ca->data_type == CA_OBJECT || ca->data_type == CA_FIXLEN) {
195
- rb_raise(rb_eCADataTypeError,
196
- "random! is not supported for object/fixlen arrays");
197
- }
198
-
199
- /* Materialize (low, high) as the correct scalar type and validate.
200
- * For integer data types, `..` closed adds 1 to high (turns into half-open
201
- * for the sampler); for float data types, closed and half-open are
202
- * equivalent so no adjustment. */
203
- if (!is_default) {
204
- int is_integer_type = (ca->data_type >= CA_INT8
205
- && ca->data_type <= CA_UINT64);
206
- if (is_integer_type) {
207
- low_long = NUM2LONG(low_val);
208
- high_long = NUM2LONG(high_val);
209
- if (high_is_closed) high_long += 1;
210
- if (low_long >= high_long) {
211
- rb_raise(rb_eArgError,
212
- "random: low must be less than high "
213
- "(got low=%ld, high=%ld)",
214
- low_long, high_long);
215
- }
216
- limit = (unsigned long)(high_long - low_long) - 1;
217
- } else {
218
- low_dbl = NUM2DBL(low_val);
219
- high_dbl = NUM2DBL(high_val);
220
- if (low_dbl >= high_dbl) {
221
- rb_raise(rb_eArgError,
222
- "random: low must be less than high "
223
- "(got low=%g, high=%g)",
224
- low_dbl, high_dbl);
225
- }
226
- }
227
- }
228
-
229
- n = ca->elements;
230
- ca_attach(ca);
231
- ca_rng_open(rng, &source);
232
-
155
+ ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
156
+ CArray *ca = c->ca;
157
+ ca_size_t i, n = c->n;
158
+ int is_default = c->is_default;
159
+ double low_dbl = c->low_dbl, high_dbl = c->high_dbl;
160
+ long low_long = c->low_long;
161
+ unsigned long limit = c->limit;
162
+
163
+ ca_rng_bind(&c->source);
233
164
  switch (ca->data_type) {
234
165
  case CA_FLOAT64: {
235
166
  double *p = (double *)ca->ptr;
236
167
  if (is_default) {
237
168
  for (i = 0; i < n; i++)
238
- p[i] = ca_random_real(&source);
169
+ p[i] = ca_random_real(&c->source);
239
170
  } else {
240
171
  double range = high_dbl - low_dbl;
241
172
  for (i = 0; i < n; i++)
242
- p[i] = low_dbl + ca_random_real(&source) * range;
173
+ p[i] = low_dbl + ca_random_real(&c->source) * range;
243
174
  }
244
175
  break;
245
176
  }
@@ -247,11 +178,11 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
247
178
  float *p = (float *)ca->ptr;
248
179
  if (is_default) {
249
180
  for (i = 0; i < n; i++)
250
- p[i] = (float)ca_random_real(&source);
181
+ p[i] = (float)ca_random_real(&c->source);
251
182
  } else {
252
183
  double range = high_dbl - low_dbl;
253
184
  for (i = 0; i < n; i++)
254
- p[i] = (float)(low_dbl + ca_random_real(&source) * range);
185
+ p[i] = (float)(low_dbl + ca_random_real(&c->source) * range);
255
186
  }
256
187
  break;
257
188
  }
@@ -259,12 +190,12 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
259
190
  double complex *p = (double complex *)ca->ptr;
260
191
  if (is_default) {
261
192
  for (i = 0; i < n; i++)
262
- p[i] = ca_random_real(&source) + ca_random_real(&source) * I;
193
+ p[i] = ca_random_real(&c->source) + ca_random_real(&c->source) * I;
263
194
  } else {
264
195
  double range = high_dbl - low_dbl;
265
196
  for (i = 0; i < n; i++)
266
- p[i] = (low_dbl + ca_random_real(&source) * range)
267
- + (low_dbl + ca_random_real(&source) * range) * I;
197
+ p[i] = (low_dbl + ca_random_real(&c->source) * range)
198
+ + (low_dbl + ca_random_real(&c->source) * range) * I;
268
199
  }
269
200
  break;
270
201
  }
@@ -272,27 +203,24 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
272
203
  float complex *p = (float complex *)ca->ptr;
273
204
  if (is_default) {
274
205
  for (i = 0; i < n; i++)
275
- p[i] = (float)ca_random_real(&source) + (float)ca_random_real(&source) * I;
206
+ p[i] = (float)ca_random_real(&c->source) + (float)ca_random_real(&c->source) * I;
276
207
  } else {
277
208
  double range = high_dbl - low_dbl;
278
209
  for (i = 0; i < n; i++)
279
- p[i] = (float)(low_dbl + ca_random_real(&source) * range)
280
- + (float)(low_dbl + ca_random_real(&source) * range) * I;
210
+ p[i] = (float)(low_dbl + ca_random_real(&c->source) * range)
211
+ + (float)(low_dbl + ca_random_real(&c->source) * range) * I;
281
212
  }
282
213
  break;
283
214
  }
284
215
  case CA_BOOLEAN: {
285
216
  boolean8_t *p = (boolean8_t *)ca->ptr;
286
217
  for (i = 0; i < n; i++)
287
- p[i] = (ca_random_real(&source) < 0.5) ? 1 : 0;
218
+ p[i] = (ca_random_real(&c->source) < 0.5) ? 1 : 0;
288
219
  break;
289
220
  }
290
221
  default: {
291
222
  /* integer types: CA_INT8..CA_UINT64 */
292
223
  if (is_default) {
293
- ca_rng_close(&source);
294
- ca_sync(ca);
295
- ca_detach(ca);
296
224
  rb_raise(rb_eArgError,
297
225
  "random! on an integer array requires a range: "
298
226
  "a.random!(high), a.random!(low, high), or "
@@ -302,55 +230,52 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
302
230
  case CA_INT8: {
303
231
  int8_t *p = (int8_t *)ca->ptr;
304
232
  for (i = 0; i < n; i++)
305
- p[i] = (int8_t)(low_long + ca_random_ulong_limited(&source, limit));
233
+ p[i] = (int8_t)(low_long + ca_random_ulong_limited(&c->source, limit));
306
234
  break;
307
235
  }
308
236
  case CA_UINT8: {
309
237
  uint8_t *p = (uint8_t *)ca->ptr;
310
238
  for (i = 0; i < n; i++)
311
- p[i] = (uint8_t)(low_long + ca_random_ulong_limited(&source, limit));
239
+ p[i] = (uint8_t)(low_long + ca_random_ulong_limited(&c->source, limit));
312
240
  break;
313
241
  }
314
242
  case CA_INT16: {
315
243
  int16_t *p = (int16_t *)ca->ptr;
316
244
  for (i = 0; i < n; i++)
317
- p[i] = (int16_t)(low_long + ca_random_ulong_limited(&source, limit));
245
+ p[i] = (int16_t)(low_long + ca_random_ulong_limited(&c->source, limit));
318
246
  break;
319
247
  }
320
248
  case CA_UINT16: {
321
249
  uint16_t *p = (uint16_t *)ca->ptr;
322
250
  for (i = 0; i < n; i++)
323
- p[i] = (uint16_t)(low_long + ca_random_ulong_limited(&source, limit));
251
+ p[i] = (uint16_t)(low_long + ca_random_ulong_limited(&c->source, limit));
324
252
  break;
325
253
  }
326
254
  case CA_INT32: {
327
255
  int32_t *p = (int32_t *)ca->ptr;
328
256
  for (i = 0; i < n; i++)
329
- p[i] = (int32_t)(low_long + ca_random_ulong_limited(&source, limit));
257
+ p[i] = (int32_t)(low_long + ca_random_ulong_limited(&c->source, limit));
330
258
  break;
331
259
  }
332
260
  case CA_UINT32: {
333
261
  uint32_t *p = (uint32_t *)ca->ptr;
334
262
  for (i = 0; i < n; i++)
335
- p[i] = (uint32_t)(low_long + ca_random_ulong_limited(&source, limit));
263
+ p[i] = (uint32_t)(low_long + ca_random_ulong_limited(&c->source, limit));
336
264
  break;
337
265
  }
338
266
  case CA_INT64: {
339
267
  int64_t *p = (int64_t *)ca->ptr;
340
268
  for (i = 0; i < n; i++)
341
- p[i] = (int64_t)(low_long + ca_random_ulong_limited(&source, limit));
269
+ p[i] = (int64_t)(low_long + ca_random_ulong_limited(&c->source, limit));
342
270
  break;
343
271
  }
344
272
  case CA_UINT64: {
345
273
  uint64_t *p = (uint64_t *)ca->ptr;
346
274
  for (i = 0; i < n; i++)
347
- p[i] = (uint64_t)(low_long + ca_random_ulong_limited(&source, limit));
275
+ p[i] = (uint64_t)(low_long + ca_random_ulong_limited(&c->source, limit));
348
276
  break;
349
277
  }
350
278
  default:
351
- ca_rng_close(&source);
352
- ca_sync(ca);
353
- ca_detach(ca);
354
279
  rb_raise(rb_eCADataTypeError,
355
280
  "random! is not supported for this data type");
356
281
  }
@@ -358,9 +283,115 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
358
283
  }
359
284
  }
360
285
 
361
- ca_rng_close(&source);
362
- ca_sync(ca);
363
- ca_detach(ca);
286
+ return Qnil;
287
+ }
288
+
289
+ /* CArray#random!([low,] [high], rng:) — fill self with uniform random
290
+ * numbers in-place, returning self. Range surface mirrors Numo/NumPy
291
+ * (half-open [low, high) as the default) plus Ruby idiom (`..` closed,
292
+ * `...` half-open):
293
+ *
294
+ * random! float [0.0, 1.0); integer -> ArgumentError
295
+ * random!(high) [0, high) (Ruby rand shorthand)
296
+ * random!(low, high) [low, high) (Numo positional)
297
+ * random!(a..b) [a, b] closed (integer: b included)
298
+ * random!(a...b) [a, b) half-open
299
+ * random!(..., rng: r) any of the above with a custom Random source
300
+ *
301
+ * For float, `..` and `...` return the same distribution (endpoint
302
+ * probability ~2^-53); the closed form is honored syntactically but not
303
+ * enforced at the mantissa level, matching NumPy/SciPy convention.
304
+ * Boolean fills 0/1 with 50% probability each and ignores the range
305
+ * argument. Rejects CA_OBJECT / CA_FIXLEN. */
306
+ static VALUE
307
+ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
308
+ {
309
+ CArray *ca;
310
+ VALUE arg1 = Qnil, arg2 = Qnil, opts = Qnil;
311
+ VALUE low_val = Qnil, high_val = Qnil;
312
+ int is_default = 1;
313
+ int high_is_closed = 0;
314
+ double low_dbl = 0.0, high_dbl = 0.0;
315
+ long low_long = 0, high_long = 0;
316
+ unsigned long limit = 0;
317
+ VALUE rng = Qnil;
318
+
319
+ rb_scan_args(argc, argv, "02:", &arg1, &arg2, &opts);
320
+ rb_scan_options(opts, "rng", &rng);
321
+
322
+ /* Parse positional args into (low_val, high_val) + high_is_closed. */
323
+ if (rb_obj_is_kind_of(arg1, rb_cRange)) {
324
+ if (!NIL_P(arg2)) {
325
+ rb_raise(rb_eArgError,
326
+ "random: cannot combine a Range with a second positional arg");
327
+ }
328
+ low_val = rb_funcall(arg1, rb_intern("begin"), 0);
329
+ high_val = rb_funcall(arg1, rb_intern("end"), 0);
330
+ if (NIL_P(low_val) || NIL_P(high_val)) {
331
+ rb_raise(rb_eArgError,
332
+ "random: Range must have finite begin and end");
333
+ }
334
+ high_is_closed = ! RTEST(rb_funcall(arg1, rb_intern("exclude_end?"), 0));
335
+ is_default = 0;
336
+ } else if (!NIL_P(arg1) && !NIL_P(arg2)) {
337
+ low_val = arg1;
338
+ high_val = arg2;
339
+ is_default = 0;
340
+ } else if (!NIL_P(arg1)) {
341
+ low_val = INT2FIX(0);
342
+ high_val = arg1;
343
+ is_default = 0;
344
+ }
345
+
346
+ rb_ca_modify(self);
347
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
348
+
349
+ if (ca->data_type == CA_OBJECT || ca->data_type == CA_FIXLEN) {
350
+ rb_raise(rb_eCADataTypeError,
351
+ "random! is not supported for object/fixlen arrays");
352
+ }
353
+
354
+ /* Materialize (low, high) as the correct scalar type and validate.
355
+ * For integer data types, `..` closed adds 1 to high (turns into half-open
356
+ * for the sampler); for float data types, closed and half-open are
357
+ * equivalent so no adjustment. */
358
+ if (!is_default) {
359
+ int is_integer_type = (ca->data_type >= CA_INT8
360
+ && ca->data_type <= CA_UINT64);
361
+ if (is_integer_type) {
362
+ low_long = NUM2LONG(low_val);
363
+ high_long = NUM2LONG(high_val);
364
+ if (high_is_closed) high_long += 1;
365
+ if (low_long >= high_long) {
366
+ rb_raise(rb_eArgError,
367
+ "random: low must be less than high "
368
+ "(got low=%ld, high=%ld)",
369
+ low_long, high_long);
370
+ }
371
+ limit = (unsigned long)(high_long - low_long) - 1;
372
+ } else {
373
+ low_dbl = NUM2DBL(low_val);
374
+ high_dbl = NUM2DBL(high_val);
375
+ if (low_dbl >= high_dbl) {
376
+ rb_raise(rb_eArgError,
377
+ "random: low must be less than high "
378
+ "(got low=%g, high=%g)",
379
+ low_dbl, high_dbl);
380
+ }
381
+ }
382
+ }
383
+
384
+ {
385
+ ca_random_ctx_t c;
386
+ c.ca = ca;
387
+ c.n = ca->elements;
388
+ c.is_default = is_default;
389
+ c.low_dbl = low_dbl;
390
+ c.high_dbl = high_dbl;
391
+ c.low_long = low_long;
392
+ c.limit = limit;
393
+ ca_random_run(&c, rng, ca_random_bang_fill);
394
+ }
364
395
  return self;
365
396
  }
366
397
 
@@ -396,53 +427,31 @@ box_muller_pair(ca_rng_t *source, double *r1, double *r2)
396
427
  *r2 = r * sin(theta);
397
428
  }
398
429
 
399
- /* CArray#randomn!(rng:) — fill self with standard normal N(0, 1)
400
- * samples in-place via Box-Muller, returning self.
401
- *
402
- * Restricted to float / complex data types. Complex fills real + imag as
403
- * two independent normals per cell. */
404
430
  static VALUE
405
- rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
431
+ ca_randomn_bang_fill (VALUE arg)
406
432
  {
407
- CArray *ca;
408
- VALUE opts = Qnil;
409
- ca_size_t i, n;
410
-
411
- rb_scan_args(argc, argv, "0:", &opts);
412
- VALUE rng = Qnil;
413
- ca_rng_t source;
414
- rb_scan_options(opts, "rng", &rng);
415
-
416
- rb_ca_modify(self);
417
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
418
-
419
- if (ca->data_type != CA_FLOAT64 && ca->data_type != CA_FLOAT32 &&
420
- ca->data_type != CA_CMPLX128 && ca->data_type != CA_CMPLX64) {
421
- rb_raise(rb_eCADataTypeError,
422
- "randomn! requires float or complex array");
423
- }
424
-
425
- n = ca->elements;
426
- ca_attach(ca);
427
- ca_rng_open(rng, &source);
433
+ ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
434
+ CArray *ca = c->ca;
435
+ ca_size_t i, n = c->n;
428
436
 
437
+ ca_rng_bind(&c->source);
429
438
  /* A CArray::Rng fills one cell per call rather than two, so that this
430
439
  and a kernel drawing afterwards are the one sequence: both are
431
440
  `ca_random_normal` run once per cell. It costs a draw a cell over
432
441
  the paired form, and what it buys is that where the generator
433
442
  stands can be worked out rather than run. */
434
- if (source.kind == CA_RNG_OWN) {
443
+ if (c->source.kind == CA_RNG_OWN) {
435
444
  switch (ca->data_type) {
436
445
  case CA_FLOAT64: {
437
446
  double *p = (double *)ca->ptr;
438
447
  for (i = 0; i < n; i++)
439
- p[i] = ca_random_normal(&source);
448
+ p[i] = ca_random_normal(&c->source);
440
449
  break;
441
450
  }
442
451
  case CA_FLOAT32: {
443
452
  float *p = (float *)ca->ptr;
444
453
  for (i = 0; i < n; i++)
445
- p[i] = (float) ca_random_normal(&source);
454
+ p[i] = (float) ca_random_normal(&c->source);
446
455
  break;
447
456
  }
448
457
  case CA_CMPLX128: {
@@ -450,8 +459,8 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
450
459
  for (i = 0; i < n; i++) {
451
460
  /* Into locals first: C does not say which order a call's
452
461
  arguments are evaluated in, and these two advance a state. */
453
- double re = ca_random_normal(&source);
454
- double im = ca_random_normal(&source);
462
+ double re = ca_random_normal(&c->source);
463
+ double im = ca_random_normal(&c->source);
455
464
  p[i] = re + im * I;
456
465
  }
457
466
  break;
@@ -459,8 +468,8 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
459
468
  case CA_CMPLX64: {
460
469
  float complex *p = (float complex *)ca->ptr;
461
470
  for (i = 0; i < n; i++) {
462
- double re = ca_random_normal(&source);
463
- double im = ca_random_normal(&source);
471
+ double re = ca_random_normal(&c->source);
472
+ double im = ca_random_normal(&c->source);
464
473
  p[i] = (float) re + (float) im * I;
465
474
  }
466
475
  break;
@@ -468,10 +477,7 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
468
477
  default:
469
478
  break;
470
479
  }
471
- ca_rng_close(&source);
472
- ca_sync(ca);
473
- ca_detach(ca);
474
- return self;
480
+ return Qnil;
475
481
  }
476
482
 
477
483
  switch (ca->data_type) {
@@ -479,11 +485,11 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
479
485
  double *p = (double *)ca->ptr;
480
486
  ca_size_t pairs = n / 2;
481
487
  for (i = 0; i < pairs; i++) {
482
- box_muller_pair(&source, &p[2*i], &p[2*i+1]);
488
+ box_muller_pair(&c->source, &p[2*i], &p[2*i+1]);
483
489
  }
484
490
  if (n % 2 == 1) {
485
491
  double r1, r2;
486
- box_muller_pair(&source, &r1, &r2);
492
+ box_muller_pair(&c->source, &r1, &r2);
487
493
  p[n-1] = r1;
488
494
  }
489
495
  break;
@@ -493,13 +499,13 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
493
499
  ca_size_t pairs = n / 2;
494
500
  for (i = 0; i < pairs; i++) {
495
501
  double r1, r2;
496
- box_muller_pair(&source, &r1, &r2);
502
+ box_muller_pair(&c->source, &r1, &r2);
497
503
  p[2*i] = (float)r1;
498
504
  p[2*i+1] = (float)r2;
499
505
  }
500
506
  if (n % 2 == 1) {
501
507
  double r1, r2;
502
- box_muller_pair(&source, &r1, &r2);
508
+ box_muller_pair(&c->source, &r1, &r2);
503
509
  p[n-1] = (float)r1;
504
510
  }
505
511
  break;
@@ -508,7 +514,7 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
508
514
  double complex *p = (double complex *)ca->ptr;
509
515
  for (i = 0; i < n; i++) {
510
516
  double r1, r2;
511
- box_muller_pair(&source, &r1, &r2);
517
+ box_muller_pair(&c->source, &r1, &r2);
512
518
  p[i] = r1 + r2 * I;
513
519
  }
514
520
  break;
@@ -517,7 +523,7 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
517
523
  float complex *p = (float complex *)ca->ptr;
518
524
  for (i = 0; i < n; i++) {
519
525
  double r1, r2;
520
- box_muller_pair(&source, &r1, &r2);
526
+ box_muller_pair(&c->source, &r1, &r2);
521
527
  p[i] = (float)r1 + (float)r2 * I;
522
528
  }
523
529
  break;
@@ -526,9 +532,39 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
526
532
  break;
527
533
  }
528
534
 
529
- ca_rng_close(&source);
530
- ca_sync(ca);
531
- ca_detach(ca);
535
+ return Qnil;
536
+ }
537
+
538
+ /* CArray#randomn!(rng:) — fill self with standard normal N(0, 1)
539
+ * samples in-place via Box-Muller, returning self.
540
+ *
541
+ * Restricted to float / complex data types. Complex fills real + imag as
542
+ * two independent normals per cell. */
543
+ static VALUE
544
+ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
545
+ {
546
+ CArray *ca;
547
+ VALUE opts = Qnil;
548
+
549
+ rb_scan_args(argc, argv, "0:", &opts);
550
+ VALUE rng = Qnil;
551
+ rb_scan_options(opts, "rng", &rng);
552
+
553
+ rb_ca_modify(self);
554
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
555
+
556
+ if (ca->data_type != CA_FLOAT64 && ca->data_type != CA_FLOAT32 &&
557
+ ca->data_type != CA_CMPLX128 && ca->data_type != CA_CMPLX64) {
558
+ rb_raise(rb_eCADataTypeError,
559
+ "randomn! requires float or complex array");
560
+ }
561
+
562
+ {
563
+ ca_random_ctx_t c;
564
+ c.ca = ca;
565
+ c.n = ca->elements;
566
+ ca_random_run(&c, rng, ca_randomn_bang_fill);
567
+ }
532
568
  return self;
533
569
  }
534
570
 
@@ -550,59 +586,35 @@ swap_chunks(char *a, char *b, ca_size_t chunk_bytes, char *tmp)
550
586
  * as a byte chunk and swapped whole, so multi-dim slices move
551
587
  * together). */
552
588
  static VALUE
553
- rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
589
+ ca_shuffle_bang_fill (VALUE arg)
554
590
  {
555
- CArray *ca;
556
- VALUE opts = Qnil, v_axis = Qnil;
591
+ ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
592
+ CArray *ca = c->ca;
557
593
  ca_size_t n;
558
- int axis = -1;
559
594
 
560
- rb_scan_args(argc, argv, "0:", &opts);
561
- VALUE rng = Qnil;
562
- ca_rng_t source;
563
- rb_scan_options(opts, "rng,axis", &rng, &v_axis);
564
-
565
- rb_ca_modify(self);
566
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
567
-
568
- if (ca->elements <= 1) return self;
569
-
570
- ca_attach(ca);
571
- ca_rng_open(rng, &source);
595
+ ca_rng_bind(&c->source);
572
596
 
573
- if (NIL_P(v_axis)) {
597
+ if (c->axis < 0) {
574
598
  /* shuffle all elements */
575
- n = ca->elements;
599
+ volatile VALUE tmp_holder;
576
600
  ca_size_t elem_bytes = ca->bytes;
577
- char *tmp = (char *)xmalloc(elem_bytes);
601
+ char *tmp = ALLOCV_N(char, tmp_holder, elem_bytes);
578
602
  char *p = ca->ptr;
579
603
 
604
+ n = ca->elements;
580
605
  for (ca_size_t i = n - 1; i > 0; i--) {
581
- unsigned long j = ca_random_ulong_limited(&source, (unsigned long)i);
606
+ unsigned long j = ca_random_ulong_limited(&c->source, (unsigned long)i);
582
607
  if ((ca_size_t)j != i) {
583
608
  swap_chunks(p + i * elem_bytes, p + j * elem_bytes, elem_bytes, tmp);
584
609
  }
585
610
  }
586
- xfree(tmp);
611
+ ALLOCV_END(tmp_holder);
587
612
  }
588
613
  else {
589
- axis = NUM2INT(v_axis);
590
- if (axis < 0) axis += ca->ndim;
591
- if (axis < 0 || axis >= ca->ndim) {
592
- ca_rng_close(&source);
593
- ca_sync(ca);
594
- ca_detach(ca);
595
- rb_raise(rb_eArgError,
596
- "axis %d is out of range for ndim %d", axis, ca->ndim);
597
- }
614
+ int axis = c->axis;
615
+ volatile VALUE tmp_holder;
598
616
 
599
617
  n = ca->dim[axis];
600
- if (n <= 1) {
601
- ca_rng_close(&source);
602
- ca_sync(ca);
603
- ca_detach(ca);
604
- return self;
605
- }
606
618
 
607
619
  ca_size_t outer = 1;
608
620
  for (int d = 0; d < axis; d++)
@@ -614,12 +626,12 @@ rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
614
626
 
615
627
  ca_size_t chunk_bytes = inner * ca->bytes;
616
628
  ca_size_t stride = n * chunk_bytes;
617
- char *tmp = (char *)xmalloc(chunk_bytes);
629
+ char *tmp = ALLOCV_N(char, tmp_holder, chunk_bytes);
618
630
 
619
631
  for (ca_size_t o = 0; o < outer; o++) {
620
632
  char *base = ca->ptr + o * stride;
621
633
  for (ca_size_t i = n - 1; i > 0; i--) {
622
- unsigned long j = ca_random_ulong_limited(&source, (unsigned long)i);
634
+ unsigned long j = ca_random_ulong_limited(&c->source, (unsigned long)i);
623
635
  if ((ca_size_t)j != i) {
624
636
  swap_chunks(base + i * chunk_bytes,
625
637
  base + j * chunk_bytes,
@@ -627,12 +639,38 @@ rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
627
639
  }
628
640
  }
629
641
  }
630
- xfree(tmp);
642
+ ALLOCV_END(tmp_holder);
643
+ }
644
+ return Qnil;
645
+ }
646
+
647
+ static VALUE
648
+ rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
649
+ {
650
+ CArray *ca;
651
+ VALUE opts = Qnil, v_axis = Qnil;
652
+ int axis = -1;
653
+ ca_random_ctx_t c;
654
+
655
+ rb_scan_args(argc, argv, "0:", &opts);
656
+ VALUE rng = Qnil;
657
+ rb_scan_options(opts, "rng,axis", &rng, &v_axis);
658
+
659
+ rb_ca_modify(self);
660
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
661
+
662
+ if (ca->elements <= 1) return self;
663
+
664
+ if (!NIL_P(v_axis)) {
665
+ axis = rb_ca_normalize_axis_for_ndim(ca_axis_integer(v_axis, NULL), ca->ndim, NULL);
666
+ if (ca->dim[axis] <= 1) {
667
+ return self;
668
+ }
631
669
  }
632
670
 
633
- ca_rng_close(&source);
634
- ca_sync(ca);
635
- ca_detach(ca);
671
+ c.ca = ca;
672
+ c.axis = axis;
673
+ ca_random_run(&c, rng, ca_shuffle_bang_fill);
636
674
  return self;
637
675
  }
638
676
 
@@ -698,7 +736,7 @@ ca_rng_cells (VALUE self, int64_t **cells)
698
736
  CArray *ca;
699
737
  VALUE state = rb_ivar_get(self, rb_intern("@state"));
700
738
  TypedData_Get_Struct(state, CArray, &carray_data_type, ca);
701
- ca_attach(ca);
739
+ ca_attach(ca); /* window: the state is an entity, whose attach holds nothing */
702
740
  *cells = (int64_t *) ca->ptr;
703
741
  return ca;
704
742
  }
@@ -752,8 +790,7 @@ rb_ca_rng_reset (int argc, VALUE *argv, VALUE self)
752
790
 
753
791
  ca = ca_rng_cells(self, &cells);
754
792
  ca_xoshiro256pp_seed(cells, (uint64_t) NUM2ULL(masked));
755
- ca_sync(ca);
756
- ca_detach(ca);
793
+ ca_sync_detach(ca);
757
794
 
758
795
  rb_ivar_set(self, rb_intern("@seed"), seed);
759
796
  return self;
@@ -779,8 +816,7 @@ rb_ca_rng_random (VALUE self)
779
816
 
780
817
  ca = ca_rng_cells(self, &cells);
781
818
  value = ca_xoshiro256pp_next_real(cells);
782
- ca_sync(ca);
783
- ca_detach(ca);
819
+ ca_sync_detach(ca);
784
820
  return rb_float_new(value);
785
821
  }
786
822
 
@@ -799,8 +835,7 @@ rb_ca_rng_bits (VALUE self)
799
835
 
800
836
  ca = ca_rng_cells(self, &cells);
801
837
  value = ca_xoshiro256pp_next(cells);
802
- ca_sync(ca);
803
- ca_detach(ca);
838
+ ca_sync_detach(ca);
804
839
  return ULL2NUM(value);
805
840
  }
806
841
 
@@ -823,8 +858,7 @@ rb_ca_rng_randomn (VALUE self)
823
858
  source.state = NULL;
824
859
  source.cells = cells;
825
860
  value = ca_random_normal(&source);
826
- ca_sync(ca);
827
- ca_detach(ca);
861
+ ca_sync_detach(ca);
828
862
  return rb_float_new(value);
829
863
  }
830
864