carray 2.0.0 → 3.0.0

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 (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +774 -298
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +230 -792
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +208 -241
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -330
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1342 -199
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +4 -14
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
data/ext/carray_cast.c CHANGED
@@ -1,14 +1,37 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- carray_cast.c
4
-
5
- This file is part of Ruby/CArray extension library.
6
-
7
- Copyright (C) 2005-2020 Hiroki Motoyoshi
3
+ Cast surface (3 layers):
4
+
5
+ to_<type> `a.to_int32` etc. Returns a fresh entity CArray
6
+ of the target data_type. Independent storage from
7
+ `self`.
8
+
9
+ as_<type> `a.as_int32` etc. Returns a writable CAMonOp view
10
+ that reinterprets `self` cell-by-cell at the target
11
+ data_type. No new storage.
12
+
13
+ CA_<TYPE>(data)
14
+ `CA_INT32(data)`, `CA_FLOAT64(data)`, ...,
15
+ `CA_OBJECT(data)`, `CA_FIXLEN(...)` — global
16
+ polymorphic cast functions (registered at file
17
+ bottom via rb_define_global_function). Coerce
18
+ `data` (Numeric / Array / CArray / Range /
19
+ String / nil / object responding to `to_ca` /
20
+ `to_a`) into a CArray of the target data_type,
21
+ dispatching on `data`'s Ruby class. Type-name
22
+ aliases (CA_BYTE / CA_INT / CA_FLOAT / CA_SIZE /
23
+ ...) reuse the canonical entry.
24
+
25
+ Sibling of ext/carray_data_type.c (type-tag class shells +
26
+ data_type id <-> class mapping); the CA_<TYPE>(data) functions
27
+ here pair with the typed classes registered there.
8
28
 
9
29
  ---------------------------------------------------------------------------- */
10
30
 
11
31
  #include "carray.h"
32
+ #include "ruby/memory_view.h"
33
+ #include "ca_monop_dispatch.h" /* CA_MONOP_CAST_BASE for as_type routing */
34
+ #include "ca_obj_face.h" /* ca_strip_face for write path lift */
12
35
 
13
36
  boolean8_t
14
37
  OBJ2BOOL (VALUE v)
@@ -38,6 +61,36 @@ BOOL2OBJ (boolean8_t x)
38
61
  return ( x != 0 ) ? INT2NUM(1) : INT2NUM(0);
39
62
  }
40
63
 
64
+ /* Recognise an explicit non-finite float literal. `str` must already be
65
+ whitespace-stripped. The whole token is matched case-insensitively as an
66
+ exact match (not a prefix): only nan / inf / infinity with an optional
67
+ +/- sign map to a non-finite value. Returns 1 and sets *out on a match,
68
+ 0 otherwise -- so "nancy" / "info" / "inflation" fall through to the
69
+ numeric parser instead of being read as NaN/Infinity. */
70
+ static int
71
+ ca_str_nonfinite (const char *str, double *out)
72
+ {
73
+ if ( ! strcasecmp(str, "nan")
74
+ || ! strcasecmp(str, "+nan")
75
+ || ! strcasecmp(str, "-nan") ) {
76
+ *out = 0.0/0.0;
77
+ return 1;
78
+ }
79
+ else if ( ! strcasecmp(str, "inf")
80
+ || ! strcasecmp(str, "+inf")
81
+ || ! strcasecmp(str, "infinity")
82
+ || ! strcasecmp(str, "+infinity") ) {
83
+ *out = 1.0/0.0;
84
+ return 1;
85
+ }
86
+ else if ( ! strcasecmp(str, "-inf")
87
+ || ! strcasecmp(str, "-infinity") ) {
88
+ *out = -1.0/0.0;
89
+ return 1;
90
+ }
91
+ return 0;
92
+ }
93
+
41
94
  double
42
95
  OBJ2DBL (VALUE val)
