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
@@ -1,4 +1,5 @@
1
1
  data_type = []
2
+ type_const = {}
2
3
  %w[
3
4
  CA_FIXLEN
4
5
  CA_BOOLEAN
@@ -20,6 +21,7 @@ data_type = []
20
21
  ].each_with_index do |name, i|
21
22
  eval("#{name} = #{i}")
22
23
  data_type.push(i)
24
+ type_const[i] = name
23
25
  end
24
26
 
25
27
  ctype = {
@@ -107,8 +109,8 @@ FLOAT = [
107
109
  CA_FLOAT64 ,
108
110
  ]
109
111
 
110
- # object -> int/float cast: a parse failure masks the output cell (UNDEF)
111
- # instead of a silent 0.0 (float) or a raise (int). These parsers report
112
+ # object -> int/float/complex cast: a parse failure masks the output cell
113
+ # (UNDEF) instead of raising. These parsers report
112
114
  # failure through an out-param whose C type is `oktype`.
113
115
  obj2cval_ok = {
114
116
  CA_INT8 => "rb_obj2long_ok",
@@ -121,6 +123,8 @@ obj2cval_ok = {
121
123
  CA_UINT64 => "rb_obj2ull_ok",
122
124
  CA_FLOAT32 => "ca_obj2dbl_ok",
123
125
  CA_FLOAT64 => "ca_obj2dbl_ok",
126
+ CA_CMPLX64 => "ca_obj2cmplx_ok",
127
+ CA_CMPLX128 => "ca_obj2cmplx_ok",
124
128
  }
125
129
 
126
130
  oktype = {
@@ -134,6 +138,8 @@ oktype = {
134
138
  CA_UINT64 => "unsigned long long",
135
139
  CA_FLOAT32 => "double",
136
140
  CA_FLOAT64 => "double",
141
+ CA_CMPLX64 => "double complex",
142
+ CA_CMPLX128 => "double complex",
137
143
  }
138
144
 
139
145
  BOOLEAN = [
@@ -172,6 +178,7 @@ puts %{
172
178
  ---------------------------------------------------------------------------- */
173
179
 
174
180
  #include "carray.h"
181
+ #include "carray_internal.h" /* ca_decimal_string_to / _store */
175
182
 
176
183
  static void __attribute__((noreturn))
177
184
  ca_cast_not_implemented(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
@@ -295,7 +302,8 @@ puts
295
302
  *p2 = q;
296
303
  }
297
304
  else {
298
- rb_raise(rb_eRuntimeError, "out of range to cast to boolean (0 or 1)");
305
+ rb_raise(rb_eRangeError, "%s: out of range to cast to boolean (0 or 1)",
306
+ ca_calling_method_name());
299
307
  }
300
308
  }
301
309
  p1++; p2++; m++;
@@ -308,7 +316,8 @@ puts
308
316
  *p2 = q;
309
317
  }
310
318
  else {
311
- rb_raise(rb_eRuntimeError, "out of range to cast to boolean (0 or 1)");
319
+ rb_raise(rb_eRangeError, "%s: out of range to cast to boolean (0 or 1)",
320
+ ca_calling_method_name());
312
321
  }
313
322
  p1++; p2++;
314
323
  }
@@ -327,7 +336,34 @@ puts
327
336
  ctype1 = ctype[type1]
328
337
  ctype2 = ctype[type2]
329
338
  CA_CAST_TABLE[type1][type2] = "ca_cast_#{ctype1}_#{ctype2}"
330
- puts <<-END_DEF .gsub(/^ {6}/, '')
339
+ # float / complex -> integer: a NaN has no integer and C leaves its
340
+ # conversion undefined. A cell that is not masked refuses it before
341
+ # anything is written. #to_type masks NaN cells first (UNDEF), so
342
+ # only a store, a view and a single cell reach the raise.
343
+ nan_check = ""
344
+ if (FLOAT+[CA_CMPLX64, CA_CMPLX128]).include?(type1) && INTEGER.include?(type2)
345
+ isnan = if [CA_CMPLX64, CA_CMPLX128].include?(type1)
346
+ "( isnan(creal(q1[k])) || isnan(cimag(q1[k])) )"
347
+ else
348
+ "isnan(q1[k])"
349
+ end
350
+ nan_check = <<-END_CHECK.gsub(/^ {6}/, '')
351
+ for (k=0; k<n; k++) {
352
+ if ( ( ! m || ! m[k] ) && #{isnan} ) {
353
+ rb_raise(rb_eFloatDomainError, "%s: NaN cannot be converted to %s",
354
+ ca_calling_method_name(), ca_type_name[#{type_const[type2]}]);
355
+ }
356
+ }
357
+ END_CHECK
358
+ end
359
+ # int64 / uint64 -> float32 / cmplx64 goes through double, as a Ruby
360
+ # Integer stored into a float32 cell does (Integer#to_f). Converting in
361
+ # one step rounds once where the compiler keeps the loop scalar and twice
362
+ # where it vectorises it through double, so the answer for a value past
363
+ # 2**53 would depend on its place in the array.
364
+ via = ( [CA_INT64, CA_UINT64].include?(type1) &&
365
+ [CA_FLOAT32, CA_CMPLX64].include?(type2) ) ? "(double) " : ""
366
+ puts <<-END_DEF.gsub(/^ {6}/, '').sub("@@NAN_CHECK@@\n", nan_check).gsub("@@VIA@@", via)
331
367
  static void
332
368
  ca_cast_#{ctype1}_#{ctype2}(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
333
369
  {
@@ -336,6 +372,7 @@ puts
336
372
  #{ctype2} *q2 = ptr2;
337
373
  #{ctype2} *p2 = q2;
338
374
  ca_size_t k;
375
+ @@NAN_CHECK@@
339
376
  if ( m ) {
340
377
  boolean8_t *pm = m;
341
378
  for (k=0; k<n; k++) {
@@ -343,7 +380,7 @@ puts
343
380
  if ( ! *pm ) {
344
381
  p1 = q1 + k;
345
382
  p2 = q2 + k;
346
- *p2 = (#{ctype2}) *p1;
383
+ *p2 = (#{ctype2}) @@VIA@@*p1;
347
384
  }
348
385
  }
349
386
  }
@@ -351,7 +388,7 @@ puts
351
388
  for (k=0; k<n; k++) {
352
389
  p1 = q1 + k;
353
390
  p2 = q2 + k;
354
- *p2 = (#{ctype2}) *p1;
391
+ *p2 = (#{ctype2}) @@VIA@@*p1;
355
392
  }
356
393
  }
357
394
  return;
@@ -456,17 +493,38 @@ OBJECT.each do |type1|
456
493
  ctype1 = ctype[type1]
457
494
  ctype2 = ctype[type2]
458
495
  CA_CAST_TABLE[type1][type2] = "ca_cast_#{ctype1}_#{ctype2}"
459
- if (INTEGER+FLOAT).include?(type2)
460
- # object -> int/float: parse failure -> masked (UNDEF) output cell.
461
- # The mask-aware loop runs only when a mask buffer is supplied (m);
462
- # #to_type always supplies one for these targets so the whole
463
- # user-facing object->numeric surface gets the masking semantics.
464
- # With no mask buffer (internal single-cell / view paths) the legacy
465
- # value parser keeps its old behaviour (lenient 0.0 float / raising
466
- # int), leaving those paths byte-identical.
496
+ if (INTEGER+FLOAT+[CA_CMPLX64, CA_CMPLX128]).include?(type2)
497
+ # object -> int/float/complex: parse failure -> masked (UNDEF) output
498
+ # cell. The mask-aware loop runs only when a mask buffer is supplied
499
+ # (m); #to_type always supplies one for these targets. With no mask
500
+ # buffer (a store, single-cell and view paths) the value parser
501
+ # raises for a cell it cannot read.
467
502
  conv_ok = obj2cval_ok[type2]
468
503
  conv = obj2cval[type2]
469
504
  oktype2 = oktype[type2]
505
+ # A String cell of an integer or float target is read as a decimal
506
+ # number (ca_decimal_string_to / _store in carray_cast.c), not as a
507
+ # Ruby literal. Complex keeps its own reader.
508
+ if (INTEGER+FLOAT).include?(type2)
509
+ type_name = "CA_" + ctype2.sub(/_t\z/, "").upcase
510
+ read_ok = "( RB_TYPE_P(*p1, T_STRING) ? " \
511
+ "ca_decimal_string_to(*p1, #{type_name}, p2) : " \
512
+ "( #{conv_ok}(*p1, &tmp) ? ( *p2 = (#{ctype2}) tmp, 1 ) : 0 ) )"
513
+ # A store refuses an integer the cell cannot hold
514
+ # (ca_obj2integer_fitting); to_type, above, keeps the C conversion.
515
+ store_conv = if INTEGER.include?(type2)
516
+ num = [CA_UINT8, CA_UINT16, CA_UINT32, CA_UINT64].include?(type2) ? "rb_num2ull" : "NUM2LL"
517
+ "#{num}(ca_obj2integer_fitting(*p1, #{type_name}))"
518
+ else
519
+ "#{conv}(*p1)"
520
+ end
521
+ read = "if ( RB_TYPE_P(*p1, T_STRING) ) { " \
522
+ "ca_decimal_string_store(*p1, #{type_name}, p2); } " \
523
+ "else { *p2 = (#{ctype2}) #{store_conv}; }"
524
+ else
525
+ read_ok = "( #{conv_ok}(*p1, &tmp) ? ( *p2 = (#{ctype2}) tmp, 1 ) : 0 )"
526
+ read = "*p2 = (#{ctype2}) #{conv}(*p1);"
527
+ end
470
528
  puts <<-END_DEF .gsub(/^ {6}/, '')
471
529
  static void
472
530
  ca_cast_#{ctype1}_#{ctype2}(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
@@ -476,20 +534,15 @@ OBJECT.each do |type1|
476
534
  #{oktype2} tmp;
477
535
  if ( m ) {
478
536
  while ( n-- ) {
479
- if ( !*m ) {
480
- if ( #{conv_ok}(*p1, &tmp) ) {
481
- *p2 = (#{ctype2}) tmp;
482
- }
483
- else {
484
- *m = 1;
485
- *p2 = 0;
486
- }
537
+ if ( !*m && ! #{read_ok} ) {
538
+ *m = 1;
539
+ *p2 = 0;
487
540
  }
488
541
  p1++; p2++; m++;
489
542
  }
490
543
  }
491
544
  else {
492
- while ( n-- ) { *p2 = (#{ctype2}) #{conv}(*p1); p1++; p2++; }
545
+ while ( n-- ) { #{read} p1++; p2++; }
493
546
  }
494
547
  return;
495
548
  }
data/ext/carray_class.c CHANGED
@@ -6,6 +6,7 @@
6
6
  ---------------------------------------------------------------------------- */
7
7
 
8
8
  #include "carray.h"
9
+ #include "carray_internal.h" /* CA_CHECK_*_GIVEN */
9
10
 
10
11
  /* ------------------------------------------------------------------- */
11
12
  /* Symbol cache for data_type code → Symbol lookup.
@@ -25,7 +26,7 @@ ID ca_data_type_sym[CA_NTYPE];
25
26
  VALUE
26
27
  rb_ca_data_type_to_sym (int8_t data_type)
27
28
  {
28
- CA_CHECK_DATA_TYPE(data_type);
29
+ CA_CHECK_DATA_TYPE_GIVEN(data_type);
29
30
  return ID2SYM(ca_data_type_sym[data_type]);
30
31
  }
31
32
 
@@ -69,7 +70,7 @@ static VALUE
69
70
  rb_ca_s_data_type_name (VALUE klass, VALUE type)
70
71
  {
71
72
  int8_t data_type = rb_ca_guess_type(type);
72
- CA_CHECK_DATA_TYPE(data_type);
73
+ CA_CHECK_DATA_TYPE_GIVEN(data_type);
73
74
  return rb_str_new2(ca_type_name[data_type]);
74
75
  }
75
76
 
@@ -84,7 +85,7 @@ static VALUE
84
85
  rb_ca_s_data_type_code (VALUE klass, VALUE type)
85
86
  {
86
87
  int8_t data_type = rb_ca_guess_type(type);
87
- CA_CHECK_DATA_TYPE(data_type);
88
+ CA_CHECK_DATA_TYPE_GIVEN(data_type);
88
89
  return INT2NUM(data_type);
89
90
  }
90
91
 
@@ -12,8 +12,11 @@
12
12
  ---------------------------------------------------------------------------- */
13
13
 
14
14
  #include "carray.h"
15
+ #include "carray_internal.h" /* ca_attach_window */
15
16
 
16
17
  #include "ruby/io.h"
18
+ #include "ruby/util.h" /* ruby_strtod */
19
+ #include <math.h>
17
20
 
18
21
  /* to_a dispatch:
19
22
  numeric / boolean data type + entity or attach-able view -> fast path
@@ -175,19 +178,34 @@ rb_ca_to_a_loop_universal (VALUE self, int32_t level, ca_size_t *idx, VALUE ary)
175
178
  * fast path, so all view kinds land in the data type-direct leaf loop when
176
179
  * the data type is numeric or boolean; CA_OBJECT / CA_FIXLEN fall through
177
180
  * to the universal fetch_index walk. */
181
+ /* The universal walk reads each cell through the fetch path, which
182
+ decodes -- an object cell, a Face's element -- and can raise, so it runs
183
+ inside an attach window. The fast path only copies numbers out. */
184
+ typedef struct {
185
+ VALUE self;
186
+ VALUE ary;
187
+ } ca_to_a_ctx_t;
188
+
189
+ static VALUE
190
+ ca_to_a_universal (VALUE arg)
191
+ {
192
+ ca_to_a_ctx_t *c = (ca_to_a_ctx_t *) arg;
193
+ ca_size_t idx[CA_RANK_MAX];
194
+ rb_ca_to_a_loop_universal(c->self, 0, idx, c->ary);
195
+ return Qnil;
196
+ }
197
+
178
198
  VALUE
179
199
  rb_ca_to_a (VALUE self)
180
200
  {
181
201
  volatile VALUE ary;
182
202
  CArray *ca;
183
- ca_size_t idx[CA_RANK_MAX];
184
203
  int data_type, fast_eligible;
185
204
 
186
205
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
187
206
  ary = rb_ca_is_empty(self) ? rb_ary_new() : rb_ary_new2(ca->dim[0]);
188
- ca_attach(ca);
189
207
 
190
- /* After ca_attach, ca->ptr is a contig row-major buffer (alias for
208
+ /* Once attached, ca->ptr is a contig row-major buffer (alias for
191
209
  contig views, materialised otherwise) and ca->mask matches, so the
192
210
  fast path handles every view kind. CA_OBJECT / CA_FIXLEN stay on
193
211
  the universal path because their ptr is VALUE / byte string and
@@ -210,16 +228,21 @@ rb_ca_to_a (VALUE self)
210
228
  || data_type == CA_BOOLEAN);
211
229
 
212
230
  if ( fast_eligible ) {
213
- int has_mask = (ca->mask != NULL);
214
- const boolean8_t *mask_base = has_mask
215
- ? (const boolean8_t *) ca->mask->ptr : NULL;
231
+ int has_mask;
232
+ const boolean8_t *mask_base;
233
+ ca_attach(ca); /* window: nothing raises inside */
234
+ has_mask = (ca->mask != NULL);
235
+ mask_base = has_mask ? (const boolean8_t *) ca->mask->ptr : NULL;
216
236
  to_a_fast_recurse(ca, 0, ca->ptr, mask_base, ary,
217
237
  data_type, ca->bytes, has_mask);
238
+ ca_detach(ca);
218
239
  }
219
240
  else if ( ca->elements > 0 ) {
220
- rb_ca_to_a_loop_universal(self, 0, idx, ary);
241
+ ca_to_a_ctx_t c;
242
+ c.self = self;
243
+ c.ary = ary;
244
+ ca_attach_window(1, &ca, "r", ca_to_a_universal, (VALUE) &c);
221
245
  }
222
- ca_detach(ca);
223
246
  return ary;
224
247
  }
225
248
 
@@ -228,65 +251,44 @@ rb_ca_to_a (VALUE self)
228
251
  #undef TO_A_DISPATCH_LEAF
229
252
  #undef TO_A_DISPATCH_LEAF_MASKED
230
253
 
231
- /* CArray#convert(data_type=nil, bytes: nil) { |elem| ... } — map every cell
232
- * through the block, into a new array whose data type is chosen by argv via
233
- * CArray#template (shape is inherited from self).
234
- *
235
- * Masked cells skip the block; if the block returns CA_UNDEF, the
236
- * corresponding output cell is masked instead of stored. Both self
237
- * and the template result are attached symmetrically (R3-compliant) so
238
- * an overridden #template returning a view stays on the in-ptr path. */
254
+ /* convert yields inside the window over self and the output, so the loop
255
+ runs through ca_attach_window. An output mask made part way (the first
256
+ UNDEF from the block) is attached inside the loop, and closed by an
257
+ ensure of its own. */
258
+
259
+ typedef struct {
260
+ VALUE self;
261
+ VALUE obj;
262
+ CArray *ca;
263
+ CArray *co;
264
+ int has_mask;
265
+ int co_mask_attached;
266
+ } ca_convert_ctx_t;
267
+
239
268
  static VALUE
240
- rb_ca_convert (int argc, VALUE *argv, VALUE self)
269
+ ca_convert_loop (VALUE arg)
241
270
  {
242
- volatile VALUE obj;
243
- CArray *ca, *co;
244
- ca_size_t i, n, sbytes, dbytes;
245
- int has_mask;
246
- const boolean8_t *src_mask = NULL;
271
+ ca_convert_ctx_t *c = (ca_convert_ctx_t *) arg;
272
+ CArray *ca = c->ca, *co = c->co;
273
+ ca_size_t i, n = ca->elements;
274
+ ca_size_t sbytes = ca->bytes, dbytes = co->bytes;
275
+ const char *src_ptr = ca->ptr;
276
+ char *dst_ptr = co->ptr;
247
277
  boolean8_t *dst_mask = NULL;
248
- const char *src_ptr;
249
- char *dst_ptr;
250
278
 
251
- rb_need_block();
252
-
253
- obj = rb_apply(self, rb_intern("template"), rb_ary_new4(argc, argv));
254
-
255
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
256
- TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
257
-
258
- n = ca->elements;
259
- has_mask = ca_has_mask(ca);
260
- sbytes = ca->bytes;
261
- dbytes = co->bytes;
262
-
263
- /* Attach every storage touched by ptr (self, obj, and either side's
264
- mask) — parent → child order per the R4 attach protocol. */
265
- ca_attach(ca);
266
- ca_attach(co);
267
- if ( has_mask ) {
268
- ca_attach(ca->mask);
269
- if ( ! co->mask ) {
270
- ca_create_mask(co);
271
- }
272
- ca_attach(co->mask);
273
- src_mask = (const boolean8_t *) ca->mask->ptr;
279
+ if ( c->has_mask ) {
280
+ const boolean8_t *src_mask = (const boolean8_t *) ca->mask->ptr;
274
281
  dst_mask = (boolean8_t *) co->mask->ptr;
275
282
  memset(dst_mask, 0, (size_t) n);
276
- }
277
- src_ptr = ca->ptr;
278
- dst_ptr = co->ptr;
279
-
280
- if ( has_mask ) {
281
283
  for (i = 0; i < n; i++) {
282
284
  if ( ! src_mask[i] ) {
283
- VALUE val = rb_ca_ptr2obj(self, (void *) (src_ptr + i * sbytes));
285
+ VALUE val = rb_ca_ptr2obj(c->self, (void *) (src_ptr + i * sbytes));
284
286
  VALUE ret = rb_yield(val);
285
287
  if ( ret == CA_UNDEF ) {
286
288
  dst_mask[i] = 1;
287
289
  }
288
290
  else {
289
- rb_ca_obj2ptr(obj, ret, dst_ptr + i * dbytes);
291
+ rb_ca_obj2ptr(c->obj, ret, dst_ptr + i * dbytes);
290
292
  }
291
293
  }
292
294
  else {
@@ -296,33 +298,78 @@ rb_ca_convert (int argc, VALUE *argv, VALUE self)
296
298
  }
297
299
  else {
298
300
  for (i = 0; i < n; i++) {
299
- VALUE val = rb_ca_ptr2obj(self, (void *) (src_ptr + i * sbytes));
301
+ VALUE val = rb_ca_ptr2obj(c->self, (void *) (src_ptr + i * sbytes));
300
302
  VALUE ret = rb_yield(val);
301
303
  if ( ret == CA_UNDEF ) {
302
304
  if ( ! co->mask ) {
303
305
  ca_create_mask(co);
304
- ca_attach(co->mask);
306
+ ca_attach(co->mask); /* window: closed by the ensure in ca_convert_body */
307
+ c->co_mask_attached = 1;
305
308
  dst_mask = (boolean8_t *) co->mask->ptr;
306
309
  memset(dst_mask, 0, (size_t) n);
307
310
  }
308
311
  dst_mask[i] = 1;
309
312
  }
310
313
  else {
311
- rb_ca_obj2ptr(obj, ret, dst_ptr + i * dbytes);
314
+ rb_ca_obj2ptr(c->obj, ret, dst_ptr + i * dbytes);
312
315
  }
313
316
  }
314
317
  }
318
+ return Qnil;
319
+ }
315
320
 
316
- if ( co->mask ) {
317
- ca_sync(co->mask);
318
- ca_detach(co->mask);
321
+ static VALUE
322
+ ca_convert_close_mask (VALUE arg)
323
+ {
324
+ ca_convert_ctx_t *c = (ca_convert_ctx_t *) arg;
325
+ if ( c->co_mask_attached ) {
326
+ c->co_mask_attached = 0;
327
+ ca_sync_detach(c->co->mask);
319
328
  }
320
- if ( has_mask ) {
321
- ca_detach(ca->mask);
329
+ return Qnil;
330
+ }
331
+
332
+ static VALUE
333
+ ca_convert_body (VALUE arg)
334
+ {
335
+ return rb_ensure(ca_convert_loop, arg, ca_convert_close_mask, arg);
336
+ }
337
+
338
+ /* CArray#convert(data_type=nil, bytes: nil) { |elem| ... } — map every cell
339
+ * through the block, into a new array whose data type is chosen by argv via
340
+ * CArray#template (shape is inherited from self).
341
+ *
342
+ * Masked cells skip the block; if the block returns CA_UNDEF, the
343
+ * corresponding output cell is masked instead of stored. Both self
344
+ * and the template result are attached symmetrically (R3-compliant) so
345
+ * an overridden #template returning a view stays on the in-ptr path. */
346
+ static VALUE
347
+ rb_ca_convert (int argc, VALUE *argv, VALUE self)
348
+ {
349
+ volatile VALUE obj;
350
+ ca_convert_ctx_t c;
351
+ CArray *list[2];
352
+
353
+ rb_need_block();
354
+
355
+ obj = rb_apply(self, rb_intern("template"), rb_ary_new4(argc, argv));
356
+
357
+ TypedData_Get_Struct(self, CArray, &carray_data_type, c.ca);
358
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, c.co);
359
+ c.self = self;
360
+ c.obj = obj;
361
+ c.has_mask = ca_has_mask(c.ca);
362
+ c.co_mask_attached = 0;
363
+
364
+ /* Attaching an array attaches its mask, so the output's mask is made
365
+ before the window opens when the source already has one. */
366
+ if ( c.has_mask && ! c.co->mask ) {
367
+ ca_create_mask(c.co);
322
368
  }
323
- ca_sync(co);
324
- ca_detach(co);
325
- ca_detach(ca);
369
+
370
+ list[0] = c.ca;
371
+ list[1] = c.co;
372
+ ca_attach_window(2, list, "rw", ca_convert_body, (VALUE) &c);
326
373
 
327
374
  return obj;
328
375
  }
@@ -395,7 +442,7 @@ rb_ca_dump_binary (int argc, VALUE *argv, VALUE self)
395
442
  rb_funcall(io, rb_intern("write"), 1, buf);
396
443
  }
397
444
  else {
398
- rb_raise(rb_eRuntimeError, "IO like object should have 'write' method");
445
+ rb_raise(rb_eTypeError, "IO like object should have 'write' method");
399
446
  }
400
447
  }
401
448
 
@@ -422,6 +469,7 @@ rb_ca_load_binary (VALUE self, VALUE io)
422
469
  {
423
470
  CArray *ca;
424
471
 
472
+ rb_ca_modify(self);
425
473
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
426
474
 
427
475
  if ( ca_is_object_type(ca) ) {
@@ -431,11 +479,17 @@ rb_ca_load_binary (VALUE self, VALUE io)
431
479
  switch ( TYPE(io) ) {
432
480
  case T_STRING:
433
481
  if ( ca_length(ca) != RSTRING_LEN(io) ) {
434
- rb_raise(rb_eRuntimeError,
435
- "data size mismatch (got %" PRId64 " bytes, expected %" PRId64 ")",
436
- (ca_size_t) RSTRING_LEN(io), (ca_size_t) ca_length(ca));
482
+ rb_raise(rb_eArgError,
483
+ "%s: data size mismatch (got %" PRId64 " bytes, expected %" PRId64 ")",
484
+ ca_calling_method_name(), (ca_size_t) RSTRING_LEN(io), (ca_size_t) ca_length(ca));
485
+ }
486
+ if ( ca->data_type == CA_BOOLEAN ) {
487
+ io = rb_str_dup(io);
488
+ rb_str_modify(io);
489
+ ca_boolean_normalize_bytes(RSTRING_PTR(io), ca->elements);
437
490
  }
438
491
  ca_sync_data(ca, RSTRING_PTR(io));
492
+ RB_GC_GUARD(io);
439
493
  return self;
440
494
  break;
441
495
  default:
@@ -447,13 +501,13 @@ rb_ca_load_binary (VALUE self, VALUE io)
447
501
  const ca_size_t CHUNK = 65536;
448
502
  volatile VALUE scratch = rb_str_buf_new(CHUNK < total ? CHUNK : total);
449
503
  ca_size_t off = 0;
450
- ca_allocate(ca);
504
+ ca_allocate(ca); /* window: an entity, whose allocate holds nothing */
451
505
  while ( off < total ) {
452
506
  ca_size_t want = (total - off < CHUNK) ? (total - off) : CHUNK;
453
507
  VALUE r = rb_funcall(io, id_read, 2, SIZE2NUM(want), scratch);
454
508
  if ( NIL_P(r) || RSTRING_LEN(r) != want ) {
455
509
  ca_detach(ca);
456
- rb_raise(rb_eRuntimeError,
510
+ rb_raise(rb_eArgError,
457
511
  "short read from IO at offset %" PRId64 " "
458
512
  "(got %" PRId64 " bytes, expected %" PRId64 ")",
459
513
  (ca_size_t) off,
@@ -463,15 +517,17 @@ rb_ca_load_binary (VALUE self, VALUE io)
463
517
  memcpy(ca->ptr + off, RSTRING_PTR(r), want);
464
518
  off += want;
465
519
  }
466
- ca_sync(ca);
467
- ca_detach(ca);
520
+ if ( ca->data_type == CA_BOOLEAN ) {
521
+ ca_boolean_normalize_bytes(ca->ptr, ca->elements);
522
+ }
523
+ ca_sync_detach(ca);
468
524
  }
469
525
  else {
470
526
  /* ca_sync_data scatters in one pass, so buffer the full read
471
527
  and recurse via T_STRING. */
472
528
  volatile VALUE buf = rb_funcall(io, id_read, 1, SIZE2NUM(total));
473
529
  if ( NIL_P(buf) || RSTRING_LEN(buf) != total ) {
474
- rb_raise(rb_eRuntimeError,
530
+ rb_raise(rb_eArgError,
475
531
  "short read from IO (got %" PRId64 " bytes, expected %" PRId64 ")",
476
532
  (ca_size_t) (NIL_P(buf) ? 0 : RSTRING_LEN(buf)),
477
533
  (ca_size_t) total);
@@ -481,13 +537,89 @@ rb_ca_load_binary (VALUE self, VALUE io)
481
537
  return self;
482
538
  }
483
539
  else {
484
- rb_raise(rb_eRuntimeError, "IO like object should have 'read' method");
540
+ rb_raise(rb_eTypeError, "IO like object should have 'read' method");
485
541
  }
486
542
  }
487
543
 
488
544
  return self;
489
545
  }
490
546
 
547
+ /* The double nearest to the shortest decimal that reads back as x, the
548
+ float32 value, as from_csv reads text into float32: strtod, then the cast
549
+ to float. Its digits are those of the decimal (at most 9, which a double
550
+ holds), so Float#to_s writes them. */
551
+ static double
552
+ ca_double_of_shortest_decimal_of_float32 (float x)
553
+ {
554
+ char buf[32];
555
+ int lo = 1, hi = 9;
556
+ double best;
557
+ if ( isnan(x) || isinf(x) ) {
558
+ return (double) x;
559
+ }
560
+ /* Nine significant digits always read back; search for the fewest. More
561
+ digits round closer to x, so the digit counts that read back are those
562
+ from some count up. */
563
+ best = 0.0;
564
+ while ( lo < hi ) {
565
+ int mid = (lo + hi) / 2;
566
+ double d;
567
+ snprintf(buf, sizeof(buf), "%.*e", mid - 1, (double) x);
568
+ d = ruby_strtod(buf, NULL);
569
+ if ( (float) d == x ) {
570
+ best = d;
571
+ hi = mid;
572
+ }
573
+ else {
574
+ lo = mid + 1;
575
+ }
576
+ }
577
+ if ( lo == 9 ) {
578
+ snprintf(buf, sizeof(buf), "%.8e", (double) x);
579
+ best = ruby_strtod(buf, NULL);
580
+ }
581
+ return best;
582
+ }
583
+
584
+ /* float32 -> float64 and cmplx64 -> cmplx128, each value replaced by the
585
+ double its shortest decimal reads as, for to_csv to write that decimal.
586
+ The mask is kept. */
587
+ static VALUE
588
+ rb_ca_widen_keeping_shortest_decimal (VALUE self)
589
+ {
590
+ volatile VALUE out, holder = 0;
591
+ CArray *ca, *co;
592
+ ca_size_t i, n;
593
+ int8_t type;
594
+
595
+ GetCArray(self, ca);
596
+ if ( ca->data_type == CA_FLOAT32 ) {
597
+ type = CA_FLOAT64;
598
+ }
599
+ else if ( ca->data_type == CA_CMPLX64 ) {
600
+ type = CA_CMPLX128;
601
+ }
602
+ else {
603
+ rb_raise(rb_eCADataTypeError, "__widen_keeping_shortest_decimal__ takes float32 or cmplx64");
604
+ }
605
+ n = ca->elements * (ca->data_type == CA_CMPLX64 ? 2 : 1);
606
+ out = rb_carray_new_safe(type, ca->ndim, ca->dim, 0, NULL);
607
+ GetCArray(out, co);
608
+ {
609
+ float *src = ALLOCV_N(float, holder, n > 0 ? n : 1);
610
+ double *dst = (double *) co->ptr;
611
+ ca_copy_data(ca, (char *) src);
612
+ for (i = 0; i < n; i++) {
613
+ dst[i] = ca_double_of_shortest_decimal_of_float32(src[i]);
614
+ }
615
+ ALLOCV_END(holder);
616
+ }
617
+ if ( ca_has_mask(ca) ) {
618
+ ca_copy_mask_overlay(co, co->elements, 1, ca);
619
+ }
620
+ return out;
621
+ }
622
+
491
623
  /* [MOVED] str_format (2.0-era per-element formatter) is replaced by the Ruby
492
624
  CArray#format / CArray.format surface in lib/carray/methods/string_format.rb,
493
625
  which yields a CAString. */
@@ -504,6 +636,7 @@ Init_carray_conversion (void)
504
636
  rb_define_method(rb_cCArray, "dump_binary", rb_ca_dump_binary, -1);
505
637
  rb_define_method(rb_cCArray, "to_s", rb_ca_to_s, 0);
506
638
  rb_define_method(rb_cCArray, "load_binary", rb_ca_load_binary, 1);
639
+ rb_define_method(rb_cCArray, "__widen_keeping_shortest_decimal__", rb_ca_widen_keeping_shortest_decimal, 0);
507
640
 
508
641
  /* CAREFUL: do not bind #to_ary — implicit array coercion in Ruby core
509
642
  (splat / multiple assignment / Array()) then triggers on any CArray