carray 3.0.4 → 3.0.5

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +205 -0
  3. data/carray.gemspec +1 -1
  4. data/ext/ca_array_pool.c +25 -1
  5. data/ext/ca_axis_dispatch.c +13 -20
  6. data/ext/ca_categorical_iterator.c +27 -20
  7. data/ext/ca_compare.c +4 -4
  8. data/ext/ca_composite_dispatch.c +9 -9
  9. data/ext/ca_kernel_iterator.c +6 -6
  10. data/ext/ca_kernel_iterator.h +112 -83
  11. data/ext/ca_obj_array.c +69 -19
  12. data/ext/ca_obj_bincmp.c +1 -0
  13. data/ext/ca_obj_binop.c +1 -0
  14. data/ext/ca_obj_bitarray.c +5 -1
  15. data/ext/ca_obj_bitfield.c +58 -43
  16. data/ext/ca_obj_byte_swap.c +2 -1
  17. data/ext/ca_obj_const_string.c +15 -3
  18. data/ext/ca_obj_face.c +42 -1
  19. data/ext/ca_obj_field.c +13 -3
  20. data/ext/ca_obj_grid.c +52 -5
  21. data/ext/ca_obj_monop.c +46 -10
  22. data/ext/ca_obj_object.c +64 -12
  23. data/ext/ca_obj_record.c +3 -0
  24. data/ext/ca_obj_reduce.c +4 -0
  25. data/ext/ca_obj_refer.c +18 -9
  26. data/ext/ca_obj_roll.c +26 -6
  27. data/ext/ca_obj_select.c +34 -5
  28. data/ext/ca_obj_shift.c +31 -7
  29. data/ext/ca_obj_stack.c +1 -0
  30. data/ext/ca_obj_stride.c +28 -19
  31. data/ext/ca_obj_string.c +11 -2
  32. data/ext/ca_obj_tile.c +27 -7
  33. data/ext/ca_obj_transpose.c +8 -4
  34. data/ext/ca_obj_triop.c +3 -0
  35. data/ext/ca_obj_window.c +79 -50
  36. data/ext/ca_op_ipower.c +4 -4
  37. data/ext/ca_sort_kernels.h +6 -3
  38. data/ext/carray.h +33 -0
  39. data/ext/carray_access.c +16 -3
  40. data/ext/carray_attribute.c +24 -0
  41. data/ext/carray_cast.c +243 -82
  42. data/ext/carray_cast_func.rb +56 -26
  43. data/ext/carray_conversion.c +2 -2
  44. data/ext/carray_core.c +3 -0
  45. data/ext/carray_count.c +6 -0
  46. data/ext/carray_element.c +9 -4
  47. data/ext/carray_factorize.c +20 -20
  48. data/ext/carray_generate.c +8 -2
  49. data/ext/carray_histogram.c +1 -1
  50. data/ext/carray_index_classifier.c +4 -4
  51. data/ext/carray_internal.h +12 -0
  52. data/ext/carray_kernels_bincmp.c +168 -168
  53. data/ext/carray_kernels_binop.c +900 -900
  54. data/ext/carray_kernels_init.c +4 -4
  55. data/ext/carray_kernels_moncmp.c +96 -96
  56. data/ext/carray_kernels_monop.c +1984 -1984
  57. data/ext/carray_kernels_reduce_aggregate.c +1733 -1733
  58. data/ext/carray_kernels_reduce_cumulative.c +642 -642
  59. data/ext/carray_kernels_reduce_extreme.c +1150 -1150
  60. data/ext/carray_kernels_reduce_variance.c +258 -258
  61. data/ext/carray_kernels_scan.c +128 -128
  62. data/ext/carray_kernels_search.c +340 -340
  63. data/ext/carray_kernels_sort.c +295 -304
  64. data/ext/carray_kernels_triop.c +114 -114
  65. data/ext/carray_key_windows.c +4 -0
  66. data/ext/carray_mask.c +45 -22
  67. data/ext/carray_memory_view.c +2 -2
  68. data/ext/carray_operator.c +5 -5
  69. data/ext/carray_order.c +227 -8
  70. data/ext/carray_random.c +18 -18
  71. data/ext/carray_scatter.c +24 -7
  72. data/ext/carray_slab.c +32 -9
  73. data/ext/carray_sort.c +224 -16
  74. data/ext/carray_sort_kernel.c +5 -7
  75. data/ext/carray_struct.c +35 -43
  76. data/ext/carray_time_text.c +4 -2
  77. data/ext/carray_utils.c +63 -21
  78. data/ext/mkkernel.rb +210 -115
  79. data/ext/version.h +4 -4
  80. data/lib/carray/attribute.rb +17 -4
  81. data/lib/carray/basics.rb +27 -7
  82. data/lib/carray/categorical.rb +30 -16
  83. data/lib/carray/categorical_iterator.rb +6 -6
  84. data/lib/carray/const_string.rb +10 -0
  85. data/lib/carray/data_type_extension.rb +8 -1
  86. data/lib/carray/fixlen_string.rb +41 -2
  87. data/lib/carray/frame/column_verbs.rb +4 -3
  88. data/lib/carray/frame/concat.rb +17 -3
  89. data/lib/carray/frame/frame.rb +6 -7
  90. data/lib/carray/frame/group.rb +100 -13
  91. data/lib/carray/frame/io.rb +3 -1
  92. data/lib/carray/fuse_source.rb +31 -2
  93. data/lib/carray/lazy.rb +35 -0
  94. data/lib/carray/methods/is_in.rb +8 -1
  95. data/lib/carray/segment_iterator.rb +12 -8
  96. data/lib/carray/serialize.rb +19 -2
  97. data/lib/carray/stack.rb +15 -12
  98. data/lib/carray/string.rb +8 -0
  99. data/lib/carray/string_operation_extension.rb +13 -5
  100. data/lib/carray/struct_builder.rb +5 -3
  101. data/lib/carray/time.rb +107 -68
  102. data/yard-stubs/ca_obj_transpose.rb +5 -4
  103. data/yard-stubs/carray_attribute.rb +3 -1
  104. data/yard-stubs/carray_cast.rb +2 -0
  105. data/yard-stubs/carray_conversion.rb +5 -1
  106. data/yard-stubs/carray_element.rb +7 -6
  107. data/yard-stubs/carray_mask.rb +8 -3
  108. data/yard-stubs/carray_math.rb +14 -4
  109. data/yard-stubs/carray_order.rb +100 -4
  110. data/yard-stubs/carray_scatter.rb +7 -5
  111. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2edeb545754cf31d330c667913471c1a0035c97de72dabad7e7b2afb4fe272b