43
96
  {
@@ -46,30 +99,160 @@ OBJ2DBL (VALUE val)
46
99
  return NUM2DBL(val);
47
100
  case T_NIL:
48
101
  return 0.0/0.0;
49
- /* rb_raise(rb_eTypeError, "no implicit conversion to float from nil"); */
50
- break;
51
102
  case T_STRING: {
103
+ double d;
52
104
  volatile VALUE rstr = rb_funcall(val, rb_intern("strip"), 0);
53
105
  char *str = StringValuePtr(rstr);
54
- if ( ! strncasecmp("nan", str, 3) ) {
55
- return 0.0/0.0;
106
+ if ( ca_str_nonfinite(str, &d) ) {
107
+ return d;
56
108
  }
57
- else if ( ! strncasecmp("inf", str, 3) ) {
58
- return 1.0/0.0;
109
+ return rb_cstr_to_dbl(str, 0);
110
+ }
111
+ default:
112
+ return NUM2DBL(rb_Float(val));
113
+ }
114
+ }
115
+
116
+ /* rb_protect bodies: Kernel#Float / Kernel#Integer applied to one value.
117
+ Used by the mask-aware object->numeric parsers below so a conversion
118
+ error is caught (and turned into a masked cell) rather than raised. */
119
+ static VALUE
120
+ ca_kernel_float (VALUE v)
121
+ {
122
+ return rb_Float(v);
123
+ }
124
+
125
+ static VALUE
126
+ ca_kernel_integer (VALUE v)
127
+ {
128
+ return rb_Integer(v);
129
+ }
130
+
131
+ /* Object -> double with parse-failure signalling. Returns 1 and sets *out
132
+ on success; returns 0 when the cell cannot be parsed, in which case the
133
+ caller writes UNDEF (mask) for that cell. nil and unparseable
134
+ strings/objects fail; already-numeric values and explicit nan/inf
135
+ literals succeed. Strictness mirrors Ruby Float() (whitespace strip,
136
+ "1e3" ok, ...) but a bad token masks instead of raising. */
137
+ int
138
+ ca_obj2dbl_ok (VALUE val, double *out)
139
+ {
140
+ if ( NIL_P(val) ) {
141
+ return 0;
142
+ }
143
+ if ( RB_FLOAT_TYPE_P(val) || RB_INTEGER_TYPE_P(val) ) {
144
+ *out = NUM2DBL(val);
145
+ return 1;
146
+ }
147
+ if ( RB_TYPE_P(val, T_STRING) ) {
148
+ volatile VALUE rstr = rb_funcall(val, rb_intern("strip"), 0);
149
+ if ( ca_str_nonfinite(StringValuePtr(rstr), out) ) {
150
+ return 1;
59
151
  }
60
- else if ( ! strncasecmp("-inf", str, 4) ) {
61
- return -1.0/0.0;
152
+ }
153
+ {
154
+ int state = 0;
155
+ volatile VALUE r = rb_protect(ca_kernel_float, val, &state);
156
+ if ( state ) {
157
+ rb_set_errinfo(Qnil);
158
+ return 0;
62
159
  }
63
- else if ( ! strncasecmp("infinity", str, 8) ) {
64
- return 1.0/0.0;
160
+ *out = NUM2DBL(r);
161
+ return 1;
162
+ }
163
+ }
164
+
165
+ /* Object -> integer with parse-failure signalling (int/long lane). Same
166
+ contract as ca_obj2dbl_ok: 1 + *out on success, 0 (mask) on failure.
167
+ Strictness mirrors Ruby Integer() -- a non-integer string ("1.5") fails
168
+ (no truncation), nil fails, "0xff"/"1_000" parse. A valid Integer that
169
+ overflows the widest C integer still raises (a domain error, not a parse
170
+ failure). */
171
+ int
172
+ rb_obj2long_ok (VALUE val, long *out)
173
+ {
174
+ if ( NIL_P(val) ) {
175
+ return 0;
176
+ }
177
+ if ( RB_TYPE_P(val, T_FIXNUM) ) {
178
+ *out = NUM2LONG(val);
179
+ return 1;
180
+ }
181
+ {
182
+ int state = 0;
183
+ volatile VALUE r = rb_protect(ca_kernel_integer, val, &state);
184
+ if ( state ) {
185
+ rb_set_errinfo(Qnil);
186
+ return 0;
65
187
  }
66
- else if ( ! strncasecmp("-infinity", str, 9) ) {
67
- return -1.0/0.0;
188
+ *out = NUM2LONG(r);
189
+ return 1;
190
+ }
191
+ }
192
+
193
+ int
194
+ rb_obj2ulong_ok (VALUE val, unsigned long *out)
195
+ {
196
+ if ( NIL_P(val) ) {
197
+ return 0;
198
+ }
199
+ if ( RB_TYPE_P(val, T_FIXNUM) ) {
200
+ *out = NUM2ULONG(val);
201
+ return 1;
202
+ }
203
+ {
204
+ int state = 0;
205
+ volatile VALUE r = rb_protect(ca_kernel_integer, val, &state);
206
+ if ( state ) {
207
+ rb_set_errinfo(Qnil);
208
+ return 0;
68
209
  }
69
- return rb_cstr_to_dbl(str, 0);
210
+ *out = NUM2ULONG(r);
211
+ return 1;
70
212
  }
71
- default:
72
- return NUM2DBL(rb_Float(val));
213
+ }
214
+
215
+ int
216
+ rb_obj2ll_ok (VALUE val, long long *out)
217
+ {
218
+ if ( NIL_P(val) ) {
219
+ return 0;
220
+ }
221
+ if ( RB_TYPE_P(val, T_FIXNUM) ) {
222
+ *out = NUM2LL(val);
223
+ return 1;
224
+ }
225
+ {
226
+ int state = 0;
227
+ volatile VALUE r = rb_protect(ca_kernel_integer, val, &state);
228
+ if ( state ) {
229
+ rb_set_errinfo(Qnil);
230
+ return 0;
231
+ }
232
+ *out = NUM2LL(r);
233
+ return 1;
234
+ }
235
+ }
236
+
237
+ int
238
+ rb_obj2ull_ok (VALUE val, unsigned long long *out)
239
+ {
240
+ if ( NIL_P(val) ) {
241
+ return 0;
242
+ }
243
+ if ( RB_TYPE_P(val, T_FIXNUM) ) {
244
+ *out = NUM2ULL(val);
245
+ return 1;
246
+ }
247
+ {
248
+ int state = 0;
249
+ volatile VALUE r = rb_protect(ca_kernel_integer, val, &state);
250
+ if ( state ) {
251
+ rb_set_errinfo(Qnil);
252
+ return 0;
253
+ }
254
+ *out = rb_num2ull(r);
255
+ return 1;
73
256
  }
74
257
  }
75
258
 
@@ -112,7 +295,7 @@ rb_obj2ll (VALUE val)
112
295
  case T_FIXNUM:
113
296
  return NUM2LONG(val);
114
297
  case T_NIL:
115
- rb_raise(rb_eTypeError, "no implicit conversion fron nil to integer");
298
+ rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
116
299
  break;
117
300
  default:
118
301
  return NUM2LL(rb_Integer(val));
@@ -133,6 +316,26 @@ rb_obj2ull (VALUE val)
133
316
  }
134
317
  }
135
318
 
319
+ /* Face has surface != storage; cast operates on the storage
320
+ * data_type, which lives at the parent chain bottom (the first
321
+ * non-Face ancestor). This predicate is defensive: stack-allocated
322
+ * dummy CArrays may have an uninitialised obj_type, so we additionally
323
+ * require obj_type to fall in the valid range before treating `ca`
324
+ * as a Face. */
325
+ static inline int
326
+ ca_face_safe_check (CArray *ca)
327
+ {
328
+ return ca && ca_is_face(ca)
329
+ && ca->obj_type >= 0 && ca->obj_type < CA_OBJ_TYPE_MAX;
330
+ }
331
+
332
+ static inline int8_t
333
+ ca_storage_type_of (CArray *ca)
334
+ {
335
+ while (ca_face_safe_check(ca)) ca = ((CAView *) ca)->parent;
336
+ return ca ? ca->data_type : CA_FIXLEN;
337
+ }
338
+
136
339
  void
137
340
  ca_cast_block (ca_size_t n, void *ap1, void *ptr1,
138
341
  void *ap2, void *ptr2)
@@ -140,7 +343,7 @@ ca_cast_block (ca_size_t n, void *ap1, void *ptr1,
140
343
  CArray *ca1 = (CArray *) ap1;
141
344
  CArray *ca2 = (CArray *) ap2;
142
345
  if ( n < 0 ) {
143
- rb_raise(rb_eRuntimeError,
346
+ rb_raise(rb_eRuntimeError,
144
347
  "[BUG] in ca_cast_block(): negative count");
145
348
  }
146
349
  ca_cast_func_table[ca1->data_type][ca2->data_type](n, ca1, ptr1, ca2, ptr2, NULL);
@@ -153,7 +356,7 @@ ca_cast_block_with_mask (ca_size_t n, void *ap1, void *ptr1,
153
356
  CArray *ca1 = (CArray *) ap1;
154
357
  CArray *ca2 = (CArray *) ap2;
155
358
  if ( n < 0 ) {
156
- rb_raise(rb_eRuntimeError,
359
+ rb_raise(rb_eRuntimeError,
157
360
  "[BUG] in ca_cast_block_with_mask(): negative count");
158
361
  }
159
362
  ca_cast_func_table[ca1->data_type][ca2->data_type](n, ca1, ptr1, ca2, ptr2, m);
@@ -225,12 +428,20 @@ ca_val2val (int8_t data_type1, void *ptr1, int8_t data_type2, void *ptr2)
225
428
 
226
429
  /* --------------------------------------------------------------------- */
227
430
 
431
+ /* Cast to CA_OBJECT for an array whose cells mean something other than their
432
+ storage bytes: a data_class array (CARecord / CAStruct) or a Face (CATime,
433
+ CAConstString, CACategorical, ...). Both decode per cell -- rb_ca_fetch_addr
434
+ goes through the data_class decode and CA_FACE_STORAGE_TO_SCALAR_IF_FACE --
435
+ so the object array holds the *surface* values (labels, not codes;
436
+ CATime::Element, not serials; the string, not the descriptor bytes). Reading
437
+ the storage instead is `.parent`, which stays available and explicit. Mask
438
+ and shape carry. */
228
439
  static VALUE
229
- rb_ca_data_class_to_object (VALUE self)
440
+ rb_ca_surface_to_object (VALUE self)
230
441
  {
231
442
  volatile VALUE obj;
232
443
  CArray *ca;
233
- int i;
444
+ ca_size_t i;
234
445
 
235
446
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
236
447
 
@@ -244,10 +455,71 @@ rb_ca_data_class_to_object (VALUE self)
244
455
  for (i=0; i<ca->elements; i++) {
245
456
  rb_ca_store_addr(obj, i, rb_ca_fetch_addr(self, i));
246
457
  }
247
-
458
+
248
459
  return obj;
249
460
  }
250
461
 
462
+ /* Cast a NonNumeric Face (= one whose surface declares CA_FIXLEN, so the
463
+ numeric kernel dispatch is gated off) to a type other than :object.
464
+
465
+ Reading the storage would hand back the bytes the surface exists to hide,
466
+ and there is no way for the core to guess how a surface becomes a number:
467
+ a fixed-point cell is its storage over a scale, a categorical cell is a
468
+ label, a time cell is not a number at all. So the Face declares it, with
469
+ #to_numeric returning a plain CArray of its values, and everything else is
470
+ an ordinary cast of that. A Face that declares nothing raises: it is the
471
+ Face saying "my values are not numbers", which is information, unlike an
472
+ array of UNDEF.
473
+
474
+ The projection is array-level on purpose. Going through the surface cell
475
+ by cell (to_type(:object) and Ruby #to_f) measured 449 ms against 1.45 ms
476
+ for the vectorised expression the author writes anyway (1M cells,
477
+ CAFixedPoint = parent.float64 / scale). */
478
+ static VALUE
479
+ rb_ca_face_numeric_projection (VALUE self, CArray *ca)
480
+ {
481
+ static ID id_to_numeric = 0;
482
+ VALUE num;
483
+ CArray *cn;
484
+
485
+ if ( id_to_numeric == 0 ) {
486
+ id_to_numeric = rb_intern("to_numeric");
487
+ }
488
+
489
+ if ( ! rb_respond_to(self, id_to_numeric) ) {
490
+ rb_raise(rb_eTypeError,
491
+ "%s declares no numeric conversion: define #to_numeric to say "
492
+ "how its values become numbers, or take the surface with "
493
+ "to_type(:object) / the storage with #parent",
494
+ rb_obj_classname(self));
495
+ }
496
+
497
+ num = rb_funcall(self, id_to_numeric, 0);
498
+
499
+ if ( ! rb_obj_is_carray(num) ) {
500
+ rb_raise(rb_eTypeError,
501
+ "%s#to_numeric must return a CArray (got %s)",
502
+ rb_obj_classname(self), rb_obj_classname(num));
503
+ }
504
+
505
+ TypedData_Get_Struct(num, CArray, &carray_data_type, cn);
506
+
507
+ if ( ca_is_face(cn) ) {
508
+ rb_raise(rb_eTypeError,
509
+ "%s#to_numeric must return a plain CArray, not a Face (got %s)",
510
+ rb_obj_classname(self), rb_obj_classname(num));
511
+ }
512
+
513
+ if ( cn->elements != ca->elements ) {
514
+ rb_raise(rb_eArgError,
515
+ "%s#to_numeric returned %lld elements, expected %lld",
516
+ rb_obj_classname(self),
517
+ (long long) cn->elements, (long long) ca->elements);
518
+ }
519
+
520
+ return num;
521
+ }
522
+
251
523
  static VALUE
252
524
  rb_ca_object_to_data_class (VALUE self, VALUE rtype, ca_size_t bytes)
253
525
  {
@@ -264,7 +536,6 @@ rb_ca_object_to_data_class (VALUE self, VALUE rtype, ca_size_t bytes)
264
536
  else {
265
537
  obj = rb_carray_new(CA_FIXLEN, ca->ndim, ca->dim, bytes, ca->mask);
266
538
  }
267
- rb_ca_data_type_import(obj, rtype);
268
539
 
269
540
  for (i=0; i<ca->elements; i++) {
270
541
  rval = rb_ca_fetch_addr(self, i);
@@ -279,11 +550,9 @@ rb_ca_object_to_data_class (VALUE self, VALUE rtype, ca_size_t bytes)
279
550
  return obj;
280
551
  }
281
552
 
282
- /* @overload to_type (data_type, bytes: nil)
283
-
284
- (Conversion) Returns an array of elements that are converted
285
- to the given data type from the object.
286
- */
553
+ /* CArray#to_type(data_type, bytes:) -- eager copy of self converted to
554
+ data_type (a new entity owning its storage). User doc lives in
555
+ yard-stubs/carray_cast.rb. */
287
556
 
288
557
  static VALUE
289
558
  rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
@@ -300,14 +569,24 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
300
569
 
301
570
  rb_ca_guess_type_and_bytes(rtype, rbytes, &data_type, &bytes);
302
571
 
303
- if ( rb_ca_has_data_class(self) && data_type == CA_OBJECT ) {
304
- return rb_ca_data_class_to_object(self);
572
+ if ( ( rb_ca_has_data_class(self) || ca_is_face(ca) ) && data_type == CA_OBJECT ) {
573
+ return rb_ca_surface_to_object(self);
305
574
  }
306
575
 
307
576
  if ( rb_ca_is_object_type(self) && rb_obj_is_data_class(rtype) ) {
308
577
  return rb_ca_object_to_data_class(self, rtype, bytes);
309
578
  }
310
579
 
580
+ /* NonNumeric Face (CA_FIXLEN surface = the numeric gate) to anything but
581
+ :object: the values come from the Face's own #to_numeric, and the
582
+ request is then served by an ordinary cast of that plain array. A
583
+ Numeric Face is not routed here -- its surface *is* its storage, so the
584
+ ordinary cast below is already right. */
585
+ if ( ca_is_face(ca) && ca->data_type == CA_FIXLEN ) {
586
+ return rb_ca_to_type_internal(argc, argv,
587
+ rb_ca_face_numeric_projection(self, ca));
588
+ }
589
+
311
590
  ca_update_mask(ca);
312
591
 
313
592
  if ( ca_is_scalar(ca) ) {
@@ -317,13 +596,45 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
317
596
  obj = rb_carray_new(data_type, ca->ndim, ca->dim, bytes, ca->mask);
318
597
  }
319
598
 
320
- rb_ca_data_type_import(obj, rtype);
321
599
 
322
600
  TypedData_Get_Struct(obj, CArray, &carray_data_type, cb);
323
601
 
324
602
  ca_attach(ca);
325
- if ( ca_has_mask(ca) ) {
326
- ca_cast_block_with_mask(cb->elements, ca, ca->ptr, cb, cb->ptr,
603
+ if ( ca->data_type == CA_OBJECT
604
+ && ( (data_type >= CA_INT8 && data_type <= CA_UINT64)
605
+ || data_type == CA_FLOAT32 || data_type == CA_FLOAT64 ) ) {
606
+ /* object -> int/float: give the cast a mask buffer so an unparseable
607
+ cell becomes UNDEF (see ext/carray_cast_func.rb). Cast into a
608
+ scratch mask seeded from the source mask, then attach it to the
609
+ output only if some cell ended up masked -- an all-valid cast keeps
610
+ the mask-less result the legacy path produced. */
611
+ ca_size_t ne = cb->elements;
612
+ ca_size_t i;
613
+ boolean8_t any = 0;
614
+ boolean8_t *scratch = ALLOC_N(boolean8_t, ne > 0 ? ne : 1);
615
+ if ( cb->mask ) {
616
+ memcpy(scratch, cb->mask->ptr, ne); /* copy of source mask */
617
+ }
618
+ else {
619
+ memset(scratch, 0, ne);
620
+ }
621
+ ca_cast_block_with_mask(ne, ca, ca->ptr, cb, cb->ptr, scratch);
622
+ for (i=0; i<ne; i++) {
623
+ if ( scratch[i] ) {
624
+ any = 1;
625
+ break;
626
+ }
627
+ }
628
+ if ( any ) {
629
+ if ( ! cb->mask ) {
630
+ ca_create_mask(cb);
631
+ }
632
+ memcpy(cb->mask->ptr, scratch, ne);
633
+ }
634
+ xfree(scratch);
635
+ }
636
+ else if ( ca_has_mask(ca) ) {
637
+ ca_cast_block_with_mask(cb->elements, ca, ca->ptr, cb, cb->ptr,
327
638
  (boolean8_t*)ca->mask->ptr);
328
639
  }
329
640
  else {
@@ -331,6 +642,13 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
331
642
  }
332
643
  ca_detach(ca);
333
644
 
645
+ /* When rtype is a data_class (e.g. CAStruct subclass), wrap the
646
+ result in CARecord so the cast output carries data_class. */
647
+ if ( rb_obj_is_data_class(rtype) ) {
648
+ obj = rb_funcall(rb_const_get(rb_cObject, rb_intern("CARecord")),
649
+ rb_intern("wrap"), 2, obj, rtype);
650
+ }
651
+
334
652
  return obj;
335
653
  }
336
654
 
@@ -349,161 +667,97 @@ rb_ca_to_type (VALUE self, VALUE rtype, VALUE rbytes)
349
667
  return rb_ca_to_type_internal(1, &rcode, self); \
350
668
  }
351
669
 
352
- /* @overload fixlen (bytes:)
353
-
354
- (Conversion) Short-Hand of "CArray#to_type(:fixlen, bytes:)"
355
- */
670
+ /* CArray#fixlen(bytes:) -- short-hand of to_type(:fixlen, bytes:). */
356
671
 
357
672
  VALUE
358
673
  rb_ca_to_fixlen (int argc, VALUE *argv, VALUE self)
359
674
  {
360
675
  volatile VALUE ropt = rb_pop_options(&argc, &argv);
361
676
  VALUE list[2];
362
- // rb_scan_args(argc, argv, "0");
363
677
  list[0] = INT2NUM(CA_FIXLEN);
364
678
  list[1] = ropt;
365
679
  return rb_ca_to_type_internal(2, list, self);
366
680
  }
367
681
 
368
- /* @overload fixlen (bytes:)
369
-
370
- (Conversion) Short-Hand of "CArray#to_type(:boolean)"
371
- */
682
+ /* CArray#boolean -- short-hand of to_type(:boolean). */
372
683
  VALUE rb_ca_to_boolean (VALUE self)
373
684
  {
374
685
  rb_ca_to_type_method_body(CA_BOOLEAN);
375
686
  }
376
687
 
377
- /* @overload int8
378
-
379
- (Conversion) Short-Hand of "CArray#to_type(:int8)"
380
- */
688
+ /* CArray#int8 -- short-hand of to_type(:int8). */
381
689
  VALUE rb_ca_to_int8 (VALUE self)
382
690
  {
383
691
  rb_ca_to_type_method_body(CA_INT8);
384
692
  }
385
693
 
386
- /* @overload uint8
387
-
388
- (Conversion) Short-Hand of "CArray#to_type(:uint8)"
389
- */
694
+ /* CArray#uint8 -- short-hand of to_type(:uint8). */
390
695
  VALUE rb_ca_to_uint8 (VALUE self)
391
696
  {
392
697
  rb_ca_to_type_method_body(CA_UINT8);
393
698
  }
394
699
 
395
- /* @overload int16
396
-
397
- (Conversion) Short-Hand of "CArray#to_type(:int16)"
398
- */
700
+ /* CArray#int16 -- short-hand of to_type(:int16). */
399
701
  VALUE rb_ca_to_int16 (VALUE self)
400
702
  {
401
703
  rb_ca_to_type_method_body(CA_INT16);
402
704
  }
403
705
 
404
- /* @overload uint16
405
-
406
- (Conversion) Short-Hand of "CArray#to_type(:uint16)"
407
- */
706
+ /* CArray#uint16 -- short-hand of to_type(:uint16). */
408
707
  VALUE rb_ca_to_uint16 (VALUE self)
409
708
  {
410
709
  rb_ca_to_type_method_body(CA_UINT16);
411
710
  }
412
711
 
413
- /* @overload int32
414
-
415
- (Conversion) Short-Hand of "CArray#to_type(:int32)"
416
- */
712
+ /* CArray#int32 -- short-hand of to_type(:int32). */
417
713
  VALUE rb_ca_to_int32 (VALUE self)
418
714
  {
419
715
  rb_ca_to_type_method_body(CA_INT32);
420
716
  }
421
717
 
422
- /* @overload uint32
423
-
424
- (Conversion) Short-Hand of "CArray#to_type(:uint32)"
425
- */
718
+ /* CArray#uint32 -- short-hand of to_type(:uint32). */
426
719
  VALUE rb_ca_to_uint32 (VALUE self)
427
720
  {
428
721
  rb_ca_to_type_method_body(CA_UINT32);
429
722
  }
430
723
 
431
- /* @overload int64
432
-
433
- (Conversion) Short-Hand of "CArray#to_type(:int64)"
434
- */
724
+ /* CArray#int64 -- short-hand of to_type(:int64). */
435
725
  VALUE rb_ca_to_int64 (VALUE self)
436
726
  {
437
727
  rb_ca_to_type_method_body(CA_INT64);
438
728
  }
439
729
 
440
- /* @overload uint64
441
-
442
- (Conversion) Short-Hand of "CArray#to_type(:uint64)"
443
- */
730
+ /* CArray#uint64 -- short-hand of to_type(:uint64). */
444
731
  VALUE rb_ca_to_uint64 (VALUE self)
445
732
  {
446
733
  rb_ca_to_type_method_body(CA_UINT64);
447
734
  }
448
735
 
449
- /* @overload float32
450
-
451
- (Conversion) Short-Hand of "CArray#to_type(:float32)"
452
- */
736
+ /* CArray#float32 -- short-hand of to_type(:float32). */
453
737
  VALUE rb_ca_to_float32 (VALUE self)
454
738
  {
455
739
  rb_ca_to_type_method_body(CA_FLOAT32);
456
740
  }
457
741
 
458
- /* @overload float64
459
-
460
- (Conversion) Short-Hand of "CArray#to_type(:float64)"
461
- */
742
+ /* CArray#float64 -- short-hand of to_type(:float64). */
462
743
  VALUE rb_ca_to_float64 (VALUE self)
463
744
  {
464
745
  rb_ca_to_type_method_body(CA_FLOAT64);
465
746
  }
466
747
 
467
- /* @overload float128
468
-
469
- (Conversion) Short-Hand of "CArray#to_type(:float128)"
470
- */
471
- VALUE rb_ca_to_float128 (VALUE self)
472
- {
473
- rb_ca_to_type_method_body(CA_FLOAT128);
474
- }
475
-
476
- /* @overload cmplx64
477
-
478
- (Conversion) Short-Hand of "CArray#to_type(:cmplx64)"
479
- */
748
+ /* CArray#cmplx64 -- short-hand of to_type(:cmplx64). */
480
749
  VALUE rb_ca_to_cmplx64 (VALUE self)
481
750
  {
482
751
  rb_ca_to_type_method_body(CA_CMPLX64);
483
752
  }
484
753
 
485
- /* @overload cmplx128
486
-
487
- (Conversion) Short-Hand of "CArray#to_type(:cmplx128)"
488
- */
754
+ /* CArray#cmplx128 -- short-hand of to_type(:cmplx128). */
489
755
  VALUE rb_ca_to_cmplx128 (VALUE self)
490
756
  {
491
757
  rb_ca_to_type_method_body(CA_CMPLX128);
492
758
  }
493
759
 
494
- /* @overload cmplx256
495
-
496
- (Conversion) Short-Hand of "CArray#to_type(:cmplx256)"
497
- */
498
- VALUE rb_ca_to_cmplx256 (VALUE self)
499
- {
500
- rb_ca_to_type_method_body(CA_CMPLX256);
501
- }
502
-
503
- /* @overload object
504
-
505
- (Conversion) Short-Hand of "CArray#to_type(:object)"
506
- */
760
+ /* CArray#object -- short-hand of to_type(:object). */
507
761
  VALUE rb_ca_to_VALUE (VALUE self)
508
762
  {
509
763
  rb_ca_to_type_method_body(CA_OBJECT);
@@ -513,6 +767,31 @@ VALUE rb_ca_to_VALUE (VALUE self)
513
767
 
514
768
  /* CArray#as_type */
515
769
 
770
+ /* Forward declaration: CAMonOp builder (ext/ca_obj_monop.c). */
771
+ extern VALUE rb_ca_monop_build (VALUE cary, uint16_t op_id);
772
+
773
+ /* Forward declaration: clip kernel (ext/carray_kernels.c, generated). */
774
+ extern VALUE rb_ca_clip (VALUE self, VALUE lo, VALUE hi);
775
+
776
+ /* Routes a type-adapt view request to CAMonOp(cast_<dt>) when the
777
+ target is a plain numeric data_type (NOT CA_FIXLEN, NOT CA_OBJECT,
778
+ and `rtype` is not a Class). Falls back to CAFake
779
+ (`rb_ca_fake_type`) for CA_FIXLEN sizing, CA_OBJECT, and class-rtype
780
+ data_class overlay cases.
781
+ Called by rb_ca_as_type_internal, rb_ca_wrap_readonly, and
782
+ rb_ca_wrap_writable in this file. */
783
+ static VALUE
784
+ ca_type_adapt_view (VALUE obj, VALUE rtype, int8_t data_type)
785
+ {
786
+ if ( data_type != CA_FIXLEN
787
+ && data_type != CA_OBJECT
788
+ && TYPE(rtype) != T_CLASS ) {
789
+ uint16_t cast_op_id = (uint16_t)(CA_MONOP_CAST_BASE + data_type);
790
+ return rb_ca_monop_build(obj, cast_op_id);
791
+ }
792
+ return rb_ca_fake_type(obj, rtype, Qnil);
793
+ }
794
+
516
795
  static VALUE
517
796
  rb_ca_as_type_internal (int argc, VALUE *argv, VALUE self)
518
797
  {
@@ -533,16 +812,23 @@ rb_ca_as_type_internal (int argc, VALUE *argv, VALUE self)
533
812
  }
534
813
  }
535
814
 
815
+ /* Natural-width cast: dispatch via helper (CAMonOp for numeric,
816
+ CAFake for CA_FIXLEN sizing / CA_OBJECT / class-rtype). */
817
+ if ( bytes == (ca_size_t) ca_sizeof[data_type] ) {
818
+ obj = ca_type_adapt_view(self, rtype, data_type);
819
+ return obj;
820
+ }
821
+
822
+ /* Byte-reinterpret variant (bytes != natural for non-FIXLEN, or
823
+ explicit fixlen sizing): CAFake. */
536
824
  obj = rb_ca_fake_type(self, rtype, rbytes);
537
- rb_ca_data_type_import(obj, rtype);
538
825
 
539
826
  return obj;
540
827
  }
541
828
 
542
- /* @overload as_type (data_type, bytes: nil)
543
-
544
- (Reference) Creates CAFake object of the given data type refers to the object.
545
- */
829
+ /* CArray#as_type(data_type, bytes:) -- CAFake view of self reinterpreted
830
+ as data_type (no copy; reads/writes cast on the fly through the shared
831
+ parent storage). User doc lives in yard-stubs/carray_cast.rb. */
546
832
 
547
833
  VALUE
548
834
  rb_ca_as_type (VALUE self, VALUE rtype, VALUE rbytes)
@@ -559,10 +845,7 @@ rb_ca_as_type (VALUE self, VALUE rtype, VALUE rbytes)
559
845
  return rb_ca_as_type_internal(1, &rcode, self); \
560
846
  }
561
847
 
562
- /* @overload as_fixlen (bytes: nil)
563
-
564
- (Reference) Short-Hand of `CArray#as_type(:fixlen, bytes: nil)`
565
- */
848
+ /* CArray#as_fixlen(bytes:) -- short-hand of as_type(:fixlen, bytes:). */
566
849
  VALUE
567
850
  rb_ca_as_fixlen (int argc, VALUE *argv, VALUE self)
568
851
  {
@@ -574,145 +857,97 @@ rb_ca_as_fixlen (int argc, VALUE *argv, VALUE self)
574
857
  return rb_ca_as_type_internal(2, list, self);
575
858
  }
576
859
 
577
- /* @overload as_boolean
578
-
579
- (Reference) Short-Hand of `CArray#as_type(:boolean)`
580
- */
860
+ /* CArray#as_boolean -- short-hand of as_type(:boolean). */
581
861
  VALUE rb_ca_as_boolean (VALUE self)
582
862
  {
583
863
  rb_ca_as_type_method_body(CA_BOOLEAN);
584
864
  }
585
865
 
586
- /* @overload as_int8
587
-
588
- (Reference) Short-Hand of `CArray#as_type(:int8)`
589
- */
866
+ /* CArray#as_int8 -- short-hand of as_type(:int8). */
590
867
  VALUE rb_ca_as_int8 (VALUE self)
591
868
  {
592
869
  rb_ca_as_type_method_body(CA_INT8);
593
870
  }
594
871
 
595
- /* @overload as_uint8
596
-
597
- (Reference) Short-Hand of `CArray#as_type(:uint8)`
598
- */
872
+ /* CArray#as_uint8 -- short-hand of as_type(:uint8). */
599
873
  VALUE rb_ca_as_uint8 (VALUE self)
600
874
  {
601
875
  rb_ca_as_type_method_body(CA_UINT8);
602
876
  }
603
877
 
604
- /* @overload as_int16
605
-
606
- (Reference) Short-Hand of `CArray#as_type(:int16)`
607
- */
878
+ /* CArray#as_int16 -- short-hand of as_type(:int16). */
608
879
  VALUE rb_ca_as_int16 (VALUE self)
609
880
  {
610
881
  rb_ca_as_type_method_body(CA_INT16);
611
882
  }
612
883
 
613
- /* @overload as_uint16
614
-
615
- (Reference) Short-Hand of `CArray#as_type(:uint16)`
616
- */
884
+ /* CArray#as_uint16 -- short-hand of as_type(:uint16). */
617
885
  VALUE rb_ca_as_uint16 (VALUE self)
618
886
  {
619
887
  rb_ca_as_type_method_body(CA_UINT16);
620
888
  }
621
889
 
622
- /* @overload as_int32
623
-
624
- (Reference) Short-Hand of `CArray#as_type(:int32)`
625
- */
890
+ /* CArray#as_int32 -- short-hand of as_type(:int32). */
626
891
  VALUE rb_ca_as_int32 (VALUE self)
627
892
  {
628
893
  rb_ca_as_type_method_body(CA_INT32);
629
894
  }
630
895
 
631
- /* @overload as_uint32
632
-
633
- (Reference) Short-Hand of `CArray#as_type(:uint32)`
634
- */
896
+ /* CArray#as_uint32 -- short-hand of as_type(:uint32). */
635
897
  VALUE rb_ca_as_uint32 (VALUE self)
636
898
  {
637
899
  rb_ca_as_type_method_body(CA_UINT32);
638
900
  }
639
901
 
640
- /* @overload as_int64
641
-
642
- (Reference) Short-Hand of `CArray#as_type(:int64)`
643
- */
902
+ /* CArray#as_int64 -- short-hand of as_type(:int64). */
644
903
  VALUE rb_ca_as_int64 (VALUE self)
645
904
  {
646
905
  rb_ca_as_type_method_body(CA_INT64);
647
906
  }
648
907
 
649
- /* @overload as_uint64
650
-
651
- (Reference) Short-Hand of `CArray#as_type(:uint64)`
652
- */
908
+ /* CArray#as_uint64 -- short-hand of as_type(:uint64). */
653
909
  VALUE rb_ca_as_uint64 (VALUE self)
654
910
  {
655
911
  rb_ca_as_type_method_body(CA_UINT64);
656
912
  }
657
913
 
658
- /* @overload as_float32
659
-
660
- (Reference) Short-Hand of `CArray#as_type(:float32)`
661
- */
914
+ /* CArray#as_float32 -- short-hand of as_type(:float32). */
662
915
  VALUE rb_ca_as_float32 (VALUE self)
663
916
  {
664
917
  rb_ca_as_type_method_body(CA_FLOAT32);
665
918
  }
666
919
 
667
- /* @overload as_float64
668
-
669
- (Reference) Short-Hand of `CArray#as_type(:float64)`
670
- */
920
+ /* CArray#as_float64 -- short-hand of as_type(:float64). */
671
921
  VALUE rb_ca_as_float64 (VALUE self)
672
922
  {
673
923
  rb_ca_as_type_method_body(CA_FLOAT64);
674
924
  }
675
925
 
676
- /* @overload as_float128
677
-
678
- (Reference) Short-Hand of `CArray#as_type(:float128)`
679
- */
926
+ /* CArray#as_float128 -- short-hand of as_type(:float128). */
680
927
  VALUE rb_ca_as_float128 (VALUE self)
681
928
  {
682
929
  rb_ca_as_type_method_body(CA_FLOAT128);
683
930
  }
684
931
 
685
- /* @overload as_cmplx64
686
-
687
- (Reference) Short-Hand of `CArray#as_type(:cmplx64)`
688
- */
932
+ /* CArray#as_cmplx64 -- short-hand of as_type(:cmplx64). */
689
933
  VALUE rb_ca_as_cmplx64 (VALUE self)
690
934
  {
691
935
  rb_ca_as_type_method_body(CA_CMPLX64);
692
936
  }
693
937
 
694
- /* @overload as_cmplx128
695
-
696
- (Reference) Short-Hand of `CArray#as_type(:cmplx128)`
697
- */
938
+ /* CArray#as_cmplx128 -- short-hand of as_type(:cmplx128). */
698
939
  VALUE rb_ca_as_cmplx128 (VALUE self)
699
940
  {
700
941
  rb_ca_as_type_method_body(CA_CMPLX128);
701
942
  }
702
943
 
703
- /* @overload as_cmplx256
704
-
705
- (Reference) Short-Hand of `CArray#as_type(:cmplx256)`
706
- */
944
+ /* CArray#as_cmplx256 -- short-hand of as_type(:cmplx256). */
707
945
  VALUE rb_ca_as_cmplx256 (VALUE self)
708
946
  {
709
947
  rb_ca_as_type_method_body(CA_CMPLX256);
710
948
  }
711
949
 
712
- /* @overload as_object
713
-
714
- (Reference) Short-Hand of `CArray#as_type(:object)`
715
- */
950
+ /* CArray#as_object -- short-hand of as_type(:object). */
716
951
  VALUE rb_ca_as_VALUE (VALUE self)
717
952
  {
718
953
  rb_ca_as_type_method_body(CA_OBJECT);
@@ -762,6 +997,36 @@ rb_ca_ptr2obj (VALUE self, void *ptr)
762
997
  }
763
998
  }
764
999
 
1000
+ /* Surface -> storage-domain conversion for a Face store (mirror of the
1001
+ storage_to_scalar read hook). Non-Face `ca` returns `obj` unchanged, so
1002
+ the non-Face store path is untouched. For a Face, the per-obj_type C
1003
+ table is consulted first (fast path); if none is registered, the Ruby
1004
+ `scalar_to_storage` method is called when defined. A Face that recognizes
1005
+ `obj` returns a storage-domain value (e.g. an Integer in its unit); a bare
1006
+ Integer / String falls through unchanged to the storage cast. */
1007
+ VALUE
1008
+ ca_face_scalar_to_storage (VALUE self, CArray *ca, VALUE obj)
1009
+ {
1010
+ ca_face_scalar_to_storage_fn fn;
1011
+ if ( ! ca_face_safe_check(ca) || obj == CA_UNDEF ) {
1012
+ return obj;
1013
+ }
1014
+ fn = ca_face_scalar_to_storage_table[ca->obj_type];
1015
+ if ( fn != NULL ) {
1016
+ return fn(self, obj);
1017
+ }
1018
+ else {
1019
+ static ID id_scalar_to_storage = 0;
1020
+ if ( id_scalar_to_storage == 0 ) {
1021
+ id_scalar_to_storage = rb_intern("scalar_to_storage");
1022
+ }
1023
+ if ( rb_respond_to(self, id_scalar_to_storage) ) {
1024
+ return rb_funcall(self, id_scalar_to_storage, 1, obj);
1025
+ }
1026
+ }
1027
+ return obj;
1028
+ }
1029
+
765
1030
  VALUE
766
1031
  rb_ca_obj2ptr (VALUE self, VALUE obj, void *ptr)
767
1032
  {
@@ -772,9 +1037,21 @@ rb_ca_obj2ptr (VALUE self, VALUE obj, void *ptr)
772
1037
  memset(ptr, 0, ca->bytes);
773
1038
  }
774
1039
  else {
775
- obj = ( ca_is_fixlen_type(ca) ) ? rb_ca_data_class_encode(self, obj) : obj;
1040
+ /* Face surfaces as FIXLEN but its storage is the parent chain
1041
+ bottom (e.g. int64); the obj -> bytes conversion must use the
1042
+ storage data_type, not the Face surface. */
1043
+ int8_t convert_type = ca->data_type;
1044
+ if ( ca_face_safe_check(ca) ) {
1045
+ /* Face write hook: turn a surface value object (Scalar / Time /
1046
+ DateTime) into a storage-domain value the cast below consumes. */
1047
+ obj = ca_face_scalar_to_storage(self, ca, obj);
1048
+ convert_type = ca_storage_type_of(ca);
1049
+ }
1050
+ if ( convert_type == CA_FIXLEN ) {
1051
+ obj = rb_ca_data_class_encode(self, obj);
1052
+ }
776
1053
  dummy.data_type = CA_OBJECT;
777
- ca_cast_func_table[CA_OBJECT][ca->data_type](1, &dummy, &obj, ca, ptr, NULL);
1054
+ ca_cast_func_table[CA_OBJECT][convert_type](1, &dummy, &obj, ca, ptr, NULL);
778
1055
  }
779
1056
  return Qnil;
780
1057
  }
@@ -798,7 +1075,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
798
1075
  data_type = rb_ca_guess_type(rtype);
799
1076
  }
800
1077
  if ( ca->data_type != data_type ) {
801
- obj = rb_ca_fake_type(obj, rtype, Qnil);
1078
+ obj = ca_type_adapt_view(obj, rtype, data_type);
802
1079
  }
803
1080
  }
804
1081
  else if ( NIL_P(obj) ) { /* obj == nil */
@@ -810,9 +1087,25 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
810
1087
  }
811
1088
  obj = rb_cscalar_new(data_type, 0, NULL);
812
1089
  }
813
- else if ( rb_respond_to(obj, rb_intern("ca")) ) { /* respond_to obj.ca */
814
- obj = rb_funcall(obj, rb_intern("ca"), 0);
1090
+ else if ( rb_respond_to(obj, rb_intern("to_ca")) ) { /* respond_to obj.to_ca */
1091
+ /* `writable: true` is the caller's half of the bargain: it says the
1092
+ result has to be one whose writes reach `obj`, and a to_ca that can
1093
+ only produce a copy raises rather than swallowing them. An object
1094
+ whose to_ca predates the keyword raises ArgumentError here, which is
1095
+ the honest report that it does not implement writable intake. */
1096
+ volatile VALUE kw = rb_hash_new();
1097
+ rb_hash_aset(kw, ID2SYM(rb_intern("writable")), Qtrue);
1098
+ obj = rb_funcallv_kw(obj, rb_intern("to_ca"), 1, (VALUE *) &kw,
1099
+ RB_PASS_KEYWORDS);
1100
+ if ( ! rb_obj_is_carray(obj) ) {
1101
+ volatile VALUE inspect = rb_inspect(CLASS_OF(arg));
1102
+ rb_raise(rb_eTypeError,
1103
+ "%s#to_ca did not return a CArray", StringValuePtr(inspect));
1104
+ }
815
1105
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1106
+ if ( ca_is_readonly(ca) ) {
1107
+ rb_raise(rb_eRuntimeError, "can't modify read-only carray");
1108
+ }
816
1109
  if ( NIL_P(rtype) ) {
817
1110
  data_type = ca->data_type;
818
1111
  }
@@ -820,7 +1113,24 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
820
1113
  data_type = rb_ca_guess_type(rtype);
821
1114
  }
822
1115
  if ( ca->data_type != data_type ) {
823
- obj = rb_ca_fake_type(obj, INT2NUM(data_type), Qnil);
1116
+ obj = ca_type_adapt_view(obj, INT2NUM(data_type), data_type);
1117
+ }
1118
+ }
1119
+ else if ( rb_memory_view_available_p(obj) ) { /* MemoryView producer */
1120
+ obj = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, obj);
1121
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1122
+ if ( ca_is_readonly(ca) ) {
1123
+ rb_raise(rb_eRuntimeError,
1124
+ "MemoryView source is read-only; can't wrap as writable");
1125
+ }
1126
+ if ( NIL_P(rtype) ) {
1127
+ data_type = ca->data_type;
1128
+ }
1129
+ else {
1130
+ data_type = rb_ca_guess_type(rtype);
1131
+ }
1132
+ if ( ca->data_type != data_type ) {
1133
+ obj = ca_type_adapt_view(obj, INT2NUM(data_type), data_type);
824
1134
  }
825
1135
  }
