carray 3.0.3 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
@@ -71,9 +71,11 @@ class CAWindowIterator < CAIterator
71
71
  # `:nearest` / `:truncate`). `fill_value:` is a constant margin value
72
72
  # (an escape for `:constant` padding); when given it overrides `:skip`.
73
73
  #
74
- # For backward compatibility `initialize(window_view)` accepts a CAWindow
75
- # view (the old `CAWindowIterator.new(a.window(...))` form): the geometry
76
- # (offset ranges, bounds, fill value) is read back from the view.
74
+ # For backward compatibility `initialize(window_view)` with no ranges
75
+ # accepts a CAWindow view (the old `CAWindowIterator.new(a.window(...))`
76
+ # form): the geometry (offset ranges, bounds, fill value) is read back
77
+ # from the view. Given ranges, a CAWindow view is rolled over like any
78
+ # other array.
77
79
  #
78
80
  # @param source [CArray, CAWindow] the array to roll over, or a CAWindow
79
81
  # view to read the geometry from.
@@ -81,7 +83,7 @@ class CAWindowIterator < CAIterator
81
83
  # @param bounds [Symbol] `:skip` / `:nearest` / `:truncate`.
82
84
  # @param fill_value [Object, nil] constant margin value, overriding :skip.
83
85
  def initialize (source, *ranges, bounds: :skip, fill_value: nil)
84
- if source.is_a?(CArray) && source.obj_type == CA_OBJ_WINDOW
86
+ if ranges.empty? && source.is_a?(CArray) && source.obj_type == CA_OBJ_WINDOW
85
87
  # Backward-compat: read geometry from a CAWindow view built by #window.
86
88
  # start[i] = lo, shape[i] (window width) = w, so hi = lo + w - 1.
87
89
  win = source
@@ -198,7 +200,7 @@ class CAWindowIterator < CAIterator
198
200
  buffer = padded_entity
199
201
  wanted = @ndim.times.map { |k| @origins[k]...(@origins[k] + @shape[k] + @widths[k] - 1) }
200
202
  return buffer if wanted.each_with_index.all? { |range, k|
201
- range.begin.zero? && range.end == buffer.dim[k]
203
+ range.begin.zero? && range.end == buffer.shape[k]
202
204
  }
203
205
  buffer[*wanted]
204
206
  end
@@ -269,9 +271,9 @@ class CAWindowIterator < CAIterator
269
271
  # the Face's own. A buffer of the Face's surface type could not take the
270
272
  # margin values at all.
271
273
  def pad_face (src, lefts, rights, mode, value)
272
- storage = src.__send__(:__face_storage__)
274
+ storage = src.__send__(:face_storage)
273
275
  if mode == :constant && !value.nil?
274
- cell = src.__send__(:__blank_like__, [1])
276
+ cell = src.__send__(:blank_like, [1])
275
277
  cell[0] = value
276
278
  padded = pad_source(storage, lefts, rights, :constant, cell.parent[0])
277
279
  else
@@ -472,6 +474,28 @@ class CAWindowIterator < CAIterator
472
474
  RUBY
473
475
  end
474
476
 
477
+ # @!method first
478
+ # Rolling first present value, in row-major order within the window;
479
+ # UNDEF for a window with none (the UNDEF margin of `:skip` is not
480
+ # present, so a window over the edge starts at its first source cell).
481
+ # @return [CArray] reference-shaped (or shrunk, for :truncate)
482
+ # @!method last
483
+ # Rolling last present value; UNDEF for a window with none.
484
+ # @return [CArray] reference-shaped (or shrunk, for :truncate)
485
+ # @!method nunique
486
+ # Rolling number of distinct present values (int64); 0 for a window with
487
+ # none.
488
+ # @return [CArray] reference-shaped (or shrunk, for :truncate)
489
+ #
490
+ # These take no `min_count:` or `fill_value:`, as the core ones do not.
491
+ [:first, :last, :nunique].each do |op|
492
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
493
+ def #{op} (**kw)
494
+ sliding_view.#{op}(axis: @window_axes, **CAIterator.reduce_keywords(:#{op}, kw))
495
+ end
496
+ RUBY
497
+ end
498
+
475
499
  # @overload min_addr
476
500
  # Rolling flat SOURCE address of the window minimum — which source cell holds
477
501
  # it, so `source.reshape(source.elements)[sw.min_addr]` are the window minima.
