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
@@ -0,0 +1,604 @@
1
+ # ----------------------------------------------------------------------------
2
+ #
3
+ # carray/block_iterator.rb
4
+ #
5
+ # CABlockIterator — a non-overlapping tile reduction dispatcher, the Block
6
+ # member of the 3.0 iterator family (sibling of CASlabIterator /
7
+ # CAWindowIterator / CACategoricalIterator). Where a window iterator folds an
8
+ # overlapping window per anchor, a block iterator folds each non-overlapping
9
+ # tile of a fixed per-axis size, so the output is a tile grid (pooling /
10
+ # block statistics / downsampling).
11
+ #
12
+ # bi = a.blocks(3, 3) # 3x3 non-overlapping tiles
13
+ # bi.mean # per-tile mean (average pooling)
14
+ # bi.max # per-tile max (max pooling)
15
+ #
16
+ # Engine: block_view decomposition. The source splits into an interior region
17
+ # (all axes on a size-divisible extent) plus 2^m - 1 boundary regions (m = the
18
+ # number of axes whose length is not a multiple of the tile size). Each region
19
+ # is a `block_view` (a CAStride: zero-copy over the source via compose-fold),
20
+ # reduced over the trailing tile axes by a core reduction and scattered into the
21
+ # ceil-shaped tile grid. One core reduction per region; the named reductions
22
+ # delegate straight to the core, so their dtype / mask / empty (ERI) / epsilon
23
+ # contracts are the core's, unchanged. Because tiles do not overlap, no
24
+ # padded entity is built (unlike CAWindowIterator) and the interior stays on
25
+ # the source buffer.
26
+ #
27
+ # The remainder is not the user's concern. There is no boundary-policy knob:
28
+ # the sole behaviour is full coverage — every cell belongs to a tile, the
29
+ # edge tiles simply fold whatever cells are present (present-only), and the
30
+ # output is the ceil tile grid. A partial edge tile carries fewer real cells,
31
+ # so a core `min_count:` naturally marks it UNDEF when it is not full. "Valid"
32
+ # pooling (drop the remainder, floor grid) is expressed explicitly by slicing
33
+ # first: `a[0...q*b].blocks(b)`.
34
+ #
35
+ # The class name is kept from 2.0 (the concept — a tile — is stable);
36
+ # this is the Ruby family member that supersedes the retired C engine
37
+ # (ext/ca_iter_block.c). Loaded lazily via
38
+ # autoload from lib/carray/autoload_carray.rb the first time `a.blocks(...)`
39
+ # is used.
40
+ #
41
+ # ----------------------------------------------------------------------------
42
+
43
+ require "carray"
44
+
45
+ # Non-overlapping tile reduction dispatcher — the Block member of the
46
+ # iterator family (sibling of `CASlabIterator` / {CAWindowIterator} /
47
+ # `CACategoricalIterator`). Where a window iterator folds an overlapping
48
+ # window per anchor, a block iterator folds each non-overlapping tile of a
49
+ # fixed per-axis size, so the result is a tile grid: pooling, downsampling,
50
+ # block statistics.
51
+ #
52
+ # Obtained from `CArray#blocks`, not constructed directly.
53
+ #
54
+ # @example
55
+ # bi = a.blocks(3, 3) # 3x3 non-overlapping tiles
56
+ # bi.mean # per-tile mean (average pooling)
57
+ # bi.max # per-tile max (max pooling)
58
+ class CABlockIterator < CAIterator
59
+
60
+ # @overload initialize(source, *blocks)
61
+ # Builds a block iterator tiling `source` with a per-axis tile size. Each
62
+ # `blocks[i]` is either an Integer tile size (offset 0) or a `lo..hi` range
63
+ # whose length is the tile size and whose start is a leading offset
64
+ # (backward compatible with the 2.0 `a.blocks(2..4)` form). A single Array
65
+ # argument is taken as the per-axis size list.
66
+ #
67
+ # @param source [CArray] the array to tile.
68
+ # @param blocks [Array<Integer, Range>] per-axis tile sizes (or ranges).
69
+ def initialize (source, *blocks)
70
+ blocks = blocks[0] if blocks.size == 1 && blocks[0].is_a?(Array)
71
+
72
+ unless blocks.size == source.ndim
73
+ raise ArgumentError,
74
+ "blocks: expected #{source.ndim} tile sizes (one per axis), " \
75
+ "got #{blocks.size}"
76
+ end
77
+
78
+ @sndim = source.ndim
79
+ offsets = Array.new(@sndim, 0)
80
+ @sizes = Array.new(@sndim)
81
+ blocks.each_with_index do |b, i|
82
+ if b.is_a?(Range)
83
+ offsets[i] = b.begin
84
+ @sizes[i] = b.end - b.begin + (b.exclude_end? ? 0 : 1)
85
+ else
86
+ @sizes[i] = Integer(b)
87
+ end
88
+ if @sizes[i] < 1
89
+ raise ArgumentError, "blocks: tile size on axis #{i} must be >= 1"
90
+ end
91
+ end
92
+
93
+ # Absorb a leading offset with a zero-copy pre-slice, so the tile geometry
94
+ # below always starts at index 0.
95
+ @source =
96
+ if offsets.all?(&:zero?)
97
+ source
98
+ else
99
+ source[*offsets.map { |o| o..-1 }]
100
+ end
101
+
102
+ n = @source.shape
103
+ @q = @sndim.times.map { |i| n[i] / @sizes[i] } # full tiles per axis
104
+ @r = @sndim.times.map { |i| n[i] % @sizes[i] } # remainder per axis
105
+
106
+ # Ceil tile grid: a partial edge tile adds one grid cell on that axis.
107
+ @shape = @sndim.times.map { |i| @q[i] + (@r[i] > 0 ? 1 : 0) }
108
+ @ndim = @shape.size
109
+
110
+ # Trailing tile axes of a block_view: [ndim .. 2*ndim-1].
111
+ @tile_axes = (@sndim...(2 * @sndim)).to_a
112
+ self
113
+ end
114
+
115
+ # @overload source
116
+ # Returns the array being tiled (with any leading offset already applied).
117
+ # @return [CArray]
118
+ attr_reader :source
119
+
120
+ # ---- region decomposition ---------------------------------------------
121
+ #
122
+ # The tile grid splits into regions by whether each axis sits on its interior
123
+ # (full tiles, extent q_i) or its boundary (the one partial edge tile). A
124
+ # region is the Cartesian product of these per-axis choices; the all-interior
125
+ # choice is the interior region, the rest are the 2^m - 1 boundary regions.
126
+ # Each region is a plain block_view whose tile size on an axis is the full
127
+ # size (interior) or the remainder (boundary) — always dividing the region's
128
+ # extent, so block_view never rejects.
129
+
130
+ private
131
+
132
+ # Yield each region as (strip_ranges, tiles, out_ranges): the source slice to
133
+ # tile, the per-axis tile sizes, and the sub-block of the output grid it fills.
134
+ def each_region
135
+ axis_choices = (0...@sndim).map do |i|
136
+ ch = []
137
+ ch << :interior if @q[i] > 0
138
+ ch << :boundary if @r[i] > 0
139
+ ch
140
+ end
141
+ combos = axis_choices[0].product(*axis_choices[1..-1])
142
+ combos.each do |choice|
143
+ strip_ranges = []
144
+ tiles = []
145
+ out_ranges = []
146
+ (0...@sndim).each do |i|
147
+ base = @q[i] * @sizes[i]
148
+ if choice[i] == :interior
149
+ strip_ranges << (0...base)
150
+ tiles << @sizes[i]
151
+ out_ranges << (0...@q[i])
152
+ else
153
+ strip_ranges << (base...(base + @r[i]))
154
+ tiles << @r[i]
155
+ out_ranges << (@q[i]...(@q[i] + 1))
156
+ end
157
+ end
158
+ yield strip_ranges, tiles, out_ranges
159
+ end
160
+ end
161
+
162
+ # Reduce every region with the yielded block (a block_view over the region
163
+ # and its tile sizes) and scatter the region results into the ceil tile grid.
164
+ # The block returns one CArray, or an Array of CArrays (minmax / percentile /
165
+ # quantile) which are assembled into that many grids. Boundary regions use a
166
+ # smaller (unmasked, ragged) block_view whose present cell count is naturally
167
+ # below a full tile, so `min_count:` marks them UNDEF with no masking. The
168
+ # output dtype is seeded from the first region (all regions share it).
169
+ def assemble
170
+ outs = nil
171
+ each_region do |strip_ranges, tiles, out_ranges|
172
+ view = @source[*strip_ranges].block_view(*tiles)
173
+ res = yield(view, tiles)
174
+ if res.is_a?(Array)
175
+ outs ||= res.map { |r| CArray.new(r.data_type, @shape) }
176
+ res.each_index { |k| outs[k][*out_ranges] = res[k] }
177
+ else
178
+ outs ||= CArray.new(res.data_type, @shape)
179
+ outs[*out_ranges] = res
180
+ end
181
+ end
182
+ outs
183
+ end
184
+
185
+ # Fold every region with a single core reduction `op` (the tier-1 shape).
186
+ def fold (op, **kw)
187
+ assemble { |view, _tiles| view.send(op, axis: @tile_axes, **kw) }
188
+ end
189
+
190
+ public
191
+
192
+ # ---- named reductions (core delegation, drift zero) -------------------
193
+ #
194
+ # A per-tile fold to one value over the trailing tile axes is exactly a core
195
+ # per-axis reduction over those axes, so every reduction delegates to
196
+ # `block_view.<op>(axis: tile_axes, ...)` per region. This inherits the core
197
+ # dtype, mask, empty / all-masked (identity vs UNDEF) and epsilon-close
198
+ # contracts unchanged. `min_count:` / `fill_value:` pass straight to the core.
199
+
200
+ # @overload sum(min_count: nil, fill_value: nil)
201
+ # Per-tile sum. @return [CArray] tile-grid shaped
202
+ # The rest are analogous: prod / mean / min / max, sample and population
203
+ # variance / stddev, all / any.
204
+ [:sum, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
205
+ :variancep, :stddevp].each do |op|
206
+ define_method(op) do |min_count: nil, fill_value: nil|
207
+ kw = {}
208
+ kw[:min_count] = min_count unless min_count.nil?
209
+ kw[:fill_value] = fill_value unless fill_value.nil?
210
+ fold(op, **kw)
211
+ end
212
+ end
213
+
214
+ # ---- count family and elements ----------------------------------------
215
+
216
+ # @overload count(v = <none>)
217
+ # Per-tile count. No argument counts present (non-masked) cells (fewer at a
218
+ # partial edge tile); `count(UNDEF)` counts masked cells; `count(v)` counts
219
+ # cells equal to `v`.
220
+ # @return [CArray] tile-grid shaped
221
+ def count (*args)
222
+ return count_not_masked if args.empty?
223
+ out = nil
224
+ each_region do |strip_ranges, tiles, out_ranges|
225
+ view = @source[*strip_ranges].block_view(*tiles)
226
+ # block_view is a CAStride, so #count is not shadowed; dispatch
227
+ # CArray#count explicitly anyway, matching the family regularity.
228
+ red = CArray.instance_method(:count).bind_call(view, *args, axis: @tile_axes)
229
+ out ||= CArray.new(red.data_type, @shape)
230
+ out[*out_ranges] = red
231
+ end
232
+ out
233
+ end
234
+
235
+ # @overload count_not_masked
236
+ # Per-tile count of present (non-masked) cells. For a partial edge tile
237
+ # this is the real cell count (the OOB cells are not present).
238
+ # @return [CArray]
239
+ def count_not_masked
240
+ fold(:count_not_masked)
241
+ end
242
+
243
+ # @overload count_masked
244
+ # Per-tile count of masked cells. Equals `elements - count_not_masked`;
245
+ # the OOB cells of a partial edge tile count here.
246
+ # @return [CArray]
247
+ def count_masked
248
+ elements - count_not_masked
249
+ end
250
+
251
+ # @overload elements
252
+ # Tile cell count (structural, mask-independent): the constant tile size
253
+ # `Π b_i`, shaped like the tile grid. Every tile — including a partial
254
+ # edge tile — reports the full size; how many of those cells are real is
255
+ # `count_not_masked`, and `elements = count_not_masked + count_masked`.
256
+ # @return [CArray]
257
+ def elements
258
+ sz = @sizes.inject(1) { |p, b| p * b }
259
+ out = CArray.int64(*@shape)
260
+ out[] = sz
261
+ out
262
+ end
263
+
264
+ # ---- tier 2: minmax, position, weighted -------------------------------
265
+
266
+ # @overload minmax(min_count: nil, fill_value: nil)
267
+ # Per-tile `[min, max]` (two tile-grid-shaped CArrays, a single fused pass).
268
+ # @return [Array<CArray>]
269
+ def minmax (min_count: nil, fill_value: nil)
270
+ kw = {}
271
+ kw[:min_count] = min_count unless min_count.nil?
272
+ kw[:fill_value] = fill_value unless fill_value.nil?
273
+ assemble { |view, _| view.minmax(axis: @tile_axes, **kw) }
274
+ end
275
+
276
+ # @overload min_index
277
+ # Per-tile position of the minimum, as a flat index within the tile (a
278
+ # partial edge tile indexes within its own present cells).
279
+ # @return [CArray] tile-grid shaped
280
+ # @overload max_index
281
+ # Per-tile position of the maximum (tile-local flat index).
282
+ # @return [CArray]
283
+ [:min_index, :max_index].each do |op|
284
+ define_method(op) { assemble { |view, _| view.send(op, axis: @tile_axes) } }
285
+ end
286
+
287
+ # @overload min_addr
288
+ # Per-tile flat SOURCE address of the minimum — which cell of the source
289
+ # holds it, so `source.reshape(source.elements)[bi.min_addr]` are the tile
290
+ # minima. Unlike `min_index` (the tile-local position) this indexes back
291
+ # into the original array. An all-masked tile is a masked cell.
292
+ # @return [CArray] tile-grid shaped int64
293
+ def min_addr; winner_addr(:min_index); end
294
+
295
+ # @overload max_addr
296
+ # Per-tile flat source address of the maximum. See {#min_addr}.
297
+ # @return [CArray] tile-grid shaped int64
298
+ def max_addr; winner_addr(:max_index); end
299
+
300
+ private
301
+
302
+ # Per-tile source address of the winner. The tile-local flat index (min_index /
303
+ # max_index) is looked up in a source-address companion tiled the same way, so
304
+ # the tile geometry is reused rather than recomputed. An all-masked tile yields
305
+ # a masked index; it is gathered at 0 and re-masked in the result.
306
+ def winner_addr (idx_op)
307
+ saddr = CArray.int64(*@source.shape).seq!
308
+ out = nil
309
+ each_region do |strip_ranges, tiles, out_ranges|
310
+ vview = @source[*strip_ranges].block_view(*tiles)
311
+ sview = saddr[*strip_ranges].block_view(*tiles)
312
+ mi = vview.send(idx_op, axis: @tile_axes) # tile-local flat index
313
+ grid = (0...@sndim).map { |i| vview.shape[i] }
314
+ cells = tiles.inject(1) { |p, t| p * t }
315
+ safe = mi.copy
316
+ safe[mi.is_masked.eq(1)] = 0 if mi.has_mask? # placeholder for the gather
317
+ picked = sview.reshape(*(grid + [cells]))
318
+ .take_along_axis(safe.reshape(*(grid + [1])), axis: @sndim)
319
+ .reshape(*grid)
320
+ picked[mi.is_masked.eq(1)] = UNDEF if mi.has_mask? # re-mask the empty tiles
321
+ out ||= CArray.int64(*@shape)
322
+ out[*out_ranges] = picked
323
+ end
324
+ out
325
+ end
326
+
327
+ public
328
+
329
+ # @overload sort_addr
330
+ # Per-tile sort by flat SOURCE address, source-shaped: each tile's cells hold
331
+ # that tile's source addresses in ascending-value order (reading the tile
332
+ # row-major gives the sorted addresses), so
333
+ # `source.reshape(source.elements)[bi.sort_addr]` is the source sorted within
334
+ # each tile. Multi-axis tiles are flattened (as for the order statistics). A
335
+ # partial edge tile sorts only its present cells. Masked values sort to the
336
+ # tail of their tile (as `CArray#sort`).
337
+ # @return [CArray] source-shaped int64
338
+ def sort_addr
339
+ saddr = CArray.int64(*@source.shape).seq!
340
+ out = CArray.int64(*@source.shape)
341
+ out[] = UNDEF
342
+ each_region do |strip_ranges, tiles, _out_ranges|
343
+ vview = @source[*strip_ranges].block_view(*tiles)
344
+ sview = saddr[*strip_ranges].block_view(*tiles)
345
+ grid = (0...@sndim).map { |i| vview.shape[i] }
346
+ cells = tiles.inject(1) { |p, t| p * t }
347
+ order = vview.copy.reshape(*(grid + [cells])).sort_addr(axis: @sndim)
348
+ src_sorted = sview.reshape(*(grid + [cells])).take_along_axis(order, axis: @sndim)
349
+ out[*strip_ranges].block_view(*tiles)[] = src_sorted.reshape(*(grid + tiles))
350
+ end
351
+ out
352
+ end
353
+
354
+ # @overload wsum(weights)
355
+ # Per-tile weighted sum; `weights` is shaped like one full tile (`Π b_i`).
356
+ # At a partial edge tile the weight kernel is sliced to the present cells.
357
+ # @return [CArray]
358
+ def wsum (weights)
359
+ weighted(weights) { |view, w| view.wsum(w, axis: @tile_axes) }
360
+ end
361
+
362
+ # @overload wmean(weights)
363
+ # Per-tile weighted mean; `weights` shaped like one full tile.
364
+ # @return [CArray]
365
+ def wmean (weights)
366
+ weighted(weights) { |view, w| view.wmean(w, axis: @tile_axes) }
367
+ end
368
+
369
+ private
370
+
371
+ # Broadcast a full-tile weight kernel over each region (sliced to the region's
372
+ # possibly-ragged tile) and yield (view, broadcast weights) for the reduction.
373
+ def weighted (weights)
374
+ unless weights.shape == @sizes
375
+ raise ArgumentError,
376
+ "wsum/wmean: weights shape #{weights.shape.inspect} != " \
377
+ "tile shape #{@sizes.inspect}"
378
+ end
379
+ assemble do |view, tiles|
380
+ wt = tiles == @sizes ? weights : weights[*tiles.map { |t| 0...t }]
381
+ wshape = ([1] * @sndim) + tiles
382
+ # Pass the weights through unchanged, like the window iterator: core wsum /
383
+ # wmean own weight/dtype coercion, so do not pre-coerce the weights here.
384
+ wfull = wt.reshape(*wshape).broadcast_to(*view.shape)
385
+ yield view, wfull
386
+ end
387
+ end
388
+
389
+ public
390
+
391
+ # ---- tier 3: order statistics (median / percentile / quantile) --------
392
+ #
393
+ # Core per-axis order statistics take a single integer axis, so a 1-D tile
394
+ # delegates straight to the C path; a multi-axis tile is materialized per
395
+ # region and its tile axes flattened into one before the single-axis core
396
+ # order-stat. A masked source raises (the known per-axis masked limitation);
397
+ # strip the mask with `ca.value` first if needed.
398
+
399
+ # @overload median
400
+ # Per-tile median. @return [CArray]
401
+ def median
402
+ order_stat { |v, axis| v.median(axis: axis) }
403
+ end
404
+
405
+ # @overload percentile(*pers)
406
+ # Per-tile percentile(s). One argument returns one CArray, several an array
407
+ # of CArrays (as `CArray#percentile`).
408
+ # @return [CArray, Array<CArray>]
409
+ def percentile (*pers)
410
+ order_stat { |v, axis| v.percentile(*pers, axis: axis) }
411
+ end
412
+
413
+ # @overload quantile
414
+ # Per-tile five-number summary `[min, Q1, median, Q3, max]` (five CArrays).
415
+ # @return [Array<CArray>]
416
+ def quantile
417
+ order_stat { |v, axis| v.quantile(axis: axis) }
418
+ end
419
+
420
+ private
421
+
422
+ # Drive an order statistic (yielded as `block.call(view, axis)`) per region:
423
+ # a 1-D tile passes the tile axis straight through; a multi-axis tile is
424
+ # materialized (peak O(region cells)) and its tile axes flattened into one.
425
+ def order_stat (&op)
426
+ assemble do |view, tiles|
427
+ if @sndim == 1
428
+ op.call(view, @tile_axes[0])
429
+ else
430
+ grid = (0...@sndim).map { |i| view.shape[i] }
431
+ cells = tiles.inject(1) { |p, t| p * t }
432
+ op.call(view.copy.reshape(*(grid + [cells])), @sndim)
433
+ end
434
+ end
435
+ end
436
+
437
+ public
438
+
439
+ # ---- generic iteration (escape hatch, slow) ---------------------------
440
+ #
441
+ # `each` / `reduce` yield each tile as a uniform `Π b_i`-shaped CArray whose
442
+ # out-of-bounds cells (at a partial edge tile) are masked, so the user never
443
+ # sees a ragged shape — the remainder is absorbed into the mask. This uses a
444
+ # padded (masked-margin) entity, materialised once, so it is the slow path;
445
+ # named reductions above stay on the zero-copy region engine. `each` is the
446
+ # receptacle for statistics not in the named surface.
447
+
448
+ # @overload each { |tile| ... }
449
+ # Yields each tile as a uniform `Π b_i`-shaped CArray (partial edge tiles
450
+ # have their out-of-bounds cells masked). Without a block, returns an
451
+ # Enumerator. Per-tile materialize, slow; use a named reduction for speed.
452
+ # @yieldparam tile [CArray]
453
+ # @return [Enumerator, self]
454
+ def each
455
+ return to_enum(:each) unless block_given?
456
+ tgv = tile_grid_view
457
+ nils = Array.new(@sndim, nil)
458
+ CArray.each_index(*@shape) { |*g| yield tgv[*g, *nils] }
459
+ self
460
+ end
461
+
462
+ # @overload reduce { |tile| ... }
463
+ # Custom per-tile reduction: the block receives each tile (a CArray, masked
464
+ # at partial edges) and returns one value per tile. The escape hatch for
465
+ # statistics not in the named surface.
466
+ # @yieldparam tile [CArray]
467
+ # @return [CArray] tile-grid shaped
468
+ # @overload reduce(init) { |acc, elem| ... }
469
+ # Per-tile fiber fold: each tile's cells are folded element by element from
470
+ # `init` (masked cells are skipped by CArray#each).
471
+ # @param init [Object] initial accumulator.
472
+ # @return [CArray]
473
+ def reduce (*args, data_type: nil, &blk)
474
+ raise LocalJumpError, "no block given (yield)" unless blk
475
+ out = CArray.new(data_type || CA_OBJECT, @shape)
476
+ tgv = tile_grid_view
477
+ nils = Array.new(@sndim, nil)
478
+ if args.empty?
479
+ CArray.each_index(*@shape) { |*g| out[*g] = blk.call(tgv[*g, *nils]) }
480
+ else
481
+ init = args[0]
482
+ CArray.each_index(*@shape) do |*g|
483
+ acc = init
484
+ tgv[*g, *nils].each { |e| acc = blk.call(acc, e) }
485
+ out[*g] = acc
486
+ end
487
+ end
488
+ out
489
+ end
490
+
491
+ # @overload map { |tile| ... }
492
+ # Per-tile element-wise transform: the block receives each tile (a uniform
493
+ # `Π b_i`-shaped CArray, masked at partial edges) and returns a same-shaped
494
+ # tile (or a scalar to broadcast). The transformed tiles are scattered back
495
+ # into a source-shaped CArray (the out-of-bounds cells of a partial edge
496
+ # tile are dropped). Well-defined because tiles do not overlap; the source
497
+ # is not modified (a new array is returned). Without a block, an Enumerator.
498
+ # @yieldparam tile [CArray]
499
+ # @return [CArray, Enumerator] source-shaped
500
+ def map
501
+ return to_enum(:map) unless block_given?
502
+ pout = CArray.new(padded_source.data_type, padded_source.shape)
503
+ pgv = pout.block_view(*@sizes)
504
+ tgv = tile_grid_view
505
+ nils = Array.new(@sndim, nil)
506
+ CArray.each_index(*@shape) { |*g| pgv[*g, *nils] = yield(tgv[*g, *nils]) }
507
+ pout[*@sndim.times.map { |i| 0...@source.shape[i] }].copy
508
+ end
509
+
510
+ # ---- segment scan: within-tile running statistics ---------------------
511
+ #
512
+ # A tile is a partition (each cell is in exactly one tile), so a per-cell
513
+ # running statistic is well-defined: each tile accumulates in its internal
514
+ # row-major order. Each tile is flattened (row-major), scanned by the core
515
+ # value scan, reshaped back, and scattered into a source-shaped result --
516
+ # reusing the same padded (masked-margin) entity as the iterate escape
517
+ # hatches, so this is the slow path (a per-tile materialize), and the
518
+ # out-of-bounds cells of a partial edge tile are dropped from the result.
519
+ # cumsum / cumprod -> float64, cummax / cummin preserve the value dtype,
520
+ # cumcount -> int64 running count of present cells; the output dtype is seeded
521
+ # from the first tile's scan.
522
+
523
+ # @overload cumsum
524
+ # Per-tile inclusive running sum (float64), source-shaped.
525
+ # @return [CArray]
526
+ # @overload cumprod
527
+ # Per-tile inclusive running product (float64), source-shaped.
528
+ # @return [CArray]
529
+ # @overload cummax
530
+ # Per-tile inclusive running maximum (value dtype), source-shaped.
531
+ # @return [CArray]
532
+ # @overload cummin
533
+ # Per-tile inclusive running minimum (value dtype), source-shaped.
534
+ # @return [CArray]
535
+ # @overload cumcount
536
+ # Per-tile running count of present cells (int64), source-shaped.
537
+ # @return [CArray]
538
+ [:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
539
+ define_method(op) { block_scan(op) }
540
+ end
541
+
542
+ private
543
+
544
+ # Drive a within-tile segment scan: scan each tile flattened row-major and
545
+ # scatter back to a source-shaped result. The output dtype is taken from the
546
+ # first tile's scan (all tiles share it); the padded margin is UNDEF and the
547
+ # OOB cells of a partial edge tile are cropped from the result.
548
+ def block_scan (op)
549
+ tgv = tile_grid_view
550
+ nils = Array.new(@sndim, nil)
551
+ pout = nil
552
+ pgv = nil
553
+ CArray.each_index(*@shape) do |*g|
554
+ tile = tgv[*g, *nils]
555
+ scanned = tile.copy.reshape(tile.elements).send(op).reshape(*@sizes)
556
+ unless pout
557
+ pout = CArray.new(scanned.data_type, padded_source.shape)
558
+ pout[] = UNDEF
559
+ pgv = pout.block_view(*@sizes)
560
+ end
561
+ pgv[*g, *nils] = scanned
562
+ end
563
+ pout[*@sndim.times.map { |i| 0...@source.shape[i] }].copy
564
+ end
565
+
566
+ # A padded entity whose interior holds the source and whose partial-edge
567
+ # margin is masked (UNDEF), sized to exactly cover the ceil tile grid. When
568
+ # the source already divides evenly this is the source itself (no copy), so
569
+ # the tiles alias it. Memoised; feeds the iterate escape hatches.
570
+ def padded_source
571
+ @padded_source ||= begin
572
+ pshape = @sndim.times.map { |i| @shape[i] * @sizes[i] }
573
+ if pshape == @source.shape.to_a
574
+ @source
575
+ else
576
+ pad = CArray.new(@source.data_type, pshape)
577
+ pad[] = UNDEF
578
+ pad[*@sndim.times.map { |i| 0...@source.shape[i] }] = @source
579
+ pad
580
+ end
581
+ end
582
+ end
583
+
584
+ # block_view of the padded entity: shape [g_0..g_{n-1}, b_0..b_{n-1}], so
585
+ # `view[*grid_index, *nils]` is one uniform tile.
586
+ def tile_grid_view
587
+ @tile_grid_view ||= padded_source.block_view(*@sizes)
588
+ end
589
+ end
590
+
591
+
592
+ class CArray
593
+ # @overload blocks(*blocks)
594
+ # Returns a {CABlockIterator} tiling `self` with non-overlapping tiles of a
595
+ # per-axis size. Each argument is an Integer tile size (offset 0) or a
596
+ # `lo..hi` range (length = tile size, start = leading offset). The
597
+ # remainder is covered by present-only edge tiles (ceil tile grid); slice
598
+ # first for "valid" tiling.
599
+ # @param blocks [Array<Integer, Range>] per-axis tile sizes (or ranges).
600
+ # @return [CABlockIterator]
601
+ def blocks (*blocks)
602
+ CABlockIterator.new(self, *blocks)
603
+ end
604
+ end