826
1136
  else {
@@ -833,10 +1143,9 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
833
1143
  return obj;
834
1144
  }
835
1145
 
836
- /* @overload wrap_writable (other, date_type = nil)
837
-
838
- [TBD]
839
- */
1146
+ /* CArray.wrap_writable(other, data_type = nil) -- singleton entry that
1147
+ scans (other, data_type) and delegates to rb_ca_wrap_writable.
1148
+ User doc lives in yard-stubs/carray_cast.rb. */
840
1149
 
841
1150
  static VALUE
842
1151
  rb_ca_s_wrap_writable (int argc, VALUE *argv, VALUE klass)
@@ -862,7 +1171,7 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
862
1171
  data_type = rb_ca_guess_type(rtype);
863
1172
  }
864
1173
  if ( ca->data_type != data_type ) {
865
- obj = rb_ca_fake_type(obj, rtype, Qnil);
1174
+ obj = ca_type_adapt_view(obj, rtype, data_type);
866
1175
  }
867
1176
  }
868
1177
  else if ( rb_obj_is_kind_of(obj, rb_cNumeric) ) { /* number */
@@ -884,7 +1193,7 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
884
1193
  data_type = rb_ca_guess_type(rtype);
885
1194
  }
886
1195
  if ( ca->data_type != data_type ) {
887
- obj = rb_ca_fake_type(obj, INT2NUM(data_type), Qnil);
1196
+ obj = ca_type_adapt_view(obj, INT2NUM(data_type), data_type);
888
1197
  }
