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
data/lib/carray/complex.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Complex-component accessors (real / real= / imag / imag= / real? /
|
|
2
|
-
# is_real). Loaded eagerly BEFORE carray/lazy.rb, because lazy.rb
|
|
3
|
-
# `
|
|
4
|
-
#
|
|
2
|
+
# is_real). Loaded eagerly BEFORE carray/lazy.rb, because lazy.rb takes
|
|
3
|
+
# `instance_method(:real)` (and the imag equivalent) at load time -- these
|
|
4
|
+
# defs must exist by then.
|
|
5
5
|
|
|
6
6
|
class CArray
|
|
7
7
|
|
|
@@ -40,9 +40,9 @@ class CArray
|
|
|
40
40
|
# Architecturally:
|
|
41
41
|
# - lib/carray/lazy.rb's `real`/`imag` overrides dispatch to CAMonOp
|
|
42
42
|
# when self is a lazy view, and fall through to the defs here for eager
|
|
43
|
-
# parents (via `
|
|
43
|
+
# parents (via `instance_method(:real)` taken at load time).
|
|
44
44
|
# This file MUST be required before carray/lazy.rb (see carray.rb), and
|
|
45
|
-
# removing these defs would break
|
|
45
|
+
# removing these defs would break the lazy.rb load.
|
|
46
46
|
#
|
|
47
47
|
# - ext/ca_obj_stride.c recognises `CAField .real/.imag over complex`
|
|
48
48
|
# as a first-class byte-mismatched reinterpret pattern. Architectural
|
|
@@ -52,6 +52,14 @@ class CArray
|
|
|
52
52
|
# framework's read-only chain does not replace the CAField mutable view).
|
|
53
53
|
# ---------------------------------------------------------------------------
|
|
54
54
|
|
|
55
|
+
# Whether a view kept by {#real} / {#imag} was made for this array.
|
|
56
|
+
# `dup` and `clone` copy instance variables, so a copy arrives holding the
|
|
57
|
+
# original's view, whose parent is the original; the copy makes its own.
|
|
58
|
+
def part_of_self? (view)
|
|
59
|
+
view && view.parent.equal?(self)
|
|
60
|
+
end
|
|
61
|
+
private :part_of_self?
|
|
62
|
+
|
|
55
63
|
# @overload real
|
|
56
64
|
# Returns the real part of `self` as a zero-copy view. For a
|
|
57
65
|
# complex array the view is a mutable {CAField} into the
|
|
@@ -59,19 +67,16 @@ class CArray
|
|
|
59
67
|
# over `self`. Writing to the view updates `self` in place.
|
|
60
68
|
# @return [CArray]
|
|
61
69
|
def real
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
@__real__
|
|
70
|
+
return @__real__ if part_of_self?(@__real__)
|
|
71
|
+
view = if complex?
|
|
72
|
+
case data_type
|
|
73
|
+
when CA_CMPLX64 then field(0, CA_FLOAT32)
|
|
74
|
+
when CA_CMPLX128 then field(0, CA_FLOAT64)
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
self[]
|
|
78
|
+
end
|
|
79
|
+
frozen? ? view : (@__real__ = view)
|
|
75
80
|
end
|
|
76
81
|
|
|
77
82
|
# @overload real=(val)
|
|
@@ -85,35 +90,37 @@ class CArray
|
|
|
85
90
|
# @overload imag
|
|
86
91
|
# Returns the imaginary part of `self`. For a complex array the
|
|
87
92
|
# result is a mutable {CAField} view of the imaginary slot;
|
|
88
|
-
# writing to it updates `self` in place. For
|
|
89
|
-
#
|
|
93
|
+
# writing to it updates `self` in place. For an object array each
|
|
94
|
+
# cell's `imaginary`; for a real array a fresh independent CArray
|
|
95
|
+
# filled with 0. Masked cells stay masked.
|
|
90
96
|
# @return [CArray]
|
|
91
97
|
def imag
|
|
92
|
-
if
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
end
|
|
100
|
-
else
|
|
101
|
-
@__imag__ = self.template { 0 }
|
|
102
|
-
end
|
|
98
|
+
if object?
|
|
99
|
+
return imag_i
|
|
100
|
+
end
|
|
101
|
+
if not complex?
|
|
102
|
+
zero = template { 0 }
|
|
103
|
+
zero[is_masked] = UNDEF if has_mask?
|
|
104
|
+
return zero
|
|
103
105
|
end
|
|
104
|
-
return @__imag__
|
|
106
|
+
return @__imag__ if part_of_self?(@__imag__)
|
|
107
|
+
view = case data_type
|
|
108
|
+
when CA_CMPLX64 then field(4, CA_FLOAT32)
|
|
109
|
+
when CA_CMPLX128 then field(8, CA_FLOAT64)
|
|
110
|
+
end
|
|
111
|
+
frozen? ? view : (@__imag__ = view)
|
|
105
112
|
end
|
|
106
113
|
|
|
107
114
|
# @overload imag=(val)
|
|
108
115
|
# Sets the imaginary-part slot to `val` (complex arrays only).
|
|
109
116
|
# @param val [CArray, Numeric] value to broadcast.
|
|
110
117
|
# @return [Object] `val`.
|
|
111
|
-
# @raise [
|
|
118
|
+
# @raise [CArray::DataTypeError] when `self` is not a complex array.
|
|
112
119
|
def imag= (val)
|
|
113
120
|
if complex?
|
|
114
121
|
imag[] = val
|
|
115
122
|
else
|
|
116
|
-
raise "not a complex array"
|
|
123
|
+
raise CArray::DataTypeError, "not a complex array"
|
|
117
124
|
end
|
|
118
125
|
end
|
|
119
126
|
|
data/lib/carray/conditional.rb
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# same "pick a value per cell based on a boolean condition" contract but
|
|
7
7
|
# differ in where the condition lives and how the branches are supplied:
|
|
8
8
|
#
|
|
9
|
-
# cond.then_else(x, y) # boolean receiver, 2 values (
|
|
9
|
+
# cond.then_else(x, y) # boolean receiver, 2 values (lazy if an operand is)
|
|
10
10
|
# self.replace_where(cond, b) # value receiver, 1 replacement (eager)
|
|
11
11
|
# self.conditional(cond, f_then, f_else) # value receiver, 2 callables (per-region)
|
|
12
12
|
#
|
|
@@ -25,10 +25,25 @@ class CArray
|
|
|
25
25
|
# reading as "if `self` then `x` else `y`". The boolean 2-way
|
|
26
26
|
# case of {#choose} that additionally propagates `self`'s mask
|
|
27
27
|
# (`UNDEF` in `self` produces `UNDEF` in the result).
|
|
28
|
+
# The result's data type is the common type of the two branches; a
|
|
29
|
+
# branch given as `UNDEF` masks its cells and leaves the type to the
|
|
30
|
+
# other branch (`cond.then_else(a, UNDEF)` has `a`'s data type).
|
|
31
|
+
# String branches make the result a {CAString}
|
|
32
|
+
# (`cond.then_else("yes", "no")`, also with `UNDEF` for one of them).
|
|
33
|
+
# A Symbol branch, or a String beside a value of another kind, makes
|
|
34
|
+
# the result an object array.
|
|
35
|
+
# When `self` or a branch is a lazy view, the result is a lazy view
|
|
36
|
+
# (read-only) that joins the expression around it; `copy` makes an
|
|
37
|
+
# array of it. An object result is always computed at once.
|
|
28
38
|
# @param x [CArray, Numeric, Object] true-branch value(s).
|
|
29
39
|
# @param y [CArray, Numeric, Object] false-branch value(s).
|
|
30
|
-
# @return [CArray] new array with the same shape as `self
|
|
31
|
-
#
|
|
40
|
+
# @return [CArray] new array with the same shape as `self` (a
|
|
41
|
+
# {CAString} for String branches), or a lazy view (CATriOp) when an
|
|
42
|
+
# operand is lazy.
|
|
43
|
+
# @raise [ArgumentError] when `self` is not a boolean CArray, or when
|
|
44
|
+
# an array branch's shape does not pair with `self`'s as an
|
|
45
|
+
# operator's operands would (equal, or size-1 axes of the same ndim;
|
|
46
|
+
# an object or fixlen result needs `self`'s shape exactly).
|
|
32
47
|
def then_else (x, y)
|
|
33
48
|
# Guard: self must be boolean. Integer / float receiver would be
|
|
34
49
|
# silently reinterpreted by the indexer setter (`result[self] = ...`)
|
|
@@ -38,10 +53,22 @@ class CArray
|
|
|
38
53
|
raise ArgumentError,
|
|
39
54
|
"then_else: receiver must be a boolean CArray (data_type == CA_BOOLEAN), got #{self.data_type}"
|
|
40
55
|
end
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
reshaped = [x, y].any? { |v| v.is_a?(CArray) && !v.scalar? && v.shape != shape }
|
|
57
|
+
face = [x, y].find { |v| v.is_a?(CArray) && !v.scalar? && v.face? }
|
|
58
|
+
if face
|
|
59
|
+
refuse_branch_shape(x, y) if reshaped
|
|
60
|
+
refuse_branch_mix(x, y, face)
|
|
61
|
+
return face_then_else(x, y, face)
|
|
62
|
+
end
|
|
63
|
+
dt = CArray.send(:selection_type, [x, y])
|
|
64
|
+
return lazy_then_else(x, y, dt) if lazy_then_else?(x, y, dt)
|
|
65
|
+
# A branch shaped unlike the condition goes through the lazy select,
|
|
66
|
+
# which pairs the shapes by the operator rule (equal, or size-1 axes
|
|
67
|
+
# with the same ndim) and refuses the rest.
|
|
68
|
+
if reshaped
|
|
69
|
+
refuse_branch_shape(x, y) if scalar? || dt == CA_OBJECT || dt == CA_FIXLEN
|
|
70
|
+
return lazy_then_else(x, y, dt).copy
|
|
71
|
+
end
|
|
45
72
|
# A CScalar (scalar? CArray) is treated as a scalar value, not as a
|
|
46
73
|
# self-shaped operand: full CArray -> gather/copy, scalar -> broadcast.
|
|
47
74
|
y_full = y.is_a?(CArray) && !y.scalar?
|
|
@@ -57,6 +84,82 @@ class CArray
|
|
|
57
84
|
if self.has_mask?
|
|
58
85
|
result[self.is_masked] = UNDEF
|
|
59
86
|
end
|
|
87
|
+
CArray.send(:selection_text?, [x, y]) ? CAString.wrap(result) : result
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# A lazy receiver or branch makes the selection a lazy node (CATriOp
|
|
91
|
+
# select), so it joins the expression around it rather than computing
|
|
92
|
+
# it here. The branches carry the result type: a scalar branch becomes
|
|
93
|
+
# a CScalar of it, UNDEF a masked one.
|
|
94
|
+
private def lazy_then_else? (x, y, dt)
|
|
95
|
+
return false if scalar? || dt == CA_OBJECT || dt == CA_FIXLEN
|
|
96
|
+
[self, x, y].any? { |v| v.is_a?(CArray) && v.lazy_view? }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The types the lazy select cannot carry keep the strict form: a full
|
|
100
|
+
# branch must have the condition's shape.
|
|
101
|
+
private def refuse_branch_shape (x, y)
|
|
102
|
+
bad = [x, y].find { |v| v.is_a?(CArray) && !v.scalar? && v.shape != shape }
|
|
103
|
+
raise ArgumentError,
|
|
104
|
+
"then_else: branch shape #{bad.shape.inspect} does not match the condition's #{shape.inspect}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# An array branch beside a Face branch must be a Face of the same kind
|
|
108
|
+
# (the same obj_type, so a CARecord subclass and a CARecord of one data
|
|
109
|
+
# class pair, as their store takes): the Face decides what a cell means,
|
|
110
|
+
# so a plain array would be read as its storage (an integer as a time's
|
|
111
|
+
# ticks) and the result would take the class of whichever branch came
|
|
112
|
+
# second. A scalar branch is a value and goes through the Face's store.
|
|
113
|
+
private def refuse_branch_mix (x, y, face)
|
|
114
|
+
arrays = [x, y].select { |v| v.is_a?(CArray) && !v.scalar? }
|
|
115
|
+
return if arrays.all? { |v| v.face? && v.obj_type == face.obj_type }
|
|
116
|
+
hint = face.is_a?(CAString) ?
|
|
117
|
+
" (CArray.string(a) makes a CAString of an object array of Strings)" : ""
|
|
118
|
+
raise ArgumentError,
|
|
119
|
+
"then_else: cannot mix Face and non-Face arrays, or arrays of " \
|
|
120
|
+
"different Face classes; convert them first to one Face class or to no Face" + hint
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private def lazy_then_else (x, y, dt)
|
|
124
|
+
branch = lambda do |v|
|
|
125
|
+
next v if v.is_a?(CArray) && !v.scalar?
|
|
126
|
+
cell = CScalar.new(dt)
|
|
127
|
+
cell[0] = v.is_a?(CArray) ? v[0] : v
|
|
128
|
+
cell
|
|
129
|
+
end
|
|
130
|
+
CATriOp.__build__(self, branch.(x), branch.(y), CATriOp::OP_SELECT)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private def face_then_else (x, y, face)
|
|
134
|
+
# A Face branch decides the result: the other branch is written into a
|
|
135
|
+
# copy of it through the Face's own store, so an instant stays an instant.
|
|
136
|
+
# Promoting the two branches to a common data type would compare a Face's
|
|
137
|
+
# surface type with its storage's. A CAConstString cannot be written, so
|
|
138
|
+
# it selects among its strings and packs them again.
|
|
139
|
+
if face.is_a?(CAConstString)
|
|
140
|
+
sx = x.is_a?(CAConstString) ? x.to_string : x
|
|
141
|
+
sy = y.is_a?(CAConstString) ? y.to_string : y
|
|
142
|
+
return CArray.const_string(then_else(sx, sy), encoding: face.encoding)
|
|
143
|
+
end
|
|
144
|
+
x_full = x.is_a?(CArray) && !x.scalar?
|
|
145
|
+
y_full = y.is_a?(CArray) && !y.scalar?
|
|
146
|
+
base = y_full ? y : x
|
|
147
|
+
if base.read_only?
|
|
148
|
+
# No store to write through (a CACategorical's codes): select among
|
|
149
|
+
# the values themselves.
|
|
150
|
+
result = CArray.object(*shape)
|
|
151
|
+
result[] = y_full ? y.to_type(CA_OBJECT) : y
|
|
152
|
+
result[self] = x_full ? x.to_type(CA_OBJECT)[self] : x
|
|
153
|
+
result[self.is_masked] = UNDEF if self.has_mask?
|
|
154
|
+
return result
|
|
155
|
+
end
|
|
156
|
+
result = base.copy
|
|
157
|
+
if y_full
|
|
158
|
+
result[self] = x_full ? x[self] : x
|
|
159
|
+
else
|
|
160
|
+
result[self.not] = y.is_a?(CArray) ? y[0] : y
|
|
161
|
+
end
|
|
162
|
+
result[self.is_masked] = UNDEF if self.has_mask?
|
|
60
163
|
result
|
|
61
164
|
end
|
|
62
165
|
|
|
@@ -91,7 +194,9 @@ class CArray
|
|
|
91
194
|
# Scalar returns from a callable (e.g. `->(v) { 0 }`) broadcast
|
|
92
195
|
# to the subset shape. The result `data_type` is the promotion
|
|
93
196
|
# of the two subset results via `CArray.result_type`, or
|
|
94
|
-
# `data_type` when given.
|
|
197
|
+
# `data_type` when given. A String result is a value (not the name
|
|
198
|
+
# of a data type); when both callables give Strings or {CAString}s
|
|
199
|
+
# the result is a {CAString}. Masked cells in `cond` propagate to
|
|
95
200
|
# `UNDEF` in the result.
|
|
96
201
|
#
|
|
97
202
|
# @param cond [CArray] boolean selector; same shape as `self`.
|
|
@@ -118,18 +223,19 @@ class CArray
|
|
|
118
223
|
x_else = self[cond.not]
|
|
119
224
|
y_then = then_fn.call(x_then)
|
|
120
225
|
y_else = else_fn.call(x_else)
|
|
226
|
+
text = !data_type && CArray.send(:selection_text?, [y_then, y_else])
|
|
121
227
|
|
|
122
228
|
# A callable that returns a scalar (e.g. `->(v) { 0 }`) broadcasts
|
|
123
229
|
# to the subset shape; wrap it here so the scatter step below sees a
|
|
124
230
|
# same-length CArray.
|
|
125
231
|
unless y_then.is_a?(CArray)
|
|
126
|
-
y_then = CArray.new(data_type || CArray.
|
|
232
|
+
y_then = CArray.new(data_type || CArray.send(:selection_type, [y_then]), x_then.shape).fill(y_then)
|
|
127
233
|
end
|
|
128
234
|
unless y_else.is_a?(CArray)
|
|
129
|
-
y_else = CArray.new(data_type || CArray.
|
|
235
|
+
y_else = CArray.new(data_type || CArray.send(:selection_type, [y_else]), x_else.shape).fill(y_else)
|
|
130
236
|
end
|
|
131
237
|
|
|
132
|
-
dt = data_type || CArray.
|
|
238
|
+
dt = data_type || CArray.send(:selection_type, [y_then, y_else])
|
|
133
239
|
out = CArray.new(dt, self.shape)
|
|
134
240
|
out[cond] = y_then
|
|
135
241
|
out[cond.not] = y_else
|
|
@@ -138,7 +244,7 @@ class CArray
|
|
|
138
244
|
# positions, so both scatters leave the cell untouched — an explicit
|
|
139
245
|
# fix-up is required.
|
|
140
246
|
out[cond.is_masked] = UNDEF if cond.has_mask?
|
|
141
|
-
out
|
|
247
|
+
text ? CAString.wrap(out) : out
|
|
142
248
|
end
|
|
143
249
|
|
|
144
250
|
# @overload select(condlist, choicelist, default: 0, data_type: nil)
|
|
@@ -151,7 +257,9 @@ class CArray
|
|
|
151
257
|
# Each `choicelist[k]` is either a same-shape CArray or a scalar
|
|
152
258
|
# broadcast to every cell. The result `data_type` is the promotion
|
|
153
259
|
# of every choice plus `default` via `CArray.result_type`, or
|
|
154
|
-
# `data_type` when given.
|
|
260
|
+
# `data_type` when given. A String choice is a value (not the name of
|
|
261
|
+
# a data type); when every choice and `default` is a String or a
|
|
262
|
+
# {CAString}, the result is a {CAString}.
|
|
155
263
|
#
|
|
156
264
|
# @param condlist [Array<CArray>] boolean selectors.
|
|
157
265
|
# @param choicelist [Array<CArray, Numeric, Object>] values, one
|
|
@@ -187,7 +295,7 @@ class CArray
|
|
|
187
295
|
end
|
|
188
296
|
shape = first.shape
|
|
189
297
|
|
|
190
|
-
dt = data_type ||
|
|
298
|
+
dt = data_type || selection_type(choicelist + [default])
|
|
191
299
|
# `default` can be either a same-shape CArray (per-cell fallback) or a
|
|
192
300
|
# scalar (broadcast to every cell).
|
|
193
301
|
default_full = default.is_a?(CArray) && !default.scalar?
|
|
@@ -210,7 +318,35 @@ class CArray
|
|
|
210
318
|
v = choicelist[k]
|
|
211
319
|
out[c] = v.is_a?(CArray) ? v[c] : v
|
|
212
320
|
end
|
|
321
|
+
if !data_type && selection_text?(choicelist + [default])
|
|
322
|
+
out = CAString.wrap(out)
|
|
323
|
+
end
|
|
213
324
|
out
|
|
214
325
|
end
|
|
215
326
|
|
|
327
|
+
# The data type a selection among `values` writes into: their common type,
|
|
328
|
+
# by the rule an operator promotes its operands by (a scalar takes the
|
|
329
|
+
# type of an array beside it). UNDEF is a missing value, not a value of
|
|
330
|
+
# some type, so it leaves the type to the others. A String or Symbol is a
|
|
331
|
+
# value, which CArray.result_type would read as the name of a data type
|
|
332
|
+
# ("int32"), so it makes the result object.
|
|
333
|
+
def self.selection_type (values)
|
|
334
|
+
typed = values.reject { |v| v.equal?(UNDEF) }
|
|
335
|
+
if typed.empty? || typed.any? { |v| v.is_a?(String) || v.is_a?(Symbol) }
|
|
336
|
+
CA_OBJECT
|
|
337
|
+
else
|
|
338
|
+
CArray.result_type(*typed)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
private_class_method :selection_type
|
|
342
|
+
|
|
343
|
+
# Whether a selection among `values` gives text: every value that is not
|
|
344
|
+
# UNDEF is a String or a CAString, and there is at least one. The result
|
|
345
|
+
# is then a CAString rather than an object array of Strings.
|
|
346
|
+
def self.selection_text? (values)
|
|
347
|
+
present = values.reject { |v| v.equal?(UNDEF) }
|
|
348
|
+
!present.empty? && present.all? { |v| v.is_a?(String) || v.is_a?(CAString) }
|
|
349
|
+
end
|
|
350
|
+
private_class_method :selection_text?
|
|
351
|
+
|
|
216
352
|
end
|
data/lib/carray/const_string.rb
CHANGED
|
@@ -55,7 +55,7 @@ class CArray
|
|
|
55
55
|
# @return [CAConstString]
|
|
56
56
|
def self.const_string (arg, encoding: Encoding::UTF_8, &block)
|
|
57
57
|
if arg.is_a?(CArray)
|
|
58
|
-
return string_face_of(arg).to_const_string(encoding: encoding)
|
|
58
|
+
return string_face_of(arg, "CArray.const_string").to_const_string(encoding: encoding)
|
|
59
59
|
end
|
|
60
60
|
if block
|
|
61
61
|
n = Integer(arg)
|
|
@@ -135,7 +135,7 @@ class CAConstString
|
|
|
135
135
|
def sort_addr (*args, **kw)
|
|
136
136
|
# The native scan has no notion of an incomparable sentinel, so a masked
|
|
137
137
|
# column goes the same way a per-axis one does.
|
|
138
|
-
return
|
|
138
|
+
return sort_addr_by_bytes if args.empty? && kw.empty? && ! has_mask?
|
|
139
139
|
to_string.sort_addr(*args, **kw)
|
|
140
140
|
end
|
|
141
141
|
|
|
@@ -143,6 +143,12 @@ class CAConstString
|
|
|
143
143
|
# Returns the per-fiber indices that index a sort by string order.
|
|
144
144
|
# @return [CArray] `:int64` indices.
|
|
145
145
|
def sort_index (*args, **kw)
|
|
146
|
+
if args.empty? && kw.empty? && ! has_mask?
|
|
147
|
+
# With no axis the indices are those of the flattened array, which are
|
|
148
|
+
# its addresses.
|
|
149
|
+
addr = sort_addr_by_bytes
|
|
150
|
+
return ndim == 1 ? addr : addr.flatten.copy
|
|
151
|
+
end
|
|
146
152
|
to_string.sort_index(*args, **kw)
|
|
147
153
|
end
|
|
148
154
|
|
|
@@ -163,7 +169,7 @@ class CAConstString
|
|
|
163
169
|
# every cell is masked or the array is empty.
|
|
164
170
|
# @return [String, CAConstString]
|
|
165
171
|
def min (*args, **kw)
|
|
166
|
-
return
|
|
172
|
+
return min_string_by_bytes || UNDEF if args.empty? && kw.empty?
|
|
167
173
|
const_string_lift(to_string.min(*args, **kw))
|
|
168
174
|
end
|
|
169
175
|
|
|
@@ -171,7 +177,7 @@ class CAConstString
|
|
|
171
177
|
# Byte-largest counterpart of {#min}.
|
|
172
178
|
# @return [String, CAConstString]
|
|
173
179
|
def max (*args, **kw)
|
|
174
|
-
return
|
|
180
|
+
return max_string_by_bytes || UNDEF if args.empty? && kw.empty?
|
|
175
181
|
const_string_lift(to_string.max(*args, **kw))
|
|
176
182
|
end
|
|
177
183
|
|
|
@@ -182,6 +188,13 @@ class CAConstString
|
|
|
182
188
|
to_string.minmax(*args, **kw).map { |r| const_string_lift(r) }
|
|
183
189
|
end
|
|
184
190
|
|
|
191
|
+
# @overload clip(min, max = nil, fill_value = nil, lfill: nil, ufill: nil)
|
|
192
|
+
# Clips the strings in byte order (see {CArray#clip}).
|
|
193
|
+
# @return [CAConstString]
|
|
194
|
+
def clip (*args, **kw)
|
|
195
|
+
const_string_lift(to_string.clip(*args, **kw))
|
|
196
|
+
end
|
|
197
|
+
|
|
185
198
|
# @overload min_index(axis: nil)
|
|
186
199
|
# @return [Integer, CArray] where the byte-smallest string sits.
|
|
187
200
|
def min_index (*args, **kw)
|
|
@@ -312,9 +325,38 @@ class CAConstString
|
|
|
312
325
|
# @overload categorize(labels: nil, sort_labels: false)
|
|
313
326
|
# @return [CACategorical] categories keyed on the strings.
|
|
314
327
|
def categorize (labels: nil, sort_labels: false)
|
|
328
|
+
# Discovery reads the bytes (a String per distinct value only); a given
|
|
329
|
+
# vocabulary is matched through the strings.
|
|
330
|
+
return super if labels.nil?
|
|
315
331
|
to_string.categorize(labels: labels, sort_labels: sort_labels)
|
|
316
332
|
end
|
|
317
333
|
|
|
334
|
+
# ---- comparison operators ---------------------------------------------
|
|
335
|
+
#
|
|
336
|
+
# The core operators compare storage, and a storage cell is a byte range:
|
|
337
|
+
# `x < "dd"` compared where each string sits in the buffer and answered
|
|
338
|
+
# true everywhere. They read the strings here instead. `eq` has a native
|
|
339
|
+
# byte scan and stays; `<=>` composes from `>` and `<`, so it follows.
|
|
340
|
+
|
|
341
|
+
# @!method ne(other)
|
|
342
|
+
# @return [CArray] boolean, true where the strings differ.
|
|
343
|
+
# @!method lt(other)
|
|
344
|
+
# @return [CArray] boolean, true where self sorts before `other`.
|
|
345
|
+
# @!method le(other)
|
|
346
|
+
# @return [CArray] boolean, true where self sorts before or equals `other`.
|
|
347
|
+
# @!method gt(other)
|
|
348
|
+
# @return [CArray] boolean, true where self sorts after `other`.
|
|
349
|
+
# @!method ge(other)
|
|
350
|
+
# @return [CArray] boolean, true where self sorts after or equals `other`.
|
|
351
|
+
[:ne, :lt, :le, :gt, :ge].each do |op|
|
|
352
|
+
define_method(op, &Ractor.make_shareable(proc { |other| to_string.public_send(op, string_operand(other)) }))
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
alias < lt
|
|
356
|
+
alias <= le
|
|
357
|
+
alias > gt
|
|
358
|
+
alias >= ge
|
|
359
|
+
|
|
318
360
|
# A CAConstString operand has to be decoded too; anything else (a CAString,
|
|
319
361
|
# an object array, an Array) already compares as strings.
|
|
320
362
|
private def string_operand (other)
|
|
@@ -330,3 +372,16 @@ class CAConstString
|
|
|
330
372
|
end
|
|
331
373
|
|
|
332
374
|
end
|
|
375
|
+
|
|
376
|
+
class CAConstString
|
|
377
|
+
# The decimal readers of CAFrame#infer_types / #cast, read from the buffer
|
|
378
|
+
# without a String per cell.
|
|
379
|
+
def __infer_number_type_of_text__
|
|
380
|
+
CArray.__const_string_infer_number_type_of_text__(self)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def __read_text_as_number__(type, report = nil)
|
|
384
|
+
out = CArray.__const_string_read_text_as_number__(self, type, report)
|
|
385
|
+
out && ndim > 1 ? out.reshape(*shape) : out
|
|
386
|
+
end
|
|
387
|
+
end
|
data/lib/carray/construct.rb
CHANGED
|
@@ -287,7 +287,7 @@ class CArray
|
|
|
287
287
|
"(its contents are left undefined); use CArray.new to fill."
|
|
288
288
|
end
|
|
289
289
|
if args.size == 2 and args[1].is_a?(Array)
|
|
290
|
-
return
|
|
290
|
+
return empty_of_data_type(*args, **options)
|
|
291
291
|
end
|
|
292
292
|
unless options.empty?
|
|
293
293
|
raise ArgumentError,
|
|
@@ -314,32 +314,36 @@ class CArray
|
|
|
314
314
|
CArray.new(self::DataType, shape)
|
|
315
315
|
end
|
|
316
316
|
|
|
317
|
-
# @overload from_memory_view(obj)
|
|
317
|
+
# @overload from_memory_view(obj, mask: nil)
|
|
318
318
|
# Returns a fresh CArray interpreted as this typed subclass's
|
|
319
319
|
# `data_type`, imported by copy from a MemoryView producer.
|
|
320
320
|
# Forwards to
|
|
321
|
-
# `CArray.from_memory_view(obj, data_type: self::DataType)
|
|
322
|
-
#
|
|
321
|
+
# `CArray.from_memory_view(obj, data_type: self::DataType)`. A
|
|
322
|
+
# typed producer keeps its shape and must agree with the type; a
|
|
323
|
+
# typeless one is read as 1-D (chain `.reshape` for higher rank).
|
|
323
324
|
# @param obj [Object] MemoryView producer.
|
|
325
|
+
# @param mask [Object, nil] MemoryView producer of the mask.
|
|
324
326
|
# @return [CArray]
|
|
325
|
-
def from_memory_view (obj)
|
|
326
|
-
CArray.from_memory_view(obj, data_type: self::DataType)
|
|
327
|
+
def from_memory_view (obj, mask: nil)
|
|
328
|
+
CArray.from_memory_view(obj, data_type: self::DataType, mask: mask)
|
|
327
329
|
end
|
|
328
330
|
|
|
329
|
-
# @overload wrap_memory_view(obj)
|
|
331
|
+
# @overload wrap_memory_view(obj, mask: nil)
|
|
330
332
|
# Returns a zero-copy CAWrap over `obj`'s MemoryView interpreted
|
|
331
333
|
# as this typed subclass's `data_type`. Forwards to
|
|
332
|
-
# `CArray.wrap_memory_view(obj, data_type: self::DataType)
|
|
333
|
-
#
|
|
334
|
+
# `CArray.wrap_memory_view(obj, data_type: self::DataType)`. A
|
|
335
|
+
# typed producer keeps its shape and must agree with the type; a
|
|
336
|
+
# typeless one is read as 1-D (chain `.reshape` for higher rank).
|
|
334
337
|
#
|
|
335
338
|
# Here the receiver names the element type, not the class of the
|
|
336
339
|
# result: the call forwards with `CArray` as the receiver, so a
|
|
337
340
|
# `CAWrap` comes back. Choosing the class is the other reading of
|
|
338
341
|
# the receiver, and it lives on `CArray.wrap_memory_view` itself.
|
|
339
342
|
# @param obj [Object] MemoryView producer.
|
|
343
|
+
# @param mask [Object, nil] MemoryView producer of the mask.
|
|
340
344
|
# @return [CAWrap]
|
|
341
|
-
def wrap_memory_view (obj)
|
|
342
|
-
CArray.wrap_memory_view(obj, data_type: self::DataType)
|
|
345
|
+
def wrap_memory_view (obj, mask: nil)
|
|
346
|
+
CArray.wrap_memory_view(obj, data_type: self::DataType, mask: mask)
|
|
343
347
|
end
|
|
344
348
|
|
|
345
349
|
end
|
|
@@ -164,6 +164,7 @@ class CArray
|
|
|
164
164
|
# midpoints go the same way as numpy's integer linspace).
|
|
165
165
|
float_out = CArray.new(CA_FLOAT64, [n]).span(x1.to_f..x2.to_f)
|
|
166
166
|
return float_out if data_type == CA_FLOAT64
|
|
167
|
+
# coerce: the type the caller asked for
|
|
167
168
|
float_out.floor.to_type(data_type)
|
|
168
169
|
end
|
|
169
170
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CArray
|
|
4
|
+
|
|
5
|
+
# The length a match by distance is limited by -- the +tolerance:+ of
|
|
6
|
+
# locate_nearest_addr (and of align_nearest_addr and CAFrame#join_asof
|
|
7
|
+
# through it), the +before:+, +after:+ and +interval:+ of a key window --
|
|
8
|
+
# read the same way wherever it is given: a length of the key's kind,
|
|
9
|
+
# not below 0.
|
|
10
|
+
#
|
|
11
|
+
# - For a number key it is a real number; NaN, which no distance is
|
|
12
|
+
# within or beyond, and a negative length are refused, as is a String.
|
|
13
|
+
# - For a time or duration key it is a duration: a String such as
|
|
14
|
+
# "10 minutes" (and "0 minutes") or a CATimedelta value, not negative.
|
|
15
|
+
# A bare number is refused, since its length would depend on the key's
|
|
16
|
+
# unit.
|
|
17
|
+
#
|
|
18
|
+
# Every refusal is ArgumentError naming +label+, the keyword as the
|
|
19
|
+
# caller says it ("tolerance:", "windows: before:").
|
|
20
|
+
module DistanceLimit
|
|
21
|
+
|
|
22
|
+
ZERO_DURATION = /\A\s*0+\s+([A-Za-z]+)\s*\z/
|
|
23
|
+
|
|
24
|
+
# +width+ as a Numeric (number key) or a CATimedelta::Element (time or
|
|
25
|
+
# duration key); +nil+ passes through.
|
|
26
|
+
def self.read (width, key, label)
|
|
27
|
+
return nil if width.nil?
|
|
28
|
+
if key.is_a?(CATime) || key.is_a?(CATimedelta)
|
|
29
|
+
duration(width, label)
|
|
30
|
+
else
|
|
31
|
+
number(width, key, label)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.number (width, key, label)
|
|
36
|
+
if width.is_a?(String) || width.is_a?(CATimedelta::Element)
|
|
37
|
+
raise ArgumentError,
|
|
38
|
+
"#{label} #{width.inspect} is a duration, but the key is " \
|
|
39
|
+
"#{CArray::Inspector.type_label(key)}; give a number"
|
|
40
|
+
end
|
|
41
|
+
unless width.is_a?(Numeric) && width.real? && !(width.is_a?(Float) && width.nan?) && width >= 0
|
|
42
|
+
raise ArgumentError, "#{label} is a number not below 0 (got #{width.inspect})"
|
|
43
|
+
end
|
|
44
|
+
width
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.duration (width, label)
|
|
48
|
+
element =
|
|
49
|
+
case width
|
|
50
|
+
when CATimedelta::Element
|
|
51
|
+
width
|
|
52
|
+
when String
|
|
53
|
+
if (zero = width.match(ZERO_DURATION))
|
|
54
|
+
CATimedelta::Element.new(0, CATime::Resolution.parse(zero[1]).base)
|
|
55
|
+
else
|
|
56
|
+
res = begin
|
|
57
|
+
CATime::Resolution.parse(width)
|
|
58
|
+
rescue ArgumentError
|
|
59
|
+
raise ArgumentError,
|
|
60
|
+
"#{label} #{width.inspect} is not a duration " \
|
|
61
|
+
"(use e.g. \"10 minutes\" / \"1 hour\")"
|
|
62
|
+
end
|
|
63
|
+
CATimedelta::Element.new(res.count, res.base)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
unless element
|
|
67
|
+
raise ArgumentError,
|
|
68
|
+
"#{label} a time key needs a duration such as \"10 minutes\" " \
|
|
69
|
+
"or a CATimedelta value (got #{width.inspect})"
|
|
70
|
+
end
|
|
71
|
+
if element.value < 0
|
|
72
|
+
raise ArgumentError, "#{label} is a duration not below 0 (got #{width.inspect})"
|
|
73
|
+
end
|
|
74
|
+
element
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/carray/fixlen_string.rb
CHANGED
|
@@ -46,7 +46,7 @@ class CArray
|
|
|
46
46
|
# @return [CAFixlenString]
|
|
47
47
|
def self.fixlen_string (arg, bytes: nil, truncate: :error, &block)
|
|
48
48
|
if arg.is_a?(CArray)
|
|
49
|
-
return string_face_of(arg).to_fixlen_string(bytes: bytes, truncate: truncate)
|
|
49
|
+
return string_face_of(arg, "CArray.fixlen_string").to_fixlen_string(bytes: bytes, truncate: truncate)
|
|
50
50
|
end
|
|
51
51
|
unless [:error, :silent].include?(truncate)
|
|
52
52
|
raise ArgumentError, "truncate: must be :error or :silent (got #{truncate.inspect})"
|
|
@@ -90,6 +90,12 @@ end
|
|
|
90
90
|
# Mutable fixed-width String Face: L1 ops + in-place variants. In-place
|
|
91
91
|
# transforms re-store through the native fixlen path (silent truncate).
|
|
92
92
|
class CAFixlenString
|
|
93
|
+
# inspect's name for the array: the class and the cell width, which is the
|
|
94
|
+
# longest string a cell holds.
|
|
95
|
+
private def inspect_label
|
|
96
|
+
"#{self.class}[#{bytes}]"
|
|
97
|
+
end
|
|
98
|
+
|
|
93
99
|
include CArray::StringOperationMixin
|
|
94
100
|
include CArray::StringOperationMixin::Mutable
|
|
95
101
|
end
|