carray 3.0.0 → 3.0.2

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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +845 -3
  4. data/{NEWS.md → CHANGELOG.v1.md} +1 -1
  5. data/README.md +33 -33
  6. data/carray.gemspec +12 -8
  7. data/ext/ca_axis_dispatch.c +33 -4
  8. data/ext/ca_axis_group.c +256 -114
  9. data/ext/ca_binop_dispatch.c +5 -6
  10. data/ext/ca_binop_dispatch.h +0 -7
  11. data/ext/ca_categorical_iterator.c +121 -67
  12. data/ext/ca_for_each_element.h +6 -8
  13. data/ext/ca_group_iter.c +1 -1
  14. data/ext/ca_kernel_iterator.c +386 -74
  15. data/ext/ca_kernel_iterator.h +145 -45
  16. data/ext/ca_obj_array.c +62 -20
  17. data/ext/ca_obj_bincmp.c +57 -27
  18. data/ext/ca_obj_binop.c +86 -30
  19. data/ext/ca_obj_block.c +4 -4
  20. data/ext/ca_obj_const_string.c +88 -27
  21. data/ext/ca_obj_face.c +104 -0
  22. data/ext/ca_obj_face.h +48 -1
  23. data/ext/ca_obj_fake.c +11 -0
  24. data/ext/ca_obj_fixlen_string.c +21 -6
  25. data/ext/ca_obj_grid.c +11 -3
  26. data/ext/ca_obj_meld.c +125 -22
  27. data/ext/ca_obj_moncmp.c +33 -14
  28. data/ext/ca_obj_monop.c +19 -7
  29. data/ext/ca_obj_object.c +107 -4
  30. data/ext/ca_obj_record.c +3 -1
  31. data/ext/ca_obj_refer.c +30 -6
  32. data/ext/ca_obj_roll.c +13 -5
  33. data/ext/ca_obj_select.c +49 -34
  34. data/ext/ca_obj_select_axis.c +16 -6
  35. data/ext/ca_obj_shift.c +3 -3
  36. data/ext/ca_obj_stack.c +3 -8
  37. data/ext/ca_obj_stride.c +178 -24
  38. data/ext/ca_obj_string.c +11 -5
  39. data/ext/ca_obj_tile.c +13 -5
  40. data/ext/ca_obj_time.c +3 -1
  41. data/ext/ca_obj_timedelta.c +3 -1
  42. data/ext/ca_obj_transpose.c +1 -1
  43. data/ext/ca_obj_triop.c +62 -23
  44. data/ext/ca_obj_window.c +94 -19
  45. data/ext/ca_op_cmplx64.h +123 -0
  46. data/ext/ca_op_ipower.c +1 -5
  47. data/ext/ca_rng_normal.h +42 -0
  48. data/ext/ca_rng_xoshiro256pp.h +105 -0
  49. data/ext/ca_sort_kernels.h +5 -5
  50. data/ext/ca_sweep_engine.c +362 -155
  51. data/ext/ca_sweep_engine.h +39 -9
  52. data/ext/ca_transform_common.c +7 -0
  53. data/ext/carray.h +148 -132
  54. data/ext/carray_access.c +83 -49
  55. data/ext/carray_address_basis.c +590 -0
  56. data/ext/carray_bincount.c +8 -8
  57. data/ext/carray_broadcast.c +102 -9
  58. data/ext/carray_build_flags.h +3 -0
  59. data/ext/carray_call_cfunc.c +2010 -335
  60. data/ext/carray_call_cfunc.h +153 -0
  61. data/ext/carray_cast.c +184 -72
  62. data/ext/carray_conversion.c +22 -22
  63. data/ext/carray_copy.c +67 -32
  64. data/ext/carray_core.c +140 -13
  65. data/ext/carray_count.c +10 -11
  66. data/ext/carray_element.c +7 -7
  67. data/ext/carray_factorize.c +72 -51
  68. data/ext/carray_hold.c +1 -1
  69. data/ext/carray_index_classifier.c +7 -21
  70. data/ext/carray_internal.h +36 -3
  71. data/ext/carray_kernels_bincmp.c +1 -0
  72. data/ext/carray_kernels_binop.c +320 -298
  73. data/ext/carray_kernels_init.c +1095 -0
  74. data/ext/carray_kernels_map.c +1 -0
  75. data/ext/carray_kernels_moncmp.c +1 -0
  76. data/ext/carray_kernels_monop.c +412 -411
  77. data/ext/carray_kernels_reduce_aggregate.c +256 -87
  78. data/ext/carray_kernels_reduce_boolean.c +1 -0
  79. data/ext/carray_kernels_reduce_cumulative.c +324 -54
  80. data/ext/carray_kernels_reduce_extreme.c +605 -58
  81. data/ext/carray_kernels_reduce_variance.c +1 -0
  82. data/ext/carray_kernels_scan.c +5 -4
  83. data/ext/carray_kernels_search.c +187 -106
  84. data/ext/carray_kernels_sort.c +1 -0
  85. data/ext/carray_kernels_triop.c +1 -0
  86. data/ext/carray_lazy.c +175 -5
  87. data/ext/carray_loop.c +7 -1
  88. data/ext/carray_mask.c +29 -13
  89. data/ext/carray_median_percentile.c +56 -1
  90. data/ext/carray_memory_view.c +46 -21
  91. data/ext/carray_operator.c +50 -53
  92. data/ext/carray_order.c +1 -1
  93. data/ext/carray_partition.c +4 -4
  94. data/ext/carray_random.c +391 -47
  95. data/ext/carray_scatter.c +1 -1
  96. data/ext/carray_slab.c +13 -0
  97. data/ext/carray_sort.c +22 -24
  98. data/ext/carray_sort_kernel.c +10 -10
  99. data/ext/carray_test.c +1 -1
  100. data/ext/extconf.rb +18 -0
  101. data/ext/mk_call_cfunc.rb +323 -145
  102. data/ext/mkkernel.rb +651 -129
  103. data/ext/ruby_carray.c +28 -17
  104. data/ext/version.h +4 -4
  105. data/lib/carray/autoload_carray.rb +24 -3
  106. data/lib/carray/autoload_method_extension.rb +14 -1
  107. data/lib/carray/axis_group.rb +84 -7
  108. data/lib/carray/basics.rb +63 -59
  109. data/lib/carray/bincount_nd.rb +12 -12
  110. data/lib/carray/block_iterator.rb +111 -25
  111. data/lib/carray/categorical.rb +154 -37
  112. data/lib/carray/categorical_iterator.rb +300 -151
  113. data/lib/carray/conditional.rb +14 -14
  114. data/lib/carray/const_string.rb +131 -27
  115. data/lib/carray/construct.rb +61 -0
  116. data/lib/carray/core_extensions.rb +62 -5
  117. data/lib/carray/data_type_extension.rb +21 -1
  118. data/lib/carray/data_type_limits.rb +91 -0
  119. data/lib/carray/fixlen_string.rb +1 -1
  120. data/lib/carray/frame/concat.rb +3 -3
  121. data/lib/carray/frame/convert.rb +1 -1
  122. data/lib/carray/frame/csv_parser.rb +11 -4
  123. data/lib/carray/frame/frame.rb +85 -21
  124. data/lib/carray/frame/group.rb +36 -3
  125. data/lib/carray/frame/io.rb +224 -15
  126. data/lib/carray/frame/records.rb +18 -4
  127. data/lib/carray/frame/sort.rb +1 -1
  128. data/lib/carray/frame/verbs.rb +32 -12
  129. data/lib/carray/fuse_source.rb +123 -0
  130. data/lib/carray/fusion.rb +218 -0
  131. data/lib/carray/histogram.rb +16 -16
  132. data/lib/carray/inspect.rb +43 -16
  133. data/lib/carray/iterator.rb +147 -3
  134. data/lib/carray/lazy.rb +88 -73
  135. data/lib/carray/mask_gap_fill.rb +3 -1
  136. data/lib/carray/meld_reduce.rb +2 -2
  137. data/lib/carray/methods/align_addr.rb +1 -1
  138. data/lib/carray/methods/composition.rb +1 -1
  139. data/lib/carray/methods/discovery_along.rb +74 -0
  140. data/lib/carray/methods/factorize.rb +50 -0
  141. data/lib/carray/methods/is_in.rb +25 -14
  142. data/lib/carray/methods/locate_addr.rb +81 -3
  143. data/lib/carray/methods/mask_duplicates.rb +36 -2
  144. data/lib/carray/methods/meshgrid.rb +4 -5
  145. data/lib/carray/methods/mode.rb +2 -2
  146. data/lib/carray/methods/nunique.rb +23 -2
  147. data/lib/carray/methods/repeat.rb +110 -0
  148. data/lib/carray/methods/snap.rb +7 -2
  149. data/lib/carray/methods/unique.rb +44 -4
  150. data/lib/carray/methods/value_counts.rb +2 -2
  151. data/lib/carray/rng.rb +86 -0
  152. data/lib/carray/runtime.rb +0 -19
  153. data/lib/carray/slab_iterator.rb +77 -19
  154. data/lib/carray/string_operation_extension.rb +10 -6
  155. data/lib/carray/time.rb +906 -491
  156. data/lib/carray/window_iterator.rb +421 -27
  157. data/lib/carray.rb +7 -5
  158. data/yard-stubs/ca_obj_array.rb +385 -0
  159. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  160. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  161. data/yard-stubs/ca_obj_block.rb +68 -0
  162. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  163. data/yard-stubs/ca_obj_fake.rb +31 -0
  164. data/yard-stubs/ca_obj_farray.rb +32 -0
  165. data/yard-stubs/ca_obj_field.rb +45 -0
  166. data/yard-stubs/ca_obj_grid.rb +35 -0
  167. data/yard-stubs/ca_obj_refer.rb +72 -0
  168. data/yard-stubs/ca_obj_roll.rb +45 -0
  169. data/yard-stubs/ca_obj_shift.rb +43 -0
  170. data/yard-stubs/ca_obj_stride.rb +181 -0
  171. data/yard-stubs/ca_obj_tile.rb +29 -0
  172. data/yard-stubs/ca_obj_transpose.rb +40 -0
  173. data/yard-stubs/ca_obj_window.rb +57 -0
  174. data/yard-stubs/carray_access.rb +131 -0
  175. data/yard-stubs/carray_attribute.rb +246 -0
  176. data/yard-stubs/carray_broadcast.rb +37 -0
  177. data/yard-stubs/carray_cast.rb +489 -0
  178. data/yard-stubs/carray_class.rb +65 -0
  179. data/yard-stubs/carray_conversion.rb +76 -0
  180. data/yard-stubs/carray_copy.rb +79 -0
  181. data/yard-stubs/carray_core.rb +34 -0
  182. data/yard-stubs/carray_count.rb +84 -0
  183. data/yard-stubs/carray_element.rb +108 -0
  184. data/yard-stubs/carray_generate.rb +66 -0
  185. data/yard-stubs/carray_lazy.rb +228 -0
  186. data/yard-stubs/carray_loop.rb +140 -0
  187. data/yard-stubs/carray_mask.rb +259 -0
  188. data/yard-stubs/carray_math.rb +1615 -0
  189. data/yard-stubs/carray_mathfunc.rb +45 -0
  190. data/yard-stubs/carray_median_percentile.rb +103 -0
  191. data/yard-stubs/carray_memory_view.rb +163 -0
  192. data/yard-stubs/carray_order.rb +252 -0
  193. data/yard-stubs/carray_random.rb +89 -0
  194. data/yard-stubs/carray_scatter.rb +106 -0
  195. data/yard-stubs/carray_slab.rb +59 -0
  196. data/yard-stubs/carray_sort.rb +165 -0
  197. data/yard-stubs/carray_test.rb +85 -0
  198. data/yard-stubs/carray_undef.rb +64 -0
  199. data/yard-stubs/carray_utils.rb +97 -0
  200. data/yard-stubs/ruby_carray.rb +193 -0
  201. metadata +67 -10
  202. data/ext/ca_obj_unbound_repeat.c +0 -496