4
- data.tar.gz: 8b34390fcabbb04c5e1f0926ae103052345196112ec3a6a16f244fb310df5583
3
+ metadata.gz: ccd30e8ea5b53c3a2654a86a0ab7473b3073f7feffa7db7be0e5c9aef5bc17c8
4
+ data.tar.gz: ffcd91fe93ca10b4b12dbff035ffc25533699472b364482a59bf2aa635296032
5
5
  SHA512:
6
- metadata.gz: 0f7430ff0556faf7b609673fb8cf9ef08279e226192995ea58a5dd203e5285733582c5cff0405fc753c39cad7056acd9194f55ba26ce9eb1d1a27cf1de8e37f0
7
- data.tar.gz: 48d35829402ba2949e865712d8aa89dd9d4228f0ca4abf62719d3b2d32280541d1c6b7b01dba3e2a68927a89e258e0d6b474a20fc6177415154e1ab57f2b380d
6
+ metadata.gz: 53c08f4ccf6a4c25219cef4e8f27e7de9740dc8c69ee493ff8e3631c87499cd15ed121d5f7707bd1017f77e0309dc1be2628fa3611b9b3c979e0e075008842d6
7
+ data.tar.gz: 2200b047a456603d6274d9dfaf8f93c011ce1cc1eb457c454fa7f9cdc9c75ea4b96c9bbe1dab0b37710f8b872ac5e8b630db5c0dacae12a9012b1b34d4c7f1ec
data/CHANGELOG.md CHANGED
@@ -34,6 +34,211 @@ and a newer one. The 1.x history, up to the 2.0.0 release, is in
34
34
  neighbour ("as well", "the producer above") or leave unnamed the
35
35
  method, class or keyword it is about. -->
36
36
 
