carray 3.0.3 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
data/ext/carray_element.c CHANGED
@@ -20,18 +20,35 @@
20
20
  #include "ca_obj_face.h"
21
21
  #include "carray_internal.h" /* ca_obj_extremum_replaces */
22
22
 
23
+ /* An element index is an Integer, as it is for `[]`; a Float would be cut
24
+ to an integer by NUM2SIZE. */
25
+ static inline ca_size_t
26
+ elem_index_value (VALUE v)
27
+ {
28
+ if ( FIXNUM_P(v) ) return (ca_size_t) FIX2LONG(v);
29
+ if ( ! RB_INTEGER_TYPE_P(v) ) {
30
+ rb_raise(rb_eIndexError, "object '%"PRIsVALUE"' is invalid for an index",
31
+ rb_inspect(v));
32
+ }
33
+ return NUM2SIZE(v);
34
+ }
35
+
23
36
  /* T_ARRAY index parser. ridx is assumed already known to be T_ARRAY.
24
37
  Direct VALUE * access + FIXNUM_P fast path eliminates rb_ary_entry
25
38
  bounds check and NUM2SIZE Bignum branching for the common case. */
26
39
  static inline void
27
40
  elem_parse_idx_array (CArray *ca, VALUE rary, ca_size_t *idx)
28
41
  {
29
- const VALUE *iarr = RARRAY_CONST_PTR(rary);
42
+ const VALUE *iarr;
30
43
  int8_t ndim = ca->ndim;
31
44
  int8_t i;
45
+ if ( RARRAY_LEN(rary) != ndim ) {
46
+ rb_raise(rb_eIndexError, "%ld indices given for a carray of ndim %i",
47
+ (long) RARRAY_LEN(rary), (int) ndim);
48
+ }
49
+ iarr = RARRAY_CONST_PTR(rary);
32
50
  for (i=0; i<ndim; i++) {
33
- VALUE v = iarr[i];
34
- ca_size_t k = FIXNUM_P(v) ? (ca_size_t)FIX2LONG(v) : NUM2SIZE(v);
51
+ ca_size_t k = elem_index_value(iarr[i]);
35
52
  CA_CHECK_INDEX(k, ca->dim[i]);
36
53
  idx[i] = k;
37
54
  }
@@ -41,7 +58,7 @@ elem_parse_idx_array (CArray *ca, VALUE rary, ca_size_t *idx)
41
58
  static inline ca_size_t
42
59
  elem_parse_idx_scalar (CArray *ca, VALUE ridx)
43
60
  {
44
- ca_size_t k = FIXNUM_P(ridx) ? (ca_size_t)FIX2LONG(ridx) : NUM2SIZE(ridx);
61
+ ca_size_t k = elem_index_value(ridx);
45
62
  CA_CHECK_INDEX(k, ca->elements);
46
63
  return k;
47
64
  }
@@ -278,33 +295,56 @@ rb_ca_elem_store (VALUE self, VALUE ridx, VALUE obj)
278
295
  if ( FIXNUM_P(obj) ) { *(int64_t *)p = (int64_t)FIX2LONG(obj); return obj; }
279
296
  break;
280
297
  case CA_INT32:
281
- if ( FIXNUM_P(obj) ) { *(int32_t *)p = (int32_t)FIX2LONG(obj); return obj; }
298
+ if ( FIXNUM_P(obj) && FIX2LONG(obj) >= INT32_MIN && FIX2LONG(obj) <= INT32_MAX ) {
299
+ /* fits: checked against the type's range above */
300
+ *(int32_t *)p = (int32_t)FIX2LONG(obj); return obj;
301
+ }
282
302
  break;
283
303
  case CA_INT16:
284
- if ( FIXNUM_P(obj) ) { *(int16_t *)p = (int16_t)FIX2LONG(obj); return obj; }
304
+ if ( FIXNUM_P(obj) && FIX2LONG(obj) >= INT16_MIN && FIX2LONG(obj) <= INT16_MAX ) {
305
+ /* fits: checked against the type's range above */
306
+ *(int16_t *)p = (int16_t)FIX2LONG(obj); return obj;
307
+ }
285
308
  break;
286
309
  case CA_INT8:
287
- if ( FIXNUM_P(obj) ) { *(int8_t *)p = (int8_t)FIX2LONG(obj); return obj; }
310
+ if ( FIXNUM_P(obj) && FIX2LONG(obj) >= INT8_MIN && FIX2LONG(obj) <= INT8_MAX ) {
311
+ /* fits: checked against the type's range above */
312
+ *(int8_t *)p = (int8_t)FIX2LONG(obj); return obj;
313
+ }
288
314
  break;
289
315
  case CA_UINT64:
290
- if ( FIXNUM_P(obj) ) { *(uint64_t *)p = (uint64_t)FIX2LONG(obj); return obj; }
316
+ if ( FIXNUM_P(obj) && FIX2LONG(obj) >= 0 ) {
317
+ *(uint64_t *)p = (uint64_t)FIX2LONG(obj); return obj;
318
+ }
291
319
  break;
292
320
  case CA_UINT32:
293
- if ( FIXNUM_P(obj) ) { *(uint32_t *)p = (uint32_t)FIX2LONG(obj); return obj; }
321
+ if ( FIXNUM_P(obj) && FIX2LONG(obj) >= 0 && FIX2LONG(obj) <= (long) UINT32_MAX ) {
322
+ /* fits: checked against the type's range above */
323
+ *(uint32_t *)p = (uint32_t)FIX2LONG(obj); return obj;
324
+ }
294
325
  break;
295
326
  case CA_UINT16:
296
- if ( FIXNUM_P(obj) ) { *(uint16_t *)p = (uint16_t)FIX2LONG(obj); return obj; }
327
+ if ( FIXNUM_P(obj) && FIX2LONG(obj) >= 0 && FIX2LONG(obj) <= (long) UINT16_MAX ) {
328
+ /* fits: checked against the type's range above */
329
+ *(uint16_t *)p = (uint16_t)FIX2LONG(obj); return obj;
330
+ }
297
331
  break;
298
332
  case CA_UINT8:
299
- if ( FIXNUM_P(obj) ) { *(uint8_t *)p = (uint8_t)FIX2LONG(obj); return obj; }
333
+ if ( FIXNUM_P(obj) && FIX2LONG(obj) >= 0 && FIX2LONG(obj) <= (long) UINT8_MAX ) {
334
+ /* fits: checked against the type's range above */
335
+ *(uint8_t *)p = (uint8_t)FIX2LONG(obj); return obj;
336
+ }
300
337
  break;
301
338
  case CA_BOOLEAN:
302
339
  if ( obj == Qtrue ) { *(uint8_t *)p = 1; return obj; }
303
340
  if ( obj == Qfalse ) { *(uint8_t *)p = 0; return obj; }
304
- if ( FIXNUM_P(obj) ) { *(uint8_t *)p = (FIX2LONG(obj) != 0); return obj; }
341
+ if ( obj == INT2FIX(0) || obj == INT2FIX(1) ) {
342
+ *(uint8_t *)p = (obj == INT2FIX(1)); return obj;
343
+ }
305
344
  break;
306
345
  }
