carray 3.0.6 → 3.0.7
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 +110 -0
- data/README.md +24 -3
- data/carray.gemspec +1 -1
- data/ext/ca_axis_group.c +6 -6
- data/ext/ca_binop_dispatch.c +8 -0
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +7 -7
- data/ext/ca_kernel_iterator.h +6 -4
- data/ext/ca_obj_array.c +4 -4
- data/ext/ca_obj_bincmp.c +19 -3
- data/ext/ca_obj_binop.c +36 -11
- data/ext/ca_obj_bitarray.c +2 -4
- data/ext/ca_obj_bitfield.c +86 -4
- data/ext/ca_obj_byte_swap.c +2 -4
- data/ext/ca_obj_const_string.c +422 -257
- data/ext/ca_obj_face.c +71 -33
- data/ext/ca_obj_fake.c +112 -13
- data/ext/ca_obj_farray.c +7 -1
- data/ext/ca_obj_fixlen_string.c +51 -17
- data/ext/ca_obj_grid.c +12 -7
- data/ext/ca_obj_meld.c +5 -5
- data/ext/ca_obj_monop.c +65 -39
- data/ext/ca_obj_object.c +9 -9
- data/ext/ca_obj_reduce.c +2 -4
- data/ext/ca_obj_refer.c +104 -10
- data/ext/ca_obj_repeat.c +1 -1
- data/ext/ca_obj_roll.c +2 -4
- data/ext/ca_obj_select.c +8 -6
- data/ext/ca_obj_select_axis.c +8 -6
- data/ext/ca_obj_stack.c +5 -5
- data/ext/ca_obj_stride.c +46 -8
- data/ext/ca_obj_string.c +2 -2
- data/ext/ca_obj_tile.c +2 -4
- data/ext/ca_obj_triop.c +15 -12
- data/ext/ca_obj_window.c +22 -15
- data/ext/ca_op_ipower.c +63 -15
- data/ext/ca_op_powi.h +6 -29
- data/ext/ca_sort_kernels.h +3 -0
- data/ext/ca_sweep_engine.c +90 -23
- data/ext/ca_sweep_engine.h +5 -6
- data/ext/caframe_csv_reader.c +1 -1
- data/ext/carray.h +3 -1
- data/ext/carray_access.c +98 -39
- data/ext/carray_address_basis.c +98 -24
- data/ext/carray_broadcast.c +2 -2
- data/ext/carray_call_cfunc.c +5 -8
- data/ext/carray_cast.c +311 -37
- data/ext/carray_cast_func.rb +37 -5
- data/ext/carray_conversion.c +56 -2
- data/ext/carray_copy.c +24 -0
- data/ext/carray_core.c +69 -6
- data/ext/carray_count.c +28 -9
- data/ext/carray_element.c +9 -3
- data/ext/carray_factorize.c +11 -11
- data/ext/carray_histogram.c +2 -2
- data/ext/carray_index_classifier.c +7 -5
- data/ext/carray_internal.h +94 -0
- data/ext/carray_kernels_binop.c +55 -55
- data/ext/carray_kernels_init.c +14 -14
- data/ext/carray_kernels_monop.c +90 -140
- data/ext/carray_lazy.c +16 -1
- data/ext/carray_loop.c +9 -2
- data/ext/carray_mask.c +118 -11
- data/ext/carray_median_percentile.c +13 -8
- data/ext/carray_memory_view.c +41 -22
- data/ext/carray_operator.c +72 -11
- data/ext/carray_order.c +52 -9
- data/ext/carray_partition.c +1 -1
- data/ext/carray_random.c +1 -1
- data/ext/carray_round.c +24 -1
- data/ext/carray_scatter.c +4 -0
- data/ext/carray_segment.c +1 -1
- data/ext/carray_slab.c +13 -4
- data/ext/carray_sort.c +6 -3
- data/ext/carray_sort_kernel.c +24 -0
- data/ext/carray_struct.c +21 -6
- data/ext/carray_test.c +85 -39
- data/ext/carray_time_text.c +6 -0
- data/ext/carray_utils.c +124 -54
- data/ext/mk_call_cfunc.rb +6 -11
- data/ext/mkkernel.rb +37 -27
- data/ext/version.h +4 -4
- data/lib/carray/axis_group.rb +9 -6
- data/lib/carray/basics.rb +26 -8
- data/lib/carray/bincount_nd.rb +8 -2
- data/lib/carray/block_iterator.rb +48 -21
- data/lib/carray/categorical.rb +21 -8
- data/lib/carray/categorical_iterator.rb +2 -1
- data/lib/carray/conditional.rb +52 -4
- data/lib/carray/const_string.rb +74 -0
- data/lib/carray/construct.rb +2 -2
- data/lib/carray/core_extensions.rb +19 -68
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/fixlen_string.rb +83 -1
- data/lib/carray/frame/column_verbs.rb +61 -6
- data/lib/carray/frame/concat.rb +50 -6
- data/lib/carray/frame/csv_parser.rb +13 -4
- data/lib/carray/frame/frame.rb +38 -6
- data/lib/carray/frame/group.rb +67 -40
- data/lib/carray/frame/io.rb +10 -5
- data/lib/carray/frame/join.rb +20 -6
- data/lib/carray/frame/records.rb +6 -4
- data/lib/carray/frame/reshape.rb +25 -5
- data/lib/carray/frame/verbs.rb +78 -25
- data/lib/carray/frame/windows.rb +30 -6
- data/lib/carray/fuse_source.rb +4 -2
- data/lib/carray/iterator.rb +8 -0
- data/lib/carray/key_windows.rb +10 -4
- data/lib/carray/lazy.rb +12 -17
- data/lib/carray/methods/bin.rb +3 -0
- data/lib/carray/methods/composition.rb +1 -1
- data/lib/carray/methods/factorize.rb +2 -0
- data/lib/carray/methods/gather_nd.rb +4 -1
- data/lib/carray/methods/is_in.rb +23 -3
- data/lib/carray/methods/locate_addr.rb +63 -12
- data/lib/carray/methods/repeat.rb +11 -1
- data/lib/carray/record_order.rb +35 -6
- data/lib/carray/runtime.rb +10 -0
- data/lib/carray/segment_iterator.rb +19 -16
- data/lib/carray/serialize.rb +22 -0
- data/lib/carray/slab_iterator.rb +1 -1
- data/lib/carray/stack.rb +3 -3
- data/lib/carray/string.rb +6 -6
- data/lib/carray/string_operation_extension.rb +25 -4
- data/lib/carray/struct_builder.rb +4 -1
- data/lib/carray/time.rb +190 -46
- data/lib/carray/window_iterator.rb +48 -12
- data/yard-stubs/ca_obj_bitfield.rb +4 -1
- data/yard-stubs/ca_obj_refer.rb +3 -0
- data/yard-stubs/ca_obj_window.rb +2 -1
- data/yard-stubs/carray_access.rb +4 -2
- data/yard-stubs/carray_cast.rb +6 -0
- data/yard-stubs/carray_count.rb +6 -3
- data/yard-stubs/carray_math.rb +5 -0
- data/yard-stubs/carray_order.rb +6 -0
- data/yard-stubs/carray_scatter.rb +2 -1
- data/yard-stubs/carray_test.rb +18 -5
- metadata +2 -2
|
@@ -59,7 +59,17 @@ class CArray
|
|
|
59
59
|
end
|
|
60
60
|
CArray.int64(width).fill(count)
|
|
61
61
|
else
|
|
62
|
-
|
|
62
|
+
unless count.respond_to?(:to_ca)
|
|
63
|
+
raise TypeError, "repeat: count must be an Integer or a list of them (got #{count.class})"
|
|
64
|
+
end
|
|
65
|
+
given = count.to_ca
|
|
66
|
+
# A list arrives as an object array; a Float in it is no count, as a
|
|
67
|
+
# float array is not.
|
|
68
|
+
unless given.integer? || (given.data_type == CA_OBJECT &&
|
|
69
|
+
given.to_a.flatten.all? { |c| c.is_a?(Integer) || c.equal?(UNDEF) })
|
|
70
|
+
raise TypeError, "repeat: counts must be Integers"
|
|
71
|
+
end
|
|
72
|
+
given = given.int64
|
|
63
73
|
unless given.elements == width
|
|
64
74
|
raise ArgumentError,
|
|
65
75
|
"repeat: #{given.elements} counts for #{width} " \
|
data/lib/carray/record_order.rb
CHANGED
|
@@ -52,20 +52,20 @@ class CARecord
|
|
|
52
52
|
define_method(op, &Ractor.make_shareable(proc { |*args, **kw| record_rank.public_send(op, *args, **kw) }))
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
# @overload min(axis: nil, keep_axis: false)
|
|
55
|
+
# @overload min(axis: nil, keep_axis: false, min_count: nil, fill_value: nil)
|
|
56
56
|
# Returns the smallest record by the declared order, skipping masked
|
|
57
57
|
# records; UNDEF when every record is masked. With `axis:` a
|
|
58
58
|
# {CARecord} of the smallest along that axis.
|
|
59
59
|
# @return [CAStruct, CARecord]
|
|
60
|
-
def min (axis: nil, keep_axis: false)
|
|
61
|
-
|
|
60
|
+
def min (axis: nil, keep_axis: false, min_count: nil, fill_value: nil)
|
|
61
|
+
record_extreme_kept(:min_index, axis, keep_axis, min_count, fill_value)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
# @overload max(axis: nil, keep_axis: false)
|
|
64
|
+
# @overload max(axis: nil, keep_axis: false, min_count: nil, fill_value: nil)
|
|
65
65
|
# The largest record; see {#min}.
|
|
66
66
|
# @return [CAStruct, CARecord]
|
|
67
|
-
def max (axis: nil, keep_axis: false)
|
|
68
|
-
|
|
67
|
+
def max (axis: nil, keep_axis: false, min_count: nil, fill_value: nil)
|
|
68
|
+
record_extreme_kept(:max_index, axis, keep_axis, min_count, fill_value)
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
# @overload minmax(axis: nil, keep_axis: false)
|
|
@@ -162,6 +162,28 @@ class CARecord
|
|
|
162
162
|
end
|
|
163
163
|
|
|
164
164
|
# The record at each min_index / max_index, gathered from self.
|
|
165
|
+
# An extreme under the reduction keywords every extreme takes: UNDEF where
|
|
166
|
+
# fewer than `min_count` cells are present, then `fill_value` for UNDEF.
|
|
167
|
+
def record_extreme_kept (index_op, axis, keep_axis, min_count, fill_value)
|
|
168
|
+
out = record_extreme(index_op, axis, keep_axis)
|
|
169
|
+
name = index_op.to_s.sub("_index", "").to_sym
|
|
170
|
+
need = CArray.__present_cells_needed_per_result_cell__(name, min_count, 0)
|
|
171
|
+
if need > 0
|
|
172
|
+
if axis.nil?
|
|
173
|
+
out = UNDEF if count_not_masked < need
|
|
174
|
+
else
|
|
175
|
+
counts = count_not_masked(axis: axis, keep_axis: keep_axis)
|
|
176
|
+
if counts.is_a?(CArray)
|
|
177
|
+
few = counts.lt(need)
|
|
178
|
+
out[few] = UNDEF if few.any
|
|
179
|
+
elsif counts < need
|
|
180
|
+
out = UNDEF
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
CArray.__fill_undefined_result_cells__(out, fill_value)
|
|
185
|
+
end
|
|
186
|
+
|
|
165
187
|
def record_extreme (index_op, axis, keep_axis)
|
|
166
188
|
rank = record_rank
|
|
167
189
|
if axis.nil?
|
|
@@ -175,6 +197,13 @@ class CARecord
|
|
|
175
197
|
return record_extreme_over(index_op, axes, keep_axis)
|
|
176
198
|
end
|
|
177
199
|
axis = normalize_axis(axis, name)
|
|
200
|
+
if ndim == 1
|
|
201
|
+
r = record_extreme(index_op, nil, false)
|
|
202
|
+
return r unless keep_axis
|
|
203
|
+
out = flatten[0..0].copy
|
|
204
|
+
out[0] = r
|
|
205
|
+
return out
|
|
206
|
+
end
|
|
178
207
|
idx = rank.public_send(index_op, axis: axis)
|
|
179
208
|
empty = idx.is_masked
|
|
180
209
|
picked_shape = shape.dup
|
data/lib/carray/runtime.rb
CHANGED
|
@@ -172,5 +172,15 @@ class CArray
|
|
|
172
172
|
end
|
|
173
173
|
private_class_method :define_named_methods
|
|
174
174
|
|
|
175
|
+
# CArray.promote_list, its refusal named after the verb that gave it the
|
|
176
|
+
# list (stack, meld, concatenate, ...), not after promote_list.
|
|
177
|
+
def self.promote_list_for (verb, list, data_type: nil)
|
|
178
|
+
CArray.promote_list(list, data_type: data_type)
|
|
179
|
+
rescue ArgumentError, CArray::DataTypeError => e
|
|
180
|
+
raise unless e.message.start_with?("promote_list: ")
|
|
181
|
+
raise e.class, "#{verb}: #{e.message.delete_prefix("promote_list: ")}"
|
|
182
|
+
end
|
|
183
|
+
private_class_method :promote_list_for
|
|
184
|
+
|
|
175
185
|
end
|
|
176
186
|
|
|
@@ -125,8 +125,7 @@ class CASegmentIterator < CAIterator
|
|
|
125
125
|
# CABlock, whose own #count is the block geometry accessor, so dispatch
|
|
126
126
|
# CArray#count explicitly. (Not fused: a value-equality reduceat would have
|
|
127
127
|
# to reproduce core's cross-type / out-of-range equality exactly.)
|
|
128
|
-
|
|
129
|
-
per_segment(CA_INT64) { |s| cnt.bind_call(s, *args) }
|
|
128
|
+
per_segment(CA_INT64) { |s| count_cells(s, *args) }
|
|
130
129
|
end
|
|
131
130
|
|
|
132
131
|
# @overload count_masked
|
|
@@ -233,8 +232,10 @@ class CASegmentIterator < CAIterator
|
|
|
233
232
|
# as five length-k float64 CArrays (matching `CArray#quantile`): the
|
|
234
233
|
# percentiles at 0 / 25 / 50 / 75 / 100. Empty / all-masked segments are
|
|
235
234
|
# MASKED. For a single fraction q in 0..1 use `percentile(q * 100)`.
|
|
235
|
+
# @param axis [Integer, nil] as for {#percentile}.
|
|
236
236
|
# @return [Array<CArray>]
|
|
237
|
-
def quantile
|
|
237
|
+
def quantile (axis: nil)
|
|
238
|
+
return CArray.__name_errors_after_method__(:quantile) { [0, 25, 50, 75, 100].map { |p| percentile(p, axis: axis) } } if axis
|
|
238
239
|
unless kernel_reads?
|
|
239
240
|
return CArray.__name_errors_after_method__(:quantile) { [0, 25, 50, 75, 100].map { |p| percentile(p) } }
|
|
240
241
|
end
|
|
@@ -297,7 +298,8 @@ class CASegmentIterator < CAIterator
|
|
|
297
298
|
# iff every present value is truthy (empty segment -> true, vacuously).
|
|
298
299
|
# The value data type must be boolean, as for `CArray#all`.
|
|
299
300
|
# @return [CArray]
|
|
300
|
-
def all
|
|
301
|
+
def all (axis: nil)
|
|
302
|
+
return axis_by_core(axis, :all) if axis
|
|
301
303
|
aa = all_any
|
|
302
304
|
aa ? aa[:all] : per_segment(CA_BOOLEAN) { |s| s.all }
|
|
303
305
|
end
|
|
@@ -307,7 +309,8 @@ class CASegmentIterator < CAIterator
|
|
|
307
309
|
# iff some present value is truthy (empty segment -> false). The value
|
|
308
310
|
# data type must be boolean, as for `CArray#any`.
|
|
309
311
|
# @return [CArray]
|
|
310
|
-
def any
|
|
312
|
+
def any (axis: nil)
|
|
313
|
+
return axis_by_core(axis, :any) if axis
|
|
311
314
|
aa = all_any
|
|
312
315
|
aa ? aa[:any] : per_segment(CA_BOOLEAN) { |s| s.any }
|
|
313
316
|
end
|
|
@@ -731,7 +734,9 @@ class CASegmentIterator < CAIterator
|
|
|
731
734
|
|
|
732
735
|
def scan_answered (op)
|
|
733
736
|
# An empty Face has no cell to ask the core about, and nothing to scan.
|
|
734
|
-
|
|
737
|
+
if @value.face? && @value.elements.zero?
|
|
738
|
+
return op == :cumcount ? CArray.int64(*@src_shape) : blank_output(@src_shape)
|
|
739
|
+
end
|
|
735
740
|
answer = core_scan_answer(op)
|
|
736
741
|
unless kernel_scans?(op, answer)
|
|
737
742
|
return scatter_segments(->(shape) { blank_like(answer, shape) }) { |s| s.public_send(op) }
|
|
@@ -757,7 +762,7 @@ class CASegmentIterator < CAIterator
|
|
|
757
762
|
# (the core counts a boolean's running sum in uint64).
|
|
758
763
|
def kernel_scans? (op, answer)
|
|
759
764
|
return false unless CArray.__fast_paths_enabled__(:segment_scan_kernel)
|
|
760
|
-
return false if @value.face? || answer.face? || @value.complex?
|
|
765
|
+
return false if @value.face? || answer.face? || @value.complex? || @value.data_type == CA_FIXLEN
|
|
761
766
|
kernel_type =
|
|
762
767
|
case op
|
|
763
768
|
when :cumcount then CA_INT64
|
|
@@ -798,10 +803,10 @@ class CASegmentIterator < CAIterator
|
|
|
798
803
|
end
|
|
799
804
|
|
|
800
805
|
# A blank array of +shape+ of the same kind as +model+: its data type, or its
|
|
801
|
-
# Face. A
|
|
802
|
-
# held as the surface objects they are.
|
|
806
|
+
# Face. A Face that holds no new value cannot be written cell by cell, so its
|
|
807
|
+
# values are held as the surface objects they are.
|
|
803
808
|
def blank_like (model, shape)
|
|
804
|
-
if model.face? && model.
|
|
809
|
+
if model.face? && !model.__send__(:holds_new_values?)
|
|
805
810
|
CArray.new(CA_OBJECT, shape)
|
|
806
811
|
else
|
|
807
812
|
model.__send__(:blank_like, shape)
|
|
@@ -1042,10 +1047,10 @@ class CASegmentIterator < CAIterator
|
|
|
1042
1047
|
|
|
1043
1048
|
# Whether an output can be built by lifting one. A Face is filled by writing
|
|
1044
1049
|
# surface values into storage, so this needs a Face that can be written
|
|
1045
|
-
# into;
|
|
1050
|
+
# into; another -- a const string's records index a shared pool, a
|
|
1046
1051
|
# categorical's codes index a vocabulary -- has no blank form to fill.
|
|
1047
1052
|
def face_output?
|
|
1048
|
-
@value.face? &&
|
|
1053
|
+
@value.face? && @value.__send__(:holds_new_values?)
|
|
1049
1054
|
end
|
|
1050
1055
|
|
|
1051
1056
|
# Build a length-k typed output by folding each segment's members with the
|
|
@@ -1056,8 +1061,6 @@ class CASegmentIterator < CAIterator
|
|
|
1056
1061
|
# for free (an all-masked segment reduces like an empty one; identity-bearing
|
|
1057
1062
|
# reductions return their identity, ratios return UNDEF).
|
|
1058
1063
|
def per_segment (kind)
|
|
1059
|
-
# a data type the values' own Face answers in names that Face
|
|
1060
|
-
kind = @value if ! kind.is_a?(CArray) && @value.face? && kind == @value.data_type
|
|
1061
1064
|
data_type = kind.is_a?(CArray) ? kind.data_type : kind
|
|
1062
1065
|
out = if kind.nil?
|
|
1063
1066
|
CArray.new(CA_OBJECT, [@k])
|
|
@@ -1066,13 +1069,13 @@ class CASegmentIterator < CAIterator
|
|
|
1066
1069
|
elsif data_type == @value.data_type then @value.bytes
|
|
1067
1070
|
else 0 end
|
|
1068
1071
|
CArray.new(data_type, [@k], bytes: bytes)
|
|
1069
|
-
elsif
|
|
1072
|
+
elsif kind.__send__(:holds_new_values?)
|
|
1070
1073
|
# the core answered in a Face, so the output is one too:
|
|
1071
1074
|
# CATime#min hands back a CATime::Element, which only a CATime
|
|
1072
1075
|
# has anywhere to put
|
|
1073
1076
|
kind.__send__(:blank_like, [@k])
|
|
1074
1077
|
else
|
|
1075
|
-
# a
|
|
1078
|
+
# a Face that holds no new value cannot be filled, so its answers are collected
|
|
1076
1079
|
# as the surface objects they already are
|
|
1077
1080
|
CArray.new(CA_OBJECT, [@k])
|
|
1078
1081
|
end
|
data/lib/carray/serialize.rb
CHANGED
|
@@ -662,6 +662,12 @@ class CArray
|
|
|
662
662
|
case tag
|
|
663
663
|
when "object"
|
|
664
664
|
shape, values, mask, attrs = rest
|
|
665
|
+
# The shape is only a claim until the cells are seen to fill it, and
|
|
666
|
+
# a damaged one can ask for more memory than the machine has.
|
|
667
|
+
unless marshal_cells_fill_shape?(values, shape) &&
|
|
668
|
+
(mask.nil? || marshal_cells_fill_shape?(mask, shape))
|
|
669
|
+
raise ArgumentError, "corrupt CArray Marshal payload (the cells do not fill shape #{shape.inspect})"
|
|
670
|
+
end
|
|
665
671
|
ca = CArray.object(*shape)
|
|
666
672
|
ca[] = values
|
|
667
673
|
if mask
|
|
@@ -683,6 +689,22 @@ class CArray
|
|
|
683
689
|
end
|
|
684
690
|
private_class_method :array_from_marshal_payload
|
|
685
691
|
|
|
692
|
+
# Whether `cells`, nested as #to_a nests them, has the extent of every axis
|
|
693
|
+
# of `shape`. A cell may itself hold an Array, so only shape.size levels
|
|
694
|
+
# are counted.
|
|
695
|
+
def self.marshal_cells_fill_shape? (cells, shape)
|
|
696
|
+
return false unless shape.is_a?(Array) && !shape.empty? &&
|
|
697
|
+
shape.all? { |extent| extent.is_a?(Integer) && extent >= 0 }
|
|
698
|
+
level = [cells]
|
|
699
|
+
shape.each do |extent|
|
|
700
|
+
return false unless level.all? { |row| row.is_a?(Array) && row.size == extent }
|
|
701
|
+
level = level.flat_map { |row| row }
|
|
702
|
+
return true if level.empty?
|
|
703
|
+
end
|
|
704
|
+
true
|
|
705
|
+
end
|
|
706
|
+
private_class_method :marshal_cells_fill_shape?
|
|
707
|
+
|
|
686
708
|
end
|
|
687
709
|
|
|
688
710
|
# Marshal of a view, a mask and a scalar: the {CArray#marshal_dump} payload,
|
data/lib/carray/slab_iterator.rb
CHANGED
|
@@ -204,7 +204,7 @@ class CASlabIterator < CAIterator
|
|
|
204
204
|
return count_not_masked(**kw) if args.empty?
|
|
205
205
|
# CABlock / CAWindow shadow #count with a geometry accessor, and @reference
|
|
206
206
|
# may be such a view, so dispatch CArray#count explicitly for count(v).
|
|
207
|
-
|
|
207
|
+
count_cells(@reference, *args, axis: @slab_axes,
|
|
208
208
|
**CAIterator.reduce_keywords(:count, kw))
|
|
209
209
|
end
|
|
210
210
|
|
data/lib/carray/stack.rb
CHANGED
|
@@ -60,7 +60,7 @@ class CArray
|
|
|
60
60
|
def self.stack (list, axis: 0, data_type: nil)
|
|
61
61
|
raise ArgumentError, "stack: list must not be empty" if list.empty?
|
|
62
62
|
axis = 0 if axis.nil?
|
|
63
|
-
list =
|
|
63
|
+
list = promote_list_for("stack", list, data_type: data_type)
|
|
64
64
|
axis = CArray.normalize_axis(axis, list[0].ndim + 1, "stack")
|
|
65
65
|
CAStack.new(list, axis: axis) # CAStack.new does Face lift internally
|
|
66
66
|
end
|
|
@@ -101,7 +101,7 @@ class CArray
|
|
|
101
101
|
raise ArgumentError, "meld: entries must be CArray (got #{first.class})"
|
|
102
102
|
end
|
|
103
103
|
axis_norm = CArray.normalize_axis(axis, first.ndim, "meld")
|
|
104
|
-
arrays =
|
|
104
|
+
arrays = promote_list_for("meld", arrays, data_type: data_type)
|
|
105
105
|
# Flatten nested CAMeld inputs that share our meld axis: they already
|
|
106
106
|
# describe a segment sequence, so absorbing their parents keeps chain
|
|
107
107
|
# depth at 1 (avoids 2-level xfer_all / reduce chains through the
|
|
@@ -183,7 +183,7 @@ class CArray
|
|
|
183
183
|
"montage: tdim product (#{expected}) must equal list size (#{list.size})"
|
|
184
184
|
end
|
|
185
185
|
|
|
186
|
-
list =
|
|
186
|
+
list = promote_list_for("montage", list, data_type: data_type)
|
|
187
187
|
parent_shape = list[0].shape
|
|
188
188
|
ntile = tdim.size
|
|
189
189
|
nparent = parent_shape.size
|
data/lib/carray/string.rb
CHANGED
|
@@ -115,22 +115,22 @@ class CAString
|
|
|
115
115
|
def mod (other) ; as_text(super) ; end
|
|
116
116
|
# The later String of each cell and `other`, cell by cell.
|
|
117
117
|
# @return [CAString]
|
|
118
|
-
def pmax (other) ; as_text(super) ; end
|
|
118
|
+
def pmax (other) ; refuse_unordered_operands("pmax", other) ; as_text(super) ; end
|
|
119
119
|
# The earlier String of each cell and `other`, cell by cell.
|
|
120
120
|
# @return [CAString]
|
|
121
|
-
def pmin (other) ; as_text(super) ; end
|
|
121
|
+
def pmin (other) ; refuse_unordered_operands("pmin", other) ; as_text(super) ; end
|
|
122
122
|
# Another name of {#pmax} (a String has no NaN to let win).
|
|
123
123
|
# @return [CAString]
|
|
124
|
-
def maximum (other) ; as_text(super) ; end
|
|
124
|
+
def maximum (other) ; refuse_unordered_operands("maximum", other) ; as_text(super) ; end
|
|
125
125
|
# Another name of {#pmin}.
|
|
126
126
|
# @return [CAString]
|
|
127
|
-
def minimum (other) ; as_text(super) ; end
|
|
127
|
+
def minimum (other) ; refuse_unordered_operands("minimum", other) ; as_text(super) ; end
|
|
128
128
|
# Another name of {#pmax}.
|
|
129
129
|
# @return [CAString]
|
|
130
|
-
def fmax (other) ; as_text(super) ; end
|
|
130
|
+
def fmax (other) ; refuse_unordered_operands("fmax", other) ; as_text(super) ; end
|
|
131
131
|
# Another name of {#pmin}.
|
|
132
132
|
# @return [CAString]
|
|
133
|
-
def fmin (other) ; as_text(super) ; end
|
|
133
|
+
def fmin (other) ; refuse_unordered_operands("fmin", other) ; as_text(super) ; end
|
|
134
134
|
|
|
135
135
|
# The String a blank cell holds: the cell a fresh {#template} gives and
|
|
136
136
|
# the one {#shift} fills by default. Frozen, because every blank cell
|
|
@@ -44,6 +44,25 @@ class CArray
|
|
|
44
44
|
end
|
|
45
45
|
private :string_map
|
|
46
46
|
|
|
47
|
+
# An ordering (pmax and its kin, clip) with a value that is not a String
|
|
48
|
+
# is refused as String#< refuses it. nil (no bound, for clip), UNDEF,
|
|
49
|
+
# an Array and a CArray are left to the operation.
|
|
50
|
+
def refuse_unordered_operands (verb, *operands)
|
|
51
|
+
operands.each do |v|
|
|
52
|
+
next if v.nil? || v.is_a?(String) || v.is_a?(Array) || v.is_a?(CArray) || UNDEF.equal?(v)
|
|
53
|
+
name = v.is_a?(Symbol) ? v.inspect : v.class
|
|
54
|
+
raise ArgumentError, "#{verb}: comparison of String with #{name} failed"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
private :refuse_unordered_operands
|
|
58
|
+
|
|
59
|
+
# Clips the strings in byte order (see {CArray#clip}); a bound that is
|
|
60
|
+
# not a String is refused.
|
|
61
|
+
def clip (min, max = nil, *rest, **kw)
|
|
62
|
+
refuse_unordered_operands("clip", min, max)
|
|
63
|
+
super
|
|
64
|
+
end
|
|
65
|
+
|
|
47
66
|
# ---- transforms (String result -> CAString) --------------------------
|
|
48
67
|
|
|
49
68
|
# Per-cell `String#upcase`.
|
|
@@ -93,7 +112,7 @@ class CArray
|
|
|
93
112
|
def encode (*a) ; string_map { |s| s.encode(*a) } ; end
|
|
94
113
|
# Per-cell `String#force_encoding`.
|
|
95
114
|
# @return [CAString] shape and mask of `self` preserved.
|
|
96
|
-
def force_encoding (e) ; string_map { |s| s.force_encoding(e) } ; end
|
|
115
|
+
def force_encoding (e) ; string_map { |s| s.dup.force_encoding(e) } ; end
|
|
97
116
|
# Per-cell `String#scrub`.
|
|
98
117
|
# @return [CAString] shape and mask of `self` preserved.
|
|
99
118
|
def scrub () ; string_map { |s| s.scrub } ; end
|
|
@@ -221,14 +240,16 @@ class CArray
|
|
|
221
240
|
end
|
|
222
241
|
entity = CArray.new(CA_FIXLEN, shape, :bytes => width)
|
|
223
242
|
values.each_with_index { |s, i| entity[i] = s.nil? ? UNDEF : s }
|
|
224
|
-
CAFixlenString.
|
|
243
|
+
text = respond_to?(:encoding) ? encoding : CAFixlenString.__send__(:encoding_of_strings, values)
|
|
244
|
+
CAFixlenString.wrap(entity, encoding: text)
|
|
225
245
|
end
|
|
226
246
|
|
|
227
247
|
# Self (zero-copy) when already a {CAConstString} of the same encoding;
|
|
228
248
|
# otherwise materialises a compact column.
|
|
229
|
-
# @param encoding [Encoding] column encoding
|
|
249
|
+
# @param encoding [Encoding] column encoding; defaults to the encoding
|
|
250
|
+
# the array carries, and to UTF-8 for one that carries none.
|
|
230
251
|
# @return [CAConstString]
|
|
231
|
-
def to_const_string (encoding: Encoding::UTF_8)
|
|
252
|
+
def to_const_string (encoding: respond_to?(:encoding) ? self.encoding : Encoding::UTF_8)
|
|
232
253
|
if is_a?(CAConstString) && encoding == self.encoding
|
|
233
254
|
return self
|
|
234
255
|
end
|
|
@@ -699,7 +699,10 @@ class CAStruct::Builder # :nodoc:
|
|
|
699
699
|
# Declares a bitfield member. Bits accumulate within the
|
|
700
700
|
# current byte; when a byte member follows, the cursor rounds
|
|
701
701
|
# up to the next byte boundary. Only supported in `:struct`
|
|
702
|
-
# (not `:union`) and always requires a name.
|
|
702
|
+
# (not `:union`) and always requires a name. A member of more
|
|
703
|
+
# than one bit keeps the low `bits` bits of an Integer written to
|
|
704
|
+
# it (300 into 3 bits is 4), through an element or the member's
|
|
705
|
+
# column alike; a one-bit member is boolean.
|
|
703
706
|
# @param name [Symbol, String] member name.
|
|
704
707
|
# @param bits [Integer] bit width in `1..64`.
|
|
705
708
|
# @return [void]
|