37
+ ## 3.0.5
38
+
39
+ - New: `GroupedFrame#aggregate` takes a block instead of a spec. The block
40
+ is given the grouping once and returns a Hash of output name => a
41
+ CArray with one value per group, built from `g["col"]` iterators and
42
+ CArray arithmetic (`g["temp"].max - g["temp"].min`). `grp[array]`
43
+ groups any CArray with one entry per row, such as a value computed
44
+ from columns or a column with cells masked.
45
+
46
+ - New: a `CAFrame` grouped reduction takes a Hash of column name => value
47
+ as `fill_value:`, filling only the named columns. A single
48
+ `fill_value:` still goes to every column and has to suit each.
49
+
50
+ - New: `CArray.meld` brings pieces of different data types to a common
51
+ one, as `CArray.stack` does, and takes `data_type:`. `CAFrame.meld` and
52
+ a frame splice still refuse a column whose pieces differ in data type;
53
+ cast the column first, or use `concatenate`.
54
+
55
+ - New: `scatter_replace!` takes an object or fixed-length array, as
56
+ `put_nd` and `put_along_axis` do. The accumulating members
57
+ (`scatter_add!` and the others) still take numeric arrays only.
58
+
59
+ - New: `CAConstString#count` and `CACategorical#count` with no argument
60
+ count the cells that are not masked, as `CArray#count` does.
61
+
62
+ - New: `CAFixlenString` answers `minmax`, `cummax` and `cummin`, as
63
+ `CAString` does.
64
+
65
+ - New (C extensions): `carray.h` defines `ca_u_<type>` cell types and
66
+ `CA_U(type)`, of the same size and value as the plain types but with no
67
+ alignment requirement, for reading cells that need not be aligned (a
68
+ refer with a byte offset, a struct member, a wrapped buffer). The slab
69
+ macros in `ca_kernel_iterator.h` use them for their pointers.
70
+
71
+ - Change: `CACategorical#sort_addr` is renamed `group_addr`, the order of
72
+ the cells by category. `sort_addr` on a categorical now raises, as
73
+ `sort_index` does, since a categorical has no order of values.
74
+
75
+ - Change: a Face's `parent` (of a `CATime`, `CARecord`, ...) is a
76
+ read-only view of its storage. Write the storage of a time through
77
+ `CATime#ticks`.
78
+
79
+ - Change: an operator that cannot bring two data types together raises
80
+ `CArray::DataTypeError` instead of `TypeError`. `DataTypeError` is a
81
+ `TypeError`, so `rescue TypeError` still catches it.
82
+
83
+ - Change: a float cell with no integer of the target type -- an infinity,
84
+ or a value out of the type's range -- is now treated the way NaN
85
+ already was. `to_type` (`int32`, `as_int64`, ...) masks it to `UNDEF`;
86
+ storing it into an integer array raises `RangeError`
87
+ (`FloatDomainError` for an infinity). Before, it became whatever the
88
+ machine's C conversion gave, which differed between x86 and ARM.
89
+ `clip_int64` and the other `clip_<type>` methods saturate a float
90
+ source to the type's limits on every machine; integer `linspace` raises
91
+ `RangeError` for a span the type cannot hold.
92
+
93
+ - Change: a Ruby Array operand of a numeric or boolean array is read as
94
+ its cells: `a.eq([1, 5, 3])` compares cell by cell (it compared every
95
+ cell with the whole Array), and `a + [1, 2, 3]` adds (it raised).
96
+ Nested Arrays give the axes. An object array still takes an Array as
97
+ one value.
98
+
99
+ - Change: `window`'s `bounds:` is a policy named by a Symbol or String,
100
+ matched whole; an Integer and an abbreviation raise. A `fill_value:`
101
+ given with a policy that does not use it (`:nearest`, `:strict`, ...)
102
+ raises instead of being dropped.
103
+
104
+ - Change: a length, count, offset or shift given to a view
105
+ (`sliding_windows`, `block_view`, `tile`, `roll`, `shift`, `field`,
106
+ `diagonal`) must be an Integer; a Float raises `TypeError` instead of
107
+ being truncated. `transpose` reads its axes as the other axis arguments
108
+ are read: negative from the end, an Integer only.
109
+
110
+ - Change: `drop_axis` on an array whose every axis has size 1 keeps one
111
+ axis of size 1, and `reshape` with no axes raises.
112
+
113
+ - Change: in an index of one key per axis, an index array of more than
114
+ one axis on one axis raises `ArgumentError`; it was read flat. Pass
115
+ `idx.flatten` for the old reading.
116
+
117
+ - Change: `elem_fetch`, `elem_store` and the other `elem_*` methods read
118
+ an index as `[]` reads a key: a Float with no fraction is an index, a
119
+ Float with a fraction raises `IndexError`.
120
+
121
+ - Change: two string arrays of different kinds (`CAString`,
122
+ `CAFixlenString`, `CAConstString`) or of different widths compare by
123
+ their strings. A String query longer than a fixed-width cell matches
124
+ no cell in `eq`, `count`, `search`, `bsearch` and `is_in`; it was cut
125
+ to the cell's width.
126
+
127
+ - Change: `str_index` and `rindex` give `UNDEF` for a cell that does not
128
+ hold the substring; they raised for the whole array.
129
+
130
+ - Change: `bsearch` and `bsearch_addr` answer the first of several equal
131
+ cells.
132
+
133
+ - Change: `search` and `search_addr` raise `ArgumentError` for a negative
134
+ or NaN `eps`, which acted as an exact match.
135
+
136
+ - Change: `inherit_mask` and `inherit_mask_replace` raise `ArgumentError`
137
+ for a source whose number of cells differs from the receiver's (a
138
+ scalar source still applies to every cell).
139
+
140
+ - Change: a reduction's `fill_value:` that its result cannot hold raises
141
+ even when no cell needs it, and `fill` on an empty array checks its
142
+ value as an assignment does.
143
+
144
+ - Change: rounding a `:M` or `:Y` `CATime` to a calendar bucket
145
+ (`round(unit: :Y)`, `"3 months"`) measures the distance by the instant,
146
+ so it agrees with the same dates on `:D`.
147
+
148
+ - Change: a `CATime` reduction (`mean`, `median`, ...) sends a tie to the
149
+ later tick on both sides of 1970; before 1970 it went to the earlier
150
+ one.
151
+
152
+ - Change: a duration String added to or subtracted from a `CATime` is
153
+ one step of its own resolution, so `"15 minutes"` on a `"10 minutes"`
154
+ grid raises, as the same duration as a `CATimedelta` does. It was read
155
+ as fifteen one-minute steps and cut to the grid.
156
+
157
+ - Change: of `-0.0` and `0.0`, `pmax` gives `0.0` and `pmin` gives
158
+ `-0.0`, in either order and on every platform, as `maximum` and
159
+ `minimum` do. Which zero `min` and `max` answer is not specified.
160
+
161
+ - Change: a one-sided `clip` passes a NaN cell through, as a two-sided
162
+ `clip` does.
163
+
164
+ - Change: a categorical's `fill_value:` (of `shift`, `window`, `project`)
165
+ is a label; a `CAConstString` takes only `UNDEF` as a fill value.
166
+
167
+ - Change: a lazy operator with a Face operand (a categorical, a time, a
168
+ string array) answers as the eager operator does; it read the Face's
169
+ storage. `CAConstString#eq` with another array compares cell by cell.
170
+
171
+ - Change: a boolean member of a record (or `field(offset, :boolean)` of a
172
+ fixed-length array) raises `RangeError` when read as a byte that is not
173
+ 0 or 1, instead of answering differently in each method.
174
+
175
+ - Change: unmasking a cell of a `CAString` that holds no String gives an
176
+ empty String.
177
+
178
+ - Change: `swap_bytes` keeps the attributes, as `refer` and `bitfield`
179
+ do. An array with no cells has no say in the attributes a `meld`,
180
+ `stack` or `concatenate` keeps.
181
+
182
+ - Change: `dim_view` keeps a Face, as `transpose` does.
183
+
184
+ - Change: `CArray.string(n)` without a block, and `CArray.string("ab")`,
185
+ raise; `fixlen_string` and `to_fixlen_string` raise for `bytes:` below 1.
186
+ `project` raises for a keyword argument, `paste` into a frozen array
187
+ raises `FrozenError`, and `CArray.result_type` refuses a MemoryView
188
+ operand whose format carray cannot read, as the operators do.
189
+
190
+ - Change: `CArray.sort_addr` with numeric keys is two to three times as fast, and
191
+ `nlargest` / `nsmallest` read each fiber once with a work area of a
192
+ few times n.
193
+
194
+ - Fix: x86-64 Linux. Inputs that reached an integer division by zero
195
+ inside the library stopped the process with SIGFPE there (on ARM they
196
+ were refused later or gave a wrong answer): a sum over a view with a
197
+ zero-length axis, `wsum` / `wmean` with 1-D weights over a zero-length
198
+ axis, a view of a stack of empty arrays, `window` with a step of 0, and
199
+ `wrap_readonly(String, CA_FIXLEN)`. Each now answers or raises
200
+ `ArgumentError`. The extension builds with GCC 14 without warnings.
201
+
202
+ - Fix: big-endian hosts. `bitfield` read 0 and lost writes, a struct's
203
+ bit members overwrote each other, and an element-wise operation applied
204
+ in place could give a wrong cell.
205
+
206
+ - Fix: `save` and `dump` with `endian: :little` or `:big` wrote the other
207
+ byte order. `load` reads the marker, so files written so far still load
208
+ correctly.
209
+
210
+ - Fix: running out of memory inside a kernel left a view attached, lost
211
+ the keywords of the call (`a.sum(axis: 1)` returned the total), or
212
+ leaked the array being built.
213
+
214
+ - Fix: an array with 16 axes (the maximum) given to `bitarray`, `field`,
215
+ a narrowing `refer` or a `:_` index wrote past an internal array.
216
+
217
+ - Fix: a lazy chain whose step narrows the cells (`a.lazy.abs` of a
218
+ complex array) overran the heap; `seq` on an empty object array wrote
219
+ past its end.
220
+
221
+ - Fix: a CAObject's copy and sync hooks: a view the hook kept of the
222
+ array it was lent, or the lent array's mask, pointed at freed memory
223
+ after the hook returned.
224
+
225
+ - Fix: `CArray.fuse` crashed when several Ractors ran a fuse block for the
226
+ first time at once.
227
+
228
+ - Fix: a grouped `variancep` / `stddevp` matches `CArray#variancep` to the
229
+ last bit.
230
+
231
+ - Fix: `count` with an array of values honours `keep_axis:`.
232
+
233
+ - Fix: `CARecord.wrap` of a record array wraps its storage, not the
234
+ record array.
235
+
236
+ - Fix: an unnamed struct's `order_by` survives `save` / `load` and
237
+ Marshal.
238
+
239
+ - Fix: `CAFrame.from_csv` and `cast(name => :time)` read a leap second
240
+ (`23:59:60`) as `CArray.time` does, into the next minute.
241
+
37
242
  ## 3.0.4