307
- /* Fall through: data type matches but obj kind needs general coercion. */
346
+ /* Fall through: the value needs the general coercion, or does not fit
347
+ the cell, which the general path refuses. */
308
348
  }
309
349
  }
310
350
 
@@ -331,14 +371,12 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
331
371
 
332
372
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
333
373
 
334
- if ( ca_is_empty(ca) ) return Qnil;
335
-
336
374
  if ( TYPE(ridx) == T_ARRAY ) {
337
375
  volatile VALUE out;
338
376
  elem_parse_idx_array(ca, ridx, idx);
339
377
 
340
378
  /* Super-hot path: entity + common numeric data type + no mask.
341
- Bypass ca_fetch_index dispatch AND rb_ca_ptr2obj cast-table dispatch.
379
+ Bypass ca_fetch_index dispatch AND rb_ca_ptr2obj_storage cast-table dispatch.
342
380
  Covers the bench's float64 / int64 / int32 cases. */
343
381
  if ( ca_is_entity(ca) && ! ca->mask ) {
344
382
  ca_size_t addr = elem_entity_addr(ca, idx);
@@ -370,13 +408,13 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
370
408
  else {
371
409
  ca_fetch_index(ca, idx, v);
372
410
  }
373
- out = rb_ca_ptr2obj(self, v);
411
+ out = rb_ca_ptr2obj_storage(self, v);
374
412
  }
375
413
  else {
376
414
  volatile VALUE holder;
377
415
  char *v = ALLOCV_N(char, holder, ca->bytes);
378
416
  ca_fetch_index(ca, idx, v);
379
- out = rb_ca_ptr2obj(self, v);
417
+ out = rb_ca_ptr2obj_storage(self, v);
380
418
  ALLOCV_END(holder);
381
419
  }
382
420
 
@@ -749,6 +787,11 @@ rb_ca_elem_unmask (VALUE self, VALUE ridx)
749
787
  * rule (NaN is treated as missing: `min(NaN, v)` returns `v`,
750
788
  * `min(x, NaN)` returns `x`). Raises for non-numeric data_type. */
751
789
 
790
+ /* The entity fast path takes a Float into a float cell and an Integer
791
+ that fits the cell. Anything else -- a Float into an integer cell, an
792
+ Integer out of the type's range, a Bignum or Rational -- goes to the
793
+ general path, which compares in Ruby and stores as `self[idx] = v`
794
+ does, so a value that wins but does not fit raises. */
752
795
  #define ELEM_MINMAX_BODY(OP) do { \
753
796
  if ( ca_is_entity(ca) ) { \
754
797
  ca_size_t addr; \
@@ -766,14 +809,6 @@ rb_ca_elem_unmask (VALUE self, VALUE ridx)
766
809
  switch ( ca->data_type ) { \
767
810
  case CA_FLOAT64: { double *q=(double *)p; if (v OP *q || *q != *q) *q = v; return self; } \
768
811
  case CA_FLOAT32: { float *q=(float *)p; float vf=(float)v; if (vf OP *q || *q != *q) *q = vf; return self; } \
769
- case CA_INT64: { int64_t *q=(int64_t *)p; if ((int64_t)v OP *q) *q = (int64_t)v; return self; } \
770
- case CA_INT32: { int32_t *q=(int32_t *)p; if ((int32_t)v OP *q) *q = (int32_t)v; return self; } \
771
- case CA_INT16: { int16_t *q=(int16_t *)p; if ((int16_t)v OP *q) *q = (int16_t)v; return self; } \
772
- case CA_INT8: { int8_t *q=(int8_t *)p; if ((int8_t )v OP *q) *q = (int8_t )v; return self; } \
773
- case CA_UINT64: { uint64_t *q=(uint64_t*)p; if ((uint64_t)v OP *q) *q = (uint64_t)v; return self; } \
774
- case CA_UINT32: { uint32_t *q=(uint32_t*)p; if ((uint32_t)v OP *q) *q = (uint32_t)v; return self; } \
775
- case CA_UINT16: { uint16_t *q=(uint16_t*)p; if ((uint16_t)v OP *q) *q = (uint16_t)v; return self; } \
776
- case CA_UINT8: { uint8_t *q=(uint8_t *)p; if ((uint8_t )v OP *q) *q = (uint8_t )v; return self; } \
777
812
  } \
778
813
  } \
779
814
  else if ( FIXNUM_P(rval) ) { \
@@ -782,13 +817,13 @@ rb_ca_elem_unmask (VALUE self, VALUE ridx)
782
817
  case CA_FLOAT64: { double *q=(double *)p; double vd=(double)v; if (vd OP *q || *q != *q) *q = vd; return self; } \
783
818
  case CA_FLOAT32: { float *q=(float *)p; float vf=(float )v; if (vf OP *q || *q != *q) *q = vf; return self; } \
784
819
  case CA_INT64: { int64_t *q=(int64_t *)p; if ((int64_t)v OP *q) *q = (int64_t)v; return self; } \
785
- case CA_INT32: { int32_t *q=(int32_t *)p; if ((int32_t)v OP *q) *q = (int32_t)v; return self; } \
786
- case CA_INT16: { int16_t *q=(int16_t *)p; if ((int16_t)v OP *q) *q = (int16_t)v; return self; } \
787
- case CA_INT8: { int8_t *q=(int8_t *)p; if ((int8_t )v OP *q) *q = (int8_t )v; return self; } \
788
- case CA_UINT64: { uint64_t *q=(uint64_t*)p; if ((uint64_t)v OP *q) *q = (uint64_t)v; return self; } \
789
- case CA_UINT32: { uint32_t *q=(uint32_t*)p; if ((uint32_t)v OP *q) *q = (uint32_t)v; return self; } \
790
- case CA_UINT16: { uint16_t *q=(uint16_t*)p; if ((uint16_t)v OP *q) *q = (uint16_t)v; return self; } \
791
- case CA_UINT8: { uint8_t *q=(uint8_t *)p; if ((uint8_t )v OP *q) *q = (uint8_t )v; return self; } \
820
+ case CA_INT32: { if ( ! (v >= INT32_MIN && v <= INT32_MAX) ) break; int32_t *q=(int32_t *)p; if ((int32_t)v OP *q) *q = (int32_t)v; return self; } \
821
+ case CA_INT16: { if ( ! (v >= INT16_MIN && v <= INT16_MAX) ) break; int16_t *q=(int16_t *)p; if ((int16_t)v OP *q) *q = (int16_t)v; return self; } \
822
+ case CA_INT8: { if ( ! (v >= INT8_MIN && v <= INT8_MAX) ) break; int8_t *q=(int8_t *)p; if ((int8_t )v OP *q) *q = (int8_t )v; return self; } \
823
+ case CA_UINT64: { if ( ! (v >= 0) ) break; uint64_t *q=(uint64_t*)p; if ((uint64_t)v OP *q) *q = (uint64_t)v; return self; } \
824
+ case CA_UINT32: { if ( ! (v >= 0 && v <= (long) UINT32_MAX) ) break; uint32_t *q=(uint32_t*)p; if ((uint32_t)v OP *q) *q = (uint32_t)v; return self; } \
825
+ case CA_UINT16: { if ( ! (v >= 0 && v <= (long) UINT16_MAX) ) break; uint16_t *q=(uint16_t*)p; if ((uint16_t)v OP *q) *q = (uint16_t)v; return self; } \
826
+ case CA_UINT8: { if ( ! (v >= 0 && v <= (long) UINT8_MAX) ) break; uint8_t *q=(uint8_t *)p; if ((uint8_t )v OP *q) *q = (uint8_t )v; return self; } \
792
827
  } \
793
828
  } \