data/CHANGELOG.md CHANGED
@@ -1,9 +1,851 @@
1
1
  # Changelog
2
2
 
3
- Releases from 3.0.0 onward are recorded here. For the pre-3.0 history
4
- (1.4.x through 2.0.1) see [NEWS.md](NEWS.md).
3
+ Releases from 3.0.0 onward are recorded here. There is no separate NEWS
4
+ file: this is where to look for what changed between the version you have
5
+ and a newer one. The 1.x history, up to the 2.0.0 release, is in
6
+ [CHANGELOG.v1.md](CHANGELOG.v1.md); 2.0.1 went unrecorded.
5
7
 
6
- ## 3.0.0 — 2026-08-25
8
+ <!-- Newest first, at both levels: a new release section goes above the
9
+ ones below it, and a new entry goes directly under its own release
10
+ heading -- not at the end of the section. The kind of change is
11
+ carried by the `- Fix:` / `- Change:` / `- New:` that opens the
12
+ entry; there are no per-kind subheadings.
13
+
14
+ A section is written newest-first while the release is open, and
15
+ sorted into New, Change, Fix when it closes -- in the same commit
16
+ that drops `(unreleased)`. Within Change, the ones that ask the
17
+ reader to change code come first. It is a reading order rather than
18
+ a classification: where it is not obvious, either place will do.
19
+
20
+ An entry says three things and stops: what changed, what to do about
21
+ it (the migration, the replacement, the condition under which nothing
22
+ changes), and what is excluded. It does not say how the code was
23
+ broken, name the internals that were fixed, break down where the
24
+ speed came from, or argue the design -- those belong in the commit
25
+ message. Two to six lines.
26
+
27
+ It is written for someone using the library, not someone working on
28
+ it: with no NEWS file, this is what a reader consults before
29
+ upgrading. An entry naming something only a C extension touches says
30
+ so in its opening words.
31
+
32
+ Every entry has to read on its own. Entries are looked at one at a
33
+ time and move about within a section, so none may lean on a
34
+ neighbour ("as well", "the producer above") or leave unnamed the
35
+ method, class or keyword it is about. -->
36
+
37
+ ## 3.0.2
38
+
39
+ - New: `CArray::AddressBasis`, for a C extension whose code addresses cells
40
+ itself rather than being handed them — a kernel generated from an
41
+ expression, which writes its own loop. `open` lends a pointer and one byte
42
+ stride per axis for the length of a block, and closes what it opened even
43
+ when the block raises; `classify` reports how an array would be opened
44
+ without opening it. It is a runtime facility at the `ca_attach` layer, not
45
+ a user API: what it lends is a raw machine address, so it is described in
46
+ the developer's guide rather than in the user documentation.
47
+
48
+ - New: a `CAString` column can be searched, not only sorted: `bsearch`,
49
+ `bsearch_addr`, `search` and `count(v)` answer where they used to raise
50
+ `ArgumentError`. A cell of one is the Ruby String it shows, so a String query
51
+ compares against it directly, with nothing to reconcile. Sorting, which
52
+ already worked, is unchanged, and so is `CAConstString`, which answers
53
+ `search` / `count(v)` natively and still has no `bsearch`.
54
+
55
+ - New: `count(v)` counts an object or fixlen array, which used to raise
56
+ `CArray::DataTypeError`. An object array compares by Ruby `==`, so
57
+ `count(1)` and `count(1.0)` agree, and `true` / `false` / `nil` are values to
58
+ count rather than the boolean array's `true` / `false`. A fixlen array
59
+ compares the whole cell by `memcmp`, with a short String query padded out to
60
+ the cell width -- so a 4-byte cell holding `"a\0\0\0"` is counted by
61
+ `count("a")`.
62
+
63
+ - New: `CArray.empty(data_type, dim, bytes: nil)` allocates without the zero
64
+ fill, for an array whose every cell is written before anything reads it. One
65
+ existing call changes: `CArray.empty(3, [4])` raised `TypeError` in 3.0.1
66
+ and now matches `CArray.new(3, [4])`.
67
+
68
+ - New: `CAFrame.from_csv` reads an open IO as well as a path, so CSV already
69
+ in memory need not go through a temporary file first. A String argument is
70
+ still always a path, never CSV text.
71
+
72
+ - New: `inspect_full` renders an array the way `inspect` does but without the
73
+ `...` abbreviation.
74
+
75
+ - New: `repeat` lays each element of an array down several times --
76
+ `v.repeat(2)`, or `v.repeat([3, 1, 2])` for a count each. It is not `tile`,
77
+ which lays the whole array down again. The result is a view.
78
+
79
+ - New: `unique`, `nunique` and `mask_duplicates` take `along: k`, comparing
80
+ whole sub-arrays instead of cells -- `z.unique(along: 0)` gives the distinct
81
+ rows of a 2-D array. Giving both `along:` and `axis:` raises.
82
+
83
+ - New: each numeric data type names its own limits on its class --
84
+ `CArray::Int32::MIN` / `MAX`, and `TINY` / `EPSILON` for float and complex
85
+ types. `MIN` is the bottom of the range, where Ruby's `Float::MIN` is what
86
+ is called `TINY` here.
87
+
88
+ - New: `CArray::Rng` is a random number generator with its own state, which
89
+ `random!`, `randomn!` and `shuffle!` accept as `rng:` alongside a Ruby
90
+ `Random`. Without `rng:`, or with a Ruby `Random`, nothing changes.
91
+
92
+ - New: `CArray#factorize` answers `[codes, levels]` in one pass, for a caller
93
+ who wants the codes as storage rather than the `CACategorical` that
94
+ `categorize` builds from the same two.
95
+
96
+ - New: C extensions only. `CA_FOR_EACH_FIBER_PAIR` and
97
+ `CA_FOR_EACH_FIBER_PAIR_MASKED` yield one contiguous fiber from each of two
98
+ sources at the same position.
99
+
100
+ - Change: `CArray.meld` (and `CAMeld.new`, and so `CAFrame.meld`) now treats a
101
+ homogeneous list of Faces the way `CArray.stack` does: a Face whose state is
102
+ per-parent is refused, and one that can be carried is kept on the result.
103
+ `CAConstString` and `CAString` now raise `ArgumentError` — weld the storage
104
+ instead, with `.parent`, or use `CArray.concatenate`. `CAFixlenString`,
105
+ `CATime` and `CATimedelta` come back as themselves rather than as the raw
106
+ storage; melding pieces whose Face state differs, such as two `CATime`
107
+ columns in different units, now raises rather than welding the ticks. Lists
108
+ of plain arrays, and a list of one, are unaffected.
109
+
110
+ - Change: `CAConstString.wrap` now checks the `(start, end)` pairs it is given
111
+ against the buffer, and takes ownership of the offsets entity by marking it
112
+ read-only. A pair outside the buffer raises `ArgumentError` naming the
113
+ element; masked cells are exempt, since their bytes may be anything. Code
114
+ that built a column with well-formed offsets is unaffected, except that the
115
+ entity it passed can no longer be written afterwards — pass `.copy` to keep
116
+ a mutable one. This also means the storage behind an existing column
117
+ (`column.parent[i] = ...`) now raises rather than silently rewriting a column
118
+ that reports itself read-only.
119
+
120
+ - Change: `all` and `any` on an `axis_group` reduction now require a boolean
121
+ payload, as `CArray#all` / `#any` and the other iterators do. They folded any
122
+ numeric payload, counting a non-zero cell as true, so `data.all` refused and
123
+ `data[g].all(axis: :group)` answered for the same float array. Convert first
124
+ if you meant the old reading: `data.ne(0)[g].all(axis: :group)`.
125
+
126
+ - Change: the `axis:` reductions on `group_by_category` now read the source
127
+ array when asked, rather than some of them answering from a result kept
128
+ from an earlier call. `sum`, `mean`, `min`, `max` and the counts shared a
129
+ kept result per axis while `prod`, the variance family and `wsum` / `wmean`
130
+ did not, so after a write through the source one iterator could report a
131
+ mean and a variance that no data can produce together. Reading several
132
+ members off one iterator now costs one kernel run each instead of one
133
+ shared run; keep the result if you want the old sharing. The no-axis
134
+ reductions are unchanged: they still work from the copy taken when the
135
+ iterator was built.
136
+
137
+ - Change: `CAFrame#at(UNDEF)` now raises `ArgumentError` instead of returning a
138
+ row. An index can hold a masked cell -- an `:outer` / `:right` join and
139
+ `align` both produce one -- but a row with no label cannot be identified by
140
+ one, and two undefined labels are not the same label; the key matching behind
141
+ `join` and `align` already treats a masked key as matching nothing. Use
142
+ `df.filter { |f| f.index.is_masked }` for the rows with no label, which also
143
+ handles more than one of them. Asking for a real label whose cell is masked
144
+ still raises `KeyError`, unchanged.
145
+
146
+ - Change: functions built on the C-extension bridge (`ca_call_cfunc_*`,
147
+ `ca_call_cslab_*`, and `CAMath.spherical_to_xyz` / `xyz_to_spherical`)
148
+ pair two array operands only when their shapes agree, and otherwise
149
+ raise `ArgumentError` naming both shapes. Arrays of the same size but
150
+ different shape, such as (2,3) and (3,2), used to be accepted and read
151
+ in flat order; reshape one of them first. Arrays of different sizes
152
+ raised `RuntimeError` before, so a `rescue` of that class needs
153
+ updating. A scalar still pairs with any array.
154
+
155
+ - Change: `min`, `max`, `minmax`, `cummin` and `cummax` answer `NaN`, and
156
+ `min_index`, `max_index`, `min_addr` and `max_addr` answer `UNDEF`, when every
157
+ cell a float array contributes is `NaN`. They used to answer `Infinity`,
158
+ `-Infinity`, the interval `[Infinity, -Infinity]` and position `0`. A `NaN`
159
+ still loses to any number, so an array holding at least one number answers as
160
+ before, as does one holding only real infinities. Empty and all-masked still
161
+ answer `UNDEF`, and integer, boolean, fixlen and object arrays are unchanged
162
+ -- an object array already answered `NaN`. To have `NaN` counted as missing
163
+ rather than skipped, call `mask_invalid` first; `min_count:` and `fill_value:`
164
+ act on masked cells and do not reach `NaN` ones.
165
+
166
+ - Change: the `CAConstString` ordering family takes `axis:` -- and `kind:` /
167
+ `masked_position:` / `keep_axis:` where CArray does -- across `min`, `max`,
168
+ `minmax`, `min_index`, `max_index`, `sort`, `sort_copy`, `sort_addr`,
169
+ `sort_index`, `rank_index`, `order`, `partition_copy` and `partition_index`.
170
+ Three answers move to CArray's: `sort_index` gives per-fiber indices where it
171
+ gave view-flat addresses (ask `sort_addr` for those); `sort` with no `axis:`
172
+ flattens first, where it kept the shape (a 1-D column is unaffected); and
173
+ `min` / `max` on an empty or wholly masked column give UNDEF, not nil.
174
+
175
+ - Change: `CABlock#count` and `CAWindow#count` are gone. They gave back the
176
+ per-axis number of cells the view exposes -- which is what `shape` answers
177
+ -- and in doing so hid `CArray#count` on the two classes an indexing
178
+ expression lands on most: `a[2...8].count(true)` raised `ArgumentError`,
179
+ and `a[2...8].count` gave a shape rather than a population. Read the
180
+ geometry with `shape`. `size0` / `start` / `step` / `offset`, which say
181
+ where the view sits in its parent, are unchanged.
182
+
183
+ - Change: `CArray.jit_for`, `CArray.jit_each` and `CArray.jit_map` are no
184
+ longer defined here; they arrive with `require "carray/jit"`. Without it a
185
+ call raises `NoMethodError` where 3.0.1 raised `NotImplementedError`, so
186
+ code that rescued that to fall back asks `CArray.respond_to?(:jit_each)`.
187
+
188
+ - Change: the Ruby attach surface is gone from released builds:
189
+ `CArray.attach` / `.attach!`, `CArray#attach` / `#attach!`, and
190
+ `#__attach__` / `#__sync__` / `#__detach__`. Write through the array
191
+ directly instead. `CArray#attached?` and the C lifecycle are unchanged.
192
+
193
+ - Change: `a[1, :_]` returns a view of the axes `:_` asked for instead of
194
+ raising `IndexError`. To keep an axis rather than drop it, index it with
195
+ something that is not a scalar -- `a[[1], :_]`.
196
+
197
+ - Change: C extensions only. A kernel iterator init the engine refuses now
198
+ raises instead of returning a code the block macros discarded. To handle a
199
+ refusal rather than propagate it, call `ca_iter_state_init_l1` / `_l2`
200
+ directly and read the code.
201
+
202
+ - Change: `CACategorical.from_codes` now materialises `codes` when it is a
203
+ view rather than an array of its own, so writing through the array the view
204
+ was taken from no longer changes the categorical underneath it. A wrapped
205
+ memory view is an array of its own and is still adopted without a copy, so a
206
+ zero-copy import stays zero-copy. The array you pass is never marked
207
+ read-only beyond what you handed over.
208
+
209
+ - Change: `CACategorical.from_codes` now checks what it is handed and
210
+ normalises it. It raises `ArgumentError` for duplicate labels, for more
211
+ labels than the codes data type can carry once its top value is reserved as
212
+ the exclusion sentinel, and for an unmasked code outside `0...labels.size`
213
+ that is not the sentinel. A cell that arrives masked also gets the sentinel
214
+ written into its code byte, so the mask and the byte now agree for every
215
+ reader, a byte-reinterpret export included. Codes built by `categorize`
216
+ already satisfy all of this, so nothing changes for a categorical made that
217
+ way.
218
+
219
+ - Change: `search_nearest` and `search_nearest_addr` work on an object array of
220
+ numbers, and say why when they cannot. They measured only with `#distance`,
221
+ and since `Numeric#distance` became an opt-in refinement -- which a C-level
222
+ call does not see -- that raised `NoMethodError` for an Integer as readily as
223
+ for a String. A number is now measured as `(query - cell).abs`, exactly for
224
+ Rational and BigDecimal; an object defining a real `#distance` still uses it;
225
+ anything else raises `CArray::DataTypeError` naming the query's class, and
226
+ points at `search` / `bsearch` for an exact match. Numeric arrays are
227
+ unaffected.
228
+
229
+ - Change: `CAFrame.from_csv` reads a missing field as UNDEF in every column,
230
+ not only in one named by `types:`. An unquoted empty field, and a cell a
231
+ short row never reached, used to arrive as a Ruby `nil` sitting in an
232
+ uncast column, so a mask written by `to_csv` did not survive the trip back.
233
+ A quoted empty field (`""`) is still the empty string, which is a value.
234
+ Code that worked around this with `col[:eq, nil] = UNDEF` can drop the line.
235
+
236
+ - Change: `CArray.time` reads a string array about eight times faster with an
237
+ explicit `format:`, and about three times faster letting it auto-detect --
238
+ so `CAFrame#parse_to_time`, which calls it, speeds up by the same amount.
239
+ Parsed values are unchanged.
240
+
241
+ - Change: `window` accepts `bounds:` as a Symbol as well as a String, which is
242
+ the spelling `windows` already took. Strings keep working.
243
+
244
+ - Change: C extensions only. A partial fill of an array backed by a CAObject
245
+ or CASource subclass takes the `fill_block` / `fill_addrs` slots where the
246
+ subclass defines them, instead of one `store_addr` per cell. Which cells are
247
+ written is unchanged, and a subclass defining no fill slot keeps the
248
+ per-cell path.
249
+
250
+ - Fix: `is_in`, `count(v)`, the set operations, `locate_addr`, `search`,
251
+ `bsearch` and `linear_section` no longer compare a Face operand by its
252
+ storage when that storage is not the value it shows. Passing a
253
+ `CAConstString` (whose cells are byte ranges) to one of these on another
254
+ Face used to answer from the byte ranges: where the two cell widths
255
+ coincided — a `CAConstString` cell is 16 bytes, and so is a
256
+ `CAFixlenString` cell whose column is 16 bytes wide — you got a wrong
257
+ answer with no error, and a set operation could return raw offset bytes as
258
+ its values. Such an operand now raises `ArgumentError`; convert it first,
259
+ with `#to_string` for a string Face, or pass `.parent` on both sides to work
260
+ in storage space. Plain operands, and Faces whose cells are their values
261
+ (`CAString`, `CAFixlenString`), are unaffected, as is the cross-unit
262
+ reconciliation `CATime` does through `to_comparable`.
263
+
264
+ - Fix: the reductions without `axis:` on `group_by_category` now agree with one
265
+ another about which values they are reducing. `cumsum` and the other scans
266
+ read the array when called while every other member worked from the copy
267
+ taken when the iterator was built, so a write through the source between two
268
+ calls was visible to one and not the other. All of them now answer about the
269
+ values as they were when the iterator was built; build a new iterator to pick
270
+ up a write. The `axis:` reductions read the array when called, unchanged.
271
+
272
+ - Fix: `CArray.load_from_file` no longer exhausts the stack. It was
273
+ registered for autoload but defined nowhere, so calling it recursed until
274
+ Ruby gave up; it now raises `NoMethodError` like any other method that does
275
+ not exist. Use `CArray.load`, which is unchanged. An autoload registration
276
+ whose library defines no such method now says which method and which
277
+ library, rather than recursing.
278
+
279
+ - Fix: a `group_by_category` reduction over a read-only Face — a
280
+ `CAConstString` column — no longer fails with an `IndexError` about a buffer
281
+ range. Such a Face cannot be built by writing into it, so `min`, `max` and
282
+ the other value members hand back the surface values (the strings) rather
283
+ than the Face. A writable Face such as `CATime` still answers in its Face.
284
+
285
+ - Fix: a group iterator from `axis_group` now answers `shape`, `ndim` and
286
+ `dim`, which every other iterator answers and which it returned `nil` for,
287
+ and its `count` takes the two forms the family declares: `count(UNDEF)` for
288
+ masked cells and `count(v)` for cells equal to `v`. Both previously raised
289
+ `ArgumentError` about the number of arguments.
290
+
291
+ - Fix: a `group_by_category` reduction over values that carry a Face (a
292
+ `CATime` column, say) now answers in that Face, as `CArray`'s own reduction
293
+ does: `min`, `max` and `median` come back as a `CATime` of elements rather
294
+ than failing with an internal message about a zero width. A member the core
295
+ does not define for that Face still refuses, in the core's own words.
296
+
297
+ - Fix: the band-only classifier shape for a `group_by_category(axis:)`
298
+ reduction is now reachable on a two-dimensional source, where it was
299
+ refused and the refusal listed it among the accepted forms. Where both the
300
+ case A shape and the band-only shape fit, which a square source allows,
301
+ case A is taken, as before. The refusal no longer names `sum` when another
302
+ reduction was the one called.
303
+
304
+ - Fix: `count(axis:)` and `count_not_masked(axis:)` on `group_by_category`
305
+ now work for a complex, boolean or object payload. They counted cells
306
+ through a numeric-only kernel, which refused those payloads for an answer
307
+ that never depended on the payload. The no-axis form already worked.
308
+
309
+ - Fix: a `group_by_category` iterator whose classifier does not line up
310
+ cell-for-cell with the value now says so. A no-axis reduction on one raises
311
+ `ArgumentError` naming the mismatch and pointing at the `axis:` form, rather
312
+ than a `NoMethodError` about `nil`; `elements` raises the same instead of
313
+ answering `nil`; and `inspect` says "per-fiber only" instead of printing an
314
+ empty grouping. `accumulate(axis:)`, which failed outright on such an
315
+ iterator, now works.
316
+
317
+ - Fix: a reduction from `group_by_category` now hands back an array of the
318
+ caller's own. `min`, `max`, `minmax`, `count`, `count_not_masked`,
319
+ `elements`, `min_index` and `max_index` returned the iterator's memo itself,
320
+ so writing into a result changed what that iterator answered from then on,
321
+ and changed it for the other members reading the same memo. `sum` already
322
+ copied. Nothing to change in calling code unless you relied on writing
323
+ through a result.
324
+
325
+ - Fix: an `axis_group` reduction or scan that raises part-way through no
326
+ longer leaks the working memory it had taken. An object-valued scan
327
+ (`cumsum`, `cummax` and the rest) calls back into Ruby for every cell, so a
328
+ value that will not coerce or a `<=>` that answers `nil` raises from an
329
+ ordinary call and used to leave roughly 36 bytes per source element behind
330
+ each time. Nothing to change in calling code.
331
+
332
+ - Fix: `CAFrame#set_index` on a frame that already has an index no longer
333
+ discards it. The index being replaced now goes back to being a column, the
334
+ same demotion `reset_index` performs and in the same position, so re-indexing
335
+ keeps every column and `set_index("b")` on a frame indexed by `"a"` is the
336
+ same as `reset_index` followed by `set_index("b")`. Previously the column the
337
+ old index had been made from was gone, with nothing said.
338
+
339
+ - Fix: `CAFrame#reset_index` now restores the row axis name the frame had
340
+ before `set_index` promoted a column over it, so the two are each other's
341
+ inverse as documented. It used to leave `"row"`, which is user-visible: the
342
+ row axis name is the header of the index's column in `to_csv` and its key in
343
+ a row `Hash`. A frame built with an index, or derived from one, never had an
344
+ earlier name, so `reset_index` still leaves the default there.
345
+
346
+ - Fix: `min` and `max` on an `axis_group` reduction now answer in the source
347
+ array's data type, as `CArray#min` / `#max` do, instead of float64 -- an
348
+ int64 beyond the float mantissa came back rounded. A boolean array answers
349
+ as its 0/1 numeric storage (`all` / `any` are the boolean-returning twins).
350
+ A group holding nothing but `NaN` now answers `NaN` rather than the
351
+ accumulator's infinity, and `min_addr` / `max_addr` answer UNDEF for it,
352
+ since no cell won.
353
+
354
+ - Fix: an `axis_group` reduction over a grouping whose group axis has length
355
+ zero now answers each output cell the way a group with no member is answered
356
+ -- `sum` 0, `prod` 1, `count` 0, `all` true, `any` false, and UNDEF for
357
+ `mean`, `min`, `max`, `min_addr`, `max_addr` and the variance family. It
358
+ previously returned an unmasked 0 for all of them, so a mean and a variance
359
+ both read as 0.0. A zero-length band axis, which reduces to no cells at all,
360
+ is unchanged.
361
+
362
+ - Fix: a per-category `min`, `max`, `min_index`, `max_index`, `median`,
363
+ `percentile` or `quantile` from `group_by_category` now treats `NaN` the way
364
+ `CArray`'s own reduction does: a `NaN` loses every contest, a category
365
+ holding nothing but `NaN` answers `NaN` for an extremum and UNDEF for a
366
+ position, and an order statistic sorts `NaN` last. Before, the answer
367
+ depended on where in the category the `NaN` sat, so the same values in a
368
+ different row order gave different results. Nothing to change in calling
369
+ code.
370
+
371
+ - Fix: `p` / `inspect` on a `CAFrame` whose only data is its index now shows the
372
+ table. It printed the summary line alone, because it gated on the column set
373
+ while the table itself counts the index as a column.
374
+
375
+ - Fix: `CAFrame` no longer reports a row count that nothing in the frame backs.
376
+ Splicing a frame that has no columns into another that has neither columns nor
377
+ an index left the target claiming the spliced frame's row count, while its own
378
+ `copy`, `head` and `filter` all answered 0 and it would then accept only
379
+ columns of that length. The count is now read off a column, or off the index
380
+ when there are no columns. Nothing to change in calling code.
381
+
382
+ - Fix: `CAFrame`'s `df[rows] = UNDEF` now refuses a row outside the frame on a
383
+ frame with no columns, as the read and delete forms already did. It used to
384
+ return quietly, because the bound check came from the column indexer the
385
+ selector was handed to and there was no column to hand it to. Masking a row
386
+ that does exist on such a frame is still a no-op -- there are no data cells,
387
+ and the index is left alone by design.
388
+
389
+ - Fix: `CAFrame`'s `to_table` (and so `p` / `puts` / `to_s`) now prints a masked
390
+ element inside an N-D cell as `_`, the marker it already used for a masked
391
+ scalar cell, instead of the literal `UNDEF`. Nothing to change in calling
392
+ code.
393
+
394
+ - Fix: `CAFrame#group_by` with a composite key no longer makes a group of its
395
+ own for rows whose key has a masked component. Such a row now forms no group,
396
+ which is what a single masked key cell already did. Nothing to change in
397
+ calling code unless you relied on the UNDEF-labelled group.
398
+
399
+ - Fix: two `CAFrame` verbs that change every column now decide before changing
400
+ any, so a column that refuses no longer leaves the frame half-changed in an
401
+ order that depends on how the columns were inserted. `df[sel] = UNDEF` on a
402
+ frame holding a read-only column (a categorical) raises without masking
403
+ anything, and `promote(type)` raises without casting anything when some
404
+ column would narrow. Nothing to change in calling code.
405
+
406
+ - Fix: `CAFrame.from_records` now reads a `nil` cell back as UNDEF in every
407
+ column, not only in one a numeric cast happens to convert. A string, boolean,
408
+ object or N-D column used to keep the `nil` as a value, so a mask written by
409
+ `to_records` did not survive the trip and a row with no index label came back
410
+ labelled `nil`. Note that the data type is still rebuilt from the values, so
411
+ an integer column returns as `int64` and a boolean column as an object
412
+ column; `cast` afterwards if the exact type matters.
413
+
414
+ - Fix: a CSV written from a frame with a single column -- or with only an
415
+ index -- now reads back with all of its rows. A masked cell is written as an
416
+ empty field, which for a one-column row is a line with nothing on it, and the
417
+ reader skipped it as a blank line. Blank lines in a file with more than one
418
+ column are still skipped, as a row there always carries a separator. Nothing
419
+ to change in calling code.
420
+
421
+ - Fix: linear gap-fill on an **integer** array no longer fills the cells
422
+ outside the interpolable span with `0` and drops their mask. It now leaves
423
+ them masked, as it already did for a float array and as the documentation
424
+ says. This covers `unmask(method: :linear)` and `strip_mask(method: :linear)`
425
+ as well as `CAFrame#fill(name, :linear)`, with or without a frame index.
426
+ Nothing to change in calling code.
427
+
428
+ - Fix: on a frame grouped by a numeric column, `CAFrame`'s `mean`, `sum`, `min`
429
+ and `max` shortcuts now work. They raised `axis_name "..." collides with a
430
+ column of the same name`, because the key column was reduced into the result
431
+ while also being its index; a key only stayed out of the way when its data
432
+ type was one a reduction skips anyway -- a string, boolean, categorical or
433
+ time column. A composite numeric key no longer returns its key columns as
434
+ reduced columns either, so it gives the same column set a composite string
435
+ key gives. `aggregate` and `table` were never affected. Nothing to change in
436
+ calling code.
437
+
438
+ - Fix: `CAFrame#filter(keep_masked: true)` no longer hands back a frame whose
439
+ index writes through to the original. Its columns were already independent,
440
+ so writing the result's index changed the original while writing its columns
441
+ did not. The result is now materialized throughout -- columns and index --
442
+ whether or not the selector actually carries a masked cell, so the same call
443
+ site no longer switches between sharing and copying depending on the data.
444
+ Code that wants a frame sharing storage with the original should use plain
445
+ `filter`, which is still a view-frame.
446
+
447
+ - Fix: reductions, scans and order statistics no longer leak memory when
448
+ reading their source raises part way through -- for example
449
+ `cumsum`, `sum` or `median` over a float64 view of an object array
450
+ holding a cell that is not a number. Each call used to leave the
451
+ slab the walk was gathering into behind. Nothing to change in calling
452
+ code.
453
+
454
+ - Fix: `a[sel]` no longer leaks memory when reading the boolean selector
455
+ raises -- for example `fake(CA_BOOLEAN)` over an int32 array holding a
456
+ 2. Nothing to change in calling code.
457
+
458
+ - Fix: a view that converts on read -- for example `fake(CA_BOOLEAN)` over
459
+ an int32 array holding a 2 -- now raises every time it is read, where
460
+ the second read used to succeed silently and return values from a
461
+ half-converted buffer. A view stacked or melded over such an array no
462
+ longer leaves its other parents attached when the read raises, and a
463
+ reshape of a lazy view no longer leaks its buffer. Nothing to change in
464
+ calling code.
465
+
466
+ - Fix: for C extensions, a callback passed to `ca_call_cfunc_*` or
467
+ `ca_call_cslab_*` may now `rb_raise` to refuse a value: the bridge
468
+ detaches and frees what it holds before the exception propagates,
469
+ where it used to leave an output view attached and its scratch memory
470
+ behind. The outputs are left partly written. Nothing to change in
471
+ calling code.
472
+
473
+ - Fix: when `to_type` on a view raises part way through the cast -- for
474
+ example an int32 value other than 0 or 1 cast to boolean -- the view is
475
+ no longer left holding a stale copy of its parent, which made later
476
+ reads through it return the old values. Nothing to change in calling
477
+ code.
478
+
479
+ - Fix: `copy` and `strip_mask(fill)` no longer leak the result's memory
480
+ when reading the source raises part way through -- for example a
481
+ float64 view of an object array holding a cell that is not a number.
482
+ Nothing to change in calling code.
483
+
484
+ - Fix: functions built on the C-extension bridge (`ca_call_cfunc_*`,
485
+ `ca_call_cslab_*`, the `CA_FOR_EACH_ELEMENT` macros, and
486
+ `CAMath.lgamma` and its siblings) no longer leak memory, or leave an
487
+ output view attached, when reading an operand raises part way through
488
+ -- for example a float64 view of an object array holding a cell that is
489
+ not a number. Nothing to change in calling code.
490
+
491
+ - Fix: `is_in`, `intersection`, `difference` and `union` take an Array or Range
492
+ of Strings against a fixlen array, where every such call raised
493
+ `CArray::DataTypeError` -- `CAFixlenString` included. The set is built at the
494
+ array's cell width, so a short String matches a padded cell the way it does
495
+ everywhere else. A set given as a CArray must still be of that width; when it
496
+ is not, the refusal now says which width was wanted instead of reporting a
497
+ data type mismatch between two fixlen arrays.
498
+
499
+ - Fix: comparing a fixlen array against a String compares it as a value of
500
+ that array's cell width. The String became an object operand, so `eq` / `ne` /
501
+ `lt` / `gt` / `ge` / `le` and the `[:eq, v]` indexer ran `String#==` per cell
502
+ against the cell's NUL-padded text -- and an array pads a short String on
503
+ write, so `a[i] = "be"` then `a.eq("be")` was false, `a.gt("be")` was true,
504
+ and the scan took 30x longer than the same one in `search`. Two fixlen
505
+ arrays of different widths compare as before, as does a Regexp for `match`.
506
+ `CAFixlenString` was never affected.
507
+
508
+ - Fix: `percentile` and `median` no longer interpolate between objects that
509
+ have no arithmetic. On a column of Strings `percentile(30)` quietly answered
510
+ `""` and even-length `median` raised `NoMethodError` from inside a funcall;
511
+ both now raise `CArray::DataTypeError` naming `method: :lower` / `:higher` /
512
+ `:nearest`, which pick an element and work. A `p` that lands exactly on an
513
+ element (`percentile(50)` of five) still answers, as does an odd-length
514
+ `median`. Numbers stored as objects -- Integer, Rational, BigDecimal -- are
515
+ unaffected.
516
+
517
+ - Fix: `sort_copy` takes whatever `sort` takes. It refused everything its own
518
+ fast path could not handle, so an object or boolean array sorted through
519
+ `sort` and raised `CArray::DataTypeError` through `sort_copy`; complex, which
520
+ neither can order, refused differently depending on which one was asked, and
521
+ now refuses alike. Numeric arrays keep the fast path and are unchanged.
522
+
523
+ - Fix: `CAConstString#sort_addr`, `#sort_index`, `#rank_index`, `#order`,
524
+ `#min_index`, `#max_index`, `#partition_copy` and `#partition_index` read the
525
+ strings. They read the `(start, end)` offsets that hold them, which order by
526
+ how the column was packed, so they gave well-formed wrong answers rather than
527
+ raising: `sort_addr` on an unsorted column gave the identity, and
528
+ `partition_copy` gave NUL bytes. `#minmax` answers instead of raising, and
529
+ sorting a column with masked cells no longer raises.
530
+
531
+ - Fix: `to_const_string` gives an N-D source back with its shape instead of
532
+ flattened, and `CAConstString#unique` / `#mode` / `#mask_duplicates` /
533
+ `#intersection` / `#difference` / `#union` keep the column's encoding --
534
+ on a column that was not UTF-8 they raised out of the builder's check.
535
+
536
+ - Fix: `each_with_index` and `map_with_index!` no longer raise
537
+ `SystemStackError` on a long array, and neither do `CArray#format` /
538
+ `CArray.format`, which are built on them. The ceiling was the C stack, so
539
+ where it fell depended on where the code ran: around a million cells on the
540
+ main thread, under a hundred thousand inside a `Thread`.
541
+
542
+ - Fix: `CArray.concatenate` and `CArray.mosaic` take a zero-length piece --
543
+ an empty slice such as `a[0...0]`, or `CArray.int32(0)` -- instead of
544
+ raising `IndexError`. The piece contributes nothing and the remaining ones
545
+ concatenate as before. `CArray#paste` likewise accepts a source covering no
546
+ cell, and writes nothing.
547
+
548
+ - Fix: C extensions only. A kernel writing into a view the caller supplied now
549
+ reaches the array; writes were lost, or crashed, for several view kinds
550
+ iterated along an axis whose fiber is not contiguous. Kernels writing into
551
+ an array they allocated themselves were never affected.
552
+
553
+ - Fix: `CArray#each_slab` yields a read-only slab, and writing through it
554
+ raises rather than reaching the array on one axis and being dropped on
555
+ another. Return values from the block instead, or assign through the array.
556
+
557
+ ## 3.0.1
558
+
559
+ - New: `CArray.jit_for`, `CArray.jit_each` and `CArray.jit_map` name a block
560
+ that is compiled rather than run. Compiling needs the carray-jit gem;
561
+ without it they raise `NotImplementedError`. An expression over whole
562
+ arrays wants `CArray.fuse`, which needs no compiler.
563
+
564
+ - New: every iterator answers `accumulate` beside `sum`; some of them did not.
565
+ It is the same fold kept in the source's own data type, where `sum` answers
566
+ in the type the core promotes to -- a window or tile count over `uint8` cells
567
+ stays one byte wide.
568
+
569
+ - New: `CArray::CoreExtensions` adds postfix math on `Complex`, so `a[0].tanh`
570
+ reads the way `a.tanh` does. It covers the seventeen functions a complex
571
+ array supports and agrees with the array form exactly, branch cuts and the
572
+ sign of a zero included. Opt in with `using CArray::CoreExtensions`.
573
+
574
+ - New: `ca_is_stride_family(ca)` in `carray.h`, for a C extension that folds a
575
+ view into `root->ptr + base + sum(idx[k] * strides[k])` itself. True for
576
+ CAStride, CARefer, CABlock, CARepeat, CATranspose, CAFarray and CAField and
577
+ the mask array of each, and for an externally installed view that shares
578
+ their operation table.
579
+
580
+ - New: `divmod` returns `[quotient, remainder]` element-wise with the quotient
581
+ floored, the pair Ruby's `Integer#divmod` and `Float#divmod` return. The
582
+ quotient keeps the receiver's data type.
583
+
584
+ - New: two optional CAObject callbacks take a partial fill as a region instead
585
+ of one `store_addr` per cell: `fill_block(starts, counts, steps, val)` for a
586
+ forward per-axis sub-region, `fill_addrs(addrs, val)` otherwise. Defining
587
+ neither keeps the old behaviour. Filling a 1000x1000 region of a 2000x2000
588
+ CAObject: 116 ms to 0.6 ms.
589
+
590
+ - New: `CAFrame#to_table` renders the frame as an aligned text table, and
591
+ `inspect` / `to_s` sit on it: `p df` summarises the first 8 and last 2 rows,
592
+ `puts df` prints the whole frame. `rows:` caps the printed rows, `precision:`
593
+ rounds float cells for display (default 6), masked cells show as `_`.
594
+
595
+ - New: `CAFrame#to_time` takes a `CATime::Grid`, positionally or as `unit:`, so
596
+ a netCDF `units` attribute goes in whole. The grid also carries an epoch phase
597
+ the `unit:` / `epoch:` pair cannot -- that pair reads the epoch on the coarse
598
+ grid and loses the time of day. The keyword form is unchanged.
599
+
600
+ - New: `CATime::Grid` packages the `(unit:, origin:)` pair that `#timesteps`,
601
+ `#snap` and `.from_timesteps` take, so it is built once and passed as one
602
+ value: `t.snap(g, direction: :floor)`, `t.timesteps(g)`, `g.at(k)`. It parses
603
+ and prints the udunits `"<unit> since <instant>"` form, holding a phased
604
+ origin that the keyword pair cannot (`"12 hours since 2017-11-30 09:00"`).
605
+ `CATime#grid` is the grid an array is stored on. Storage is unchanged.
606
+
607
+ - New: `CATime#snap(grid, direction:)` rounds to a tick grid, the shape
608
+ `CArray#snap` has for numbers. `#floor` / `#ceil` / `#round` are its
609
+ fixed-direction forms; their results and keyword forms are unchanged.
610
+
611
+ - New: a time element is taken directly as a start or origin literal, so a
612
+ `floor` / `ceil` / `to_unit` answer feeds back into `CArray.time`,
613
+ `time_range`, `time_series`, `CArray#time` and an `origin:`; it used to have
614
+ to go out through `DateTime` and be re-parsed. A `:M` or `:Y` element names
615
+ the first midnight of its granule. What an origin must satisfy is unchanged.
616
+
617
+ - New: `CArray.time` parses a year-month (`"2019-09"`) and a bare year
618
+ (`"2019"`), so the form a `:M` / `:Y` element prints reads back in. A
619
+ missing finer field names the head of that period.
620
+
621
+ - Change: `abs`, `abs2` and `arg` on a `cmplx64` array now return `float32`
622
+ instead of `float64` -- the width that type carries its real values in, the
623
+ same one `real` and `imag` already returned. `arg` on a `float32` array
624
+ likewise returns `float32` rather than widening. `cmplx128` and `float64`
625
+ are unchanged, and `arg` on an integer array still gives `float64`. To keep
626
+ the old width, add `.to_type(:float64)`.
627
+
628
+ - Change: `CArray.fuse` takes the expression rather than the arrays it is
629
+ over -- `CArray.fuse { (a + b) * c }` in place of
630
+ `CArray.fuse(a, b, c) { |x, y, z| (x + y) * z }`. What comes back is the
631
+ expression, so `x = CArray.fuse { ... }` now wants `.to_ca` for an array.
632
+ Where the block's source cannot be read -- an `irb` prompt, inside `eval` --
633
+ write `a.lazy + b.lazy`. `CArray.lazy(*args) { ... }` is gone.
634
+
635
+ - Change: a lazy expression (`a.lazy + b`, `CArray.fuse`) builds its mask when
636
+ something reads it, rather than when the expression is built. A mask set on
637
+ either operand after the expression was built is now seen; before, only the
638
+ left one was. `root_array` and `ancestors` now stop at a lazy operation
639
+ instead of walking into its left operand. Building a long masked expression
640
+ is also no longer quadratic in the length of the chain.
641
+
642
+ - Change: the top-level constant `CA_NIL` is now `CArray::UNSPECIFIED`
643
+ (`CA_UNSPECIFIED` in C). It is an internal sentinel for "the caller gave no
644
+ argument", not a value to pass in; `unmask`, `shift(fill_value:)` and
645
+ `window(fill_value:)` behave as before.
646
+
647
+ - Change: `group_by_category` reductions answer in the data type the core
648
+ reduction promotes the value to, instead of one chosen per reduction. `sum`
649
+ on an integer value now answers in float64 (`accumulate` is the spelling
650
+ that stays in the value's type), and `mean`, `variance` and `median` on an
651
+ object value stay exact. `sum` on a boolean value and `prod` or `mean` on a
652
+ complex one, which raised, now work.
653
+
654
+ - Change: the `:*` unbound repeat is retired. `a[:*, nil]` raises `IndexError`;
655
+ `CArray#unbound_repeat`, `CAUnboundRepeat` and `insert_axis(repeat: :*)` are
656
+ gone. Use `:_`, which gives the same shape and now stretches on a store as
657
+ well as in an operation. `CArray#broadcast_to` and
658
+ `CArray.meshgrid(sparse: true)` are unaffected.
659
+
660
+ - Change: a binary operation requires shapes to agree, or to differ only in
661
+ size-1 axes at equal ndim. `(3,2) + (2,3)` and `(3,2) + (6)` raise
662
+ `ArgumentError` where they used to answer in one operand's shape; flatten
663
+ both sides to combine the values in the order they lie. Comparisons, `fma`
664
+ and the lazy forms follow the same rule. Scalars are unaffected, but a
665
+ one-element 1-D array such as `CArray.int32(1)` counts as a shape.
666
+
667
+ - Change: an assignment requires the shapes to match. `t[] = src` with a
668
+ differently shaped source raises `RuntimeError`; use `src.flatten`. In
669
+ exchange a smaller source is repeated to fit, so `t[] = row[:_, nil, nil]`
670
+ and `t[] = col` (shape `(n,1)`) work where they used to raise. A 1-D side on
671
+ either end still passes, as do shapes differing only in size-1 axes; Ruby
672
+ Arrays and scalars are unchanged.
673
+
674
+ - Change: `to_ca` on a view derived from a lazy marker returns a new entity
675
+ rather than the view itself -- `a.lazy.shift(1, 0).to_ca`, inside a `fuse`
676
+ block or not. `copy` behaves as before.
677
+
678
+ - Change: `/` and `%` follow Ruby instead of C. Integer division floors and the
679
+ remainder carries the sign of the divisor; float `%` floors too, float `/` is
680
+ unchanged. For the old behaviour use `fmod`, which now takes integers as well
681
+ as floats.
682
+
683
+ - Change: `reminder` is removed -- it was IEEE 754 remainder on floats but C `%`
684
+ on integers. Use `fmod` for the truncated remainder. The IEEE form has no
685
+ replacement.
686
+
687
+ - Change: the result-type override of `CArray#conditional` and
688
+ `CArray.select` is now `data_type:`, spelled the way the rest of the
689
+ library spells it. There is no alias: `dtype:` raises `unknown keyword`.
690
+
691
+ - Change: a calendar-grid `origin:` given to the bucket-grid methods of a
692
+ `CATime` array -- `timesteps`, `snap`, `floor`, `ceil`, `round`,
693
+ `is_righttime` -- now has to be a month head (the 1st at 00:00); it used to
694
+ drop the day and time silently. A `:Y` tick likewise has to start in
695
+ January. `from_timesteps` already refused an off-grid origin.
696
+
697
+ - Change: the MemoryView producer emits the format vocabulary
698
+ `ruby/memory_view.h` specifies rather than PEP 3118's, so below 32 bits it
699
+ writes `c` / `C` / `s` / `S` in place of `b` / `B` / `h` / `H`. From 32 bits
700
+ up the two already agreed, and `?`, `Zf` / `Zd`, `T{...}` and `Ns` stay PEP
701
+ 3118, which Ruby has no spelling for. The consumer side already accepted
702
+ both, so views produced by 3.0.0 still import.
703
+
704
+ - Change: multiplying and dividing a `cmplx64` array (`*`, `/`, `rcp`,
705
+ `rcp_mul`) is computed in double and rounded once, so both are correctly
706
+ rounded; the old route could be off by about 1800 units in the last place.
707
+ A product that overflows a float but not a double no longer comes back NaN.
708
+ Division is 4.2-5.3x faster and multiplication 1.3x slower. `+`, `-` and
709
+ every `cmplx128` operation are unchanged.
710
+
711
+ - Change: element-wise math on a `float32` or `cmplx64` array is computed at
712
+ that width rather than widened and rounded back, so `sqrt`, `exp`, `log`,
713
+ the trigonometric and hyperbolic families, `atan2`, `hypot`, `abs` and `arg`
714
+ are 1.1-3.5x faster there and can move by a bit or two in the last place.
715
+ Complex `log`, `power`, `exp2` and `exp10` are unchanged, as are the
716
+ rounding, min / max, comparison and variance families and the wider types.
717
+
718
+ - Change: a rolling `sum`, `mean`, `prod`, `min`, `max`, `all` or `any` --
719
+ `a.windows(-1..1).sum` and the like -- over a window up to five cells wide on
720
+ every axis is 2-5x faster, and 1.3-4x over a masked source; `min_count:` and
721
+ `fill_value:` come along. `min`, `max` and `prod` answer exactly as before;
722
+ `sum` and `mean` may differ in the last bits. A wider window, or any other
723
+ reduction, is unchanged.
724
+
725
+ - Change: `CATime#to_unit` floors to a coarser grid instead of raising, and
726
+ crosses the calendar / fixed-length boundary (`:M` <-> `:D`) through
727
+ civil-date algebra. `:Y` / `:M` -> `:W` still raises.
728
+
729
+ - Change: `CATimedelta#to_unit` truncates toward zero instead of raising on a
730
+ coarser target. Crossing the calendar boundary still raises.
731
+
732
+ - Fix: `nextafter` on a `float32` array returned its own input. The step was
733
+ taken in double, and the next double above a float rounds back to that same
734
+ float. It now steps by one float32 ulp.
735
+
736
+ - Fix: a lazy expression over an object array (`CArray.object`,
737
+ `CA_OBJECT`) returned wrong values, and crashed when materialised
738
+ repeatedly. Affects `to_ca` and `copy` on a lazy view, an eager operation
739
+ with a lazy operand, and a reduction over one. Other data types were never
740
+ affected.
741
+
742
+ - Fix: reading a concatenated view (`CArray.concat`, `CAFrame.concat`)
743
+ backwards along the concatenated axis -- `m.reverse` and any other negative
744
+ step -- raised IndexError instead of answering.
745
+
746
+ - Fix: reading a lazy expression (`a.lazy + b`, `CArray.fuse`) with a step
747
+ other than one -- `expr[[0, 3, 2]]`, `expr.reverse` and the like -- returned
748
+ values the expression cannot produce, because its operands were read from
749
+ the wrong cells. Comparisons carried the same fault, `a.lazy > b` and
750
+ one-operand ones such as `a.lazy.is_nan` alike. Contiguous reads were never
751
+ affected.
752
+
753
+ - Fix: `a.value + b` raised `can not create mask array for the value array`
754
+ when `b` carried a mask. It now propagates `b`'s mask.
755
+
756
+ - Fix: a rolling window that does not cover the cell it is centred on --
757
+ `windows(1..2)`, the two cells after this one -- was read as if it started at
758
+ the array's edge, so every answer came out shifted by the range's start, and
759
+ `bounds: :truncate` produced anchors whose window did not fit. `windows(1..2)`
760
+ on `[1..8]` now sums to `[5, 7, 9, 11, 13, 15, 8, 0]`. A window that covers
761
+ its anchor, which is every centred one, is unaffected.
762
+
763
+ - Fix: storing a `Complex` into a `cmplx64` or `cmplx128` array kept the sign
764
+ of a negative zero real part only when the imaginary part was also negative,
765
+ so `Complex(-0.0, 0.0)` came back as `0.0+0.0i`. The sign of a zero picks the
766
+ side of a branch cut, so a value stored this way could be carried to the
767
+ wrong branch. All four sign combinations now round-trip, through element
768
+ assignment and through `to_type`.
769
+
770
+ - Fix: `sinh`, `cosh`, `tanh`, `asinh`, `acosh` and `atanh` on a complex array
771
+ gave the hyperbolic function of the real part alone: `cmplx128` and
772
+ `cmplx64` arrays came back with `tanh(Re z)` where `ctanh(z)` was meant,
773
+ wrong in both parts, and `acosh` and `atanh` also returned 0 or infinity
774
+ where the true value is finite. They now agree with C99 `complex.h`. Real
775
+ and object arrays were never affected.
776
+
777
+ - Fix: a C extension reading a view in column-major order -- axes and steps
778
+ reversed against the view's own -- got wrong values from a view with a
779
+ length-1 axis, such as `v[nil, :_]`, `v.reshape(n, 1)` or a one-column slice:
780
+ the first cell repeated, a read out of bounds, or a hang.
781
+ `carray-linalg-accelerate`'s `solve(a, b)` returned `b[0]` repeated for a
782
+ single-column right-hand side.
783
+
784
+ - Fix: an operation between two views of an array that computes its values --
785
+ a lazy expression such as `(a.lazy + b)`, or a `CAObject` over a file -- no
786
+ longer reads that source one cell at a time. `+`, `fma` and comparisons on
787
+ such a pair now run at the speed of copying each operand first, so the
788
+ `.copy` that worked around it is no longer needed. Single-operand
789
+ operations, copies, region transfers and reductions were already unaffected.
790
+
791
+ - Fix: views built inside a `CArray.fuse` block stay part of the expression
792
+ instead of dropping out of it, so a stencil written the natural way is fused.
793
+ `[]`, `shift`, `roll`, `flip`/`reverse`, `transpose`/`T`, `reshape`,
794
+ `flatten`, `window`, `diagonal`, `tile` and `refer` keep the chain;
795
+ `unbound_repeat` and `[:*, ...]` deliberately do not.
796
+
797
+ - Fix: a window or shift over a view parent no longer copies that parent in
798
+ full on every transfer. `a[nil, nil].shift(1, 0)` and friends now read at
799
+ parity with an entity parent and write several times faster.
800
+
801
+ - Fix: reductions over a bare `.lazy` marker no longer raise. Per-axis forms
802
+ and anything over a masked array failed, so `a.lazy.sum(axis: 0)` raised
803
+ while `a.lazy.sum` worked.
804
+
805
+ - Fix: `ca_test_flag` / `ca_set_flag` / `ca_unset_flag` in `carray.h`, which
806
+ only a C extension calls, did not parenthesise their flag argument, so
807
+ testing two flags at once was true for every array.
808
+
809
+ - Fix: a Face no longer hands back its storage bytes through the type casts.
810
+ `as_type`, `fake` and `CArray.wrap_writable` raise; `CArray.wrap_readonly`
811
+ converts as `to_type` does, which also makes `t.eq(o)` and `o.eq(t)` agree.
812
+ Reach the storage explicitly with `t.parent.fake(...)`. Numeric Faces are
813
+ unaffected.
814
+
815
+ - Fix: `arange` raised `NoMethodError` in every form; it builds the array now.
816
+ Integer arguments count exactly, so a step dividing the span evenly no longer
817
+ picks up an extra element. A zero step or a wrong argument count raises
818
+ `ArgumentError`.
819
+
820
+ - Fix: `from_timesteps` on a week grid answered Thursdays. The week grid counts
821
+ from the epoch, which is one, so it cannot hold an ISO Monday head; a week
822
+ bucket now answers on the day grid and round-trips against `floor` cell for
823
+ cell. A day-or-finer array keeps the Monday default, a week-stored array its
824
+ own epoch-anchored ticks.
825
+
826
+ - Fix: a masked cell decided whether a `CATime` conversion fit. The range
827
+ guards took their extremes with the mask stripped, so `to_unit` raised
828
+ `RangeError` over a wide value that was masked out. They now answer UNDEF
829
+ when there is nothing to bound -- an empty array, or every cell masked.
830
+
831
+ - Fix: `CATimedelta::Element#/` floored a negative duration, so `-30h / 4`
832
+ answered `-8h` where the array form answered `-7h`. A duration is a
833
+ magnitude, so it shrinks toward zero, matching the array form in all four
834
+ sign combinations.
835
+
836
+ - Fix: `CArray.time` no longer rolls a field that is out of range over into
837
+ another date. `"2019-02-31"` parsed to 2019-03-03, and `"201909"` -- a valid
838
+ YYMMDD to Ruby, 2020-19-09 -- to 2021-07; both now raise.
839
+
840
+ - Fix: `CATime#to_unit` and `CATimedelta#to_unit` were wrong between two
841
+ resolutions where neither tick is a whole multiple of the other: converting
842
+ 3 hours to a `"90 minutes"` grid gave 1 unit rather than 2.
843
+
844
+ - Fix: importing a MemoryView whose mask is published as `C` or `c` no longer
845
+ fails. The check knew only PEP 3118's `B`, `b` and `?`, so a producer that
846
+ spells a byte in Ruby's format vocabulary was refused.
847
+
848
+ ## 3.0.0
7
849
 
8
850
  First public release. Earlier versions existed on RubyGems, but the library
9
851
  was developed for the author's own use; 3.0 is where it is packaged,