carray 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +568 -0
- data/README.md +6 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +220 -0
- data/ext/ca_axis_group.c +201 -123
- data/ext/ca_binop_dispatch.c +46 -52
- data/ext/ca_binop_dispatch.h +1 -1
- data/ext/ca_categorical_iterator.c +249 -183
- data/ext/ca_compare.c +7 -12
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +38 -23
- data/ext/ca_kernel_iterator.c +1765 -2076
- data/ext/ca_kernel_iterator.h +584 -566
- data/ext/ca_monop_dispatch.c +86 -63
- data/ext/ca_monop_dispatch.h +5 -0
- data/ext/ca_obj_array.c +223 -56
- data/ext/ca_obj_bincmp.c +172 -138
- data/ext/ca_obj_binop.c +116 -230
- data/ext/ca_obj_bitarray.c +40 -26
- data/ext/ca_obj_bitfield.c +100 -68
- data/ext/ca_obj_block.c +19 -9
- data/ext/ca_obj_byte_swap.c +45 -46
- data/ext/ca_obj_const_string.c +269 -44
- data/ext/ca_obj_face.c +612 -89
- data/ext/ca_obj_face.h +111 -47
- data/ext/ca_obj_fake.c +36 -79
- data/ext/ca_obj_farray.c +7 -3
- data/ext/ca_obj_field.c +33 -8
- data/ext/ca_obj_fixlen_string.c +23 -24
- data/ext/ca_obj_grid.c +541 -210
- data/ext/ca_obj_meld.c +331 -111
- data/ext/ca_obj_moncmp.c +80 -65
- data/ext/ca_obj_monop.c +177 -149
- data/ext/ca_obj_object.c +664 -248
- data/ext/ca_obj_record.c +63 -23
- data/ext/ca_obj_reduce.c +18 -16
- data/ext/ca_obj_refer.c +140 -58
- data/ext/ca_obj_remap.c +85 -24
- data/ext/ca_obj_repeat.c +24 -9
- data/ext/ca_obj_roll.c +121 -16
- data/ext/ca_obj_select.c +61 -58
- data/ext/ca_obj_select_axis.c +73 -107
- data/ext/ca_obj_shift.c +32 -102
- data/ext/ca_obj_stack.c +372 -122
- data/ext/ca_obj_stride.c +348 -140
- data/ext/ca_obj_string.c +88 -22
- data/ext/ca_obj_tile.c +38 -21
- data/ext/ca_obj_time.c +45 -31
- data/ext/ca_obj_timedelta.c +43 -29
- data/ext/ca_obj_transpose.c +17 -6
- data/ext/ca_obj_triop.c +125 -141
- data/ext/ca_obj_window.c +330 -44
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +104 -81
- data/ext/ca_sweep_engine.h +7 -20
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +542 -0
- data/ext/carray.h +241 -92
- data/ext/carray_access.c +608 -276
- data/ext/carray_address_basis.c +494 -97
- data/ext/carray_attribute.c +129 -4
- data/ext/carray_bincount.c +39 -7
- data/ext/carray_broadcast.c +56 -19
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_call_cfunc.c +108 -28
- data/ext/carray_cast.c +1576 -223
- data/ext/carray_cast_func.rb +77 -24
- data/ext/carray_class.c +4 -3
- data/ext/carray_conversion.c +208 -75
- data/ext/carray_copy.c +82 -24
- data/ext/carray_core.c +646 -162
- data/ext/carray_count.c +171 -88
- data/ext/carray_element.c +94 -60
- data/ext/carray_factorize.c +670 -324
- data/ext/carray_generate.c +260 -83
- data/ext/carray_histogram.c +209 -106
- data/ext/carray_hold.c +14 -16
- data/ext/carray_index_classifier.c +422 -243
- data/ext/carray_internal.h +692 -3
- data/ext/carray_kernels_bincmp.c +7 -0
- data/ext/carray_kernels_binop.c +487 -120
- data/ext/carray_kernels_init.c +653 -50
- data/ext/carray_kernels_moncmp.c +15 -8
- data/ext/carray_kernels_monop.c +99 -43
- data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
- data/ext/carray_kernels_reduce_boolean.c +112 -36
- data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
- data/ext/carray_kernels_reduce_extreme.c +3032 -2892
- data/ext/carray_kernels_reduce_variance.c +439 -500
- data/ext/carray_kernels_scan.c +1097 -488
- data/ext/carray_kernels_search.c +3787 -2176
- data/ext/carray_kernels_sort.c +1200 -920
- data/ext/carray_kernels_triop.c +479 -7
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +967 -153
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +910 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +299 -100
- data/ext/carray_memory_view.c +872 -758
- data/ext/carray_operator.c +1171 -364
- data/ext/carray_order.c +505 -133
- data/ext/carray_partition.c +112 -65
- data/ext/carray_random.c +327 -239
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +346 -78
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +249 -86
- data/ext/carray_sort.c +224 -227
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +118 -39
- data/ext/carray_test.c +193 -69
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +579 -113
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +19 -4
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1202 -1014
- data/ext/ruby_carray.c +17 -5
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +160 -17
- data/lib/carray/autoload_carray.rb +23 -27
- data/lib/carray/autoload_method_extension.rb +20 -5
- data/lib/carray/axis_group.rb +360 -63
- data/lib/carray/basics.rb +162 -28
- data/lib/carray/bincount_nd.rb +18 -2
- data/lib/carray/block_iterator.rb +231 -58
- data/lib/carray/boolean_reduce.rb +34 -42
- data/lib/carray/categorical.rb +242 -52
- data/lib/carray/categorical_iterator.rb +437 -827
- data/lib/carray/complex.rb +41 -34
- data/lib/carray/conditional.rb +150 -14
- data/lib/carray/const_string.rb +59 -4
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +82 -7
- data/lib/carray/frame/csv_parser.rb +688 -87
- data/lib/carray/frame/describe.rb +80 -0
- data/lib/carray/frame/frame.rb +369 -45
- data/lib/carray/frame/group.rb +631 -90
- data/lib/carray/frame/io.rb +506 -79
- data/lib/carray/frame/join.rb +411 -62
- data/lib/carray/frame/records.rb +116 -19
- data/lib/carray/frame/reshape.rb +455 -0
- data/lib/carray/frame/sort.rb +30 -18
- data/lib/carray/frame/verbs.rb +868 -61
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +4 -0
- data/lib/carray/fuse_source.rb +118 -2
- data/lib/carray/fusion.rb +257 -42
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +54 -13
- data/lib/carray/iterator.rb +99 -9
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +216 -193
- data/lib/carray/mask_gap_fill.rb +112 -92
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +52 -21
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +18 -2
- data/lib/carray/methods/bit_string.rb +15 -4
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +18 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +25 -5
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +140 -10
- data/lib/carray/methods/join.rb +2 -7
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +182 -45
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +65 -17
- data/lib/carray/segment_iterator.rb +1221 -0
- data/lib/carray/serialize.rb +214 -30
- data/lib/carray/slab_iterator.rb +101 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +12 -4
- data/lib/carray/struct.rb +137 -58
- data/lib/carray/struct_builder.rb +124 -17
- data/lib/carray/time.rb +1222 -186
- data/lib/carray/window_iterator.rb +242 -81
- data/lib/carray.rb +9 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +5 -1
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +5 -4
- data/yard-stubs/ca_obj_roll.rb +2 -3
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +9 -5
- data/yard-stubs/carray_access.rb +11 -5
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +88 -43
- data/yard-stubs/carray_conversion.rb +15 -2
- data/yard-stubs/carray_copy.rb +12 -5
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +60 -12
- data/yard-stubs/carray_math.rb +52 -14
- data/yard-stubs/carray_median_percentile.rb +15 -8
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +61 -29
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +21 -9
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +10 -2
- data/yard-stubs/carray_sort.rb +49 -8
- data/yard-stubs/carray_test.rb +15 -3
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -5
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_kernels_map.c +0 -3467
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
|
@@ -12,7 +12,9 @@ class CArray
|
|
|
12
12
|
# whole array and returns an Integer. With `axis: k` it counts per
|
|
13
13
|
# fiber along axis `k`, returning a reduced `CA_INT64` CArray
|
|
14
14
|
# (shape = self.shape with axis `k` removed; `keep_axis: true` keeps
|
|
15
|
-
# it as a length-1 axis).
|
|
15
|
+
# it as a length-1 axis). An Array of axes counts over all of them
|
|
16
|
+
# together, one count per remaining position; naming every axis
|
|
17
|
+
# returns an Integer. The shape rule matches other per-axis
|
|
16
18
|
# reductions such as `sum(axis:)`.
|
|
17
19
|
#
|
|
18
20
|
# The distinct count has identity 0: an empty array, an all-masked
|
|
@@ -25,12 +27,12 @@ class CArray
|
|
|
25
27
|
# distinctness follows Ruby `eql?` / `hash` (which, unlike numeric,
|
|
26
28
|
# does not collapse distinct NaN objects).
|
|
27
29
|
#
|
|
28
|
-
# @param axis [Integer, nil] axis to count
|
|
29
|
-
# the whole array.
|
|
30
|
+
# @param axis [Integer, Array<Integer>, nil] axis or axes to count
|
|
31
|
+
# over; `nil` counts over the whole array.
|
|
30
32
|
# @param keep_axis [Boolean] when `axis` is given, keep the reduced
|
|
31
33
|
# axis as a length-1 axis instead of dropping it.
|
|
32
|
-
# @return [Integer, CArray] Integer for `axis: nil
|
|
33
|
-
# reduced `CA_INT64` CArray.
|
|
34
|
+
# @return [Integer, CArray] Integer for `axis: nil` or every axis,
|
|
35
|
+
# otherwise a reduced `CA_INT64` CArray.
|
|
34
36
|
# @overload nunique(along: k)
|
|
35
37
|
# Returns how many distinct **sub-arrays** `self` holds, comparing
|
|
36
38
|
# whole sub-arrays rather than cells: `along: k` names the axis
|
|
@@ -60,7 +62,10 @@ class CArray
|
|
|
60
62
|
# then read the single reduced cell as an Integer.
|
|
61
63
|
flatten.send(:__nunique__, 0, false)[0]
|
|
62
64
|
else
|
|
63
|
-
|
|
65
|
+
axes = Array(axis).map { |a| normalize_axis(a, "nunique") }
|
|
66
|
+
counts = __nunique__(axes, keep_axis)
|
|
67
|
+
# naming every axis reduces to one count, an Integer as sum(axis:) gives
|
|
68
|
+
(axes.size == ndim && ! keep_axis) ? counts[0] : counts
|
|
64
69
|
end
|
|
65
70
|
end
|
|
66
71
|
|
|
@@ -76,26 +76,7 @@ class CArray
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
source = along ? self : (ndim > 1 ? flatten : self)
|
|
79
|
-
source[*gather_args(
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# The source index of every position in the result: element i appears
|
|
83
|
-
# at the `counts[i]` positions starting at the exclusive prefix sum.
|
|
84
|
-
#
|
|
85
|
-
# Marking those starts with +1 and running a cumulative sum turns the
|
|
86
|
-
# marks back into indices, and does the right thing with a zero
|
|
87
|
-
# count for free -- two elements then share a start, that position
|
|
88
|
-
# carries 2, and the sum steps over the one that was asked for zero
|
|
89
|
-
# times.
|
|
90
|
-
private def repeat_index (counts)
|
|
91
|
-
total = counts.sum.to_i
|
|
92
|
-
return CArray.int64(0) if total.zero?
|
|
93
|
-
starts = counts.cumsum.int64 - counts
|
|
94
|
-
marks = CArray.int64(total)
|
|
95
|
-
# A trailing zero count starts one past the end; it marks nothing,
|
|
96
|
-
# which is what a zero count means.
|
|
97
|
-
marks.scatter_add!(starts[starts.lt(total)], 1)
|
|
98
|
-
marks.cumsum.int64 - 1
|
|
79
|
+
source[*gather_args(CArray.segment_index(lengths: counts), along)]
|
|
99
80
|
end
|
|
100
81
|
|
|
101
82
|
# One index argument per axis: the gathered index on the axis being
|
|
@@ -4,8 +4,8 @@ class CArray
|
|
|
4
4
|
# Returns `self` resized to `newdim`. The original data is
|
|
5
5
|
# placed at offset 0 (positive size) or right-aligned (negative
|
|
6
6
|
# size); the new area outside the original region is filled
|
|
7
|
-
# with `fill_value`, or
|
|
8
|
-
#
|
|
7
|
+
# with `fill_value`, or left as zero storage when it is not given
|
|
8
|
+
# (0 for a numeric array). Pass `UNDEF` to mask the new area.
|
|
9
9
|
#
|
|
10
10
|
# Each entry of `newdim` is: `nil` to keep the current
|
|
11
11
|
# `shape[i]`, a positive Integer for a new size with the
|
|
@@ -13,15 +13,19 @@ class CArray
|
|
|
13
13
|
# `d` for a new size `|d|` with the original right-aligned.
|
|
14
14
|
#
|
|
15
15
|
# Works on Face arrays (CARecord / CATime / ...) and
|
|
16
|
-
# plain fixlen: the
|
|
17
|
-
#
|
|
16
|
+
# plain fixlen: the result is the same Face, and `fill_value` is a
|
|
17
|
+
# value of it (a Time for a time array).
|
|
18
18
|
#
|
|
19
19
|
# @param newdim [Array<Integer, nil>] new shape spec, one entry
|
|
20
20
|
# per axis.
|
|
21
|
-
# @param fill_value [Object] value for the extended area
|
|
21
|
+
# @param fill_value [Object] value for the extended area (zero
|
|
22
|
+
# storage when not given).
|
|
22
23
|
# @return [CArray] resized copy.
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
NO_FILL_VALUE = ::Object.new.freeze
|
|
25
|
+
private_constant :NO_FILL_VALUE
|
|
26
|
+
|
|
27
|
+
def resize (*newdim, fill_value: NO_FILL_VALUE)
|
|
28
|
+
raise ArgumentError, "ndim mismatch" if newdim.size != ndim
|
|
25
29
|
offset = Array.new(ndim, 0)
|
|
26
30
|
newdim = newdim.each_with_index.map do |d, i|
|
|
27
31
|
case d
|
|
@@ -32,24 +36,18 @@ class CArray
|
|
|
32
36
|
offset[i] = size - shape[i] if d < 0
|
|
33
37
|
size
|
|
34
38
|
else
|
|
35
|
-
raise "invalid dimension size"
|
|
39
|
+
raise ArgumentError, "invalid dimension size"
|
|
36
40
|
end
|
|
37
41
|
end
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if dt != :fixlen || fill_value.equal?(UNDEF) || fill_value.is_a?(String)
|
|
48
|
-
out[] = fill_value
|
|
49
|
-
end
|
|
50
|
-
out.mask.paste(offset, src.false) if out.has_mask?
|
|
51
|
-
out.paste(offset, src)
|
|
52
|
-
out = out.face_lift(face_parent) if face_parent
|
|
42
|
+
# The original cells are copied as storage, so a read-only Face (a
|
|
43
|
+
# const string, a categorical) resizes too. The new area starts as zero
|
|
44
|
+
# storage; a given fill_value is written as a value of the array, which a
|
|
45
|
+
# read-only Face refuses.
|
|
46
|
+
out = blank_like(newdim)
|
|
47
|
+
out_storage = out.__send__(:face_storage)
|
|
48
|
+
out[] = fill_value unless fill_value.equal?(NO_FILL_VALUE)
|
|
49
|
+
out_storage.mask.paste(offset, self.false) if out_storage.has_mask?
|
|
50
|
+
out_storage.paste(offset, face_storage)
|
|
53
51
|
out
|
|
54
52
|
end
|
|
55
53
|
|
data/lib/carray/methods/snap.rb
CHANGED
|
@@ -66,8 +66,11 @@ class CArray
|
|
|
66
66
|
# the sample, `:ceil` at or above), and delegates to
|
|
67
67
|
# `locate_nearest_addr(direction:)`.
|
|
68
68
|
#
|
|
69
|
-
# `list` must be a 1-D ascending numeric CArray
|
|
70
|
-
#
|
|
69
|
+
# `list` must be a 1-D ascending numeric CArray, or a Ruby Array /
|
|
70
|
+
# Range. An Array grid takes `self`'s data_type, except that a Float
|
|
71
|
+
# in it makes an integer array's grid float64 (so `0.5` is not
|
|
72
|
+
# truncated); an Integer that does not fit `self`'s type raises
|
|
73
|
+
# `RangeError`. Out-of-range and NaN handling follows the
|
|
71
74
|
# same pattern as `bin_to`, but with an additional `:clamp`
|
|
72
75
|
# sentinel that snaps out-of-range cells to the nearest list end.
|
|
73
76
|
#
|
|
@@ -95,19 +98,21 @@ class CArray
|
|
|
95
98
|
# shape as `self`.
|
|
96
99
|
# @raise [ArgumentError] when `list` is not 1-D or is empty, or
|
|
97
100
|
# `direction` is not one of the accepted symbols.
|
|
101
|
+
# @raise [RangeError] when an Integer in an Array `list` does not
|
|
102
|
+
# fit `self`'s data_type.
|
|
98
103
|
# @example
|
|
99
104
|
# temp.snap_to([270.0, 280.0, 290.0, 300.0]) # clamp OOB
|
|
100
105
|
# temp.snap_to(grid, lfill: nil, ufill: nil) # mask OOB
|
|
101
106
|
# rain.snap_to([0.0, 1.0, 5.0, 20.0], direction: :floor) # list value at or below
|
|
102
107
|
def snap_to(list, lfill: :clamp, ufill: :clamp, direction: :round)
|
|
103
|
-
ref = list.is_a?(CArray) ? list :
|
|
108
|
+
ref = list.is_a?(CArray) ? list : snap_list(list)
|
|
104
109
|
raise ArgumentError, "snap_to: list must be 1-D" unless ref.ndim == 1
|
|
105
110
|
n = ref.elements
|
|
106
111
|
raise ArgumentError, "snap_to: list must have at least one value" if n < 1
|
|
107
112
|
|
|
108
113
|
if n == 1
|
|
109
114
|
# Degenerate: every finite cell snaps to the only value.
|
|
110
|
-
out = CArray.new(ref.data_type, shape).fill(ref[0])
|
|
115
|
+
out = CArray.new(ref.data_type, shape, bytes: ref.bytes).fill(ref[0])
|
|
111
116
|
out.mask = self.mask.to_ca if self.has_mask?
|
|
112
117
|
if self.float?
|
|
113
118
|
inv = self.is_invalid
|
|
@@ -118,17 +123,16 @@ class CArray
|
|
|
118
123
|
return out
|
|
119
124
|
end
|
|
120
125
|
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
# element-wise semantic on any shape.
|
|
126
|
+
# Multi-D input is snapped as its flattened cells and given its shape
|
|
127
|
+
# back.
|
|
124
128
|
if ndim > 1
|
|
125
129
|
return reshape(-1).snap_to(ref, lfill: lfill, ufill: ufill,
|
|
126
130
|
direction: direction).reshape(*shape)
|
|
127
131
|
end
|
|
128
132
|
|
|
129
|
-
# locate_nearest_addr returns int64 indices;
|
|
130
|
-
#
|
|
131
|
-
# explicit comparisons
|
|
133
|
+
# locate_nearest_addr returns int64 indices; whether a cell outside the
|
|
134
|
+
# list gets one depends on the direction, so the two sides are split with
|
|
135
|
+
# explicit comparisons and filled here independently of it.
|
|
132
136
|
idx = self.locate_nearest_addr(ref, direction: direction)
|
|
133
137
|
out = ref.project(idx)
|
|
134
138
|
|
|
@@ -137,13 +141,13 @@ class CArray
|
|
|
137
141
|
|
|
138
142
|
case lfill
|
|
139
143
|
when :clamp then out[below] = ref[0]
|
|
140
|
-
when nil then
|
|
144
|
+
when nil then out[below] = UNDEF
|
|
141
145
|
else out[below] = lfill
|
|
142
146
|
end
|
|
143
147
|
|
|
144
148
|
case ufill
|
|
145
149
|
when :clamp then out[above] = ref[-1]
|
|
146
|
-
when nil then
|
|
150
|
+
when nil then out[above] = UNDEF
|
|
147
151
|
else out[above] = ufill
|
|
148
152
|
end
|
|
149
153
|
|
|
@@ -158,4 +162,22 @@ class CArray
|
|
|
158
162
|
out
|
|
159
163
|
end
|
|
160
164
|
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
# A Ruby Array grid in the type a search query would get: a Float in it
|
|
168
|
+
# makes an integer array's grid float64 (so 0.5 is not truncated to 0),
|
|
169
|
+
# otherwise the grid takes self's type, and an Integer that does not fit
|
|
170
|
+
# it is refused rather than wrapped.
|
|
171
|
+
def snap_list(list)
|
|
172
|
+
vals = list.to_a
|
|
173
|
+
type = data_type
|
|
174
|
+
type = CA_FLOAT64 if (integer? || data_type == CA_BOOLEAN) && vals.any? { |v| v.is_a?(Float) }
|
|
175
|
+
ref = CArray.wrap_readonly(vals, type)
|
|
176
|
+
if ref.integer?
|
|
177
|
+
bad = vals.zip(ref.to_a).find { |v, w| v != w }
|
|
178
|
+
raise RangeError, "snap_to: #{bad[0].inspect} is out of range for #{ref.data_type_name}" if bad
|
|
179
|
+
end
|
|
180
|
+
ref
|
|
181
|
+
end
|
|
182
|
+
|
|
161
183
|
end
|
|
@@ -26,10 +26,13 @@ class CArray
|
|
|
26
26
|
# For `CA_OBJECT` / `CA_FIXLEN` distinctness follows Ruby
|
|
27
27
|
# `eql?` / `hash`; note that Ruby does not collapse distinct NaN
|
|
28
28
|
# objects, so a `CA_OBJECT` array of Float NaN is not collapsed
|
|
29
|
-
# (unlike the numeric path).
|
|
29
|
+
# (unlike the numeric path). Complex values follow the float rules
|
|
30
|
+
# in each component; the values come back with -0.0 written as 0.0
|
|
31
|
+
# and every NaN as one NaN.
|
|
30
32
|
#
|
|
31
33
|
# @param sort [Boolean] when true, return the distinct values
|
|
32
|
-
# sorted ascending instead of in first-appearance order.
|
|
34
|
+
# sorted ascending instead of in first-appearance order. A complex
|
|
35
|
+
# array has no order and raises {CArray::DataTypeError}.
|
|
33
36
|
# A time array (`CATime` / `CATimedelta`) answers with its own type on
|
|
34
37
|
# its own unit: the distinct values are values, so the array comes back
|
|
35
38
|
# as itself rather than as raw storage ticks.
|
|
@@ -80,6 +83,12 @@ class CArray
|
|
|
80
83
|
# collapsed and -0.0 / +0.0 normalized; object keys on rb_hash + rb_eql and
|
|
81
84
|
# fixlen on a byte-hash + memcmp, both reproducing Ruby Hash distinctness.
|
|
82
85
|
# Masked cells are skipped in the kernel.
|
|
86
|
+
if sort && complex?
|
|
87
|
+
raise CArray::DataTypeError,
|
|
88
|
+
"unique(sort: true): a #{data_type_name} value has no order " \
|
|
89
|
+
"(complex numbers are not ordered); unique without sort: gives " \
|
|
90
|
+
"the values in first-appearance order"
|
|
91
|
+
end
|
|
83
92
|
levels = __unique_flat__
|
|
84
93
|
sort ? levels.sort : levels
|
|
85
94
|
end
|
|
@@ -52,17 +52,10 @@ class CArray
|
|
|
52
52
|
[ values[CArray.int64(order.size) { |i| order[i] }],
|
|
53
53
|
counts[CArray.int64(order.size) { |i| order[i] }] ]
|
|
54
54
|
when :value
|
|
55
|
-
# Ascending value
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
order = (0...v.size).sort_by do |i|
|
|
60
|
-
x = v[i]
|
|
61
|
-
nan = x.is_a?(Float) && x.nan?
|
|
62
|
-
[nan ? 1 : 0, nan ? 0 : x, i]
|
|
63
|
-
end
|
|
64
|
-
[ values[CArray.int64(order.size) { |i| order[i] }],
|
|
65
|
-
counts[CArray.int64(order.size) { |i| order[i] }] ]
|
|
55
|
+
# Ascending value in the order sort uses (NaN after every number),
|
|
56
|
+
# then gather both arrays through it.
|
|
57
|
+
order = values.sort_index
|
|
58
|
+
[values[order], counts[order]]
|
|
66
59
|
else
|
|
67
60
|
[values, counts]
|
|
68
61
|
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# ----------------------------------------------------------------------------
|
|
2
|
+
#
|
|
3
|
+
# carray/record_order.rb
|
|
4
|
+
#
|
|
5
|
+
# The order of a CARecord: the members its struct names in `order_by:`,
|
|
6
|
+
# compared in that order.
|
|
7
|
+
#
|
|
8
|
+
# A record's storage is its bytes, and comparing bytes is no order of the
|
|
9
|
+
# values in them (a little-endian float, a signed integer). So the
|
|
10
|
+
# ordering members do not run on the storage: they run on one int64 array,
|
|
11
|
+
# each record's rank among the others by the declared members, and the
|
|
12
|
+
# values they return are gathered from the record array by the positions
|
|
13
|
+
# that array gives. A struct that declares no order has none, and these
|
|
14
|
+
# raise.
|
|
15
|
+
#
|
|
16
|
+
# ----------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
class CARecord
|
|
19
|
+
|
|
20
|
+
# @overload sort(axis: nil, kind: :quick, masked_position: :last)
|
|
21
|
+
# Returns a sorted {CARecord} view, ordered by the struct's
|
|
22
|
+
# `order_by:` members. With no `axis:` the array is flattened first.
|
|
23
|
+
# @return [CARecord]
|
|
24
|
+
# @raise [ArgumentError] when the struct declares no `order_by:`.
|
|
25
|
+
def sort (*args, **kw)
|
|
26
|
+
addr = record_rank.sort_addr(*args, **kw)
|
|
27
|
+
kw[:axis].nil? ? self[addr.flatten] : self[addr]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @overload sort_copy(axis: nil, kind: :quick, masked_position: :last)
|
|
31
|
+
# @return [CARecord] an owned sorted copy.
|
|
32
|
+
def sort_copy (*args, **kw)
|
|
33
|
+
sort(*args, **kw).copy
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @!method sort_addr(axis: nil, kind: :quick, masked_position: :last)
|
|
37
|
+
# @return [CArray] the view-flat addresses of a sort by the declared order.
|
|
38
|
+
# @!method sort_index(axis: nil, kind: :quick, masked_position: :last)
|
|
39
|
+
# @return [CArray] the per-fiber indices of a sort by the declared order.
|
|
40
|
+
# @!method rank_index(axis: nil)
|
|
41
|
+
# @return [CArray] each record's rank by the declared order.
|
|
42
|
+
# @!method order(axis: nil, descending: false, method: :ordinal, kind: :quick)
|
|
43
|
+
# @return [CArray] each record's rank by the declared order.
|
|
44
|
+
# @!method min_index(axis: nil)
|
|
45
|
+
# @return [Integer, CArray] where the smallest record sits.
|
|
46
|
+
# @!method max_index(axis: nil)
|
|
47
|
+
# @return [Integer, CArray] where the largest record sits.
|
|
48
|
+
# @!method partition_index(kth, axis: nil)
|
|
49
|
+
# @return [CArray] the indices that partition about the `kth` record.
|
|
50
|
+
[:sort_addr, :sort_index, :rank_index, :order,
|
|
51
|
+
:min_index, :max_index, :partition_index].each do |op|
|
|
52
|
+
define_method(op, &Ractor.make_shareable(proc { |*args, **kw| record_rank.public_send(op, *args, **kw) }))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @overload min(axis: nil, keep_axis: false)
|
|
56
|
+
# Returns the smallest record by the declared order, skipping masked
|
|
57
|
+
# records; UNDEF when every record is masked. With `axis:` a
|
|
58
|
+
# {CARecord} of the smallest along that axis.
|
|
59
|
+
# @return [CAStruct, CARecord]
|
|
60
|
+
def min (axis: nil, keep_axis: false)
|
|
61
|
+
record_extreme(:min_index, axis, keep_axis)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @overload max(axis: nil, keep_axis: false)
|
|
65
|
+
# The largest record; see {#min}.
|
|
66
|
+
# @return [CAStruct, CARecord]
|
|
67
|
+
def max (axis: nil, keep_axis: false)
|
|
68
|
+
record_extreme(:max_index, axis, keep_axis)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @overload minmax(axis: nil, keep_axis: false)
|
|
72
|
+
# @return [Array] `[min, max]`.
|
|
73
|
+
def minmax (axis: nil, keep_axis: false)
|
|
74
|
+
[min(axis: axis, keep_axis: keep_axis), max(axis: axis, keep_axis: keep_axis)]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @overload partition_copy(kth, axis: nil)
|
|
78
|
+
# @return [CARecord] partitioned about the `kth` record in the declared
|
|
79
|
+
# order. With no `axis:` the array is flattened first.
|
|
80
|
+
def partition_copy (kth, axis: nil)
|
|
81
|
+
if axis.nil?
|
|
82
|
+
flat = flatten
|
|
83
|
+
return flat[flat.send(:record_rank).partition_index(kth)].copy
|
|
84
|
+
end
|
|
85
|
+
take_along_axis(record_rank.partition_index(kth, axis: axis), axis: axis).copy
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @!method lt(other)
|
|
89
|
+
# Compares by the declared order. `other` is a record of the same
|
|
90
|
+
# struct, or a {CARecord} of them.
|
|
91
|
+
# @return [CArray] boolean.
|
|
92
|
+
# @!method le(other)
|
|
93
|
+
# @!method gt(other)
|
|
94
|
+
# @!method ge(other)
|
|
95
|
+
[:lt, :le, :gt, :ge].each do |op|
|
|
96
|
+
define_method(op, &Ractor.make_shareable(proc do |other|
|
|
97
|
+
mine, theirs = record_rank_with(other)
|
|
98
|
+
mine.public_send(op, theirs)
|
|
99
|
+
end))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
alias < lt
|
|
103
|
+
alias <= le
|
|
104
|
+
alias > gt
|
|
105
|
+
alias >= ge
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
# The struct's order_by: members, or a refusal naming where to declare it.
|
|
110
|
+
def record_order_by
|
|
111
|
+
data_class.order_by or
|
|
112
|
+
raise ArgumentError,
|
|
113
|
+
"#{data_class.inspect} declares no order; pass order_by: to " \
|
|
114
|
+
"CArray.struct to name the members records are ordered by"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Each record's dense rank by the declared members, shaped like self.
|
|
118
|
+
# Each member is ranked on its own (a numeric member by value, NaN last;
|
|
119
|
+
# a fixlen member by its bytes), and the ranks are folded in member order.
|
|
120
|
+
# Ranking again after each fold keeps every value below the record count.
|
|
121
|
+
def record_rank
|
|
122
|
+
record_rank_of(self).reshape(*shape)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def record_rank_of (records)
|
|
126
|
+
rank = nil
|
|
127
|
+
record_order_by.each do |key|
|
|
128
|
+
r = records[key].flatten.order(method: :dense)
|
|
129
|
+
if rank.nil?
|
|
130
|
+
rank = r
|
|
131
|
+
else
|
|
132
|
+
top = r.max
|
|
133
|
+
span = top.equal?(UNDEF) ? 1 : top + 1
|
|
134
|
+
rank = (rank * span + r).order(method: :dense)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
rank
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Ranks of self and `other` in one ordering, so they can be compared.
|
|
141
|
+
def record_rank_with (other)
|
|
142
|
+
case other
|
|
143
|
+
when CARecord
|
|
144
|
+
unless other.data_class.equal?(data_class)
|
|
145
|
+
raise ArgumentError,
|
|
146
|
+
"can not compare a record of #{data_class.inspect} " \
|
|
147
|
+
"with one of #{other.data_class.inspect}"
|
|
148
|
+
end
|
|
149
|
+
theirs = other.flatten
|
|
150
|
+
when data_class
|
|
151
|
+
theirs = CARecord.new(data_class, 1)
|
|
152
|
+
theirs[0] = other
|
|
153
|
+
else
|
|
154
|
+
raise ArgumentError,
|
|
155
|
+
"can not compare a record of #{data_class.inspect} with #{other.class}"
|
|
156
|
+
end
|
|
157
|
+
n = elements
|
|
158
|
+
both = record_rank_of(CArray.concatenate([flatten, theirs]))
|
|
159
|
+
mine = both[0...n].reshape(*shape)
|
|
160
|
+
rest = both[n..-1]
|
|
161
|
+
[mine, other.is_a?(CARecord) ? rest.reshape(*other.shape) : rest[0]]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# The record at each min_index / max_index, gathered from self.
|
|
165
|
+
def record_extreme (index_op, axis, keep_axis)
|
|
166
|
+
rank = record_rank
|
|
167
|
+
if axis.nil?
|
|
168
|
+
i = rank.flatten.public_send(index_op)
|
|
169
|
+
return i.equal?(UNDEF) ? UNDEF : flatten[i]
|
|
170
|
+
end
|
|
171
|
+
name = index_op.to_s.sub("_index", "")
|
|
172
|
+
if axis.is_a?(Array)
|
|
173
|
+
axes = axis.map { |k| normalize_axis(k, name) }.uniq
|
|
174
|
+
return record_extreme(index_op, axes[0], keep_axis) if axes.size == 1
|
|
175
|
+
return record_extreme_over(index_op, axes, keep_axis)
|
|
176
|
+
end
|
|
177
|
+
axis = normalize_axis(axis, name)
|
|
178
|
+
idx = rank.public_send(index_op, axis: axis)
|
|
179
|
+
empty = idx.is_masked
|
|
180
|
+
picked_shape = shape.dup
|
|
181
|
+
picked_shape[axis] = 1
|
|
182
|
+
out = take_along_axis(idx.strip_mask(0).reshape(*picked_shape), axis: axis).copy
|
|
183
|
+
if empty.any
|
|
184
|
+
out[empty.reshape(*picked_shape)] = UNDEF
|
|
185
|
+
end
|
|
186
|
+
return out if keep_axis
|
|
187
|
+
out.reshape(*(shape[0...axis] + shape[axis + 1..-1]))
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Over several axes: the reduced axes are moved last and merged into one.
|
|
191
|
+
def record_extreme_over (index_op, axes, keep_axis)
|
|
192
|
+
keep = (0...ndim).to_a - axes
|
|
193
|
+
kept_shape = shape.values_at(*keep)
|
|
194
|
+
if keep.empty?
|
|
195
|
+
r = record_extreme(index_op, nil, false)
|
|
196
|
+
return r unless keep_axis
|
|
197
|
+
out = flatten[0..0].copy
|
|
198
|
+
out[0] = r
|
|
199
|
+
return out.reshape(*([1] * ndim))
|
|
200
|
+
end
|
|
201
|
+
merged = transpose(*(keep + axes))
|
|
202
|
+
.reshape(*kept_shape, shape.values_at(*axes).inject(:*))
|
|
203
|
+
out = merged.send(:record_extreme, index_op, keep.size, false)
|
|
204
|
+
return out unless keep_axis
|
|
205
|
+
out.reshape(*shape.each_with_index.map { |n, k| axes.include?(k) ? 1 : n })
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|
data/lib/carray/rng.rb
CHANGED
|
@@ -29,6 +29,7 @@ class CArray
|
|
|
29
29
|
:xoshiro256pp => File.expand_path("../../ext/ca_rng_xoshiro256pp.h",
|
|
30
30
|
__dir__),
|
|
31
31
|
}.freeze
|
|
32
|
+
Ractor.make_shareable(SOURCE_FILES)
|
|
32
33
|
|
|
33
34
|
# The C each generator is, as text.
|
|
34
35
|
#
|
|
@@ -50,6 +51,7 @@ class CArray
|
|
|
50
51
|
SOURCE = SOURCE_FILES.transform_values { |path|
|
|
51
52
|
File.read(path, :encoding => "UTF-8")
|
|
52
53
|
}.freeze
|
|
54
|
+
Ractor.make_shareable(SOURCE)
|
|
53
55
|
|
|
54
56
|
# What every generator's text needs beside it, and what none of them
|
|
55
57
|
# owns: turning uniforms into a normal is the same arithmetic whichever
|
|
@@ -80,6 +82,7 @@ class CArray
|
|
|
80
82
|
:bits => "ca_xoshiro256pp_next",
|
|
81
83
|
}.freeze,
|
|
82
84
|
}.freeze
|
|
85
|
+
Ractor.make_shareable(DRAW_FUNCTIONS)
|
|
83
86
|
|
|
84
87
|
end
|
|
85
88
|
|
data/lib/carray/runtime.rb
CHANGED
|
@@ -5,13 +5,32 @@
|
|
|
5
5
|
|
|
6
6
|
class CArray
|
|
7
7
|
|
|
8
|
+
# A fast path answers by a different algorithm what a general path also
|
|
9
|
+
# answers. A development build defines this in C, false when run with
|
|
10
|
+
# CARRAY_GENERAL_PATH=1 so the test suite takes the general paths (see
|
|
11
|
+
# ext/carray_internal.h); a release build has no switch.
|
|
12
|
+
unless respond_to?(:__fast_paths_enabled__)
|
|
13
|
+
def self.__fast_paths_enabled__ # :nodoc:
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# True when a development build runs with CARRAY_GENERAL_PATH=2: the
|
|
19
|
+
# kernel iterator then delivers every source through the view's own
|
|
20
|
+
# transfer. A release build has no switch.
|
|
21
|
+
unless respond_to?(:__iter_by_transfer_only__)
|
|
22
|
+
def self.__iter_by_transfer_only__ # :nodoc:
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
8
27
|
# String fallback for the global cast functions CA_INT32(data) etc. The C
|
|
9
28
|
# dispatcher (ext/carray_cast.c) handles every other input class in C and
|
|
10
29
|
# delegates only String here, because the whitespace / `,` / `;` /
|
|
11
30
|
# `_`->UNDEF parser is far more natural (and maintainable) in Ruby.
|
|
12
31
|
# +type+ arrives as the data_type Symbol. Required: without it
|
|
13
32
|
# +CA_INT32("1 2 3")+ and friends raise.
|
|
14
|
-
def self.
|
|
33
|
+
def self.cast_string (type, v)
|
|
15
34
|
if type == CA_OBJECT
|
|
16
35
|
return CScalar.new(CA_OBJECT) { v }
|
|
17
36
|
elsif type == CA_BOOLEAN
|
|
@@ -30,7 +49,7 @@ class CArray
|
|
|
30
49
|
return CArray.new(type, CArray.guess_array_shape(v)).tap { |a| a[] = v }
|
|
31
50
|
end
|
|
32
51
|
end
|
|
33
|
-
private_class_method :
|
|
52
|
+
private_class_method :cast_string
|
|
34
53
|
|
|
35
54
|
# CA_OBJECT escape for the weighted-sum kernel (mkkernel object_escape:).
|
|
36
55
|
# The C dispatcher (carray_kernels.c rb_ca_wsum_ki) intercepts CA_OBJECT
|
|
@@ -43,18 +62,18 @@ class CArray
|
|
|
43
62
|
# scalar or a full-shape weight), matching CArray's explicit-broadcast rule.
|
|
44
63
|
# +opts+ is the trailing options Hash (axis:/keep_axis:/min_count:/
|
|
45
64
|
# fill_value:) forwarded verbatim by the C escape; it goes straight to +sum+.
|
|
46
|
-
def
|
|
65
|
+
def wsum_object (weights, opts = {}) # :nodoc:
|
|
47
66
|
(self * weights).sum(**opts)
|
|
48
67
|
end
|
|
49
|
-
private :
|
|
68
|
+
private :wsum_object
|
|
50
69
|
|
|
51
70
|
# CA_OBJECT escape for the weighted-mean kernel. Weighted mean is
|
|
52
71
|
# +Σ(w*v) / Σw+ over the cells valid after mask overlay (a masked value OR a
|
|
53
72
|
# masked weight drops the cell from BOTH sums). The denominator is guarded:
|
|
54
73
|
# an empty / all-masked slab or weights summing to zero yields UNDEF (the
|
|
55
74
|
# object analogue of the numeric kernel's 0/0 -> NaN, since objects have no
|
|
56
|
-
# NaN). Weight forms and +opts+ as in {#
|
|
57
|
-
def
|
|
75
|
+
# NaN). Weight forms and +opts+ as in {#wsum_object}.
|
|
76
|
+
def wmean_object (weights, opts = {}) # :nodoc:
|
|
58
77
|
axis = opts[:axis]
|
|
59
78
|
keep_axis = opts[:keep_axis]
|
|
60
79
|
prod = self * weights
|
|
@@ -65,25 +84,54 @@ class CArray
|
|
|
65
84
|
num = prod.sum(**nopt)
|
|
66
85
|
den = wv.sum(axis: axis, keep_axis: keep_axis)
|
|
67
86
|
result =
|
|
68
|
-
if axis
|
|
87
|
+
if den.is_a?(CArray) # axis given, or keep_axis
|
|
69
88
|
den = den.copy
|
|
70
89
|
den[den.eq(0)] = UNDEF # zero / empty denominator -> undetermined
|
|
71
90
|
num / den # UNDEF propagates cell-wise
|
|
72
|
-
elsif den.equal?(UNDEF) || den == 0
|
|
73
|
-
UNDEF
|
|
91
|
+
elsif num.equal?(UNDEF) || den.equal?(UNDEF) || den == 0
|
|
92
|
+
UNDEF # min_count unmet leaves num UNDEF
|
|
74
93
|
else
|
|
75
94
|
num / den
|
|
76
95
|
end
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
CArray.__fill_undefined_result_cells__(result, opts[:fill_value])
|
|
97
|
+
end
|
|
98
|
+
private :wmean_object
|
|
99
|
+
|
|
100
|
+
# Define on +mod+ one method per name in +names+, each a `def` that passes
|
|
101
|
+
# its own name and its arguments to the private method +to+:
|
|
102
|
+
#
|
|
103
|
+
# CArray.send(:define_named_methods, self, [:sum, :min], :fold_reduction)
|
|
104
|
+
# # def sum (...) = fold_reduction(:sum, ...)
|
|
105
|
+
#
|
|
106
|
+
# A method made by define_method carries no name in its frame, so an error
|
|
107
|
+
# raised inside it cannot name the method the user called; a `def` does.
|
|
108
|
+
# With +super_block+ the method passes its arguments as an Array and a Hash,
|
|
109
|
+
# and a block that calls the method it overrides with the arguments the
|
|
110
|
+
# block is given (`yield(*args, **kw)`).
|
|
111
|
+
#
|
|
112
|
+
# With +template+ (and no +to+) each method is written out from it, for a
|
|
113
|
+
# method on a path where a helper call would cost: __NAME__ is the name and
|
|
114
|
+
# __VALUE__ the value +names+ (a Hash) gives it, inspected.
|
|
115
|
+
NAMED_METHOD = /\A(?:[a-z_]\w*[?!]?|\+|-|\*\*?|\/|%|<=>|==|<=?|>=?|<<|>>|&|\||\^|~|[+-]@)\z/
|
|
116
|
+
def self.define_named_methods (mod, names, to = nil, super_block: false, template: nil)
|
|
117
|
+
loc = caller_locations(1, 1).first
|
|
118
|
+
names.each do |name, value|
|
|
119
|
+
unless name.to_s.match?(NAMED_METHOD)
|
|
120
|
+
raise ArgumentError, "define_named_methods: #{name.inspect} is not a method name"
|
|
121
|
+
end
|
|
122
|
+
src = if template
|
|
123
|
+
template.gsub("__NAME__", name.to_s).gsub("__VALUE__", value.inspect)
|
|
124
|
+
elsif super_block
|
|
125
|
+
"def #{name}(*args, **kw, &blk)\n" \
|
|
126
|
+
" #{to}(:#{name}, args, kw) { |*a, **k| super(*a, **k, &blk) }\n" \
|
|
127
|
+
"end\n"
|
|
128
|
+
else
|
|
129
|
+
"def #{name}(...)\n #{to}(:#{name}, ...)\nend\n"
|
|
130
|
+
end
|
|
131
|
+
mod.class_eval(src, loc.path, loc.lineno)
|
|
84
132
|
end
|
|
85
133
|
end
|
|
86
|
-
|
|
134
|
+
private_class_method :define_named_methods
|
|
87
135
|
|
|
88
136
|
end
|
|
89
137
|
|