794
829
  } \
@@ -808,6 +843,10 @@ rb_ca_elem_min (VALUE self, VALUE ridx, VALUE rval)
808
843
  rb_raise(rb_eCADataTypeError,
809
844
  "elem_min requires a numeric array");
810
845
  }
846
+ if ( ca_is_complex_type(ca) ) {
847
+ rb_raise(rb_eCADataTypeError,
848
+ "elem_min requires a real array (complex values have no order)");
849
+ }
811
850
 
812
851
  /* Super-hot path: entity + numeric data type + Fixnum/Float rval. */
813
852
  ELEM_MINMAX_BODY(<);
@@ -864,6 +903,10 @@ rb_ca_elem_max (VALUE self, VALUE ridx, VALUE rval)
864
903
  rb_raise(rb_eCADataTypeError,
865
904
  "elem_max requires a numeric array");
866
905
  }
906
+ if ( ca_is_complex_type(ca) ) {
907
+ rb_raise(rb_eCADataTypeError,
908
+ "elem_max requires a real array (complex values have no order)");
909
+ }
867
910
 
868
911
  ELEM_MINMAX_BODY(>);
869
912
 
@@ -50,7 +50,8 @@
50
50
  (Float#eql? treats them equal). The object lane adds one deviation from Ruby
51
51
  Hash, aligning it with the numeric lane: every Float NaN collapses to one
52
52
  distinct value (a NaN takes a fixed canonical key and matches any stored Float
53
- NaN), where Ruby Hash would keep distinct NaN objects apart.
53
+ NaN), where Ruby Hash would keep distinct NaN objects apart, and a Complex
54
+ with a NaN part is one value with any Complex whose parts match it that way.
54
55
  __factorize_appearance__ and the discovery members (unique, value_counts,
55
56
  mask_duplicates) all cover the numeric / object / fixlen lanes.