@@ -517,6 +541,12 @@ class CAWindowIterator < CAIterator
517
541
  :min => :pmin!, :max => :pmax!,
518
542
  :all => :and!, :any => :or! }.freeze
519
543
 
544
+ # A boolean accumulates as the core's does, by parity, so its kernel is xor.
545
+ def offset_fold_kernel (op)
546
+ return :xor! if op == :accumulate && @source.data_type == CA_BOOLEAN
547
+ OFFSET_FOLD.fetch(op)
548
+ end
549
+
520
550
  # Widest window this path takes on any one axis.
521
551
  OFFSET_FOLD_MAX_WIDTH = 5
522
552
 
@@ -524,7 +554,7 @@ class CAWindowIterator < CAIterator
524
554
  # answer for -- in which case the caller delegates as before.
525
555
  def fold_by_offset (op, min_count, fill_value = nil)
526
556
  return nil unless CArray.__fast_paths_enabled__
527
- need = CArray.__min_count_need__(op, min_count, 0)
557
+ need = CArray.__present_cells_needed_per_result_cell__(op, min_count, 0)
528
558
  return nil unless op == :mean || OFFSET_FOLD.key?(op)
529
559
  # The elementwise kernels act on a Face's storage; its reductions are its own.
530
560
  return nil if @source.face?
@@ -538,7 +568,7 @@ class CAWindowIterator < CAIterator
538
568
  folded = mask_empty_windows(folded) if EMPTY_WINDOW_IS_UNDEFINED.include?(op)
539
569
  folded = apply_min_count(folded, need)
540
570
  # A `fill_value:` on the call replaces a result that came out undefined.
541
- CArray.__reduce_fill__(folded, fill_value)
571
+ CArray.__fill_undefined_result_cells__(folded, fill_value)
542
572
  end
543
573
 
544
574
  # The mean is the sum over the same offsets, divided by the number of cells
@@ -551,13 +581,14 @@ class CAWindowIterator < CAIterator
551
581
  divisor = counts.copy
552
582
  divisor[counts.eq(0)] = 1
553
583
  end
584
+ # coerce: the mean's fixed compute type
554
585
  accumulate_offsets(:sum).to_type(offset_fold_data_type(:mean)) / divisor
555
586
  end
556
587
 
557
588
  # Walks the window offsets, accumulating each shifted plane into the result.
558
589
  def accumulate_offsets (op, base = nil, kernel = nil, data_type = nil)
559
590
  base ||= offset_fold_base(op)
560
- kernel ||= OFFSET_FOLD.fetch(op)
591
+ kernel ||= offset_fold_kernel(op)
561
592
  data_type ||= offset_fold_data_type(op)
562
593
  accumulator = nil
563
594
  offset_grid.each do |offset|
@@ -604,7 +635,7 @@ class CAWindowIterator < CAIterator
604
635
  @source.is_nan.any
605
636
  end
606
637
 
607
- # A result only where the window held `need` cells (CArray.__min_count_need__).
638
+ # A result only where the window held `need` cells (CArray.__present_cells_needed_per_result_cell__).
608
639
  def apply_min_count (folded, need)
609
640
  return folded if need == 0
610
641
  counts = window_cell_counts
@@ -708,7 +739,7 @@ class CAWindowIterator < CAIterator
708
739
  def offset_fold_data_type (op)
709
740
  @offset_fold_data_type ||= {}
710
741
  @offset_fold_data_type[op] ||=
711
- CArray.new(@source.data_type, [1, 1], bytes: @source.bytes).send(op, axis: [1]).data_type
742
+ CAIterator.core_probe(@source).public_send(op, axis: [1]).data_type
712
743
  end
713
744
 
714
745
  # Every offset within the window, as a list of per-axis positions into the
@@ -752,7 +783,7 @@ class CAWindowIterator < CAIterator
752
783
  end
753
784
  addr[oob] = UNDEF unless @bounds == :nearest
754
785
  addr[mi.is_masked.eq(1)] = UNDEF if mi.has_mask?
755
- CArray.__reduce_fill__(addr, kw[:fill_value])
786
+ CArray.__fill_undefined_result_cells__(addr, kw[:fill_value])
756
787
  end
757
788
 
758
789
  public
@@ -1063,13 +1094,51 @@ class CAWindowIterator < CAIterator
1063
1094
  # winner is well-defined).
1064
1095
  # @raise [NotImplementedError]