889
1198
  }
890
1199
  else if ( TYPE(obj) == T_STRING ) { /* string */
@@ -898,14 +1207,12 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
898
1207
  obj = rb_cscalar_new_with_value(data_type, 0, obj);
899
1208
  }
900
1209
  else {
1210
+ CArray *ca_tmp;
901
1211
  volatile VALUE ref = obj;
902
- if ( ! RB_OBJ_FROZEN(ref) ) {
903
- ref = rb_obj_dup(ref);
904
- rb_obj_freeze(ref);
905
- }
906
1212
  ca_size_t dim = RSTRING_LEN(ref)/ca_sizeof[data_type];
907
- obj = rb_ca_wrap_new(data_type, 1, &dim, ca_sizeof[data_type], NULL, RSTRING_PTR(ref));
908
- rb_ivar_set(obj, rb_intern("referred_object"), ref);
1213
+ obj = rb_carray_new(data_type, 1, &dim, 0, NULL);
1214
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, ca_tmp);
1215
+ memcpy(ca_tmp->ptr, RSTRING_PTR(ref), dim * ca_sizeof[data_type]);
909
1216
  }
910
1217
  }
911
1218
  else if ( NIL_P(obj) ) { /* nil */
@@ -917,8 +1224,8 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
917
1224
  }
918
1225
  obj = rb_cscalar_new(data_type, 0, NULL);
919
1226
  }