56
57
 
@@ -77,6 +78,7 @@
77
78
 
78
79
  #include "carray.h"
79
80
  #include "ca_obj_face.h"
81
+ #include "carray_internal.h" /* ca_object_cells_clear */
80
82
  #include <string.h>
81
83
  #include <math.h>
82
84
 
@@ -175,6 +177,7 @@ fz_complex_key (VALUE self)
175
177
  if ( ca_has_mask(ca) ) {
176
178
  ca_copy_mask(co, ca);
177
179
  }
180
+ RB_GC_GUARD(self); /* read through ca after rb_carray_new allocates */
178
181
  return out;
179
182
  }
180
183
 
@@ -206,24 +209,19 @@ fz_face_descend (VALUE self, volatile VALUE *pface, const char *name)
206
209
  }
207
210
  if ( ca_is_face(ca) ) {
208
211
  /* A record is its bytes: two records are the same value exactly when
209
- their bytes are, so the fixlen lane answers for it. */
210
- if ( ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE)
211
- || ca->obj_type == CA_OBJ_RECORD ) {
212
- *pface = self;
213
- return rb_ca_strip_face_value(self);
212
+ their bytes are, so the fixlen lane answers for it. Any other Face
213
+ is descended only when ORDERABLE. One that cannot be descended must
214
+ not flow on undescended: whatever reads it next reads its storage
215
+ anyway, which is the encoding rather than the values. A Face in this
216
+ position normally never arrives -- CAConstString and CACategorical
217
+ define their own members in Ruby -- but the overrides only fire when
218
+ the Face is the receiver, and this same helper descends the
219
+ *reference* for locate_addr, where it is the argument. */
220
+ if ( ca->obj_type != CA_OBJ_RECORD ) {
221
+ ca_face_require_orderable(self, name, "compare");
214
222
  }
215
- /* A Face that cannot be descended must not flow on undescended: whatever
216
- reads it next reads its storage anyway, which is the encoding rather
217
- than the values. A Face in this position normally never arrives --
218
- CAConstString and CACategorical define their own members in Ruby -- but
219
- the overrides only fire when the Face is the receiver, and this same
220
- helper descends the *reference* for locate_addr, where it is the
221
- argument. */
222
- rb_raise(rb_eArgError,
223
- "%s: %s is not orderable by storage (a cell encodes the value "
224
- "rather than being it); convert it first -- %s -- or pass "
225
- ".parent to work in storage space",
226
- name, rb_obj_classname(self), ca_face_conversion_hint(self));
223
+ *pface = self;
224
+ return rb_ca_strip_face_value(self);
227
225
  }