1065
1096
  [:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
1066
- define_method(op) do |*, **|
1097
+ # define_method: a refusal that names itself in its own message
1098
+ define_method(op, &Ractor.make_shareable(proc do |*, **|
1067
1099
  raise NotImplementedError,
1068
1100
  "#{self.class} has no #{op}: a segment scan needs each cell to " \
1069
1101
  "belong to exactly one piece, but overlapping windows share cells, " \
1070
1102
  "so a per-cell running value is ill-defined; use reduce for a " \
1071
1103
  "custom per-window fold."
1072
- end
1104
+ end))
1105
+ end
1106
+
1107
+ # @!method lag(n = 1)
1108
+ # Not supported for a window iterator. A lag writes each cell the value
1109
+ # some steps earlier in its piece, which needs each cell to have one
1110
+ # position in one piece; overlapping windows put a cell in many windows.
1111
+ # Use `source.lag(n, axis:)` for the lag along an axis.
1112
+ # @raise [NotImplementedError]
1113
+ def lag (*)
1114
+ raise NotImplementedError,
1115
+ "#{self.class} has no lag: a cell belongs to many overlapping windows, " \
1116
+ "so it has no single position to step back from; use " \
1117
+ "source.lag(n, axis:) for the lag along an axis."
1118
+ end
1119
+
1120
+ # @!method strip_mask
1121
+ # Not supported for a window iterator: a cell belongs to many overlapping
1122
+ # windows, so it has no single value to be filled with. Use
1123
+ # `source.strip_mask(method:, axis:)` for a fill along an axis.
1124
+ # @raise [NotImplementedError]
1125
+ def strip_mask (*, **)
1126
+ raise NotImplementedError,
1127
+ "#{self.class} has no strip_mask: a cell belongs to many overlapping " \
1128
+ "windows, so it has no single fill; use source.strip_mask(method:, axis:) " \
1129
+ "for a fill along an axis."
1130
+ end
1131
+
1132
+ # @!method order
1133
+ # Not supported for a window iterator: a cell belongs to many overlapping
1134
+ # windows, so it has no single rank. Use `source.order(axis:)` for the
1135
+ # rank along an axis.
1136
+ # @raise [NotImplementedError]
1137
+ def order (*, **)
1138
+ raise NotImplementedError,
1139
+ "#{self.class} has no order: a cell belongs to many overlapping windows, " \
1140
+ "so it has no single rank; use source.order(axis:) for the rank along " \
1141
+ "an axis."
1073
1142
  end
1074
1143
 
1075
1144
  private
@@ -1095,7 +1164,44 @@ class CArray
1095
1164
  # @param bounds [Symbol] `:skip` / `:nearest` / `:truncate`.
1096
1165
  # @param fill_value [Object, nil] constant margin value.
1097
1166
  # @return [CAWindowIterator]
1098
- def windows (*ranges, bounds: :skip, fill_value: nil)
1167
+ # @overload windows(on:, before: nil, after: nil, exclude: :lower, by: nil, interval: nil, at: nil)
1168
+ # Returns a {CAKeyWindowIterator}: a window per value bounded by a width
1169
+ # of the key `on` (a time, a duration or a number) instead of a count of
1170
+ # cells. The window of a value whose key is t holds the values whose
1171
+ # key lies between t - before and t + after; `exclude:` leaves out the
1172
+ # lower end (default), the upper end, both or neither. `by:` keeps
1173
+ # each window within the values with the same by keys, and `interval:`
1174
+ # gives the key's grid step for {CAKeyWindowIterator#is_complete}. A
1175
+ # value whose key is masked, NaN or infinite is in no window and has no
1176
+ # window. `at:` places the windows at those points of the key instead
1177
+ # of at the values, one answer per point (a grid of points gives
1178
+ # windows on a grid); it cannot be given with `by:`.
1179
+ #
1180
+ # temp.windows(on: time, before: "1 hour").mean
1181
+ # temp.windows(on: time, at: hours, after: "3 hours", exclude: :upper).mean
1182
+ # @return [CAKeyWindowIterator]
1183
+ def windows (*ranges, on: nil, **key_window)
1184
+ unless on.nil?
1185
+ unless ranges.empty?
1186
+ raise ArgumentError, "windows: give offset ranges or on:, not both"
1187
+ end
1188
+ given = [:bounds, :fill_value] & key_window.keys
1189
+ unless given.empty?
1190
+ raise ArgumentError,
1191
+ "windows: #{given.map { |k| "#{k}:" }.join(', ')} " \
1192
+ "belong to offset windows; a window on a key has no padding " \
1193
+ "(a row with no window answers UNDEF)"
1194
+ end
1195
+ require "carray/key_windows"
1196
+ return CAKeyWindowIterator.new(self, on: on, **key_window)
1197
+ end
1198
+ bounds = key_window.delete(:bounds) { :skip }
1199
+ fill_value = key_window.delete(:fill_value)
1200
+ unless key_window.empty?
1201
+ raise ArgumentError,
1202
+ "windows: #{key_window.keys.map { |k| "#{k}:" }.join(', ')} " \
1203
+ "need on:, the key the widths are measured on"
1204
+ end
1099
1205
  if ranges.size == 1 && ranges[0].is_a?(CArray) && ranges[0].obj_type == CA_OBJ_WINDOW
1100
1206
  return CAWindowIterator.new(ranges[0])
1101
1207
  end
data/lib/carray.rb CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  require 'carray_ext'
4
4
 
5
+ # Errors name the method the user called: the outermost frame of the library
6
+ # before the user's code, and the library's Ruby frames are the ones here.
7
+ CArray.send(:library_root=, __dir__)
8
+
5
9
  # Top-level CAMath module -- math namespace for CArrays.
6
10
  # `CAMath.sin(ca)` works because module functions inherited
7
11
  # from Math (via include) accept any object that responds to
@@ -49,6 +53,9 @@ require 'carray/string_operation_extension' # shared StringOperationMixin (must
49
53
  require 'carray/const_string' # CAConstString construction surface
50
54
  require 'carray/string' # CAString construction surface
51
55
  require 'carray/fixlen_string' # CAFixlenString construction surface
56
+ # Loaded here, not on first use: Ruby's own error messages call #inspect, and
57
+ # a Ractor other than the main one cannot load a file.
58
+ require 'carray/inspect'
52
59
  # CArray.format lives in carray/methods/string_format.rb, autoloaded on first call
53
60
 
54
61
  # carray/frame is loaded lazily via autoload_carray (CAFrame / GroupedFrame
@@ -39,7 +39,7 @@ class CArray
39
39
  # read as, or `nil` for the narrowest unsigned type.
40
40
  # @return [CABitfield]
41
41
  # @raise [IndexError] when `range` extends past the parent's bit
42
- # width, when the range has a step != 1, when the bit length
42
+ # width, when the range descends or has a step != 1, when the bit length
43
43
  # is outside `1..64`, or when the field reaches more than 64 bits
44
44
  # past the start of its first byte.
45
45
  # @raise [CArray::DataTypeError] when `self` is an object or complex
@@ -20,6 +20,10 @@ class CArray
20
20
  # @return [CAField]
21
21
  # @raise [ArgumentError] when `offset` is negative or the
22
22
  # `offset + bytes` window falls outside one parent record.
23
+ # @raise [CArray::DataTypeError] when a field of a numeric array is
24
+ # asked for as `:boolean` (a boolean cell holds 0 or 1; compare
25
+ # instead, `ne(0)`). A record's boolean member is a field of a
26
+ # fixlen array and is allowed.
23
27
  # @overload field(offset, template)
24
28
  # Returns a CARefer over a {CAField}: takes `template.elements *
25
29
  # template.bytes` bytes at `offset` and exposes them with
@@ -14,22 +14,33 @@ class CArray
14
14
  # number of indices selected for that axis.
15
15
  #
16
16
  # Each selector is one of:
17
- # - `nil` — the whole axis, in order.
18
- # - an integer `Range` — a contiguous sub-range along the axis.
19
17
  # - a `CArray` of integer indices — gather exactly those parent
20
18
  # indices (arbitrary order; duplicates produce duplicated cells).
21
- # - a boolean `CArray` — the indices where it is true.
19
+ # - a boolean `CArray` as long as its axis — the indices where it is
20
+ # true.
21
+ # - any other key, read as the indexer reads it in a block
22
+ # reference: `nil` the whole axis, a `Range` /
23
+ # `ArithmeticSequence` / Array form its run (a range ending before
24
+ # it starts reads backwards), the rubber marker (`false` / `:~`)
25
+ # as many whole axes as it stands for, and an `Integer` one cell,
26
+ # whose axis is then dropped from the view.
22
27
  #
23
- # A masked index `CArray` selects only its not-masked cells. The
24
- # view is writable; scattering back to overlapping cells (from
28
+ # A masked cell of an index `CArray` selects nothing: the position
29
+ # stays in the view and reads as masked (`UNDEF`), and a write to it
30
+ # is dropped, as in {#project} and {#gather_nd}. A masked cell of a
31
+ # boolean selector selects nothing either, so it adds no position.
32
+ # The view is writable; scattering back to overlapping cells (from
25
33
  # duplicate indices) is last-write-wins.
26
34
  #
27
- # @param selectors [Array<nil, Range, CArray>] one selector per axis.
35
+ # @param selectors [Array<nil, Integer, Range, Array, CArray>] one
36
+ # selector per axis.
28
37
  # @return [CArray] the grid view.
29
- # @raise [ArgumentError] when more selectors than `ndim` are given.
30
- # @raise [IndexError] when a selected index is out of range.
31
- # @raise [RuntimeError] when a selector is a plain Ruby `Array`
32
- # (not supported; pass a CArray of indices instead).
38
+ # @raise [ArgumentError] when more selectors than `ndim` are given, a
39
+ # boolean selector is not as long as its axis, or a masked index
40
+ # selects from a view of a value array or a mask array (which can
41
+ # not carry the mask that shows it).
42
+ # @raise [IndexError] when a selected index is out of range, or a
43
+ # selector is not a position (a String, a Float with a fraction).
33
44
  def grid(*selectors); end
34
45
  # @!endgroup
35
46
  end
@@ -36,7 +36,8 @@ class CArray
36
36
  # evenly, when `offset` is negative, or when the requested
37
37
  # view extends past the parent's data.
38
38
  # @raise [CArray::DataTypeError] when reinterpreting a CA_OBJECT parent
39
- # with a non-object `data_type`.
39
+ # with a non-object `data_type`, or a parent of another data type as
40
+ # `:boolean` (a boolean cell holds 0 or 1; compare instead, `ne(0)`).
40
41
  def refer(*argv); end
41
42
 
42
43
  # Returns a view of `self` with the requested shape. Element
@@ -3,7 +3,7 @@
3
3
  # See yard-stubs/README.md and yard-stubs/STYLE.md.
4
4
 
5
5
  # Cyclic-shift view of the parent. Same shape as the parent; each
6
- # axis `k` is rotated by `shift[k]` cells (modulo `parent.dim[k]`).
6
+ # axis `k` is rotated by `shift[k]` cells (modulo `parent.shape[k]`).
7
7
  # All parent cells alias through the view exactly once, so writes
8
8
  # through the view reach the parent.
9
9
  #
@@ -25,7 +25,7 @@ class CArray
25
25
  # `shifts[k]` along each axis `k`. Positive shifts move cell `i`
26
26
  # of the parent to position `i + shift`; negative shifts move it
27
27
  # the other way. Each `shift[k]` is normalised into
28
- # `[0, self.dim[k])` before use, so any integer is accepted.
28
+ # `[0, self.shape[k])` before use, so any integer is accepted.
29
29
  #
30
30
  # Fewer args than `ndim` treats the missing axes as `shift = 0`.
31
31
  # More args than `ndim` raises `ArgumentError`.
@@ -22,7 +22,11 @@ class CArray
22
22
  # parent to position `i + shift`). Out-of-range cells take `fill_value`
23
23
  # (default `0`); passing `fill_value: UNDEF` masks them instead of
24
24
  # filling. In-range cells alias the parent, so writes through the view
25
- # reach the parent.
25
+ # reach the parent. A write to an out-of-range cell has no cell to land
26
+ # in and is discarded; under `fill_value: UNDEF` such a cell stays masked
27
+ # through `unmask` too. The default `0` is the storage value, so for a time
28
+ # array it is 1970-01-01, for a CAString `""`; `ts - ts.shift(1,
29
+ # fill_value: UNDEF)` leaves the first difference missing.
26
30
  #
27
31
  # The in-place idiom is `ca[] = ca.shift(...)`; there is no `shift!`.
28
32
  # For a cyclic (wrap-around) shift use {#roll}, which returns a `CARoll`
@@ -2,7 +2,7 @@
2
2
  # Stubs for methods defined in ext/ca_obj_tile.c.
3
3
  # See yard-stubs/README.md and yard-stubs/STYLE.md.
4
4
 
5
- # Tiled repetition view. Output shape is `parent.dim[k] * reps[k]`
5
+ # Tiled repetition view. Output shape is `parent.shape[k] * reps[k]`
6
6
  # along each axis; every tile is a full-parent alias, so writes to
7
7
  # overlapping cells are last-write-wins.
8
8
  class CATile < CAView
@@ -21,7 +21,8 @@ class CArray
21
21
  # parent alias it, so writes through the view reach the parent; cells
22
22
  # outside take `fill_value` (default `0`), and `fill_value: UNDEF` masks
23
23
  # them instead. A write that lands outside the parent has no cell to land
24
- # in and is discarded.
24
+ # in and is discarded; under `fill_value: UNDEF` such a cell stays masked
25
+ # through `unmask` too.
25
26
  #
26
27
  # A range wider than its axis is how an array is **padded**: `a.window(-1..
27
28
  # a.dim0, -1..a.dim1)` puts a one-cell border all round, which is NumPy's
@@ -35,8 +36,9 @@ class CArray
35
36
  # @overload window(*ranges, fill_value: 0, bounds: "fill")
36
37
  # @param ranges [Array<Range, Integer>] one range per axis; the count
37
38
  # must equal `self.ndim`.
38
- # @param fill_value [Object] value given to out-of-range cells; `UNDEF`
39
- # masks them instead.
39
+ # @param fill_value [Object] value given to out-of-range cells, written
40
+ # as a store writes it (a Time for a time array; a CAString's default
41
+ # is `""`); `UNDEF` masks them instead.
40
42
  # @param bounds [String, Symbol] what an out-of-range index means; a
41
43
  # Symbol says the same as the String, matching {#windows}: `"fill"`
42
44
  # (default) uses `fill_value`, `"nearest"` clamps to the edge cell,
@@ -50,6 +52,9 @@ class CArray
50
52
  # cyclic shift).
51
53
  # @raise [ArgumentError] when `bounds` conflicts with `fill_value: UNDEF`,
52
54
  # or when `bounds` is not a recognised value.
55
+ # @raise [IndexError] when `bounds` is `"strict"` and a range reaches
56
+ # outside `self`, or `"ruby"` and a range reaches past the end or
57
+ # before the first cell counted from the end.
53
58
  def window(*ranges, fill_value: 0, bounds: "fill"); end
54
59
 
55
60
  # @!endgroup
@@ -20,7 +20,9 @@ class CArray
20
20
  # - Range — a contiguous (or stepped, via a stepped Range) sub-range.
21
21
  # - boolean CArray — masked selection along that axis.
22
22
  # - Integer CArray — fancy gather (index array); shape follows the
23
- # index array.
23
+ # index array. A masked cell of the index array selects nothing and
24
+ # keeps its place: it reads as masked, and a write to it has no cell
25
+ # to land in and is discarded.
24
26
  # - `:_` — newaxis: insert a size-1 axis at this position.
25
27
  # - `:%` — repeat / tiling sugar.
26
28
  # - `:>` — slab axis: wrap the result in a `CASlabIterator`.
@@ -45,6 +47,9 @@ class CArray
45
47
  #
46
48
  # Assignment through a slab iterator (`:>`) is not supported — use a
47
49
  # block index (e.g. `ca[range, nil] = val`) or `each_slab`.
50
+ #
51
+ # A store that raises may leave part of the destination written: its
52
+ # values and its mask after the raise are not specified.
48
53
  # @param index [Array<Integer, Range, nil, CArray, Symbol>] one entry
49
54
  # per axis (or a single flat address).
50
55
  # @param value [Object, Array, CArray] the value(s) to store, or
@@ -35,11 +35,6 @@ class CArray
35
35
  # @return [Integer]
36
36
  def ndim; end
37
37
 
38
- # @overload rank
39
- # @deprecated Use {#ndim}.
40
- # @return [Integer]
41
- def rank; end
42
-
43
38
  # @overload bytes
44
39
  # Returns the byte size of one element. Equals
45
40
  # `CArray.sizeof(data_type)` for numeric types; for fixed-length
@@ -104,6 +104,10 @@ class CArray
104
104
  # `infinity` (optional sign, any case) are kept as NaN / ±Infinity for
105
105
  # float types. A cell that is not a String goes through `Integer()` /
106
106
  # `Float()` as before.
107
+ #
108
+ # From a float or complex array to an integer type, a NaN cell (in
109
+ # either part) becomes UNDEF. A finite value outside the integer type
110
+ # is converted as the machine's C converts it.
107
111
  # @param data_type [Symbol, Integer, Class, String] target element type.
108
112
  # @param bytes [Integer, nil] element width in bytes, required for
109
113
  # `:fixlen`.
@@ -209,6 +213,10 @@ class CArray
209
213
  # target. Use {#to_type} instead for parse-with-mask (an unparseable
210
214
  # cell becomes UNDEF). The `CA_<TYPE>(obj)` construction shorthands
211
215
  # take this same non-masking path.
216
+ # From a float or complex array to an integer type, reading a NaN cell
217
+ # raises FloatDomainError, as storing NaN into an integer array does; a
218
+ # masked NaN cell (left by {#mask_invalid}) reads UNDEF. {#to_type}
219
+ # makes a NaN cell UNDEF instead.
212
220
  # A Face refuses: reinterpreting its storage would hand back the
213
221
  # bytes its surface exists to hide (the serial instead of the time, the
214
222
  # descriptor instead of the string), and no view decodes a surface. Take
@@ -220,6 +228,8 @@ class CArray
220
228
  # @return [CAFake]
221
229
  # @raise [TypeError] when `self` is a Face and the request would read its
222
230
  # storage under another type.
231
+ # @raise [FloatDomainError] when a NaN cell that is not masked is read
232
+ # into an integer type.
223
233
  def as_type(data_type, bytes: nil); end
224
234
 
225
235
  # @overload as_boolean
@@ -75,7 +75,8 @@ class CArray
75
75
  # Reads `ca_length(self)` bytes from `io` and overwrites the
76
76
  # element data of `self` in row-major order. `io` may be a
77
77
  # `String` of the exact size, or any object responding to
78
- # `read(n, buf = nil)`.
78
+ # `read(n, buf = nil)`. Into a boolean array, a non-zero byte is
79
+ # read as 1 (true).
79
80
  # @param io [String, IO, #read]
80
81
  # @return [self]
81
82
  # @raise [RuntimeError] on a short read.
@@ -69,12 +69,14 @@ class CArray
69
69
  # @overload template { value }
70
70
  # With a 0-arity block, fills every element of the result with
71
71
  # the block's return value (broadcast). Equivalent to
72
- # `template.tap { |t| t[] = value }`.
72
+ # `template.tap { |t| t[] = value }`. On a Face the value is
73
+ # stored as a store writes it (a String for a CAString).
73
74
  # @yieldreturn [Object] value to broadcast to every element.
74
75
  # @return [CArray]
75
76
  # @overload template { |*idx| ... }
76
77
  # With a block of arity > 0, calls the block once per cell with
77
- # the multi-dimensional index `*idx` and stores the result.
78
+ # the multi-dimensional index `*idx` and stores the result, as a
79
+ # store writes it.
78
80
  # @yieldparam idx [Array<Integer>] per-axis indices.
79
81
  # @yieldreturn [Object]
80
82
  # @return [CArray]
@@ -46,10 +46,10 @@ class CArray
46
46
  # (`2`, `1.0`, `nil`, …) raises `TypeError`.
47
47
  # - `self` is numeric, `v` is scalar: `v` must be numeric (true /
48
48
  # false are rejected -- they are the boolean array's domain).
49
- # On an integer array a Float `v`, or a `v` array of a wider
50
- # type, is compared in the type the two share, as {#eq} compares:
51
- # `CA_INT32([1, 2]).count(1.5)` is `0`. An Integer `v` that does
52
- # not fit the array raises `RangeError`.
49
+ # `v` (a scalar or an array) is compared in the type it and the
50
+ # array match in, as {#eq} compares: `CA_INT32([1, 2]).count(1.5)`
51
+ # is `0`, `CA_UINT8([255]).count(-1)` is `0` (-1 is not 255), and an
52
+ # Integer the array's type cannot hold is counted `0` times.
53
53
  # - `self.data_type == :object`, `v` is scalar: cells equal to `v`
54
54
  # by Ruby `==`, so `count(1)` and `count(1.0)` agree, and `true` /
55
55
  # `false` / `nil` are ordinary values to count.
@@ -40,9 +40,10 @@ class CArray
40
40
  # @overload elem_fetch(idx)
41
41
  # Returns the value at position `idx` (cast back to the
42
42
  # appropriate Ruby type). Returns `UNDEF` if the cell is masked.
43
- # Returns `nil` if `self.empty?`.
43
+ # A negative index counts from the end.
44
44
  # @param idx [Integer, Array<Integer>]
45
- # @return [Object, nil]
45
+ # @return [Object]
46
+ # @raise [IndexError] if `idx` names no cell, an empty array included.
46
47
  def elem_fetch(idx); end
47
48
 
48
49
  # @overload elem_incr(idx)
@@ -67,7 +68,10 @@ class CArray
67
68
  # @param idx [Integer, Array<Integer>]
68
69
  # @param v [Numeric]
69
70
  # @return [self]
70
- # @raise [CArray::DataTypeError] for non-numeric `data_type`.
71
+ # @raise [CArray::DataTypeError] for a non-numeric or complex
72
+ # `data_type` (complex values have no order).
73
+ # @raise [RangeError] when `v` wins but does not fit the type, as
74
+ # `self[idx] = v` raises.
71
75
  def elem_min(idx, v); end
72
76
 
73
77
  # @overload elem_max(idx, v)
@@ -77,7 +81,9 @@ class CArray
77
81
  # @param idx [Integer, Array<Integer>]
78
82
  # @param v [Numeric]
79
83
  # @return [self]
80
- # @raise [CArray::DataTypeError] for non-numeric `data_type`.
84
+ # @raise [CArray::DataTypeError] for a non-numeric or complex
85
+ # `data_type`.
86
+ # @raise [RangeError] when `v` wins but does not fit the type.
81
87
  def elem_max(idx, v); end
82
88
 
83
89
  # @!endgroup
@@ -8,8 +8,12 @@ class CArray
8
8
  # @overload where
9
9
  # Returns a fresh 1-D CArray of flat addresses where `self` is
10
10
  # non-zero (or `true`). Masked positions are excluded.
11
- # Non-boolean arrays are first coerced to boolean.
11
+ # An integer, float or complex array selects its non-zero cells
12
+ # (NaN is non-zero). Any other array that is not boolean is cast to
13
+ # boolean, which takes only `0`, `1`, `true` and `false`.
12
14
  # @return [CArray] 1-D, `:int64` (`ca_size_t`).
15
+ # @raise [CArray::DataTypeError, TypeError] if an array that is not
16
+ # boolean, integer, float or complex cannot be cast to boolean.
13
17
  # @example
14
18
  # CArray.int32(5) { [0, 1, 0, 2, 0] }.where.to_a
15
19
  # # => [1, 3]
@@ -32,8 +36,15 @@ class CArray
32
36
  # Fills `self` in row-major order with `init_val`, `init_val +
33
37
  # step`, `init_val + 2*step`, ...
34
38
  #
39
+ # On an integer array an `init_val` or `step` that is not an Integer
40
+ # (a Float, a Rational) makes each cell `init_val + step*i` cut
41
+ # toward zero, as a store takes a float: `int32.seq(0.5, 0.5)` is
42
+ # `[0, 1, 1, 2]`. The first and last values must fit the type.
43
+ #
35
44
  # For `:object` arrays only, `step` may be a Symbol naming the
36
45
  # stepping method to invoke on the previous element (e.g. `:succ`).
46
+ # A CAString takes only a run of Strings (`s.seq!("a", :succ)`), as a
47
+ # store into it does.
37
48
  # @param init_val [Numeric, Object]
38
49
  # @param step [Numeric, Symbol]
39
50
  # @return [self]
@@ -49,6 +60,15 @@ class CArray
49
60
  # @return [self]
50
61
  # @raise [ArgumentError] if `axis` is out of range or not a single
51
62
  # integer.
63
+ # @raise [RangeError] for an integer array, if `init_val` does not fit
64
+ # its type, or an Integer `step` is larger than the type can hold. An
65
+ # unsigned array takes a negative step (a descending run). A run of
66
+ # Integers that passes the end of the type wraps, as integer
67
+ # arithmetic does; a real run (a Float or Rational argument) whose
68
+ # first or last value does not fit raises.
69
+ # @raise [FloatDomainError] for an integer array given a NaN argument.
70
+ # @raise [TypeError] if `init_val` or `step` is not a number (a Symbol
71
+ # `step` on an `:object` array excepted).
52
72
  def seq!(*); end
53
73
 
54
74
  # @overload seq(init_val = 0, step = 1, axis: nil)