920
- else if ( rb_respond_to(obj, rb_intern("ca")) ) {
921
- obj = rb_funcall(obj, rb_intern("ca"), 0);
1227
+ else if ( rb_respond_to(obj, rb_intern("to_ca")) ) {
1228
+ obj = rb_funcall(obj, rb_intern("to_ca"), 0);
922
1229
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
923
1230
  if ( NIL_P(rtype) ) {
924
1231
  data_type = ca->data_type;
@@ -927,11 +1234,11 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
927
1234
  data_type = rb_ca_guess_type(rtype);
928
1235
  }
929
1236
  if ( ca->data_type != data_type ) {
930
- obj = rb_ca_fake_type(obj, INT2NUM(data_type), Qnil);
1237
+ obj = ca_type_adapt_view(obj, INT2NUM(data_type), data_type);
931
1238
  }
932
1239
  }
933
- else if ( rb_respond_to(obj, rb_intern("to_ca")) ) {
934
- obj = rb_funcall(obj, rb_intern("to_ca"), 0);
1240
+ else if ( rb_memory_view_available_p(obj) ) { /* MemoryView producer */
1241
+ obj = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, obj);
935
1242
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
936
1243
  if ( NIL_P(rtype) ) {
937
1244
  data_type = ca->data_type;
@@ -940,7 +1247,7 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
940
1247
  data_type = rb_ca_guess_type(rtype);
941
1248
  }
942
1249
  if ( ca->data_type != data_type ) {
943
- obj = rb_ca_fake_type(obj, INT2NUM(data_type), Qnil);
1250
+ obj = ca_type_adapt_view(obj, INT2NUM(data_type), data_type);
944
1251
  }
945
1252
  }
946
1253
  else { /* object */
@@ -956,10 +1263,9 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
956
1263
  return obj;
957
1264
  }
958
1265
 
959
- /* @overload wrap_readonly (other, date_type = nil)
960
-
961
- [TBD]
962
- */
1266
+ /* CArray.wrap_readonly(other, data_type = nil) -- singleton entry that
1267
+ scans (other, data_type) and delegates to rb_ca_wrap_readonly.
1268
+ User doc lives in yard-stubs/carray_cast.rb. */
963
1269
 
964
1270
  static VALUE
965
1271
  rb_ca_s_wrap_readonly (int argc, VALUE *argv, VALUE klass)
@@ -995,7 +1301,7 @@ rb_ca_cast (volatile VALUE self)
995
1301
  break;
996
1302
  }
997
1303
  #ifdef HAVE_COMPLEX_H
998
- if ( rb_obj_is_kind_of(obj, rb_cCComplex) ) {
1304
+ if ( RB_TYPE_P(obj, T_COMPLEX) ) {
999
1305
  obj = rb_cscalar_new_with_value(CA_CMPLX128, 0, obj);
1000
1306
  break;
1001
1307
  }
@@ -1007,10 +1313,9 @@ rb_ca_cast (volatile VALUE self)
1007
1313
  return obj;
1008
1314
  }
1009
1315
 
1010
- /* @overload cast (value)
1011
-
1012
- [TBD]
1013
- */
1316
+ /* CArray.cast(value) -- singleton entry delegating to rb_ca_cast, which
1317
+ returns a CArray unchanged and coerces a Ruby value to a CScalar / CArray.
1318
+ User doc lives in yard-stubs/carray_cast.rb. */
1014
1319
 
1015
1320
  static VALUE
1016
1321
  rb_ca_s_cast (VALUE klass, VALUE val)
@@ -1026,11 +1331,31 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1026
1331
  int self_is_object = 0;
1027
1332
  int other_is_object = 0;
1028
1333
 
1334
+ /* Promote MemoryView-aware operands to CArray (CAWrap or CAStride).
1335
+ Excludes T_STRING which has its own legacy interpretation. */
1336
+ if ( ! rb_obj_is_carray(*self) && TYPE(*self) != T_STRING &&
1337
+ rb_memory_view_available_p(*self) ) {
1338
+ *self = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *self);
1339
+ }
1340
+ if ( ! rb_obj_is_carray(*other) && TYPE(*other) != T_STRING &&
1341
+ rb_memory_view_available_p(*other) ) {
1342
+ *other = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *other);
1343
+ }
1344
+
1029
1345
  if ( ! rb_obj_is_carray(*self) ) {
1030
1346
  self_is_object = 1;
1031
1347
  if ( rb_ca_is_object_type(*other) ) {
1032
1348
  *self = rb_cscalar_new_with_value(CA_OBJECT, 0, *self);
1033
1349
  }
1350
+ #ifdef HAVE_COMPLEX_H
1351
+ /* A Complex scalar with a non-object non-complex CArray on the other
1352
+ side must wrap as CA_CMPLX128; falling into the float branch below
1353
+ would NUM2DBL(Complex) and raise. Downstream promotion lifts the
1354
+ other operand to cmplx128. */
1355
+ else if ( RB_TYPE_P(*self, T_COMPLEX) ) {
1356
+ *self = rb_cscalar_new_with_value(CA_CMPLX128, 0, *self);
1357
+ }
1358
+ #endif
1034
1359
  else if ( rb_ca_is_float_type(*other) ) {
1035
1360
  *self = rb_cscalar_new_with_value(CA_FLOAT64, 0, *self);
1036
1361
  }
@@ -1040,27 +1365,7 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1040
1365
  }
1041
1366
  #endif
1042
1367
  else {
1043
- switch ( TYPE(*self) ) {
1044
- case T_FIXNUM:
1045
- *self = rb_cscalar_new_with_value(CA_INT64, 0, *self);
1046
- break;
1047
- case T_FLOAT:
1048
- *self = rb_cscalar_new_with_value(CA_FLOAT64, 0, *self);
1049
- break;
1050
- case T_TRUE:
1051
- case T_FALSE:
1052
- *self = rb_cscalar_new_with_value(CA_BOOLEAN, 0, *self);
1053
- break;
1054
- default:
1055
- #ifdef HAVE_COMPLEX_H
1056
- if ( rb_obj_is_kind_of(*self, rb_cCComplex) ) {
1057
- *self = rb_cscalar_new_with_value(CA_CMPLX128, 0, *self);
1058
- break;
1059
- }
1060
- #endif
1061
- *self = rb_cscalar_new_with_value(CA_OBJECT, 0, *self);
1062
- break;
1063
- }
1368
+ *self = rb_cscalar_new_with_value(ca_value_to_data_type(*self), 0, *self);
1064
1369
  }
1065
1370
  }
1066
1371
 
@@ -1070,6 +1375,15 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1070
1375
  if ( rb_ca_is_object_type(*self) ) {
1071
1376
  *other = rb_cscalar_new_with_value(CA_OBJECT, 0, *other);
1072
1377
  }
1378
+ #ifdef HAVE_COMPLEX_H
1379
+ /* Symmetric to the self-branch above: Complex scalar wraps as
1380
+ cmplx128 regardless of the CArray operand's numeric width, so
1381
+ float64_array + Complex(0, 1) promotes rather than raising in
1382
+ NUM2DBL(Complex). */
1383
+ else if ( RB_TYPE_P(*other, T_COMPLEX) ) {
1384
+ *other = rb_cscalar_new_with_value(CA_CMPLX128, 0, *other);
1385
+ }
1386
+ #endif
1073
1387
  else if ( rb_ca_is_float_type(*self) ) {
1074
1388
  *other = rb_cscalar_new_with_value(CA_FLOAT64, 0, *other);
1075
1389
  }
@@ -1079,28 +1393,7 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1079
1393
  }
1080
1394
  #endif
1081
1395
  else {
1082
- switch ( TYPE(*other) ) {
1083
- case T_FIXNUM:
1084
- case T_BIGNUM:
1085
- *other = rb_cscalar_new_with_value(CA_INT64, 0, *other);
1086
- break;
1087
- case T_FLOAT:
1088
- *other = rb_cscalar_new_with_value(CA_FLOAT64, 0, *other);
1089
- break;
1090
- case T_TRUE:
1091
- case T_FALSE:
1092
- *other = rb_cscalar_new_with_value(CA_BOOLEAN, 0, *other);
1093
- break;
1094
- default:
1095
- #ifdef HAVE_COMPLEX_H
1096
- if ( rb_obj_is_kind_of(*other, rb_cCComplex) ) {
1097
- *other = rb_cscalar_new_with_value(CA_CMPLX128, 0, *other);
1098
- break;
1099
- }
1100
- #endif
1101
- *other = rb_cscalar_new_with_value(CA_OBJECT, 0, *other);
1102
- break;
1103
- }
1396
+ *other = rb_cscalar_new_with_value(ca_value_to_data_type(*other), 0, *other);
1104
1397
  }
1105
1398
  }
1106
1399
 
@@ -1117,8 +1410,17 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1117
1410
  TypedData_Get_Struct(*other, CArray, &carray_data_type, cb);
1118
1411
  }
1119
1412
 
1120
- if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ||
1121
- self_is_object ||
1413
+ /* Implicit size-1 broadcasting (case A only). No-op if either side
1414
+ is scalar, ndim differs, or shapes are already equal. If shapes
1415
+ are incompatible (neither side is 1 nor equal on some axis), also
1416
+ no-op — the downstream element-count check in ca_set_iterator
1417
+ raises with the existing message. */
1418
+ ca_broadcast_pair(self, other);
1419
+ TypedData_Get_Struct(*self, CArray, &carray_data_type, ca);
1420
+ TypedData_Get_Struct(*other, CArray, &carray_data_type, cb);
1421
+
1422
+ if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ||
1423
+ self_is_object ||
1122
1424
  other_is_object ) {
1123
1425
  if ( other_is_object ||
1124
1426
  ( ( ! other_is_object ) && ca_is_scalar(cb) ) ) {
@@ -1167,10 +1469,10 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1167
1469
  ca_type_name[cb->data_type]);
1168
1470
  }
1169
1471
 
1170
- /* @overload cast_self_or_other (other)
1171
-
1172
- [TBD]
1173
- */
1472
+ /* CArray.cast_self_or_other(self, other) — promote self and other
1473
+ * to a common data_type and return [self', other']. Wraps the
1474
+ * void rb_ca_cast_self_or_other above into a class method.
1475
+ */
1174
1476
 
1175
1477
  VALUE
1176
1478
  rb_ca_s_cast_self_or_other (VALUE klass, VALUE self, VALUE other)
@@ -1179,6 +1481,286 @@ rb_ca_s_cast_self_or_other (VALUE klass, VALUE self, VALUE other)
1179
1481
  return rb_assoc_new(self, other);
1180
1482
  }
1181
1483
 