228
226
  *pface = Qnil;
229
227
  return self;
@@ -250,6 +248,7 @@ fz_face_reconcile (VALUE reference, VALUE operand, const char *name)
250
248
  }
251
249
  GetCArray(operand, op);
252
250
  if ( op->data_type != ca->data_type ) {
251
+ /* coerce: the callers have met both sides at result_type already */
253
252
  operand = rb_ca_to_type(operand, INT2NUM(ca->data_type), Qnil);
254
253
  }
255
254
  return fz_complex_key(operand);
@@ -402,8 +401,7 @@ fz_hash_alloc (fz_hash *h, ca_size_t cap)
402
401
  h->cap = cap;
403
402
  MEMZERO(h->used, uint8_t, cap);
404
403
  if ( has_val ) {
405
- ca_size_t i;
406
- for ( i = 0; i < cap; i++ ) h->val[i] = Qnil;
404
+ ca_object_cells_clear(h->val, cap);
407
405
  fz_tmp_values(h->hbuf, h->val, cap);
408
406
  }
409
407
  }
@@ -512,25 +510,84 @@ fz_is_float_nan (VALUE v)
512
510
  return RB_FLOAT_TYPE_P(v) && isnan(RFLOAT_VALUE(v));
513
511
  }
514
512
 
513
+ /* True when v holds a NaN that eql? would keep apart from an equal value:
514
+ a Float NaN, or a Complex with a Float NaN part. */
515
+ static int
516
+ fz_obj_has_nan (VALUE v)
517
+ {
518
+ if ( fz_is_float_nan(v) ) {
519
+ return 1;
520
+ }
521
+ return RB_TYPE_P(v, T_COMPLEX)
522
+ && ( fz_is_float_nan(rb_complex_real(v))
523
+ || fz_is_float_nan(rb_complex_imag(v)) );
524
+ }
525
+
526
+ /* A part of a NaN-bearing value: every NaN is one value, the rest go by
527
+ eql?, as in Ruby Hash. */
528
+ static uint64_t
529
+ fz_obj_part_key (VALUE v)
530
+ {
531
+ return fz_is_float_nan(v) ? FZ_NAN_KEY_DOUBLE : (uint64_t) NUM2LL(rb_hash(v));
532
+ }
533
+
534
+ static int
535
+ fz_obj_part_eql (VALUE a, VALUE b)
536
+ {
537
+ if ( fz_is_float_nan(a) || fz_is_float_nan(b) ) {
538
+ return fz_is_float_nan(a) && fz_is_float_nan(b);
539
+ }
540
+ return rb_eql(a, b);
541
+ }
542
+
543
+ /* The hash key of an object cell. A NaN-bearing value is keyed part by
544
+ part with every NaN as one value, so it matches the numeric lane: all
545
+ NaN are one value, and a complex value is one value when its parts are. */
546
+ static uint64_t
547
+ fz_obj_key (VALUE v, int has_nan)
548
+ {
549
+ if ( ! has_nan ) {
550
+ return (uint64_t) NUM2LL(rb_hash(v));
551
+ }
552
+ if ( RB_TYPE_P(v, T_COMPLEX) ) {
553
+ return fz_obj_part_key(rb_complex_real(v)) * 31u
554
+ ^ fz_obj_part_key(rb_complex_imag(v)) ^ 0x9e3779b97f4a7c15ULL;
555
+ }
556
+ return FZ_NAN_KEY_DOUBLE;
557
+ }
558
+
559
+ /* Whether a stored cell is the same value as v (has_nan from fz_obj_has_nan). */
560
+ static int
561
+ fz_obj_same (VALUE stored, VALUE v, int has_nan)
562
+ {
563
+ if ( ! has_nan ) {
564
+ return rb_eql(stored, v);
565
+ }
566
+ if ( RB_TYPE_P(v, T_COMPLEX) ) {
567
+ return RB_TYPE_P(stored, T_COMPLEX)
568
+ && fz_obj_part_eql(rb_complex_real(stored), rb_complex_real(v))
569
+ && fz_obj_part_eql(rb_complex_imag(stored), rb_complex_imag(v));
570
+ }
571
+ return fz_is_float_nan(stored);
572
+ }
573
+
515
574
  /* Object-lane intern: key = rb_hash(v) (lossy), collision re-check via rb_eql.
516
575
  Matches Ruby Hash distinctness (`hash` + `eql?`) element for element, with one
517
576
  value-based exception aligning with the numeric lane: every Float NaN collapses
518
577
  to a single distinct value (Ruby Hash keeps distinct NaN objects apart via
519
- eql?, but the discovery family unifies them). A NaN takes a fixed canonical
520
- key and matches any stored Float NaN, so its second and later occurrences fold
521
- into the first. */
578
+ eql?, but the discovery family unifies them), and so does a Complex whose
579
+ parts are the same with every NaN as one (fz_obj_key / fz_obj_same). */
522
580
  static int32_t
