carray 3.0.1 → 3.0.3
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 +824 -0
- data/README.md +5 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +258 -4
- data/ext/ca_axis_group.c +368 -184
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +90 -41
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +960 -245
- data/ext/ca_kernel_iterator.h +352 -181
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +194 -42
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +5 -4
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +180 -50
- data/ext/ca_obj_face.c +428 -62
- data/ext/ca_obj_face.h +107 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +39 -26
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +235 -55
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +551 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +88 -75
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -123
- data/ext/ca_obj_stride.c +267 -35
- data/ext/ca_obj_string.c +25 -22
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +157 -29
- data/ext/ca_op_ipower.c +25 -13
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +385 -198
- data/ext/ca_sweep_engine.h +31 -23
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +463 -0
- data/ext/carray.h +90 -28
- data/ext/carray_access.c +487 -231
- data/ext/carray_address_basis.c +731 -0
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +49 -17
- data/ext/carray_call_cfunc.c +667 -511
- data/ext/carray_cast.c +1556 -247
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +84 -38
- data/ext/carray_core.c +591 -119
- data/ext/carray_count.c +148 -96
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +604 -291
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +400 -1
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +88 -66
- data/ext/carray_mask.c +705 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +263 -63
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1093 -363
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +582 -204
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +183 -70
- data/ext/carray_sort.c +235 -240
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +103 -118
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1349 -851
- data/ext/ruby_carray.c +17 -2
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +18 -0
- data/lib/carray/axis_group.rb +245 -20
- data/lib/carray/basics.rb +95 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +200 -54
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +244 -59
- data/lib/carray/categorical_iterator.rb +391 -757
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +177 -27
- data/lib/carray/construct.rb +40 -0
- data/lib/carray/data_type_extension.rb +3 -0
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +590 -76
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +205 -35
- data/lib/carray/frame/group.rb +199 -40
- data/lib/carray/frame/io.rb +452 -60
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +54 -19
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +726 -56
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +71 -20
- data/lib/carray/iterator.rb +157 -2
- data/lib/carray/lazy.rb +54 -67
- data/lib/carray/mask_gap_fill.rb +66 -50
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +130 -6
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +235 -21
- data/lib/carray/methods/mask_duplicates.rb +35 -1
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +22 -1
- data/lib/carray/methods/repeat.rb +91 -0
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +52 -3
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +109 -40
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +9 -4
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +535 -105
- data/lib/carray/window_iterator.rb +259 -82
- data/lib/carray.rb +4 -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_block.rb +2 -7
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +11 -4
- data/yard-stubs/carray_access.rb +6 -5
- data/yard-stubs/carray_broadcast.rb +2 -2
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -83
- data/yard-stubs/carray_count.rb +16 -2
- data/yard-stubs/carray_lazy.rb +210 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +1499 -4
- data/yard-stubs/carray_median_percentile.rb +26 -8
- data/yard-stubs/carray_order.rb +43 -82
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +11 -8
- data/yard-stubs/carray_sort.rb +50 -8
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -25837
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14593
- data/ext/carray_kernels_reduce_extreme.c +0 -16948
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32138
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_sort_kernel.c
CHANGED
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
#include <string.h>
|
|
52
52
|
#include <math.h>
|
|
53
53
|
|
|
54
|
-
#define CA_SORT_INSERTION_THRESHOLD 16
|
|
55
54
|
|
|
56
55
|
/* ---------------------------------------------------------------------------
|
|
57
56
|
DEFINE_SORT_MERGE(TYPE, SUFFIX)
|
|
@@ -616,5 +615,13 @@ DEFINE_PARTITION_QUICK(uint64_t, u64);
|
|
|
616
615
|
DEFINE_PARTITION_QUICK(float32_t, f32);
|
|
617
616
|
DEFINE_PARTITION_QUICK(double, f64);
|
|
618
617
|
|
|
618
|
+
/* The pair mergesort above for object pairs, which have no `<`: every
|
|
619
|
+
comparison goes through cmp (see CA_SORT_MERGE_BY_CMP). */
|
|
620
|
+
void
|
|
621
|
+
ca_sort_merge_pair_object (ca_pair_object *a, ca_pair_object *aux,
|
|
622
|
+
ca_size_t n,
|
|
623
|
+
int (*cmp)(const void *, const void *))
|
|
624
|
+
CA_SORT_MERGE_BY_CMP(ca_pair_object)
|
|
625
|
+
|
|
619
626
|
/* No Init function: this file exposes only C kernels (via ca_sort_kernels.h);
|
|
620
627
|
it registers no Ruby methods. */
|
data/ext/carray_struct.c
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
(primitive / bitfield / endian-swap)
|
|
17
17
|
DISPATCH_TABLE hit -> Proc invoked via rb_funcall("call")
|
|
18
18
|
(nested struct / CArray template / fixlen)
|
|
19
|
-
|
|
19
|
+
a Symbol, or a name that is not a member, raises: a member is
|
|
20
|
+
s["name"] or s[position], and its accessor s.name
|
|
20
21
|
|
|
21
22
|
---------------------------------------------------------------------------- */
|
|
22
23
|
|
|
@@ -54,7 +55,7 @@ ca_struct_read_primitive_fast (const char *p, int type_code)
|
|
|
54
55
|
case CA_BOOLEAN: {
|
|
55
56
|
int8_t v;
|
|
56
57
|
memcpy(&v, p, 1);
|
|
57
|
-
return v ?
|
|
58
|
+
return v ? Qtrue : Qfalse;
|
|
58
59
|
}
|
|
59
60
|
case CA_INT8: {
|
|
60
61
|
int8_t v;
|
|
@@ -116,7 +117,15 @@ ca_struct_write_primitive_fast (char *p, int type_code, VALUE val)
|
|
|
116
117
|
{
|
|
117
118
|
switch (type_code) {
|
|
118
119
|
case CA_BOOLEAN: {
|
|
119
|
-
|
|
120
|
+
/* The values a boolean array takes: true / false / 1 / 0. */
|
|
121
|
+
int8_t v;
|
|
122
|
+
if ( val == Qtrue || val == INT2FIX(1) ) v = 1;
|
|
123
|
+
else if ( val == Qfalse || val == INT2FIX(0) ) v = 0;
|
|
124
|
+
else {
|
|
125
|
+
rb_raise(rb_eCADataTypeError,
|
|
126
|
+
"can't cast object '%"PRIsVALUE"' to <boolean>",
|
|
127
|
+
rb_inspect(val));
|
|
128
|
+
}
|
|
120
129
|
memcpy(p, &v, 1);
|
|
121
130
|
return;
|
|
122
131
|
}
|
|
@@ -201,8 +210,10 @@ ca_struct_read_bitfield_fast (const char *p, int view_bytes,
|
|
|
201
210
|
uint64_t mask = (bits >= 64) ? ~(uint64_t)0
|
|
202
211
|
: ((uint64_t)1 << bits) - 1;
|
|
203
212
|
uint64_t val = (word >> bit_in_word) & mask;
|
|
204
|
-
/*
|
|
213
|
+
/* A 1-bit field is a boolean, as a record array's column of it is.
|
|
214
|
+
Wider fields are unsigned Integers; bit-fields don't carry a sign
|
|
205
215
|
bit in carray's CABitfield convention either. */
|
|
216
|
+
if (bits == 1) return val ? Qtrue : Qfalse;
|
|
206
217
|
if (bits <= 31) return INT2FIX((long) val);
|
|
207
218
|
if (bits <= 32) return ULONG2NUM((unsigned long) val);
|
|
208
219
|
return ULL2NUM(val);
|
|
@@ -212,7 +223,19 @@ static void
|
|
|
212
223
|
ca_struct_write_bitfield_fast (char *p, int view_bytes,
|
|
213
224
|
int bit_in_word, int bits, VALUE val)
|
|
214
225
|
{
|
|
215
|
-
uint64_t v
|
|
226
|
+
uint64_t v;
|
|
227
|
+
if ( bits == 1 ) {
|
|
228
|
+
if ( val == Qtrue || val == INT2FIX(1) ) v = 1;
|
|
229
|
+
else if ( val == Qfalse || val == INT2FIX(0) ) v = 0;
|
|
230
|
+
else {
|
|
231
|
+
rb_raise(rb_eCADataTypeError,
|
|
232
|
+
"can't cast object '%"PRIsVALUE"' to <boolean>",
|
|
233
|
+
rb_inspect(val));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
v = NUM2ULL(val);
|
|
238
|
+
}
|
|
216
239
|
uint64_t mask = (bits >= 64) ? ~(uint64_t)0
|
|
217
240
|
: ((uint64_t)1 << bits) - 1;
|
|
218
241
|
v &= mask;
|
|
@@ -384,6 +407,45 @@ ca_struct_resolve_name (VALUE klass, VALUE name)
|
|
|
384
407
|
return rb_funcall(name, rb_intern("to_s"), 0);
|
|
385
408
|
}
|
|
386
409
|
|
|
410
|
+
/* The member an element's `[]` / `[]=` names: a String naming a member,
|
|
411
|
+
or an Integer position. A Symbol is not a member name here -- the
|
|
412
|
+
member is s["name"] or the accessor s.name -- and a name that is not a
|
|
413
|
+
member raises rather than calling a method of that name. */
|
|
414
|
+
static VALUE
|
|
415
|
+
ca_struct_member_name (VALUE klass, VALUE name)
|
|
416
|
+
{
|
|
417
|
+
VALUE name_s;
|
|
418
|
+
if ( SYMBOL_P(name) ) {
|
|
419
|
+
VALUE str = rb_sym2str(name);
|
|
420
|
+
if ( ! NIL_P(rb_hash_aref(rb_const_get(klass, rb_intern("MEMBER_TABLE")),
|
|
421
|
+
str)) ) {
|
|
422
|
+
rb_raise(rb_eTypeError,
|
|
423
|
+
"a struct member is named by a String (s[\"%"PRIsVALUE"\"]) or "
|
|
424
|
+
"read by its accessor (s.%"PRIsVALUE"), not by a Symbol",
|
|
425
|
+
str, str);
|
|
426
|
+
}
|
|
427
|
+
rb_raise(rb_eTypeError,
|
|
428
|
+
"a struct member is named by a String, not by a Symbol "
|
|
429
|
+
"(%"PRIsVALUE" has no member %"PRIsVALUE" either)",
|
|
430
|
+
klass, rb_inspect(str));
|
|
431
|
+
}
|
|
432
|
+
if ( ! RB_TYPE_P(name, T_STRING) && ! RB_TYPE_P(name, T_FIXNUM) ) {
|
|
433
|
+
rb_raise(rb_eTypeError,
|
|
434
|
+
"a struct member is named by a String or an Integer position "
|
|
435
|
+
"(got %s)", rb_obj_classname(name));
|
|
436
|
+
}
|
|
437
|
+
name_s = ca_struct_resolve_name(klass, name);
|
|
438
|
+
if ( NIL_P(name_s)
|
|
439
|
+
|| NIL_P(rb_hash_aref(rb_const_get(klass, rb_intern("MEMBER_TABLE")),
|
|
440
|
+
name_s)) ) {
|
|
441
|
+
rb_raise(rb_eArgError, "%"PRIsVALUE" has no member %"PRIsVALUE
|
|
442
|
+
" (members: %"PRIsVALUE")",
|
|
443
|
+
klass, rb_inspect(name),
|
|
444
|
+
rb_inspect(rb_const_get(klass, rb_intern("MEMBERS"))));
|
|
445
|
+
}
|
|
446
|
+
return name_s;
|
|
447
|
+
}
|
|
448
|
+
|
|
387
449
|
/* ---------------------------------------------------------------------
|
|
388
450
|
Shared write-dispatch helper reused by `[]=` and `initialize`.
|
|
389
451
|
Returns 1 if the member was written, 0 if neither table contained
|
|
@@ -446,7 +508,7 @@ static VALUE
|
|
|
446
508
|
rb_ca_struct_aref (VALUE self, VALUE name)
|
|
447
509
|
{
|
|
448
510
|
VALUE klass = rb_obj_class(self);
|
|
449
|
-
VALUE name_s =
|
|
511
|
+
VALUE name_s = ca_struct_member_name(klass, name);
|
|
450
512
|
|
|
451
513
|
/* Fast path. FAST_PRIMITIVES may be absent on subclasses built
|
|
452
514
|
outside Builder — fall through to the dispatch table in that
|
|
@@ -454,12 +516,14 @@ rb_ca_struct_aref (VALUE self, VALUE name)
|
|
|
454
516
|
if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
|
|
455
517
|
VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
|
|
456
518
|
VALUE entry = rb_hash_aref(fast_table, name_s);
|
|
457
|
-
|
|
519
|
+
VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
|
|
520
|
+
CArray *ca;
|
|
521
|
+
TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
|
|
522
|
+
/* The fast path reads ca->ptr, which only an entity holds. A nested
|
|
523
|
+
element's @data is a view on its parent's bytes and goes through the
|
|
524
|
+
dispatch procs below, which reach them through field views. */
|
|
525
|
+
if (!NIL_P(entry) && ca_is_entity(ca)) {
|
|
458
526
|
int kind = NUM2INT(rb_ary_entry(entry, 0));
|
|
459
|
-
VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
|
|
460
|
-
CArray *ca;
|
|
461
|
-
TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
|
|
462
|
-
/* @data is a CScalar entity, so ca->ptr is always valid. */
|
|
463
527
|
switch (kind) {
|
|
464
528
|
case FAST_KIND_PRIMITIVE: {
|
|
465
529
|
/* [kind, offset, type_code] */
|
|
@@ -502,8 +566,9 @@ rb_ca_struct_aref (VALUE self, VALUE name)
|
|
|
502
566
|
return rb_funcall(reader, rb_intern("call"), 1, data_val);
|
|
503
567
|
}
|
|
504
568
|
|
|
505
|
-
|
|
506
|
-
|
|
569
|
+
rb_raise(rb_eArgError, "%"PRIsVALUE" has no reader for member %"PRIsVALUE,
|
|
570
|
+
klass, name_s);
|
|
571
|
+
return Qnil;
|
|
507
572
|
}
|
|
508
573
|
|
|
509
574
|
/* ---------------------------------------------------------------------
|
|
@@ -514,18 +579,31 @@ static VALUE
|
|
|
514
579
|
rb_ca_struct_aset (VALUE self, VALUE name, VALUE val)
|
|
515
580
|
{
|
|
516
581
|
VALUE klass = rb_obj_class(self);
|
|
517
|
-
VALUE name_s
|
|
582
|
+
VALUE name_s;
|
|
583
|
+
|
|
584
|
+
/* The fast path writes into the record's bytes directly. */
|
|
585
|
+
rb_check_frozen(self);
|
|
586
|
+
name_s = ca_struct_member_name(klass, name);
|
|
587
|
+
|
|
588
|
+
/* An element is one record of values: it has no mask to hold UNDEF. A
|
|
589
|
+
record is masked in its record array (a[i] = UNDEF). */
|
|
590
|
+
if ( val == CA_UNDEF ) {
|
|
591
|
+
rb_raise(rb_eTypeError,
|
|
592
|
+
"a struct element has no masked state; mask the record in its "
|
|
593
|
+
"record array instead");
|
|
594
|
+
}
|
|
518
595
|
|
|
519
596
|
/* Fast path (see rb_ca_struct_aref for the absent-FAST_PRIMITIVES
|
|
520
597
|
rationale and entry-shape contract). */
|
|
521
598
|
if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
|
|
522
599
|
VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
|
|
523
600
|
VALUE entry = rb_hash_aref(fast_table, name_s);
|
|
524
|
-
|
|
601
|
+
VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
|
|
602
|
+
CArray *ca;
|
|
603
|
+
TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
|
|
604
|
+
/* Only an entity @data is written through ca->ptr (see the reader). */
|
|
605
|
+
if (!NIL_P(entry) && ca_is_entity(ca)) {
|
|
525
606
|
int kind = NUM2INT(rb_ary_entry(entry, 0));
|
|
526
|
-
VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
|
|
527
|
-
CArray *ca;
|
|
528
|
-
TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
|
|
529
607
|
switch (kind) {
|
|
530
608
|
case FAST_KIND_PRIMITIVE: {
|
|
531
609
|
ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
|
|
@@ -565,9 +643,9 @@ rb_ca_struct_aset (VALUE self, VALUE name, VALUE val)
|
|
|
565
643
|
return val;
|
|
566
644
|
}
|
|
567
645
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
return
|
|
646
|
+
rb_raise(rb_eArgError, "%"PRIsVALUE" has no writer for member %"PRIsVALUE,
|
|
647
|
+
klass, name_s);
|
|
648
|
+
return Qnil;
|
|
571
649
|
}
|
|
572
650
|
|
|
573
651
|
/* ---------------------------------------------------------------------
|
data/ext/carray_test.c
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
---------------------------------------------------------------------------- */
|
|
11
11
|
|
|
12
12
|
#include "carray.h"
|
|
13
|
+
#include "carray_internal.h" /* ca_attach_window */
|
|
14
|
+
#include "ca_obj_face.h" /* ca_face_equal */
|
|
13
15
|
|
|
14
16
|
static ID id_equal;
|
|
15
17
|
static ID id_eql;
|
|
@@ -26,13 +28,34 @@ ca_check_type (void *ap, int8_t data_type)
|
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
/* For `initialize` and `initialize_copy`: raises unless the struct is as
|
|
32
|
+
the allocator left it. Setting up a struct that is already set up
|
|
33
|
+
would overwrite what it owns (its buffer, its shape arrays, its parent
|
|
34
|
+
list) without freeing it.
|
|
35
|
+
|
|
36
|
+
What is tested is dim: NULL means the struct owns nothing yet. That
|
|
37
|
+
holds on three conditions, which a new array class has to keep:
|
|
38
|
+
- its allocator zero-fills the struct (TypedData_Make_Struct does);
|
|
39
|
+
- its setup gives the struct a dim (there is no array without one);
|
|
40
|
+
- its `initialize` runs the argument checks before it takes the pool
|
|
41
|
+
or anything else (ca_array_pool_alloc sets dim), so that a rejected
|
|
42
|
+
argument leaves dim NULL and the object can be initialized again. */
|
|
43
|
+
void
|
|
44
|
+
ca_check_uninitialized (void *ap)
|
|
45
|
+
{
|
|
46
|
+
CArray *ca = (CArray *) ap;
|
|
47
|
+
if ( ca->dim != NULL ) {
|
|
48
|
+
rb_raise(rb_eTypeError, "already initialized array");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
29
52
|
void
|
|
30
53
|
ca_check_ndim (void *ap, int ndim)
|
|
31
54
|
{
|
|
32
55
|
CArray *ca = (CArray *) ap;
|
|
33
56
|
if ( ! ca_is_scalar(ca) ) {
|
|
34
57
|
if ( ca->ndim != ndim ) {
|
|
35
|
-
rb_raise(
|
|
58
|
+
rb_raise(rb_eArgError, "ndim mismatch");
|
|
36
59
|
}
|
|
37
60
|
}
|
|
38
61
|
}
|
|
@@ -44,11 +67,11 @@ ca_check_shape (void *ap, int ndim, ca_size_t *dim)
|
|
|
44
67
|
int i;
|
|
45
68
|
if ( ! ca_is_scalar(ca) ) {
|
|
46
69
|
if ( ca->ndim != ndim ) {
|
|
47
|
-
rb_raise(
|
|
70
|
+
rb_raise(rb_eArgError, "shape mismatch");
|
|
48
71
|
}
|
|
49
72
|
for (i=0; i<ndim; i++) {
|
|
50
73
|
if ( ca->dim[i] != dim[i] ) {
|
|
51
|
-
rb_raise(
|
|
74
|
+
rb_raise(rb_eArgError, "shape mismatch");
|
|
52
75
|
}
|
|
53
76
|
}
|
|
54
77
|
}
|
|
@@ -70,7 +93,7 @@ ca_check_same_ndim (void *ap1, void *ap2)
|
|
|
70
93
|
CArray *ca1 = (CArray *) ap1;
|
|
71
94
|
CArray *ca2 = (CArray *) ap2;
|
|
72
95
|
if ( ca1->ndim != ca2->ndim ) {
|
|
73
|
-
rb_raise(
|
|
96
|
+
rb_raise(rb_eArgError, "ndim mismatch");
|
|
74
97
|
}
|
|
75
98
|
}
|
|
76
99
|
|
|
@@ -80,7 +103,7 @@ ca_check_same_elements (void *ap1, void *ap2)
|
|
|
80
103
|
CArray *ca1 = (CArray *) ap1;
|
|
81
104
|
CArray *ca2 = (CArray *) ap2;
|
|
82
105
|
if ( ca1->elements != ca2->elements ) {
|
|
83
|
-
rb_raise(
|
|
106
|
+
rb_raise(rb_eArgError, "elements mismatch");
|
|
84
107
|
}
|
|
85
108
|
}
|
|
86
109
|
|
|
@@ -92,11 +115,11 @@ ca_check_same_shape (void *ap1, void *ap2)
|
|
|
92
115
|
int i;
|
|
93
116
|
if ( ( ! ca_is_scalar(ca1) ) && ( ! ca_is_scalar(ca2) ) ) {
|
|
94
117
|
if ( ca1->ndim != ca2->ndim ) {
|
|
95
|
-
rb_raise(
|
|
118
|
+
rb_raise(rb_eArgError, "shape mismatch");
|
|
96
119
|
}
|
|
97
120
|
for (i=0; i<ca1->ndim; i++) {
|
|
98
121
|
if ( ca1->dim[i] != ca2->dim[i] ) {
|
|
99
|
-
rb_raise(
|
|
122
|
+
rb_raise(rb_eArgError, "shape mismatch");
|
|
100
123
|
}
|
|
101
124
|
}
|
|
102
125
|
}
|
|
@@ -109,7 +132,7 @@ ca_check_index (void *ap, ca_size_t *idx)
|
|
|
109
132
|
int i;
|
|
110
133
|
for (i=0; i<ca->ndim; i++) {
|
|
111
134
|
if ( idx[i] < 0 || idx[i] >= ca->dim[i] ) {
|
|
112
|
-
rb_raise(
|
|
135
|
+
rb_raise(rb_eIndexError, "invalid index");
|
|
113
136
|
}
|
|
114
137
|
}
|
|
115
138
|
}
|
|
@@ -118,7 +141,7 @@ void
|
|
|
118
141
|
rb_check_carray_object (VALUE arg)
|
|
119
142
|
{
|
|
120
143
|
if ( ! rb_obj_is_carray(arg) ) {
|
|
121
|
-
rb_raise(
|
|
144
|
+
rb_raise(rb_eTypeError, "CArray required");
|
|
122
145
|
}
|
|
123
146
|
}
|
|
124
147
|
|
|
@@ -166,7 +189,7 @@ rb_ca_is_type (VALUE arg, int type)
|
|
|
166
189
|
{
|
|
167
190
|
CArray *ca;
|
|
168
191
|
if ( ! rb_obj_is_carray(arg) ) {
|
|
169
|
-
rb_raise(
|
|
192
|
+
rb_raise(rb_eTypeError, "CArray required");
|
|
170
193
|
}
|
|
171
194
|
TypedData_Get_Struct(arg, CArray, &carray_data_type, ca);
|
|
172
195
|
return ca->data_type == type;
|
|
@@ -296,25 +319,20 @@ ca_eql[CA_NTYPE] = {
|
|
|
296
319
|
eql_VALUE,
|
|
297
320
|
};
|
|
298
321
|
|
|
299
|
-
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
* == : ca_eql[data_type] (type-specific, so float NaN != NaN)
|
|
309
|
-
* eql?: memcmp for numeric, rb_funcall(:eql?) for CA_OBJECT
|
|
310
|
-
*
|
|
311
|
-
* The unmasked-unmasked branch takes a bulk memcmp or per-cell
|
|
312
|
-
* shortcut to amortise per-element function pointer dispatch. */
|
|
313
|
-
static int
|
|
314
|
-
ca_compare_common (void *ap, void *bp, int strict)
|
|
322
|
+
typedef struct {
|
|
323
|
+
CArray *ca;
|
|
324
|
+
CArray *cb;
|
|
325
|
+
int strict;
|
|
326
|
+
int flag;
|
|
327
|
+
} ca_compare_ctx_t;
|
|
328
|
+
|
|
329
|
+
static VALUE
|
|
330
|
+
ca_compare_body (VALUE arg)
|
|
315
331
|
{
|
|
316
|
-
|
|
317
|
-
CArray *
|
|
332
|
+
ca_compare_ctx_t *c = (ca_compare_ctx_t *) arg;
|
|
333
|
+
CArray *ca = c->ca;
|
|
334
|
+
CArray *cb = c->cb;
|
|
335
|
+
int strict = c->strict;
|
|
318
336
|
int flag = 1;
|
|
319
337
|
int masked_a, masked_b;
|
|
320
338
|
boolean8_t *ma, *mb;
|
|
@@ -325,28 +343,6 @@ ca_compare_common (void *ap, void *bp, int strict)
|
|
|
325
343
|
ca_eql_func eql = NULL; /* used only when !strict */
|
|
326
344
|
int is_object;
|
|
327
345
|
|
|
328
|
-
if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ) {
|
|
329
|
-
return 0;
|
|
330
|
-
}
|
|
331
|
-
if ( ca->data_type != cb->data_type ) {
|
|
332
|
-
return 0;
|
|
333
|
-
}
|
|
334
|
-
if ( ca->bytes != cb->bytes ) {
|
|
335
|
-
return 0;
|
|
336
|
-
}
|
|
337
|
-
if ( ca->ndim != cb->ndim ) {
|
|
338
|
-
return 0;
|
|
339
|
-
}
|
|
340
|
-
if ( ca->elements != cb->elements ) {
|
|
341
|
-
return 0;
|
|
342
|
-
}
|
|
343
|
-
for (i=0; i<ca->ndim; i++) {
|
|
344
|
-
if ( ca->dim[i] != cb->dim[i] ) {
|
|
345
|
-
return 0;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
ca_attach_n(2, ca, cb); /* recurses into ca->mask / cb->mask */
|
|
350
346
|
|
|
351
347
|
masked_a = ca_is_any_masked(ca);
|
|
352
348
|
masked_b = ca_is_any_masked(cb);
|
|
@@ -441,9 +437,66 @@ ca_compare_common (void *ap, void *bp, int strict)
|
|
|
441
437
|
}
|
|
442
438
|
}
|
|
443
439
|
|
|
444
|
-
|
|
440
|
+
c->flag = flag;
|
|
441
|
+
return Qnil;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/* ca_compare_common(a, b, strict) — shared body of ca_equal (==) and
|
|
445
|
+
* ca_eql_strict (eql?). Returns 1 if arrays match under the selected
|
|
446
|
+
* semantics, else 0.
|
|
447
|
+
*
|
|
448
|
+
* Common structure: metadata pre-check (scalar / data_type / bytes /
|
|
449
|
+
* ndim / elements / dim), then ca_compare_body in a window over both
|
|
450
|
+
* arrays (attaching an array attaches its mask), a unified loop over the
|
|
451
|
+
* four masked/unmasked combinations.
|
|
452
|
+
*
|
|
453
|
+
* The only difference is the per-element comparator:
|
|
454
|
+
* == : ca_eql[data_type] (type-specific, so float NaN != NaN)
|
|
455
|
+
* eql?: memcmp for numeric, rb_funcall(:eql?) for CA_OBJECT
|
|
456
|
+
*
|
|
457
|
+
* The unmasked-unmasked branch takes a bulk memcmp or per-cell
|
|
458
|
+
* shortcut to amortise per-element function pointer dispatch. */
|
|
459
|
+
static int
|
|
460
|
+
ca_compare_common (void *ap, void *bp, int strict)
|
|
461
|
+
{
|
|
462
|
+
CArray *ca = (CArray *) ap;
|
|
463
|
+
CArray *cb = (CArray *) bp;
|
|
464
|
+
int8_t i;
|
|
465
|
+
|
|
466
|
+
if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ) {
|
|
467
|
+
return 0;
|
|
468
|
+
}
|
|
469
|
+
if ( ca->data_type != cb->data_type ) {
|
|
470
|
+
return 0;
|
|
471
|
+
}
|
|
472
|
+
if ( ca->bytes != cb->bytes ) {
|
|
473
|
+
return 0;
|
|
474
|
+
}
|
|
475
|
+
if ( ca->ndim != cb->ndim ) {
|
|
476
|
+
return 0;
|
|
477
|
+
}
|
|
478
|
+
if ( ca->elements != cb->elements ) {
|
|
479
|
+
return 0;
|
|
480
|
+
}
|
|
481
|
+
for (i=0; i<ca->ndim; i++) {
|
|
482
|
+
if ( ca->dim[i] != cb->dim[i] ) {
|
|
483
|
+
return 0;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
445
486
|
|
|
446
|
-
|
|
487
|
+
{
|
|
488
|
+
ca_compare_ctx_t c;
|
|
489
|
+
CArray *list[2];
|
|
490
|
+
c.ca = ca;
|
|
491
|
+
c.cb = cb;
|
|
492
|
+
c.strict = strict;
|
|
493
|
+
c.flag = 1;
|
|
494
|
+
list[0] = ca;
|
|
495
|
+
list[1] = cb;
|
|
496
|
+
/* The object lane calls #== / #eql? with both arrays attached. */
|
|
497
|
+
ca_attach_window(2, list, "rr", ca_compare_body, (VALUE) &c);
|
|
498
|
+
return c.flag;
|
|
499
|
+
}
|
|
447
500
|
}
|
|
448
501
|
|
|
449
502
|
int
|
|
@@ -452,8 +505,8 @@ ca_equal (void *ap, void *bp)
|
|
|
452
505
|
return ca_compare_common(ap, bp, 0);
|
|
453
506
|
}
|
|
454
507
|
|
|
455
|
-
|
|
456
|
-
|
|
508
|
+
int
|
|
509
|
+
ca_eql_storage (void *ap, void *bp)
|
|
457
510
|
{
|
|
458
511
|
return ca_compare_common(ap, bp, 1);
|
|
459
512
|
}
|
|
@@ -487,6 +540,10 @@ rb_ca_equal (VALUE self, VALUE other)
|
|
|
487
540
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
488
541
|
TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
|
|
489
542
|
|
|
543
|
+
if ( ! rb_ca_has_data_class(self) && ( ca_is_face(ca) || ca_is_face(cb) ) ) {
|
|
544
|
+
return ca_face_equal(self, other, 0) ? Qtrue : Qfalse;
|
|
545
|
+
}
|
|
546
|
+
|
|
490
547
|
return ( ca_equal(ca, cb) ) ? Qtrue : Qfalse;
|
|
491
548
|
}
|
|
492
549
|
|
|
@@ -519,13 +576,20 @@ rb_ca_eql (VALUE self, VALUE other)
|
|
|
519
576
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
520
577
|
TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
|
|
521
578
|
|
|
522
|
-
|
|
579
|
+
if ( ! rb_ca_has_data_class(self) && ( ca_is_face(ca) || ca_is_face(cb) ) ) {
|
|
580
|
+
return ca_face_equal(self, other, 1) ? Qtrue : Qfalse;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return ( ca_eql_storage(ca, cb) ) ? Qtrue : Qfalse;
|
|
523
584
|
}
|
|
524
585
|
|
|
525
586
|
/* ca_hash(ca) — cheap Hash key for CArray. Mixes metadata (data_type,
|
|
526
587
|
* ndim, bytes, elements, scalar-ness, shape, mask presence) and, for
|
|
527
|
-
* unmasked arrays only, samples the leading CA_HASH_SAMPLE_BYTES
|
|
528
|
-
* data
|
|
588
|
+
* unmasked arrays only, samples the leading cells (CA_HASH_SAMPLE_BYTES
|
|
589
|
+
* of data, at least one cell), read by address so a view is not
|
|
590
|
+
* materialised for them. An object array samples the #hash of those
|
|
591
|
+
* cells, matching eql?, which compares them with #eql?. Masked arrays
|
|
592
|
+
* skip the data sample entirely — otherwise
|
|
529
593
|
* masked positions would have to be zeroed in the sample to preserve
|
|
530
594
|
* the eql?→hash invariant, and masked CArrays are rare as Hash keys.
|
|
531
595
|
* The collision rate cost buys no ca->mask->ptr access and no
|
|
@@ -557,9 +621,36 @@ ca_hash (CArray *ca)
|
|
|
557
621
|
h ^= rb_memhash(&masked_flag, sizeof(masked_flag));
|
|
558
622
|
|
|
559
623
|
if ( ! masked_flag && sample > 0 ) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
624
|
+
ca_size_t addrs[CA_HASH_SAMPLE_BYTES];
|
|
625
|
+
ca_size_t ncell = sample / ca->bytes;
|
|
626
|
+
ca_size_t i;
|
|
627
|
+
if ( ncell < 1 ) {
|
|
628
|
+
ncell = 1;
|
|
629
|
+
}
|
|
630
|
+
if ( ncell > ca->elements ) {
|
|
631
|
+
ncell = ca->elements;
|
|
632
|
+
}
|
|
633
|
+
for (i = 0; i < ncell; i++) {
|
|
634
|
+
addrs[i] = i;
|
|
635
|
+
}
|
|
636
|
+
if ( ca_is_object_type(ca) ) {
|
|
637
|
+
/* eql? compares object cells with #eql?, so the sample is their
|
|
638
|
+
#hash, not the VALUE bits. The cells sit on the stack, where the
|
|
639
|
+
GC sees them while #hash runs. */
|
|
640
|
+
VALUE cells[CA_HASH_SAMPLE_BYTES / sizeof(VALUE)];
|
|
641
|
+
ca_xfer_addrs(ca, ncell, addrs, cells, CA_XFER_GET);
|
|
642
|
+
for (i = 0; i < ncell; i++) {
|
|
643
|
+
h = rb_hash_uint(h, (st_index_t) NUM2LONG(rb_hash(cells[i])));
|
|
644
|
+
}
|
|
645
|
+
RB_GC_GUARD(cells[0]);
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
volatile VALUE holder;
|
|
649
|
+
char *buf = ALLOCV_N(char, holder, ncell * ca->bytes);
|
|
650
|
+
ca_xfer_addrs(ca, ncell, addrs, buf, CA_XFER_GET);
|
|
651
|
+
h ^= rb_memhash(buf, ncell * ca->bytes);
|
|
652
|
+
ALLOCV_END(holder);
|
|
653
|
+
}
|
|
563
654
|
}
|
|
564
655
|
|
|
565
656
|
return h;
|
|
@@ -642,6 +733,9 @@ without_read_only_ensure (VALUE arg)
|
|
|
642
733
|
struct without_read_only_state *s = (struct without_read_only_state *) arg;
|
|
643
734
|
if (s->was_readonly) {
|
|
644
735
|
ca_set_flag(s->ca, CA_FLAG_READ_ONLY);
|
|
736
|
+
if (s->ca->mask) { /* the block may have replaced it */
|
|
737
|
+
ca_set_flag(s->ca->mask, CA_FLAG_READ_ONLY);
|
|
738
|
+
}
|
|
645
739
|
}
|
|
646
740
|
return Qnil;
|
|
647
741
|
}
|
|
@@ -679,10 +773,16 @@ without_read_only_ensure (VALUE arg)
|
|
|
679
773
|
* immutable (producer's contract) but the mask slot is CArray-side metadata
|
|
680
774
|
* the bridge owns.
|
|
681
775
|
*
|
|
682
|
-
* Mechanics: clears CA_FLAG_READ_ONLY on self
|
|
683
|
-
* rb_ensure restores
|
|
684
|
-
*
|
|
685
|
-
*
|
|
776
|
+
* Mechanics: clears CA_FLAG_READ_ONLY on self and on its mask for the
|
|
777
|
+
* duration of the block; rb_ensure restores both on normal return AND on
|
|
778
|
+
* raise, setting the flag on whatever mask self has by then. (A read-only
|
|
779
|
+
* array's mask carries the flag once CArray#mask has handed it out.) Frozen
|
|
780
|
+
* objects still raise FrozenError. Does not walk parent chains.
|
|
781
|
+
*
|
|
782
|
+
* The lift covers every write, not only the mask: a value write inside the
|
|
783
|
+
* block reaches the producer's buffer, which case (a) declares immutable (a
|
|
784
|
+
* frozen CArray wrapped through MemoryView changes). Keeping the block to
|
|
785
|
+
* the mask is the caller's part, like the choice among (a) / (b) / (c).
|
|
686
786
|
*
|
|
687
787
|
* Caller discipline (not enforced by the primitive): confine usage to
|
|
688
788
|
* entities (CArray / CScalar / CAWrap). On a view of a read-only parent,
|
|
@@ -701,6 +801,9 @@ rb_ca_without_read_only_flag (VALUE self)
|
|
|
701
801
|
s.was_readonly = ca_test_flag(ca, CA_FLAG_READ_ONLY);
|
|
702
802
|
if (s.was_readonly) {
|
|
703
803
|
ca_unset_flag(ca, CA_FLAG_READ_ONLY);
|
|
804
|
+
if (ca->mask) {
|
|
805
|
+
ca_unset_flag(ca->mask, CA_FLAG_READ_ONLY);
|
|
806
|
+
}
|
|
704
807
|
}
|
|
705
808
|
return rb_ensure(without_read_only_body, Qnil,
|
|
706
809
|
without_read_only_ensure, (VALUE) &s);
|