1484
+ /* Extract a data_type code (0..CA_NTYPE-1) from an argument.
1485
+ Dispatch:
1486
+ - CArray instance -> its data_type
1487
+ - Symbol / String / Class -> data_type representation (name lookup)
1488
+ - other (Integer / Float / Complex / true / false / nil / Object)
1489
+ -> value semantic via ca_value_to_data_type
1490
+ (e.g. an Integer like `3` is interpreted
1491
+ as a value -> :int64, not as a data_type
1492
+ code). */
1493
+ static int8_t
1494
+ ca_arg_to_data_type (VALUE obj)
1495
+ {
1496
+ int8_t data_type;
1497
+ if ( rb_obj_is_carray(obj) ) {
1498
+ CArray *ca;
1499
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1500
+ data_type = ca->data_type;
1501
+ }
1502
+ else if ( TYPE(obj) == T_SYMBOL || TYPE(obj) == T_STRING ||
1503
+ TYPE(obj) == T_CLASS ) {
1504
+ data_type = rb_ca_guess_type(obj);
1505
+ }
1506
+ else if ( ( data_type = ca_mv_probe_data_type(obj) ) >= 0 ) {
1507
+ /* MemoryView producer -> its format's data_type (T_STRING excluded above,
1508
+ so a String never reaches here). data_type already assigned. */
1509
+ }
1510
+ else {
1511
+ data_type = ca_value_to_data_type(obj);
1512
+ }
1513
+ CA_CHECK_DATA_TYPE(data_type);
1514
+ if ( ! ca_valid[data_type] ) {
1515
+ rb_raise(rb_eArgError,
1516
+ "data type '%s' is not valid in this build",
1517
+ ca_type_name[data_type]);
1518
+ }
1519
+ return data_type;
1520
+ }
1521
+
1522
+ /* Infer a data_type code from a Ruby *value* (literal Numeric, true/false,
1523
+ Complex, etc.). See declaration in carray.h for the contract. */
1524
+ int8_t
1525
+ ca_value_to_data_type (VALUE obj)
1526
+ {
1527
+ switch ( TYPE(obj) ) {
1528
+ case T_FIXNUM:
1529
+ case T_BIGNUM: return CA_INT64;
1530
+ case T_FLOAT: return CA_FLOAT64;
1531
+ case T_TRUE:
1532
+ case T_FALSE: return CA_BOOLEAN;
1533
+ case T_NIL: return CA_OBJECT;
1534
+ default:
1535
+ #ifdef HAVE_COMPLEX_H
1536
+ if ( RB_TYPE_P(obj, T_COMPLEX) ) return CA_CMPLX128;
1537
+ #endif
1538
+ return CA_OBJECT;
1539
+ }
1540
+ }
1541
+
1542
+ /* Promote two data_type codes according to ca_cast_table.
1543
+ Returns the higher (= the type both can be cast to without loss
1544
+ under the table's policy). Raises if incompatible.
1545
+
1546
+ Externally callable so the lazy view layer (CABinOp constructor)
1547
+ can compute its output_data_type using the same single-source
1548
+ promotion rule as the eager binop path. */
1549
+ int8_t
1550
+ ca_promote_type (int8_t a, int8_t b)
1551
+ {
1552
+ int test;
1553
+ if ( a == b ) {
1554
+ return a;
1555
+ }
1556
+ test = ca_cast_table[a][b];
1557
+ if ( test == 0 ) {
1558
+ return b;
1559
+ }
1560
+ else if ( test > 0 ) {
1561
+ /* a should be cast to b */
1562
+ return b;
1563
+ }
1564
+ /* test < 0: try the other direction */
1565
+ test = ca_cast_table[b][a];
1566
+ if ( test > 0 ) {
1567
+ /* b should be cast to a */
1568
+ return a;
1569
+ }
1570
+ rb_raise(rb_eRuntimeError,
1571
+ "can't promote data_types '%s' and '%s' to a common type",
1572
+ ca_type_name[a], ca_type_name[b]);
1573
+ }
1574
+
1575
+ /* CArray.result_type(*args) -- common data_type each operand promotes to
1576
+ under ca_cast_table. Each arg is classified by ca_arg_to_data_type: a
1577
+ CArray / Symbol / String / Class gives a data_type representation, a
1578
+ Numeric / bool / nil / Object gives a value whose data_type is inferred.
1579
+ Folds pairwise via ca_promote_type; returns a Symbol. User doc (with
1580
+ the value-vs-code distinction and examples) lives in
1581
+ yard-stubs/carray_cast.rb. */
1582
+
1583
+ static VALUE
1584
+ rb_ca_s_result_type (int argc, VALUE *argv, VALUE klass)
1585
+ {
1586
+ int8_t cur;
1587
+ int i;
1588
+
1589
+ if ( argc < 1 ) {
1590
+ rb_raise(rb_eArgError, "result_type requires at least one argument");
1591
+ }
1592
+
1593
+ cur = ca_arg_to_data_type(argv[0]);
1594
+ for ( i = 1; i < argc; i++ ) {
1595
+ int8_t next = ca_arg_to_data_type(argv[i]);
1596
+ cur = ca_promote_type(cur, next);
1597
+ }
1598
+ /* Return Symbol for family consistency with
1599
+ CArray.value_to_data_type and ca.data_type. */
1600
+ return rb_ca_data_type_to_sym(cur);
1601
+ }
1602
+
1603
+ /* CArray.promote_list(list, data_type: nil) -- reconcile an Array of
1604
+ CArray instances into a uniformly-handleable representation (same Face
1605
+ class, or same primitive data_type) suitable for multi-parent
1606
+ constructors like CArray.stack. User doc lives in
1607
+ yard-stubs/carray_cast.rb; the dispatch branches are:
1608
+
1609
+ Auto-detect (data_type: nil):
1610
+ - All elements Face (= ca_is_face) and same Face class:
1611
+ - Reject if the class is not portable (= CAConstString)
1612
+ - Reject on pairwise state mismatch (= CATime with different units)
1613
+ - Otherwise pass through (= CAStack will lift)
1614
+ - All elements primitive: promote via result_type + wrap_readonly
1615
+ - Mixed Face + non-Face: reject (= ambiguous; user must strip Face
1616
+ manually with .parent if the storage-level layout is intended)
1617
+ - Heterogeneous Face classes: reject (= no semantics for mixing Faces)
1618
+
1619
+ Explicit data_type (Symbol):
1620
+ - All primitive elements: wrap_readonly to the requested type
1621
+ - Any Face element: reject (= Face cannot be coerced into a primitive
1622
+ without losing identity; the user must strip Face manually)
1623
+
1624
+ Explicit data_type (Class / non-Symbol):
1625
+ - Reject. Class-shaped targets (CARecord, Face subclasses, etc.) are
1626
+ not valid promotion destinations; use auto-detect or pass a primitive
1627
+ Symbol. Future work may relax this once Face-as-target promotion is
1628
+ designed.
1629
+
1630
+ Raises ArgumentError on empty list or any of the reject cases above. */
1631
+ static VALUE
1632
+ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1633
+ {
1634
+ VALUE list, kwargs, data_type = Qnil;
1635
+ long n, i;
1636
+ int all_face = 1, any_face = 0, all_primitive = 1;
1637
+ VALUE face_class = Qnil;
1638
+ CArray *ref_face = NULL;
1639
+ int8_t common = -1;
1640
+
1641
+ rb_scan_args(argc, argv, "1:", &list, &kwargs);
1642
+ Check_Type(list, T_ARRAY);
1643
+ rb_scan_options(kwargs, "data_type", &data_type);
1644
+
1645
+ n = RARRAY_LEN(list);
1646
+ if ( n <= 0 ) {
1647
+ rb_raise(rb_eArgError, "promote_list: list must not be empty");
1648
+ }
1649
+
1650
+ /* Explicit data_type validation: reject Class / non-Symbol targets up
1651
+ front so the rest of the function only deals with `nil` or a
1652
+ primitive-shaped Symbol/String/Integer. */
1653
+ if ( ! NIL_P(data_type) ) {
1654
+ if ( RB_TYPE_P(data_type, T_CLASS) || RB_TYPE_P(data_type, T_MODULE) ) {
1655
+ rb_raise(rb_eArgError,
1656
+ "promote_list: data_type must be a primitive Symbol "
1657
+ "(Class targets are not supported; use auto-detect or "
1658
+ "strip Face manually)");
1659
+ }
1660
+ }
1661
+
1662
+ /* Classify each element: Face / primitive CArray. Detect Face-class
1663
+ homogeneity as we go. */
1664
+ for ( i = 0; i < n; i++ ) {
1665
+ VALUE elem = rb_ary_entry(list, i);
1666
+ CArray *ca;
1667
+ rb_check_carray_object(elem);
1668
+ TypedData_Get_Struct(elem, CArray, &carray_data_type, ca);
1669
+ if ( ca_is_face(ca) ) {
1670
+ any_face = 1;
1671
+ all_primitive = 0;
1672
+ if ( NIL_P(face_class) ) {
1673
+ face_class = rb_obj_class(elem);
1674
+ ref_face = ca;
1675
+ } else if ( rb_obj_class(elem) != face_class ) {
1676
+ all_face = 0; /* will be caught after the loop */
1677
+ } else if ( all_face ) {
1678
+ if ( ! ca_face_state_compatible(rb_ary_entry(list, 0), ref_face,
1679
+ elem, ca) ) {
1680
+ rb_raise(rb_eArgError,
1681
+ "promote_list: Face state mismatch across elements "
1682
+ "(= %s instance at index %ld differs from index 0)",
1683
+ rb_class2name(face_class), i);
1684
+ }
1685
+ }
1686
+ } else {
1687
+ all_face = 0;
1688
+ }
1689
+ }
1690
+
1691
+ /* Explicit data_type path: primitive Symbol, all elements must be
1692
+ primitive. Reject if any Face in list. */
1693
+ if ( ! NIL_P(data_type) ) {
1694
+ if ( any_face ) {
1695
+ rb_raise(rb_eArgError,
1696
+ "promote_list: data_type %s cannot be applied to a list "
1697
+ "containing Face elements (= Face cannot be coerced to "
1698
+ "a primitive without losing identity)",
1699
+ RSTRING_PTR(rb_inspect(data_type)));
1700
+ }
1701
+ {
1702
+ VALUE out = rb_ary_new_capa(n);
1703
+ static ID id_wrap_readonly = 0;
1704
+ if ( id_wrap_readonly == 0 ) id_wrap_readonly = rb_intern("wrap_readonly");
1705
+ for ( i = 0; i < n; i++ ) {
1706
+ VALUE coerced = rb_funcall(klass, id_wrap_readonly, 2,
1707
+ rb_ary_entry(list, i), data_type);
1708
+ rb_ary_push(out, coerced);
1709
+ }
1710
+ return out;
1711
+ }
1712
+ }
1713
+
1714
+ /* Auto-detect path. */
1715
+ if ( any_face && ! all_face ) {
1716
+ if ( all_primitive == 0 && any_face ) {
1717
+ /* Mixed: at least one Face and at least one non-Face,
1718
+ OR mixed Face classes. Both are rejected. */
1719
+ rb_raise(rb_eArgError,
1720
+ "promote_list: cannot mix Face and non-Face (or heterogeneous "
1721
+ "Face classes); pass a homogeneous Face list, or strip Face "
1722
+ "manually with .parent for the storage-level layout");
1723
+ }
1724
+ }
1725
+
1726
+ if ( all_face ) {
1727
+ /* Homogeneous Face: validate portability, then pass through. */
1728
+ if ( n > 1 && ! ca_face_state_portable(ref_face->obj_type, face_class) ) {
1729
+ rb_raise(rb_eArgError,
1730
+ "promote_list: %s state is not portable across multiple "
1731
+ "elements (= per-parent storage like CAConstString's buffer); "
1732
+ "strip Face manually with .parent for a storage-level list",
1733
+ rb_class2name(face_class));
1734
+ }
1735
+ /* Return a fresh Array so callers can mutate without aliasing. */
1736
+ {
1737
+ VALUE out = rb_ary_new_capa(n);
1738
+ for ( i = 0; i < n; i++ ) rb_ary_push(out, rb_ary_entry(list, i));
1739
+ return out;
1740
+ }
1741
+ }
1742
+
1743
+ /* All primitive: result_type to determine common data_type, then
1744
+ wrap_readonly each element. */
1745
+ common = ca_arg_to_data_type(rb_ary_entry(list, 0));
1746
+ for ( i = 1; i < n; i++ ) {
1747
+ int8_t next = ca_arg_to_data_type(rb_ary_entry(list, i));
1748
+ common = ca_promote_type(common, next);
1749
+ }
1750
+ {
1751
+ VALUE common_sym = rb_ca_data_type_to_sym(common);
1752
+ VALUE out = rb_ary_new_capa(n);
1753
+ static ID id_wrap_readonly = 0;
1754
+ if ( id_wrap_readonly == 0 ) id_wrap_readonly = rb_intern("wrap_readonly");
1755
+ for ( i = 0; i < n; i++ ) {
1756
+ VALUE coerced = rb_funcall(klass, id_wrap_readonly, 2,
1757
+ rb_ary_entry(list, i), common_sym);
1758
+ rb_ary_push(out, coerced);
1759
+ }
1760
+ return out;
1761
+ }
1762
+ }
1763
+
1182
1764
  void
1183
1765
  rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1184
1766
  {
@@ -1188,6 +1770,13 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1188
1770
 
1189
1771
  TypedData_Get_Struct(*self, CArray, &carray_data_type, ca);
1190
1772
 
1773
+ /* Promote MemoryView-aware operand to CArray (CAWrap or CAStride).
1774
+ Excludes T_STRING which has its own legacy interpretation. */
1775
+ if ( ! rb_obj_is_carray(*other) && TYPE(*other) != T_STRING &&
1776
+ rb_memory_view_available_p(*other) ) {
1777
+ *other = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *other);
1778
+ }
1779
+
1191
1780
  if ( ! rb_obj_is_carray(*other) ) {
1192
1781
  if ( rb_ca_is_object_type(*self) ) {
1193
1782
  *other = rb_cscalar_new_with_value(CA_OBJECT, 0, *other);
@@ -1202,30 +1791,9 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1202
1791
  }
1203
1792
  #endif
1204
1793
  else {
1205
- switch ( TYPE(*other) ) {
1206
- case T_FIXNUM:
1207
- case T_BIGNUM:
1208
- *other = rb_cscalar_new_with_value(CA_INT64, 0, *other);
1209
- break;
1210
- case T_FLOAT:
1211
- *other = rb_cscalar_new_with_value(CA_FLOAT64, 0, *other);
1212
- break;
1213
- case T_TRUE:
1214
- case T_FALSE:
1215
- *other = rb_cscalar_new_with_value(CA_BOOLEAN, 0, *other);
1216
- break;
1217
- default:
1218
- #ifdef HAVE_COMPLEX_H
1219
- if ( rb_obj_is_kind_of(*other, rb_cCComplex) ) {
1220
- *other = rb_cscalar_new_with_value(CA_CMPLX128, 0, *other);
1221
- break;
1222
- }
1223
- #endif
1224
- *other = rb_cscalar_new_with_value(CA_OBJECT, 0, *other);
1225
- break;
1226
- }
1794
+ *other = rb_cscalar_new_with_value(ca_value_to_data_type(*other), 0, *other);
1227
1795
  }
1228
-
1796
+
1229
1797
  TypedData_Get_Struct(*other, CScalar, &cscalar_data_type, cs);
1230
1798
 
1231
1799
  test0 = ca_cast_table2[cs->data_type][ca->data_type];
@@ -1259,10 +1827,10 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1259
1827
  ca_type_name[cb->data_type]);