523
581
  fz_hash_intern_obj (fz_hash *h, VALUE v, int *is_new)
524
582
  {
525
583
  if ( (h->n + 1) * 10 >= h->cap * 7 ) { fz_hash_grow(h); } /* load factor 0.7 */
526
- int v_nan = fz_is_float_nan(v);
527
- uint64_t key = v_nan ? FZ_NAN_KEY_DOUBLE
528
- : (uint64_t) NUM2LL(rb_hash(v));
584
+ int v_nan = fz_obj_has_nan(v);
585
+ uint64_t key = fz_obj_key(v, v_nan);
529
586
  ca_size_t mask = h->cap - 1;
530
587
  ca_size_t slot = FZ_HOME(h, key);
531
588
  while ( h->used[slot] ) {
532
589
  if ( h->key[slot] == key &&
533
- ( v_nan ? fz_is_float_nan(h->val[slot]) : rb_eql(h->val[slot], v) ) ) {
590
+ fz_obj_same(h->val[slot], v, v_nan) ) {
534
591
  *is_new = 0; return h->code[slot];
535
592
  }
536
593
  slot = (slot + 1) & mask;
@@ -621,14 +678,13 @@ fz_hash_lookup (fz_hash *h, uint64_t key, int32_t *code_out)
621
678
  static int
622
679
  fz_hash_lookup_obj (fz_hash *h, VALUE v, int32_t *code_out)
623
680
  {
624
- int v_nan = fz_is_float_nan(v);
625
- uint64_t key = v_nan ? FZ_NAN_KEY_DOUBLE
626
- : (uint64_t) NUM2LL(rb_hash(v));
681
+ int v_nan = fz_obj_has_nan(v);
682
+ uint64_t key = fz_obj_key(v, v_nan);
627
683
  ca_size_t mask = h->cap - 1;
628
684
  ca_size_t slot = FZ_HOME(h, key);
629
685
  while ( h->used[slot] ) {
630
686
  if ( h->key[slot] == key &&
631
- ( v_nan ? fz_is_float_nan(h->val[slot]) : rb_eql(h->val[slot], v) ) ) {
687
+ fz_obj_same(h->val[slot], v, v_nan) ) {
632
688
  if ( code_out ) { *code_out = h->code[slot]; }
633
689
  return 1;
634
690
  }
@@ -669,9 +725,7 @@ typedef struct {
669
725
  static void
670
726
  fz_levels_values (fz_levels *l, ca_size_t from)
671
727
  {
672
- VALUE *v = (VALUE *) l->p;
673
- ca_size_t i;
674
- for ( i = from; i < l->cap; i++ ) v[i] = Qnil;
728
+ ca_object_cells_clear((VALUE *) l->p + from, l->cap - from);
675
729
  fz_tmp_values(l->hp, l->p, l->cap);
676
730
  }
677
731
 
@@ -717,7 +771,13 @@ fz_levels_free (fz_levels *l)
717
771
  in the middle of a walk. The walks' states live in this frame, outside the
718
772
  method's body, and ca_iter_ensure finishes them however the body leaves;
719
773
  the tables have owners the unwind collects (fz_tmp_alloc). Slots 0
720
- and 1 hold a method's own walks, slot 2 the walk of fz_intern_all. */
774
+ and 1 hold a method's own walks, slot 2 the walk of fz_intern_all.
775
+
776
+ A body that replaces self or an operand with an array it made (complex
777
+ keys, a Face's storage, an operand brought into self's space) writes the
778
+ new VALUE back into the frame. The walks read it only through its
779
+ CArray *, so a C local alone would leave it to the collector halfway
780
+ through. */
721
781
 
722
782
  typedef struct {
723
783
  ca_iter_state st[3];
@@ -761,7 +821,7 @@ fz_factorize_appearance_body (VALUE arg)
761
821
  VALUE self = fr->self;
762
822
  CArray *ca;
763
823
  volatile VALUE face;
764
- self = fz_face_descend(self, &face, "categorize");
824
+ fr->self = self = fz_face_descend(self, &face, "categorize");
765
825
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
766
826
 
767
827
  int8_t dt = ca->data_type;
@@ -1132,7 +1192,7 @@ fz_unique_flat_body (VALUE arg)
1132
1192
  VALUE self = fr->self;
1133
1193
  CArray *ca;
1134
1194
  volatile VALUE face;
1135
- self = fz_face_descend(self, &face, "unique");
1195
+ fr->self = self = fz_face_descend(self, &face, "unique");
1136
1196
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1137
1197
 
1138
1198
  int8_t dt = ca->data_type;
@@ -1158,8 +1218,6 @@ fz_unique_flat_body (VALUE arg)
1158
1218
  else { fz_hash_init(&h); }
1159
1219
  fz_levels_init(&lv, (int) ca->bytes, ca->data_type == CA_OBJECT);
1160
1220
 
1161
- int8_t axis = (int8_t) (ca->ndim - 1); /* innermost fiber; hash not reset =
1162
- one seen-set over the whole array */
1163
1221
  ca_iter_state *st_in = &fr->st[0];
1164
1222
  char *p_in;
1165
1223
  boolean8_t *m;
@@ -1220,7 +1278,7 @@ fz_unique_flat_body (VALUE arg)
1220
1278
  } \
1221
1279
  } while (0)
1222
1280
 
1223
- CA_FOR_EACH_FIBER_MASKED(*st_in, ca, axis, CA_KERNEL_READ, p_in, n, m) {
1281
+ CA_FOR_EACH_WHOLE_MASKED(*st_in, ca, CA_KERNEL_READ, p_in, n, m) {
1224
1282
  switch ( dt ) {
1225
1283
  case CA_INT8: UQ_LOOP(int8_t, (int64_t)); break;
1226
1284
  case CA_INT16: UQ_LOOP(int16_t, (int64_t)); break;
@@ -1265,7 +1323,7 @@ rb_ca_unique_flat (VALUE self)
1265
1323
  }
1266
1324
 
1267
1325
  /* Intern every non-masked cell of cv into h, one seen-set over the whole array
1268
- (the hash is not reset between fibers). When lv is non-NULL, the first-seen
1326
+ walked in row-major order. When lv is non-NULL, the first-seen
1269
1327
  raw element of each distinct value is pushed to it (appearance order), so the
1270
1328
  same pass builds both a probe set and the distinct-value list. Dispatches the
1271
1329
  three key lanes exactly as the discovery family: numeric widen with all NaN
@@ -1276,7 +1334,6 @@ static void
1276
1334
  fz_intern_all (ca_iter_state *st_v, fz_hash *h, CArray *cv, fz_levels *lv)
1277
1335
  {
1278
1336
  int8_t dt = cv->data_type;
1279
- int8_t vaxis = (int8_t) (cv->ndim - 1);
1280
1337
  char *p_v;
1281
1338
  boolean8_t *mv;
1282
1339
  ca_size_t nv;
@@ -1329,7 +1386,7 @@ fz_intern_all (ca_iter_state *st_v, fz_hash *h, CArray *cv, fz_levels *lv)
1329
1386
  } \
1330
1387
  } while (0)
1331
1388
 
1332
- CA_FOR_EACH_FIBER_MASKED(*st_v, cv, vaxis, CA_KERNEL_READ, p_v, nv, mv) {
1389
+ CA_FOR_EACH_WHOLE_MASKED(*st_v, cv, CA_KERNEL_READ, p_v, nv, mv) {
1333
1390
  switch ( dt ) {
1334
1391
  case CA_INT8: FZ_IA(int8_t, (int64_t)); break;
1335
1392
  case CA_INT16: FZ_IA(int16_t, (int64_t)); break;
@@ -1376,8 +1433,8 @@ fz_is_in_body (VALUE arg)
1376
1433
  VALUE rvalues = fr->a;
1377
1434
  CArray *ca, *cv;
1378
1435
  volatile VALUE face;
1379
- self = fz_face_descend(self, &face, "is_in");
1380
- rvalues = fz_face_reconcile(face, rvalues, "is_in");
1436
+ fr->self = self = fz_face_descend(self, &face, "is_in");
1437
+ fr->a = rvalues = fz_face_reconcile(face, rvalues, "is_in");
1381
1438
  GetCArray(self, ca);
1382
1439
 
1383
1440
  int8_t dt = ca->data_type;
@@ -1543,8 +1600,8 @@ fz_locate_addr_body (VALUE arg)
1543
1600
  output is an address, so nothing is lifted back. */
1544
1601
  {
1545
1602
  volatile VALUE ref_face;
1546
- rref = fz_face_descend(rref, &ref_face, "locate_addr");
1547
- self = fz_face_reconcile(ref_face, self, "locate_addr");
1603
+ fr->a = rref = fz_face_descend(rref, &ref_face, "locate_addr");
1604
+ fr->self = self = fz_face_reconcile(ref_face, self, "locate_addr");
1548
1605
  }
1549
1606
  GetCArray(self, ca);
1550
1607
 
@@ -1807,8 +1864,8 @@ fz_set_relation_body (VALUE arg)
1807
1864
  int keep_when_hit = fr->i;
1808
1865
  CArray *ca, *co;
1809
1866
  volatile VALUE face;
1810
- self = fz_face_descend(self, &face, "set relation");
1811
- rother = fz_face_reconcile(face, rother, "set relation");
1867
+ fr->self = self = fz_face_descend(self, &face, "set relation");
1868
+ fr->a = rother = fz_face_reconcile(face, rother, "set relation");
1812
1869
  GetCArray(self, ca);
1813
1870
 
1814
1871
  int8_t dt = ca->data_type;
@@ -2002,8 +2059,8 @@ fz_set_union_body (VALUE arg)
2002
2059
  VALUE rother = fr->a;
2003
2060
  CArray *ca, *co;
2004
2061
  volatile VALUE face;
2005
- self = fz_face_descend(self, &face, "union");
2006
- rother = fz_face_reconcile(face, rother, "union");
2062
+ fr->self = self = fz_face_descend(self, &face, "union");
2063
+ fr->a = rother = fz_face_reconcile(face, rother, "union");
2007
2064
  GetCArray(self, ca);
2008
2065
 
2009
2066
  int8_t dt = ca->data_type;
@@ -2095,7 +2152,7 @@ fz_value_counts_flat_body (VALUE arg)
2095
2152
  VALUE self = fr->self;
2096
2153
  CArray *ca;
2097
2154
  volatile VALUE face;
2098
- self = fz_face_descend(self, &face, "value_counts");
2155
+ fr->self = self = fz_face_descend(self, &face, "value_counts");
2099
2156
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2100
2157
 
2101
2158
  int8_t dt = ca->data_type;
@@ -2255,10 +2312,10 @@ rb_ca_value_counts_flat (VALUE self)
2255
2312
 
2256
2313
  /* @overload __nunique__(axis, keep_axis)
2257
2314
 
2258
- INTERNAL (CArray#nunique). Count the distinct values along axis, one linear
2259
- pass per fiber with an independent seen-set. Returns a reduced
2260
- CA_INT64 CArray of self.shape with axis removed (or kept as length-1 when
2261
- keep_axis). Masked cells do not participate; an all-masked fiber counts 0
2315
+ INTERNAL (CArray#nunique). Count the distinct values along axis (an Integer
2316
+ or an Array of them), one linear pass per fiber with an independent
2317
+ seen-set. Returns a reduced CA_INT64 CArray of self.shape with the axes
2318
+ removed (or kept as length-1 when keep_axis). Masked cells do not participate; an all-masked fiber counts 0
2262
2319
  (an empty set has zero distinct values, not UNDEF -- nunique has identity 0).
2263
2320
  Numeric distinctness collapses all NaN to one value and treats -0.0 == +0.0;
2264
2321
  object keys on rb_hash + rb_eql and fixlen on a byte-hash + memcmp, reproducing
@@ -2290,14 +2347,11 @@ fz_nunique_body (VALUE arg)
2290
2347
  rb_raise(rb_eRuntimeError, "__nunique__: need ndim >= 1");
2291
2348
  }
2292
2349
 
2293
- int axis = NUM2INT(vaxis);
2294
- if ( axis < 0 || axis >= ca->ndim ) {
2295
- rb_raise(rb_eArgError, "__nunique__: axis %d out of range", axis);
2296
- }
2350
+ int8_t axes[CA_RANK_MAX];
2351
+ int8_t naxes = rb_ca_parse_reduce_axes_kw_ctx(vaxis, ca, axes, "nunique");
2297
2352
  int keep_axis = RTEST(vkeep);
2298
2353
 
2299
- int8_t ax = (int8_t) axis;
2300
- VALUE vout = rb_ca_new_reduced(self, &ax, 1, CA_INT64, keep_axis);
2354
+ VALUE vout = rb_ca_new_reduced(self, axes, naxes, CA_INT64, keep_axis);
2301
2355
  CArray *cout;
2302
2356
  TypedData_Get_Struct(vout, CArray, &carray_data_type, cout);
2303
2357
  int64_t *op = (int64_t *) cout->ptr;
@@ -2308,7 +2362,7 @@ fz_nunique_body (VALUE arg)
2308
2362
  else { fz_hash_init(&h); }
2309
2363
 
2310
2364
  ca_iter_state *st = &fr->st[0];
2311
- int rc = ca_iter_state_init_l2(st, ca, CA_SLAB_AXES, &ax, 1, 0);
2365
+ int rc = ca_iter_state_init_l2(st, ca, CA_SLAB_AXES, axes, naxes, 0);
2312
2366
  if ( rc != CA_ITER_OK ) {
2313
2367
  fz_hash_free(&h);
2314
2368
  rb_raise(rb_eRuntimeError, "__nunique__: kernel_iterator init failed rc=%d", rc);
@@ -2641,7 +2695,7 @@ fz_mode_axis_body (VALUE arg)
2641
2695
  VALUE vaxis = fr->a;
2642
2696
  CArray *ca;
2643
2697
  volatile VALUE face;
2644
- self = fz_face_descend(self, &face, "mode");
2698
+ fr->self = self = fz_face_descend(self, &face, "mode");
2645
2699
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2646
2700
 
2647
2701
  int8_t dt = ca->data_type;