38
243
 
39
244
  - New: `CAFrame#verify(rules)` judges every row against named rules (each
data/carray.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "carray"
3
- s.version = "3.0.4"
3
+ s.version = "3.0.5"
4
4
  s.authors = ["himotoyoshi"]
5
5
  s.email = ["himotoyoshi@users.noreply.github.com"]
6
6
  s.summary = "Multi-dimensional numerical array class for Ruby"
data/ext/ca_array_pool.c CHANGED
@@ -43,18 +43,42 @@
43
43
  ---------------------------------------------------------------------------- */
44
44
 
45
45
  #include "carray.h"
46
+ #include "carray_internal.h"
46
47
  #include <string.h>
47
48
 
48
49
  /* Allocate struct + pool in one shot, then run pool_init.
49
50
  *
50
51
  * Called by per-class C `*_new` constructors. */
52
+ typedef struct {
53
+ CArray *ca;
54
+ int8_t obj_type;
55
+ int8_t ndim;
56
+ } ca_array_alloc_arg;
57
+
58
+ static VALUE
59
+ ca_array_alloc_pool (VALUE varg)
60
+ {
61
+ ca_array_alloc_arg *a = (ca_array_alloc_arg *) varg;
62
+ ca_array_pool_alloc(a->ca, a->obj_type, a->ndim);
63
+ return Qnil;
64
+ }
65
+
51
66
  void *
52
67
  ca_array_alloc (int8_t obj_type, int8_t ndim)
53
68
  {
54
69
  ca_operation_function_t *func = &ca_func[obj_type];
55
70
  CArray *ca = xmalloc(func->struct_size);
71
+ ca_array_alloc_arg a;
56
72
  memset(ca, 0, func->struct_size);
57
- ca_array_pool_alloc(ca, obj_type, ndim);
73
+ /* The type is set now so that a constructor whose setup raises can hand
74
+ the struct to ca_free, which dispatches on it. The setup sets it
75
+ again. */
76
+ ca->obj_type = obj_type;
77
+ /* The pool is a second allocation; when it fails, the struct goes too. */
78
+ a.ca = ca;
79
+ a.obj_type = obj_type;
80
+ a.ndim = ndim;
81
+ ca_fill_or_xfree(ca, ca_array_alloc_pool, (VALUE) &a);
58
82
  return ca;
59
83
  }
60
84
 
@@ -647,15 +647,15 @@ ca_axis_dispatch_percell_gather (CArray *parent,
647
647
  break;
648
648
  case 2:
649
649
  for (j = 0; j < inner_count; j++)
650
- *(uint16_t *)(row_dst + j*2) = *(uint16_t *)(src + j*inner_step);
650
+ __builtin_memcpy(row_dst + j*2, src + j*inner_step, 2);
651
651
  break;
652
652
  case 4:
653
653
  for (j = 0; j < inner_count; j++)
654
- *(uint32_t *)(row_dst + j*4) = *(uint32_t *)(src + j*inner_step);
654
+ __builtin_memcpy(row_dst + j*4, src + j*inner_step, 4);
655
655
  break;
656
656
  case 8:
657
657
  for (j = 0; j < inner_count; j++)
658
- *(uint64_t *)(row_dst + j*8) = *(uint64_t *)(src + j*inner_step);
658
+ __builtin_memcpy(row_dst + j*8, src + j*inner_step, 8);
659
659
  break;
660
660
  default:
661
661
  for (j = 0; j < inner_count; j++)
@@ -672,18 +672,15 @@ ca_axis_dispatch_percell_gather (CArray *parent,
672
672
  break;
673
673
  case 2:
674
674
  for (j = 0; j < inner_count; j++)
675
- *(uint16_t *)(row_dst + j*2) =
676
- *(uint16_t *)(row_src_base + inner_indices[j]*inner_pstride);
675
+ __builtin_memcpy(row_dst + j*2, row_src_base + inner_indices[j]*inner_pstride, 2);
677
676
  break;
678
677
  case 4:
679
678
  for (j = 0; j < inner_count; j++)
680
- *(uint32_t *)(row_dst + j*4) =
681
- *(uint32_t *)(row_src_base + inner_indices[j]*inner_pstride);
679
+ __builtin_memcpy(row_dst + j*4, row_src_base + inner_indices[j]*inner_pstride, 4);
682
680
  break;
683
681
  case 8:
684
682
  for (j = 0; j < inner_count; j++)
685
- *(uint64_t *)(row_dst + j*8) =
686
- *(uint64_t *)(row_src_base + inner_indices[j]*inner_pstride);
683
+ __builtin_memcpy(row_dst + j*8, row_src_base + inner_indices[j]*inner_pstride, 8);
687
684
  break;
688
685
  default:
689
686
  for (j = 0; j < inner_count; j++)
@@ -761,8 +758,7 @@ ca_axis_dispatch_gather (CArray *parent,
761
758
  so clang lowers to NEON ldp/stp directly (no libc memcpy call). */
762
759
  if ( sbytes == 8 ) {
763
760
  for ( i = 0; i < n; i++ ) {
764
- *(uint64_t *)(out_buf + i*8) =
765
- *(uint64_t *)(base + indices[i] * pstride);
761
+ __builtin_memcpy(out_buf + i*8, base + indices[i] * pstride, 8);
766
762
  }
767
763
  } else if ( sbytes == 16 ) {
768
764
  for ( i = 0; i < n; i++ ) {
@@ -887,15 +883,15 @@ ca_axis_dispatch_percell_scatter (CArray *parent,
887
883
  break;
888
884
  case 2:
889
885
  for (j = 0; j < inner_count; j++)
890
- *(uint16_t *)(dst + j*inner_step) = *(uint16_t *)(row_src + j*2);
886
+ __builtin_memcpy(dst + j*inner_step, row_src + j*2, 2);
891
887
  break;
892
888
  case 4:
893
889
  for (j = 0; j < inner_count; j++)
894
- *(uint32_t *)(dst + j*inner_step) = *(uint32_t *)(row_src + j*4);
890
+ __builtin_memcpy(dst + j*inner_step, row_src + j*4, 4);
895
891
  break;
896
892
  case 8:
897
893
  for (j = 0; j < inner_count; j++)
898
- *(uint64_t *)(dst + j*inner_step) = *(uint64_t *)(row_src + j*8);
894
+ __builtin_memcpy(dst + j*inner_step, row_src + j*8, 8);
899
895
  break;
900
896
  default:
901
897
  for (j = 0; j < inner_count; j++)
@@ -912,18 +908,15 @@ ca_axis_dispatch_percell_scatter (CArray *parent,
912
908
  break;
913
909
  case 2:
914
910
  for (j = 0; j < inner_count; j++)
915
- *(uint16_t *)(row_dst_base + inner_indices[j]*inner_pstride) =
916
- *(uint16_t *)(row_src + j*2);
911
+ __builtin_memcpy(row_dst_base + inner_indices[j]*inner_pstride, row_src + j*2, 2);
917
912
  break;
918
913
  case 4:
919
914
  for (j = 0; j < inner_count; j++)
920
- *(uint32_t *)(row_dst_base + inner_indices[j]*inner_pstride) =
921
- *(uint32_t *)(row_src + j*4);
915
+ __builtin_memcpy(row_dst_base + inner_indices[j]*inner_pstride, row_src + j*4, 4);
922
916
  break;
923
917
  case 8:
924
918
  for (j = 0; j < inner_count; j++)
925
- *(uint64_t *)(row_dst_base + inner_indices[j]*inner_pstride) =
926
- *(uint64_t *)(row_src + j*8);
919
+ __builtin_memcpy(row_dst_base + inner_indices[j]*inner_pstride, row_src + j*8, 8);
927
920
  break;
928
921
  default:
929
922
  for (j = 0; j < inner_count; j++)
@@ -457,20 +457,23 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
457
457
 
458
458
  /* ---------------------------------------------------------------------------
459
459
 
460
- __reduceat_variance__ — centred two-pass SAMPLE variance (ddof=1) reduceat.
461
- The means (from the cached moments: sum/count) drive a second walk that
462
- accumulates the per-segment centred sum of squares; variance = SS / (n-1).
463
- Centred (not the one-pass sum-of-squares) so the ε-close contract holds — a
464
- one-pass formula cancels catastrophically. Matches CArray#variance per group:
465
- count 0 or 1 -> masked (the sample variance is undefined), count >= 2 ->
466
- SS / (count-1).
467
-
468
- Surface (private): grouped.__reduceat_variance__(offsets, means, counts, out)
460
+ __reduceat_variance__ — centred two-pass variance reduceat, sample (ddof=1)
461
+ or population (ddof=0). The means (from the cached moments: sum/count)
462
+ drive a second walk that accumulates the per-segment centred sum of
463
+ squares; variance = SS / (n - ddof). Centred (not the one-pass
464
+ sum-of-squares) so the ε-close contract holds — a one-pass formula cancels
465
+ catastrophically. Matches CArray#variance / #variancep per group: count
466
+ <= ddof (or 0) -> masked, otherwise SS / (count - ddof), divided once as
467
+ the core does.
468
+
469
+ Surface (private):
470
+ grouped.__reduceat_variance__(offsets, means, counts, out, ddof)
469
471
  self = grouped (numeric value data type, may carry a mask)
470
472
  offsets = int64[k] segment STARTS
471
473
  means = float64[k] per-segment mean (ignored where count < 2)
472
474
  counts = int64[k] per-segment present count
473
- out = float64[k] output: variance; count 0 or 1 masked
475
+ out = float64[k] output: variance; count <= ddof masked
476
+ ddof = 1 (sample) or 0 (population)
474
477
 
475
478
  --------------------------------------------------------------------------- */
476
479
 
@@ -479,7 +482,7 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
479
482
  const T *gp = (const T *) grouped->ptr; \
480
483
  for ( c = 0; c < k; c++ ) { \
481
484
  int64_t cnt = countp[c]; \
482
- if ( cnt < 2 ) { outp[c] = 0.0; outm[c] = 1; continue; } /* undefined */ \
485
+ if ( cnt <= ddof || cnt < 1 ) { outp[c] = 0.0; outm[c] = 1; continue; } \
483
486
  { double mean = meanp[c], ss = 0.0; \
484
487
  ca_size_t lo = (ca_size_t) offs[c]; \
485
488
  ca_size_t hi = (c + 1 < k) ? (ca_size_t) offs[c+1] : n, j; \
@@ -494,14 +497,15 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
494
497
  double d = (double) gp[j] - mean; ss += d * d; \
495
498
  } \
496
499
  } \
497
- outp[c] = ss / (double) (cnt - 1); } \
500
+ outp[c] = ss / (double) (cnt - ddof); } \
498
501
  } \
499
502
  } while (0)
500
503
 
501
504
  static VALUE
502
505
  rb_ca_reduceat_variance (VALUE self, VALUE roffsets, VALUE rmeans,
503
- VALUE rcounts, VALUE rout)
506
+ VALUE rcounts, VALUE rout, VALUE rddof)
504
507
  {
508
+ int64_t ddof = NUM2LL(rddof);
505
509
  CArray *grouped, *offsets, *means, *counts, *out;
506
510
  int64_t k, *offs, *countp;
507
511
  ca_size_t n, c;
@@ -518,8 +522,9 @@ rb_ca_reduceat_variance (VALUE self, VALUE roffsets, VALUE rmeans,
518
522
  n = grouped->elements;
519
523
  if ( offsets->data_type != CA_INT64 || counts->data_type != CA_INT64 ||
520
524
  means->data_type != CA_FLOAT64 || out->data_type != CA_FLOAT64 ||
521
- means->elements != k || counts->elements != k || out->elements != k ) {
522
- rb_raise(rb_eArgError, "__reduceat_variance__: offsets/counts int64, means/out float64[k]");
525
+ means->elements != k || counts->elements != k || out->elements != k ||
526
+ ( ddof != 0 && ddof != 1 ) ) {
527
+ rb_raise(rb_eArgError, "__reduceat_variance__: offsets/counts int64, means/out float64[k], ddof 0 or 1");
523
528
  }
524
529
 
525
530
  offs = (int64_t *) offsets->ptr;
@@ -1104,8 +1109,13 @@ rb_ca_fiber_scatter_moments (VALUE self, VALUE rcodes, VALUE raxis, VALUE rk,
1104
1109
  h->data_type);
1105
1110
  }
1106
1111
 
1112
+ ca_detach(h);
1113
+ ca_detach(codes);
1114
+
1107
1115
  /* Mask empty (count == 0) cells in mins/maxs: value slot is 0 but meaningless.
1108
- Matches __reduceat_moments__ contract for empty segments. */
1116
+ Matches __reduceat_moments__ contract for empty segments. After the
1117
+ detach: creating a mask allocates, and a raise must not leave h and
1118
+ codes attached. */
1109
1119
  ca_create_mask(mins);
1110
1120
  ca_create_mask(maxs);
1111
1121
  minm = (boolean8_t *) mins->mask->ptr;
@@ -1113,9 +1123,6 @@ rb_ca_fiber_scatter_moments (VALUE self, VALUE rcodes, VALUE raxis, VALUE rk,
1113
1123
  for ( cell = 0; cell < total; cell++ ) {
1114
1124
  if ( countp[cell] == 0 ) { minm[cell] = 1; maxm[cell] = 1; }
1115
1125
  }
1116
-
1117
- ca_detach(h);
1118
- ca_detach(codes);
1119
1126
  return Qnil;
1120
1127
  }
1121
1128
 
@@ -1481,7 +1488,7 @@ Init_ca_categorical_iterator (void)
1481
1488
  rb_define_private_method(rb_cCArray, "__reduceat_percentile__",
1482
1489
  rb_ca_reduceat_percentile, 3);
1483
1490
  rb_define_private_method(rb_cCArray, "__reduceat_variance__",
1484
- rb_ca_reduceat_variance, 4);
1491
+ rb_ca_reduceat_variance, 5);
1485
1492
  rb_define_private_method(rb_cCArray, "__reduceat_prod__",
1486
1493
  rb_ca_reduceat_prod, 2);
1487
1494
  rb_define_private_method(rb_cCArray, "__reduceat_argminmax__",
data/ext/ca_compare.c CHANGED
@@ -21,8 +21,8 @@
21
21
  static int \
22
22
  elem_cmp_## type (const void *va, const void *vb) \
23
23
  { \
24
- const type *a = (const type *) va; \
25
- const type *b = (const type *) vb; \
24
+ const CA_U(type) *a = (const CA_U(type) *) va; \
25
+ const CA_U(type) *b = (const CA_U(type) *) vb; \
26
26
  if ( *a > *b ) return 1; \
27
27
  if ( *a < *b ) return -1; \
28
28
  return 0; \
@@ -33,8 +33,8 @@ elem_cmp_## type (const void *va, const void *vb) \
33
33
  static int \
34
34
  elem_cmp_## type (const void *va, const void *vb) \
35
35
  { \
36
- const type a = *(const type *) va; \
37
- const type b = *(const type *) vb; \
36
+ const type a = *(const CA_U(type) *) va; \
37
+ const type b = *(const CA_U(type) *) vb; \
38
38
  return CA_FLOAT_ORDER_CMP(a, b); \
39
39
  }
40
40
 
@@ -32,29 +32,29 @@ ca_fill_typed (char *dst, const char *val, ca_size_t bytes, ca_size_t n)
32
32
  break;
33
33
  }
34
34
  case 2: {
35
- uint16_t v = *(const uint16_t *)val;
36
- uint16_t *p = (uint16_t *)dst;
35
+ uint16_t v = *(const ca_u_uint16_t *)val;
36
+ ca_u_uint16_t *p = (ca_u_uint16_t *)dst;
37
37
  for ( i = 0; i < n; i++ ) p[i] = v;
38
38
  break;
39
39
  }
40
40
  case 4: {
41
- uint32_t v = *(const uint32_t *)val;
42
- uint32_t *p = (uint32_t *)dst;
41
+ uint32_t v = *(const ca_u_uint32_t *)val;
42
+ ca_u_uint32_t *p = (ca_u_uint32_t *)dst;
43
43
  for ( i = 0; i < n; i++ ) p[i] = v;
44
44
  break;
45
45
  }
46
46
  case 8: {
47
- uint64_t v = *(const uint64_t *)val;
48
- uint64_t *p = (uint64_t *)dst;
47
+ uint64_t v = *(const ca_u_uint64_t *)val;
48
+ ca_u_uint64_t *p = (ca_u_uint64_t *)dst;
49
49
  for ( i = 0; i < n; i++ ) p[i] = v;
50
50
  break;
51
51
  }
52
52
  case 16: {
53
53
  /* complex128 etc. Two uint64_t stores per element. Compiler
54
54
  pairs them into a NEON 128-bit or SSE2 store. */
55
- uint64_t v0 = ((const uint64_t *)val)[0];
56
- uint64_t v1 = ((const uint64_t *)val)[1];
57
- uint64_t *p = (uint64_t *)dst;
55
+ uint64_t v0 = ((const ca_u_uint64_t *)val)[0];
56
+ uint64_t v1 = ((const ca_u_uint64_t *)val)[1];
57
+ ca_u_uint64_t *p = (ca_u_uint64_t *)dst;
58
58
  for ( i = 0; i < n; i++ ) {
59
59
  p[2*i] = v0;
60
60
  p[2*i + 1] = v1;
@@ -1964,20 +1964,20 @@ ca_iter_state_next_slab_axes_unprotected (ca_iter_state *st,
1964
1964
  /* f64 / i64 hot path: store one cell per inner iter, compiler
1965
1965
  can keep `src` in a vector reg and stream cleanly. */
1966
1966
  for ( int32_t kk = 0; kk < K; kk++ ) {
1967
- const uint64_t *src = (const uint64_t *)
1967
+ const ca_u_uint64_t *src = (const ca_u_uint64_t *)
1968
1968
  (pptrs[kk] + parent_off);
1969
- uint64_t *dst = (uint64_t *) (cache + (ca_size_t) kk * bytes);
1969
+ ca_u_uint64_t *dst = (ca_u_uint64_t *) (cache + (ca_size_t) kk * bytes);
1970
1970
  for ( ca_size_t t = 0; t < want; t++ ) {
1971
- *(uint64_t *)((char *) dst + t * stride_t) = src[t];
1971
+ *(ca_u_uint64_t *)((char *) dst + t * stride_t) = src[t];
1972
1972
  }
1973
1973
  }
1974
1974
  } else if ( bytes == 4 ) {
1975
1975
  for ( int32_t kk = 0; kk < K; kk++ ) {
1976
- const uint32_t *src = (const uint32_t *)
1976
+ const ca_u_uint32_t *src = (const ca_u_uint32_t *)
1977
1977
  (pptrs[kk] + parent_off);
1978
- uint32_t *dst = (uint32_t *) (cache + (ca_size_t) kk * bytes);
1978
+ ca_u_uint32_t *dst = (ca_u_uint32_t *) (cache + (ca_size_t) kk * bytes);
1979
1979
  for ( ca_size_t t = 0; t < want; t++ ) {
1980
- *(uint32_t *)((char *) dst + t * stride_t) = src[t];
1980
+ *(ca_u_uint32_t *)((char *) dst + t * stride_t) = src[t];
1981
1981
  }
1982
1982
  }
1983
1983
  } else {