1260
1828
  }
1261
1829
 
1262
- /* @overload cast_with (other)
1263
-
1264
- [TBD]
1265
- */
1830
+ /* CArray#cast_with(other) -- coerce self and other to a common
1831
+ representation via rb_ca_cast_self_or_other and return them as a
1832
+ two-element [self, other] array. User doc lives in
1833
+ yard-stubs/carray_cast.rb. */
1266
1834
 
1267
1835
  VALUE
1268
1836
  rb_ca_cast_with (VALUE self, VALUE other)
@@ -1276,8 +1844,289 @@ rb_ca_cast_with (VALUE self, VALUE other)
1276
1844
  return rb_assoc_new(self, other);
1277
1845
  }
1278
1846
 
1847
+ /* -----------------------------------------------------------------------
1848
+ clip_<dtype> family — clip into the target dtype's representable
1849
+ range, then cast. Sugar for `clip(lo, hi).as_<dtype>`.
1850
+ ----------------------------------------------------------------------- */
1851
+
1852
+ #define CLIP_CAST_FUNC(name, lo_expr, hi_expr, cast_func) \
1853
+ static VALUE \
1854
+ rb_ca_##name (VALUE self) \
1855
+ { \
1856
+ return cast_func(rb_ca_clip(self, lo_expr, hi_expr)); \
1857
+ }
1858
+
1859
+ CLIP_CAST_FUNC(clip_int8, INT2NUM(-128), INT2NUM(127), rb_ca_as_int8)
1860
+ CLIP_CAST_FUNC(clip_int16, INT2NUM(-32768), INT2NUM(32767), rb_ca_as_int16)
1861
+ CLIP_CAST_FUNC(clip_int32, INT2NUM(-2147483648), INT2NUM(2147483647), rb_ca_as_int32)
1862
+ CLIP_CAST_FUNC(clip_int64, LL2NUM(-9223372036854775807-1), LL2NUM(9223372036854775807), rb_ca_as_int64)
1863
+ CLIP_CAST_FUNC(clip_uint8, INT2NUM(0), INT2NUM(255), rb_ca_as_uint8)
1864
+ CLIP_CAST_FUNC(clip_uint16, INT2NUM(0), INT2NUM(65535), rb_ca_as_uint16)
1865
+ CLIP_CAST_FUNC(clip_uint32, INT2NUM(0), UINT2NUM(4294967295U), rb_ca_as_uint32)
1866
+
1867
+ #undef CLIP_CAST_FUNC
1868
+
1869
+ /* CArray#clip_uint64 -- dedicated implementation.
1870
+
1871
+ UINT64_MAX (= 2^64-1) is not exactly representable in float64; the nearest
1872
+ double rounds up to 2^64. A generic clip-then-cast path therefore clamps
1873
+ an overflowing float source (e.g. 1e20) to 2^64 and the subsequent cast to
1874
+ uint64 wraps around to 0. For floating-point sources we take a saturating
1875
+ path: compare in float space against 2^64 (which IS representable) and
1876
+ saturate to UINT64_MAX above it and 0 below zero. Other source dtypes use
1877
+ the plain clip-then-cast path. */
1878
+ static VALUE
1879
+ rb_ca_clip_uint64 (VALUE self)
1880
+ {
1881
+ CArray *ca;
1882
+ GetCArray(self, ca);
1883
+ int dt = ca->data_type;
1884
+ if ( dt == CA_FLOAT32 || dt == CA_FLOAT64 ) {
1885
+ volatile VALUE vsrc = ( dt == CA_FLOAT64 ) ? self : rb_ca_as_float64(self);
1886
+ CArray *src;
1887
+ GetCArray(vsrc, src);
1888
+ volatile VALUE vout = rb_carray_new(CA_UINT64, ca->ndim, ca->dim, 0, NULL);
1889
+ CArray *out;
1890
+ GetCArray(vout, out);
1891
+ ca_attach(src);
1892
+ double *sp = (double *) src->ptr;
1893
+ uint64_t *op = (uint64_t *) out->ptr;
1894
+ ca_size_t n = ca->elements;
1895
+ const double LIMIT = 18446744073709551616.0; /* 2^64, exact in f64 */
1896
+ for ( ca_size_t i = 0; i < n; i++ ) {
1897
+ double v = sp[i];
1898
+ if ( v != v ) op[i] = 0; /* NaN -> 0 */
1899
+ else if ( v >= LIMIT ) op[i] = UINT64_MAX;
1900
+ else if ( v <= 0.0 ) op[i] = 0;
1901
+ else op[i] = (uint64_t) v;
1902
+ }
1903
+ ca_detach(src);
1904
+ if ( ca_has_mask(ca) ) {
1905
+ ca_copy_mask(out, ca);
1906
+ }
1907
+ return vout;
1908
+ }
1909
+ return rb_ca_as_uint64(rb_ca_clip(self, INT2NUM(0),
1910
+ ULL2NUM(18446744073709551615ULL)));
1911
+ }
1912
+
1913
+ /* ------------------------------------------------------------------------
1914
+ Global cast functions CA_INT32(data) ... CA_OBJECT(data) etc.
1915
+
1916
+ Polymorphic cast (NOT a fresh allocator): `data` is coerced into a CArray
1917
+ of the target data_type, dispatching on its Ruby class:
1918
+
1919
+ Array -> shape-guessed CArray, element-wise store (C fast path)
1920
+ CArray -> copy if same data_type, else #to_type (C fast path)
1921
+ nil -> empty CArray (C fast path)
1922
+ Range / String / Numeric / #to_ca responder (Ruby fallback)
1923
+
1924
+ The cold / Ruby-natural branches (arange arithmetic, the whitespace/`,`/`;`
1925
+ string parser, generic coercion) stay in Ruby as `CArray.__cast_rest__`
1926
+ (carray/basics.rb, eager-loaded). `type` is passed to the fallback as the
1927
+ data_type Symbol so the Ruby side keeps its original `type == CA_OBJECT`
1928
+ style comparisons.
1929
+ ------------------------------------------------------------------------ */
1930
+
1931
+ static ID id_new_cast = 0, id_guess_array_shape_cast = 0, id_cast_string = 0,
1932
+ id_to_ca_cast = 0, id_length_cast = 0,
1933
+ id_begin_cast = 0, id_end_cast = 0, id_to_a_cast = 0,
1934
+ id_exclude_end_p_cast = 0, id_abs_cast = 0, id_floor_cast = 0,
1935
+ id_op_minus = 0, id_op_div = 0, id_op_plus = 0,
1936
+ id_op_le = 0, id_op_eq = 0, id_op_uminus = 0;
1937
+
1938
+ /* Cross-type cast helper: v.to_type(sym) with no bytes (= in-file call to
1939
+ the #to_type implementation, no Ruby method dispatch). */
1940
+ static VALUE
1941
+ ca_cast_to_type1 (VALUE v, VALUE sym)
1942
+ {
1943
+ VALUE list[1];
1944
+ list[0] = sym;
1945
+ return rb_ca_to_type_internal(1, list, v);
1946
+ }
1947
+
1948
+ static VALUE
1949
+ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
1950
+ {
1951
+ VALUE v = ( argc >= 1 ) ? argv[0] : Qnil;
1952
+ VALUE sym = ID2SYM(ca_data_type_sym[data_type]);
1953
+
1954
+ if ( TYPE(v) == T_ARRAY ) {
1955
+ volatile VALUE shape, obj;
1956
+ shape = rb_funcall(rb_cCArray, id_guess_array_shape_cast, 1, v);
1957
+ obj = rb_funcall(rb_cCArray, id_new_cast, 2, sym, shape);
1958
+ rb_ca_store_all(obj, v);
1959
+ return obj;
1960
+ }
1961
+ else if ( RTEST(rb_obj_is_kind_of(v, rb_cCArray)) ) {
1962
+ CArray *ca;
1963
+ TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
1964
+ if ( ca->data_type == data_type ) {
1965
+ return rb_ca_copy(v);
1966
+ }
1967
+ return ca_cast_to_type1(v, sym);
1968
+ }
1969
+ else if ( NIL_P(v) ) {
1970
+ ca_size_t dim0 = 0;
1971
+ return rb_carray_new(data_type, 1, &dim0, 0, NULL);
1972
+ }
1973
+ else if ( RTEST(rb_obj_is_kind_of(v, rb_cRange)) ) {
1974
+ /* arange: n = ((end - begin).abs / step).floor (+1 unless exclude_end),
1975
+ then seq(begin, sign * step.abs). */
1976
+ volatile VALUE vbeg, vend, step, n;
1977
+ int sign;
1978
+ ca_size_t dn;
1979
+ VALUE obj, step_abs, signed_step;
1980
+
1981
+ vbeg = rb_funcall(v, id_begin_cast, 0);
1982
+ vend = rb_funcall(v, id_end_cast, 0);
1983
+ step = ( argc >= 2 ) ? argv[1] : Qnil;
1984
+ if ( RTEST(step) && RTEST(rb_funcall(step, id_op_eq, 1, INT2FIX(0))) ) {
1985
+ rb_raise(rb_eRuntimeError, "step should not be 0");
1986
+ }
1987
+ if ( data_type == CA_OBJECT && !RTEST(step) ) {
1988
+ VALUE va = rb_funcall(v, id_to_a_cast, 0); /* CA_OBJECT(v.to_a) */
1989
+ return ca_cast_impl(CA_OBJECT, 1, &va);
1990
+ }
1991
+ if ( !RTEST(step) ) step = INT2FIX(1);
1992
+ n = rb_funcall(vend, id_op_minus, 1, vbeg);
1993
+ n = rb_funcall(n, id_abs_cast, 0);
1994
+ n = rb_funcall(n, id_op_div, 1, step);
1995
+ n = rb_funcall(n, id_floor_cast, 0);
1996
+ if ( !RTEST(rb_funcall(v, id_exclude_end_p_cast, 0)) ) {
1997
+ n = rb_funcall(n, id_op_plus, 1, INT2FIX(1));
1998
+ }
1999
+ sign = RTEST(rb_funcall(vbeg, id_op_le, 1, vend)) ? 1 : -1;
2000
+ dn = NUM2SIZE(n);
2001
+ obj = rb_carray_new(data_type, 1, &dn, 0, NULL);
2002
+ step_abs = rb_funcall(step, id_abs_cast, 0);
2003
+ signed_step = ( sign == 1 ) ? step_abs : rb_funcall(step_abs, id_op_uminus, 0);
2004
+ return rb_ca_seq(obj, vbeg, signed_step);
2005
+ }
2006
+ else if ( TYPE(v) == T_STRING ) {
2007
+ /* The whitespace / `,` / `;` / `_`->UNDEF parser is far more natural in
2008
+ Ruby; only this branch stays there (carray/basics.rb). */
2009
+ return rb_funcall(rb_cCArray, id_cast_string, 2, sym, v);
2010
+ }
2011
+ else {
2012
+ /* Numeric / #to_ca responder */
2013
+ if ( rb_respond_to(v, id_to_ca_cast) ) {
2014
+ VALUE ca = rb_funcall(v, id_to_ca_cast, 0);
2015
+ CArray *cap;
2016
+ TypedData_Get_Struct(ca, CArray, &carray_data_type, cap);
2017
+ return ( cap->data_type == data_type ) ? ca : ca_cast_to_type1(ca, sym);
2018
+ }
2019
+ return rb_cscalar_new_with_value(data_type, 0, v);
2020
+ }
2021
+ }
2022
+
2023
+ #define DEFINE_CA_CAST(cfunc, dt) \
2024
+ static VALUE \
2025
+ cfunc (int argc, VALUE *argv, VALUE self) \
2026
+ { \
2027
+ return ca_cast_impl((dt), argc, argv); \
2028
+ }
2029
+
2030
+ DEFINE_CA_CAST(rb_ca_cast_boolean, CA_BOOLEAN)
2031
+ DEFINE_CA_CAST(rb_ca_cast_int8, CA_INT8)
2032
+ DEFINE_CA_CAST(rb_ca_cast_uint8, CA_UINT8)
2033
+ DEFINE_CA_CAST(rb_ca_cast_int16, CA_INT16)
2034
+ DEFINE_CA_CAST(rb_ca_cast_uint16, CA_UINT16)
2035
+ DEFINE_CA_CAST(rb_ca_cast_int32, CA_INT32)
2036
+ DEFINE_CA_CAST(rb_ca_cast_uint32, CA_UINT32)
2037
+ DEFINE_CA_CAST(rb_ca_cast_int64, CA_INT64)
2038
+ DEFINE_CA_CAST(rb_ca_cast_uint64, CA_UINT64)
2039
+ DEFINE_CA_CAST(rb_ca_cast_float32, CA_FLOAT32)
2040
+ DEFINE_CA_CAST(rb_ca_cast_float64, CA_FLOAT64)
2041
+ DEFINE_CA_CAST(rb_ca_cast_cmplx64, CA_CMPLX64)
2042
+ DEFINE_CA_CAST(rb_ca_cast_cmplx128, CA_CMPLX128)
2043
+ DEFINE_CA_CAST(rb_ca_cast_object, CA_OBJECT)
2044
+ DEFINE_CA_CAST(rb_ca_cast_size, CA_SIZE)
2045
+
2046
+ #undef DEFINE_CA_CAST
2047
+
2048
+ /* to_type(:fixlen, bytes: bytes_v) via in-file #to_type implementation. */
2049
+ static VALUE
2050
+ ca_cast_to_fixlen (VALUE v, VALUE fixlen_sym, VALUE bytes_v)
2051
+ {
2052
+ VALUE list[2], ropt;
2053
+ ropt = rb_hash_new();
2054
+ rb_hash_aset(ropt, ID2SYM(rb_intern("bytes")), bytes_v);
2055
+ list[0] = fixlen_sym;
2056
+ list[1] = ropt;
2057
+ return rb_ca_to_type_internal(2, list, v);
2058
+ }
2059
+
2060
+ /* CA_FIXLEN(data, bytes: N) -- fully in C (no String-regex branch to keep in
2061
+ Ruby, unlike the numeric casts).
2062
+ Array -> bytes defaults to max top-level element #length; new + store
2063
+ CArray -> copy if already fixlen, else #to_type(:fixlen, bytes:)
2064
+ nil -> empty fixlen (bytes 0 when omitted, matching the old default)
2065
+ other -> #to_ca coerce, else CScalar.new(:fixlen, bytes:) { data } */
2066
+ static VALUE
2067
+ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2068
+ {
2069
+ VALUE v, opts = Qnil, bytes_v = Qnil;
2070
+ VALUE fixlen_sym = ID2SYM(ca_data_type_sym[CA_FIXLEN]);
2071
+
2072
+ rb_scan_args(argc, argv, "11", &v, &opts);
2073
+ rb_scan_options(opts, "bytes", &bytes_v);
2074
+
2075
+ if ( RTEST(rb_obj_is_kind_of(v, rb_cCArray)) ) {
2076
+ CArray *ca;
2077
+ TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
2078
+ if ( ca->data_type == CA_FIXLEN ) {
2079
+ return rb_ca_copy(v);
2080
+ }
2081
+ return ca_cast_to_fixlen(v, fixlen_sym, bytes_v);
2082
+ }
2083
+ else if ( TYPE(v) == T_ARRAY ) {
2084
+ volatile VALUE shape;
2085
+ ca_size_t dim[CA_RANK_MAX];
2086
+ long ndim, i;
2087
+ ca_size_t bytes;
2088
+ VALUE obj;
2089
+ if ( NIL_P(bytes_v) ) {
2090
+ /* bytes = v.map{|s| s.length}.max (top-level #length, duck-typed) */
2091
+ long len = RARRAY_LEN(v);
2092
+ ca_size_t maxlen = 0;
2093
+ for ( i = 0; i < len; i++ ) {
2094
+ ca_size_t l = NUM2SIZE(rb_funcall(rb_ary_entry(v, i), id_length_cast, 0));
2095
+ if ( l > maxlen ) maxlen = l;
2096
+ }
2097
+ bytes = maxlen;
2098
+ } else {
2099
+ bytes = NUM2SIZE(bytes_v);
2100
+ }
2101
+ shape = rb_funcall(rb_cCArray, id_guess_array_shape_cast, 1, v);
2102
+ ndim = RARRAY_LEN(shape);
2103
+ for ( i = 0; i < ndim; i++ ) dim[i] = NUM2SIZE(rb_ary_entry(shape, i));
2104
+ obj = rb_carray_new(CA_FIXLEN, (int8_t) ndim, dim, bytes, NULL);
2105
+ rb_ca_store_all(obj, v);
2106
+ return obj;
2107
+ }
2108
+ else if ( NIL_P(v) ) {
2109
+ ca_size_t dim0 = 0;
2110
+ ca_size_t bytes = NIL_P(bytes_v) ? 0 : NUM2SIZE(bytes_v);
2111
+ return rb_carray_new(CA_FIXLEN, 1, &dim0, bytes, NULL);
2112
+ }
2113
+ else {
2114
+ if ( rb_respond_to(v, id_to_ca_cast) ) {
2115
+ VALUE ca = rb_funcall(v, id_to_ca_cast, 0);
2116
+ CArray *cap;
2117
+ TypedData_Get_Struct(ca, CArray, &carray_data_type, cap);
2118
+ return ( cap->data_type == CA_FIXLEN )
2119
+ ? ca : ca_cast_to_fixlen(ca, fixlen_sym, bytes_v);
2120
+ }
2121
+ {
2122
+ ca_size_t bytes = NIL_P(bytes_v) ? 0 : NUM2SIZE(bytes_v);
2123
+ return rb_cscalar_new_with_value(CA_FIXLEN, bytes, v);
2124
+ }
2125
+ }
2126
+ }
2127
+
1279
2128
  void
1280
- Init_carray_cast ()
2129
+ Init_carray_cast (void)
1281
2130
  {
1282
2131
  /* CArray data_type conversion */
1283
2132
 
@@ -1295,10 +2144,8 @@ Init_carray_cast ()
1295
2144
  rb_define_method(rb_cCArray, "uint64", rb_ca_to_uint64, 0);
1296
2145
  rb_define_method(rb_cCArray, "float32", rb_ca_to_float32, 0);
1297
2146
  rb_define_method(rb_cCArray, "float64", rb_ca_to_float64, 0);
1298
- rb_define_method(rb_cCArray, "float128", rb_ca_to_float128, 0);
1299
2147
  rb_define_method(rb_cCArray, "cmplx64", rb_ca_to_cmplx64, 0);
1300
2148
  rb_define_method(rb_cCArray, "cmplx128", rb_ca_to_cmplx128, 0);
1301
- rb_define_method(rb_cCArray, "cmplx256", rb_ca_to_cmplx256, 0);
1302
2149
  rb_define_method(rb_cCArray, "object", rb_ca_to_VALUE, 0);
1303
2150
 
1304
2151
  rb_define_alias(rb_cCArray, "byte", "uint8");
@@ -1345,7 +2192,71 @@ Init_carray_cast ()
1345
2192
  "cast", rb_ca_s_cast, 1);
1346
2193
  rb_define_singleton_method(rb_cCArray,
1347
2194
  "cast_self_or_other", rb_ca_s_cast_self_or_other, 2);
2195
+ rb_define_singleton_method(rb_cCArray,
2196
+ "result_type", rb_ca_s_result_type, -1);
2197
+
2198
+ rb_define_singleton_method(rb_cCArray,
2199
+ "promote_list", rb_ca_s_promote_list, -1);
2200
+
2201
+ /* Global polymorphic cast functions: CA_INT32(data), CA_FLOAT64(data),
2202
+ ... CA_OBJECT(data), CA_FIXLEN(...). Implementation is the
2203
+ ca_cast_impl family above; dispatch on `data`'s Ruby class.
2204
+ Type-name aliases (CA_BYTE / CA_SHORT / CA_INT / CA_FLOAT /
2205
+ CA_DOUBLE / CA_COMPLEX / CA_DCOMPLEX / CA_SIZE) reuse the same C
2206
+ function as their canonical data_type. Paired with the type-tag
2207
+ classes registered in ext/carray_data_type.c. */
2208
+ id_new_cast = rb_intern("new");
2209
+ id_guess_array_shape_cast = rb_intern("guess_array_shape");
2210
+ id_cast_string = rb_intern("__cast_string__");
2211
+ id_to_ca_cast = rb_intern("to_ca");
2212
+ id_length_cast = rb_intern("length");
2213
+ id_begin_cast = rb_intern("begin");
2214
+ id_end_cast = rb_intern("end");
2215
+ id_to_a_cast = rb_intern("to_a");
2216
+ id_exclude_end_p_cast = rb_intern("exclude_end?");
2217
+ id_abs_cast = rb_intern("abs");
2218
+ id_floor_cast = rb_intern("floor");
2219
+ id_op_minus = rb_intern("-");
2220
+ id_op_div = rb_intern("/");
2221
+ id_op_plus = rb_intern("+");
2222
+ id_op_le = rb_intern("<=");
2223
+ id_op_eq = rb_intern("==");
2224
+ id_op_uminus = rb_intern("-@");
2225
+
2226
+ rb_define_global_function("CA_BOOLEAN", rb_ca_cast_boolean, -1);
2227
+ rb_define_global_function("CA_INT8", rb_ca_cast_int8, -1);
2228
+ rb_define_global_function("CA_UINT8", rb_ca_cast_uint8, -1);
2229
+ rb_define_global_function("CA_INT16", rb_ca_cast_int16, -1);
2230
+ rb_define_global_function("CA_UINT16", rb_ca_cast_uint16, -1);
2231
+ rb_define_global_function("CA_INT32", rb_ca_cast_int32, -1);
2232
+ rb_define_global_function("CA_UINT32", rb_ca_cast_uint32, -1);
2233
+ rb_define_global_function("CA_INT64", rb_ca_cast_int64, -1);
2234
+ rb_define_global_function("CA_UINT64", rb_ca_cast_uint64, -1);
2235
+ rb_define_global_function("CA_FLOAT32", rb_ca_cast_float32, -1);
2236
+ rb_define_global_function("CA_FLOAT64", rb_ca_cast_float64, -1);
2237
+ rb_define_global_function("CA_CMPLX64", rb_ca_cast_cmplx64, -1);
2238
+ rb_define_global_function("CA_CMPLX128", rb_ca_cast_cmplx128, -1);
2239
+ rb_define_global_function("CA_OBJECT", rb_ca_cast_object, -1);
2240
+ /* type-name aliases */
2241
+ rb_define_global_function("CA_BYTE", rb_ca_cast_uint8, -1);
2242
+ rb_define_global_function("CA_SHORT", rb_ca_cast_int16, -1);
2243
+ rb_define_global_function("CA_INT", rb_ca_cast_int32, -1);
2244
+ rb_define_global_function("CA_FLOAT", rb_ca_cast_float32, -1);
2245
+ rb_define_global_function("CA_DOUBLE", rb_ca_cast_float64, -1);
2246
+ rb_define_global_function("CA_COMPLEX", rb_ca_cast_cmplx64, -1);
2247
+ rb_define_global_function("CA_DCOMPLEX", rb_ca_cast_cmplx128, -1);
2248
+ rb_define_global_function("CA_SIZE", rb_ca_cast_size, -1);
2249
+ rb_define_global_function("CA_FIXLEN", rb_ca_cast_fixlen, -1);
1348
2250
 
1349
2251
  rb_define_method(rb_cCArray, "cast_with", rb_ca_cast_with, 1);
2252
+
2253
+ rb_define_method(rb_cCArray, "clip_int8", rb_ca_clip_int8, 0);
2254
+ rb_define_method(rb_cCArray, "clip_int16", rb_ca_clip_int16, 0);
2255
+ rb_define_method(rb_cCArray, "clip_int32", rb_ca_clip_int32, 0);
2256
+ rb_define_method(rb_cCArray, "clip_int64", rb_ca_clip_int64, 0);
2257
+ rb_define_method(rb_cCArray, "clip_uint8", rb_ca_clip_uint8, 0);
2258
+ rb_define_method(rb_cCArray, "clip_uint16", rb_ca_clip_uint16, 0);
2259
+ rb_define_method(rb_cCArray, "clip_uint32", rb_ca_clip_uint32, 0);
2260
+ rb_define_method(rb_cCArray, "clip_uint64", rb_ca_clip_uint64, 0);
1350
2261
  }
1351
2262