carray 3.0.3 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +469 -0
- data/README.md +3 -2
- data/carray.gemspec +1 -1
- data/ext/ca_array_pool.c +25 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +14 -26
- data/ext/ca_axis_group.c +27 -27
- data/ext/ca_binop_dispatch.c +1 -29
- data/ext/ca_binop_dispatch.h +0 -1
- data/ext/ca_categorical_iterator.c +231 -153
- data/ext/ca_compare.c +9 -20
- data/ext/ca_composite_dispatch.c +9 -9
- data/ext/ca_group_iter.c +16 -17
- data/ext/ca_kernel_iterator.c +1240 -2000
- data/ext/ca_kernel_iterator.h +502 -519
- data/ext/ca_monop_dispatch.c +82 -59
- data/ext/ca_monop_dispatch.h +1 -0
- data/ext/ca_obj_array.c +152 -45
- data/ext/ca_obj_bincmp.c +25 -21
- data/ext/ca_obj_binop.c +16 -26
- data/ext/ca_obj_bitarray.c +8 -3
- data/ext/ca_obj_bitfield.c +63 -49
- data/ext/ca_obj_block.c +18 -9
- data/ext/ca_obj_byte_swap.c +7 -7
- data/ext/ca_obj_const_string.c +188 -22
- data/ext/ca_obj_face.c +280 -58
- data/ext/ca_obj_face.h +19 -0
- data/ext/ca_obj_fake.c +15 -29
- data/ext/ca_obj_farray.c +6 -3
- data/ext/ca_obj_field.c +30 -8
- data/ext/ca_obj_fixlen_string.c +2 -3
- data/ext/ca_obj_grid.c +455 -134
- data/ext/ca_obj_meld.c +172 -34
- data/ext/ca_obj_moncmp.c +23 -8
- data/ext/ca_obj_monop.c +113 -67
- data/ext/ca_obj_object.c +210 -52
- data/ext/ca_obj_record.c +15 -7
- data/ext/ca_obj_reduce.c +17 -8
- data/ext/ca_obj_refer.c +42 -35
- data/ext/ca_obj_remap.c +30 -9
- data/ext/ca_obj_repeat.c +9 -10
- data/ext/ca_obj_roll.c +31 -10
- data/ext/ca_obj_select.c +48 -14
- data/ext/ca_obj_select_axis.c +15 -47
- data/ext/ca_obj_shift.c +41 -7
- data/ext/ca_obj_stack.c +180 -34
- data/ext/ca_obj_stride.c +190 -134
- data/ext/ca_obj_string.c +80 -4
- data/ext/ca_obj_tile.c +32 -11
- data/ext/ca_obj_time.c +22 -6
- data/ext/ca_obj_timedelta.c +22 -6
- data/ext/ca_obj_transpose.c +13 -7
- data/ext/ca_obj_triop.c +26 -32
- data/ext/ca_obj_window.c +267 -74
- data/ext/ca_op_ipower.c +4 -4
- data/ext/ca_sort_kernels.h +6 -3
- data/ext/ca_sweep_engine.c +2 -2
- data/ext/caframe_csv_reader.c +80 -1
- data/ext/carray.h +214 -75
- data/ext/carray_access.c +200 -99
- data/ext/carray_address_basis.c +343 -87
- data/ext/carray_attribute.c +113 -6
- data/ext/carray_bincount.c +10 -8
- data/ext/carray_broadcast.c +13 -8
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +108 -0
- data/ext/carray_cast.c +375 -96
- data/ext/carray_cast_func.rb +90 -20
- data/ext/carray_class.c +4 -3
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +18 -9
- data/ext/carray_copy.c +51 -14
- data/ext/carray_core.c +150 -55
- data/ext/carray_count.c +56 -20
- data/ext/carray_element.c +81 -33
- data/ext/carray_factorize.c +138 -84
- data/ext/carray_generate.c +199 -52
- data/ext/carray_histogram.c +63 -18
- data/ext/carray_hold.c +12 -15
- data/ext/carray_index_classifier.c +388 -176
- data/ext/carray_internal.h +348 -29
- data/ext/carray_kernels_bincmp.c +4453 -0
- data/ext/carray_kernels_binop.c +11368 -0
- data/ext/carray_kernels_init.c +1734 -0
- data/ext/carray_kernels_moncmp.c +2104 -0
- data/ext/carray_kernels_monop.c +18369 -0
- data/ext/carray_kernels_reduce_aggregate.c +23818 -0
- data/ext/carray_kernels_reduce_boolean.c +406 -0
- data/ext/carray_kernels_reduce_cumulative.c +10086 -0
- data/ext/carray_kernels_reduce_extreme.c +17634 -0
- data/ext/carray_kernels_reduce_variance.c +3849 -0
- data/ext/carray_kernels_scan.c +4302 -0
- data/ext/carray_kernels_search.c +33829 -0
- data/ext/carray_kernels_sort.c +10897 -0
- data/ext/carray_kernels_triop.c +1864 -0
- data/ext/carray_key_windows.c +220 -0
- data/ext/carray_lazy.c +170 -111
- data/ext/carray_mask.c +337 -94
- data/ext/carray_median_percentile.c +82 -28
- data/ext/carray_memory_view.c +669 -561
- data/ext/carray_operator.c +129 -52
- data/ext/carray_order.c +604 -77
- data/ext/carray_partition.c +28 -27
- data/ext/carray_random.c +85 -31
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +87 -45
- data/ext/carray_segment.c +3 -3
- data/ext/carray_slab.c +123 -37
- data/ext/carray_sort.c +259 -51
- data/ext/carray_sort_kernel.c +5 -7
- data/ext/carray_struct.c +54 -61
- data/ext/carray_test.c +26 -5
- data/ext/carray_time_text.c +4 -2
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +309 -66
- data/ext/mk_call_cfunc.rb +19 -2
- data/ext/mkkernel.rb +403 -350
- data/ext/ruby_carray.c +10 -4
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +139 -22
- data/lib/carray/autoload_carray.rb +8 -31
- data/lib/carray/autoload_method_extension.rb +14 -5
- data/lib/carray/axis_group.rb +255 -106
- data/lib/carray/basics.rb +105 -17
- data/lib/carray/bincount_nd.rb +12 -2
- data/lib/carray/block_iterator.rb +126 -23
- data/lib/carray/boolean_reduce.rb +17 -17
- data/lib/carray/categorical.rb +195 -59
- data/lib/carray/categorical_iterator.rb +132 -29
- data/lib/carray/complex.rb +5 -5
- data/lib/carray/conditional.rb +67 -18
- data/lib/carray/const_string.rb +24 -5
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +9 -1
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +48 -3
- data/lib/carray/frame/column_verbs.rb +71 -0
- data/lib/carray/frame/concat.rb +17 -3
- data/lib/carray/frame/convert.rb +69 -0
- data/lib/carray/frame/csv_parser.rb +111 -17
- data/lib/carray/frame/describe.rb +3 -2
- data/lib/carray/frame/frame.rb +252 -28
- data/lib/carray/frame/group.rb +603 -101
- data/lib/carray/frame/io.rb +117 -28
- data/lib/carray/frame/join.rb +312 -51
- data/lib/carray/frame/records.rb +83 -7
- data/lib/carray/frame/reshape.rb +42 -35
- data/lib/carray/frame/sort.rb +7 -19
- data/lib/carray/frame/verbs.rb +188 -48
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +42 -6
- data/lib/carray/fusion.rb +40 -16
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +31 -8
- data/lib/carray/iterator.rb +85 -7
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +217 -183
- data/lib/carray/mask_gap_fill.rb +63 -57
- data/lib/carray/meld_reduce.rb +12 -14
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +16 -0
- data/lib/carray/methods/bit_string.rb +1 -1
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +3 -1
- data/lib/carray/methods/gather_nd.rb +14 -5
- data/lib/carray/methods/insert_block.rb +2 -2
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +61 -37
- data/lib/carray/methods/join.rb +1 -6
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +47 -51
- data/lib/carray/methods/mode.rb +1 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/resize.rb +3 -3
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +3 -3
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +53 -8
- data/lib/carray/segment_iterator.rb +323 -88
- data/lib/carray/serialize.rb +45 -14
- data/lib/carray/slab_iterator.rb +50 -0
- data/lib/carray/stack.rb +15 -12
- data/lib/carray/string.rb +113 -5
- data/lib/carray/string_operation_extension.rb +21 -6
- data/lib/carray/struct.rb +9 -21
- data/lib/carray/struct_builder.rb +24 -11
- data/lib/carray/time.rb +819 -158
- data/lib/carray/window_iterator.rb +122 -16
- data/lib/carray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +4 -0
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +2 -1
- data/yard-stubs/ca_obj_roll.rb +2 -2
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_transpose.rb +5 -4
- data/yard-stubs/ca_obj_window.rb +8 -3
- data/yard-stubs/carray_access.rb +6 -1
- data/yard-stubs/carray_attribute.rb +3 -6
- data/yard-stubs/carray_cast.rb +12 -0
- data/yard-stubs/carray_conversion.rb +7 -2
- data/yard-stubs/carray_copy.rb +4 -2
- data/yard-stubs/carray_count.rb +4 -4
- data/yard-stubs/carray_element.rb +17 -10
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_mask.rb +32 -3
- data/yard-stubs/carray_math.rb +41 -5
- data/yard-stubs/carray_median_percentile.rb +5 -2
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +129 -22
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +17 -10
- data/yard-stubs/carray_slab.rb +9 -2
- data/yard-stubs/carray_sort.rb +6 -5
- data/yard-stubs/carray_test.rb +5 -3
- metadata +25 -3
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/ext/carray_order.c
CHANGED
|
@@ -116,19 +116,23 @@ ca_project_loop (CArray *co, CArray *ca, CArray *ci, char *lfill, char *ufill)
|
|
|
116
116
|
xfree(ma);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
/* The result is wrapped before anything is copied into it: for an object
|
|
120
|
+
array the gathered cells are VALUEs that only the result's mark keeps
|
|
121
|
+
alive once the source is detached. */
|
|
122
|
+
static VALUE
|
|
120
123
|
ca_project (CArray *ca, CArray *ci, char *lfill, char *ufill)
|
|
121
124
|
{
|
|
125
|
+
volatile VALUE obj;
|
|
122
126
|
CArray *co;
|
|
123
127
|
|
|
124
|
-
|
|
128
|
+
obj = rb_carray_new(ca->data_type, ci->ndim, ci->dim, ca->bytes, NULL);
|
|
129
|
+
GetCArray(obj, co);
|
|
125
130
|
|
|
126
|
-
|
|
131
|
+
ca_attach_n(2, ca, ci); /* window: nothing raises inside */
|
|
127
132
|
ca_project_loop(co, ca, ci, lfill, ufill);
|
|
128
|
-
|
|
129
133
|
ca_detach_n(2, ca, ci); /* DETACH */
|
|
130
134
|
|
|
131
|
-
return
|
|
135
|
+
return obj;
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
/* rk.__count_below__(q, inclusive) -- for each cell of q, how many values
|
|
@@ -238,7 +242,13 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
|
|
|
238
242
|
char *lfval, *ufval;
|
|
239
243
|
int self_is_face;
|
|
240
244
|
|
|
241
|
-
|
|
245
|
+
{
|
|
246
|
+
VALUE ropt = Qnil;
|
|
247
|
+
/* project takes no keyword: one given was read as a fill value. */
|
|
248
|
+
rb_scan_args(argc, argv, "12:", (VALUE *)&ridx, (VALUE *) &vlfval,
|
|
249
|
+
(VALUE *) &vufval, &ropt);
|
|
250
|
+
rb_scan_options(ropt, "");
|
|
251
|
+
}
|
|
242
252
|
|
|
243
253
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
244
254
|
self_is_face = ca_is_face(ca);
|
|
@@ -254,12 +264,12 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
|
|
|
254
264
|
lfval = ALLOCV_N(char, hlfval, ca->bytes); /* converting a fill value */
|
|
255
265
|
ufval = ALLOCV_N(char, hufval, ca->bytes); /* can raise; the unwind frees */
|
|
256
266
|
|
|
257
|
-
if ( ! NIL_P(vlfval) ) {
|
|
267
|
+
if ( ! NIL_P(vlfval) && vlfval != CA_UNDEF ) {
|
|
258
268
|
rb_ca_obj2ptr(self, vlfval, lfval);
|
|
259
269
|
rb_ca_obj2ptr(self, vlfval, ufval);
|
|
260
270
|
}
|
|
261
271
|
|
|
262
|
-
if ( ! NIL_P(vufval) ) {
|
|
272
|
+
if ( ! NIL_P(vufval) && vufval != CA_UNDEF ) {
|
|
263
273
|
rb_ca_obj2ptr(self, vufval, ufval);
|
|
264
274
|
}
|
|
265
275
|
|
|
@@ -274,15 +284,19 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
|
|
|
274
284
|
TypedData_Get_Struct(vstorage, CArray, &carray_data_type, ca);
|
|
275
285
|
}
|
|
276
286
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
287
|
+
/* A fill of UNDEF is no fill: the cell is masked, as it is with none. */
|
|
288
|
+
{
|
|
289
|
+
VALUE vlower = vlfval;
|
|
290
|
+
VALUE vupper = NIL_P(vufval) ? vlfval : vufval;
|
|
291
|
+
obj = ca_project(ca, ci,
|
|
292
|
+
( NIL_P(vlower) || vlower == CA_UNDEF ) ? NULL : lfval,
|
|
293
|
+
( NIL_P(vupper) || vupper == CA_UNDEF ) ? NULL : ufval);
|
|
294
|
+
}
|
|
295
|
+
GetCArray(obj, co);
|
|
280
296
|
|
|
281
297
|
ALLOCV_END(hlfval);
|
|
282
298
|
ALLOCV_END(hufval);
|
|
283
299
|
|
|
284
|
-
obj = ca_wrap_struct(co);
|
|
285
|
-
|
|
286
300
|
if ( ! ca_is_any_masked(co) ) {
|
|
287
301
|
obj = rb_ca_unmask_copy(obj);
|
|
288
302
|
}
|
|
@@ -291,6 +305,7 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
|
|
|
291
305
|
obj = ca_face_lift(obj, self);
|
|
292
306
|
}
|
|
293
307
|
|
|
308
|
+
rb_ca_inherit_attr(obj, self);
|
|
294
309
|
return obj;
|
|
295
310
|
}
|
|
296
311
|
|
|
@@ -532,6 +547,41 @@ rb_ca_search_nearest_addr_kw (int argc, VALUE *argv, VALUE self)
|
|
|
532
547
|
|
|
533
548
|
/* ---- sort_by_key family ---------------------------------------------- */
|
|
534
549
|
|
|
550
|
+
/* The key orders self's cells one for one, so it has self's shape. */
|
|
551
|
+
static void
|
|
552
|
+
ca_by_key_check_shape (VALUE self, VALUE rkey, const char *name)
|
|
553
|
+
{
|
|
554
|
+
CArray *ca, *ck;
|
|
555
|
+
int8_t i;
|
|
556
|
+
rb_check_carray_object(rkey);
|
|
557
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
558
|
+
TypedData_Get_Struct(rkey, CArray, &carray_data_type, ck);
|
|
559
|
+
if ( ca->ndim == ck->ndim ) {
|
|
560
|
+
for (i = 0; i < ca->ndim; i++) {
|
|
561
|
+
if ( ca->dim[i] != ck->dim[i] ) break;
|
|
562
|
+
}
|
|
563
|
+
if ( i == ca->ndim ) return;
|
|
564
|
+
}
|
|
565
|
+
rb_raise(rb_eArgError, "%s: the key's shape %"PRIsVALUE" differs from "
|
|
566
|
+
"the array's %"PRIsVALUE, name,
|
|
567
|
+
rb_funcall(rkey, rb_intern("shape"), 0),
|
|
568
|
+
rb_funcall(self, rb_intern("shape"), 0));
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/* self at the addresses an extremum by key found. A fiber with no cell to
|
|
572
|
+
compare has a masked (or UNDEF) address and answers UNDEF. */
|
|
573
|
+
static VALUE
|
|
574
|
+
ca_by_key_fetch (VALUE self, VALUE addrs)
|
|
575
|
+
{
|
|
576
|
+
if ( addrs == CA_UNDEF ) {
|
|
577
|
+
return CA_UNDEF;
|
|
578
|
+
}
|
|
579
|
+
if ( rb_obj_is_carray(addrs) && RTEST(rb_ca_has_mask(addrs)) ) {
|
|
580
|
+
return rb_funcall(self, rb_intern("project"), 1, addrs);
|
|
581
|
+
}
|
|
582
|
+
return rb_ca_fetch(self, addrs);
|
|
583
|
+
}
|
|
584
|
+
|
|
535
585
|
/* sort_by_key(key, axis: 0) -- gather self at key.sort_addr(axis:). */
|
|
536
586
|
static VALUE
|
|
537
587
|
rb_ca_sort_by_key (int argc, VALUE *argv, VALUE self)
|
|
@@ -542,6 +592,7 @@ rb_ca_sort_by_key (int argc, VALUE *argv, VALUE self)
|
|
|
542
592
|
if ( NIL_P(raxis) ) {
|
|
543
593
|
raxis = INT2FIX(0);
|
|
544
594
|
}
|
|
595
|
+
ca_by_key_check_shape(self, rkey, "sort_by_key");
|
|
545
596
|
VALUE addrs = rb_ca_sort_addr_c(rkey, raxis, 0, 1); /* masked_last=1 (:last), unchanged default */
|
|
546
597
|
return rb_ca_fetch(self, addrs);
|
|
547
598
|
}
|
|
@@ -553,9 +604,11 @@ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
|
|
|
553
604
|
VALUE rkey, rkw = Qnil, raxis = Qnil;
|
|
554
605
|
rb_scan_args(argc, argv, "1:", &rkey, &rkw);
|
|
555
606
|
rb_scan_options(rkw, "axis", &raxis);
|
|
556
|
-
|
|
557
|
-
|
|
607
|
+
ca_by_key_check_shape(self, rkey, "max_by_key");
|
|
608
|
+
if ( NIL_P(raxis) && RTEST(rb_ca_is_empty(self)) ) {
|
|
609
|
+
return CA_UNDEF;
|
|
558
610
|
}
|
|
611
|
+
rkey = ca_order_key(rkey, "max_by_key");
|
|
559
612
|
|
|
560
613
|
VALUE addrs;
|
|
561
614
|
|
|
@@ -568,7 +621,7 @@ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
|
|
|
568
621
|
rb_hash_aset(kw, ID2SYM(id_axis), raxis);
|
|
569
622
|
addrs = rb_funcallv_kw(rkey, rb_intern("max_addr"), 1, &kw, RB_PASS_KEYWORDS);
|
|
570
623
|
}
|
|
571
|
-
return
|
|
624
|
+
return ca_by_key_fetch(self, addrs);
|
|
572
625
|
}
|
|
573
626
|
|
|
574
627
|
/* min_by_key(key, axis: nil) -- self[key.min_addr(axis:)] (or UNDEF if empty). */
|
|
@@ -578,9 +631,11 @@ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
|
|
|
578
631
|
VALUE rkey, rkw = Qnil, raxis = Qnil;
|
|
579
632
|
rb_scan_args(argc, argv, "1:", &rkey, &rkw);
|
|
580
633
|
rb_scan_options(rkw, "axis", &raxis);
|
|
581
|
-
|
|
582
|
-
|
|
634
|
+
ca_by_key_check_shape(self, rkey, "min_by_key");
|
|
635
|
+
if ( NIL_P(raxis) && RTEST(rb_ca_is_empty(self)) ) {
|
|
636
|
+
return CA_UNDEF;
|
|
583
637
|
}
|
|
638
|
+
rkey = ca_order_key(rkey, "min_by_key");
|
|
584
639
|
|
|
585
640
|
VALUE addrs;
|
|
586
641
|
|
|
@@ -593,7 +648,7 @@ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
|
|
|
593
648
|
rb_hash_aset(kw, ID2SYM(id_axis), raxis);
|
|
594
649
|
addrs = rb_funcallv_kw(rkey, rb_intern("min_addr"), 1, &kw, RB_PASS_KEYWORDS);
|
|
595
650
|
}
|
|
596
|
-
return
|
|
651
|
+
return ca_by_key_fetch(self, addrs);
|
|
597
652
|
}
|
|
598
653
|
|
|
599
654
|
/* ---- take_along_axis / put_along_axis -------------------------------- */
|
|
@@ -637,10 +692,16 @@ rb_ca_put_along_axis (int argc, VALUE *argv, VALUE self)
|
|
|
637
692
|
raxis = INT2FIX(0);
|
|
638
693
|
}
|
|
639
694
|
VALUE addrs = rb_ca_axis2addr_c(self, argv[0], raxis);
|
|
640
|
-
/* A masked index writes nothing
|
|
641
|
-
|
|
695
|
+
/* A masked index writes nothing. The values are first laid out over the
|
|
696
|
+
indices by the store itself (an array of the indices' shape, so a
|
|
697
|
+
broadcast, UNDEF or a Face value means what it means unmasked), and
|
|
698
|
+
only the cells at unmasked indices are written. */
|
|
642
699
|
if ( RTEST(rb_ca_has_mask(addrs)) ) {
|
|
643
|
-
rb_funcall(
|
|
700
|
+
VALUE sel = rb_funcall(addrs, rb_intern("is_not_masked"), 0);
|
|
701
|
+
VALUE vals = rb_funcall(self, rb_intern("project"), 1, addrs);
|
|
702
|
+
rb_funcall(vals, rb_intern("[]="), 1, argv[1]);
|
|
703
|
+
rb_ca_store(self, rb_funcall(addrs, rb_intern("[]"), 1, sel),
|
|
704
|
+
rb_funcall(vals, rb_intern("[]"), 1, sel));
|
|
644
705
|
return self;
|
|
645
706
|
}
|
|
646
707
|
rb_ca_store(self, addrs, argv[1]);
|
|
@@ -653,7 +714,12 @@ static VALUE
|
|
|
653
714
|
rb_ca_range_method (VALUE self)
|
|
654
715
|
{
|
|
655
716
|
VALUE mm_argv[1] = { Qnil };
|
|
656
|
-
|
|
717
|
+
CArray *ca;
|
|
718
|
+
VALUE pair;
|
|
719
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
720
|
+
/* A Face answers its own minmax (in its order, with its elements). */
|
|
721
|
+
pair = ca_is_face(ca) ? rb_funcall(self, rb_intern("minmax"), 0)
|
|
722
|
+
: rb_ca_minmax_ki(0, mm_argv, self);
|
|
657
723
|
VALUE lo = rb_ary_entry(pair, 0);
|
|
658
724
|
VALUE hi = rb_ary_entry(pair, 1);
|
|
659
725
|
return rb_range_new(lo, hi, 0);
|
|
@@ -661,6 +727,422 @@ rb_ca_range_method (VALUE self)
|
|
|
661
727
|
|
|
662
728
|
/* ---- nlargest / nsmallest family ------------------------------------- */
|
|
663
729
|
|
|
730
|
+
/* The answer is the cells first in the order (value, then NaN, then
|
|
731
|
+
masked; the values descending for the largest) with ties broken by
|
|
732
|
+
position, as `order` breaks them. Fewer cells than the fiber are found
|
|
733
|
+
by the buffered walk below. The other routes (the whole fiber, and the
|
|
734
|
+
keys the walk does not read) sort or partition every cell, which picks
|
|
735
|
+
the right values but, among cells that tie with the last one picked,
|
|
736
|
+
any of them; ca_topk_stable replaces the tied candidates by the
|
|
737
|
+
earliest cells that tie and sorts the candidates by (order, position). */
|
|
738
|
+
|
|
739
|
+
typedef struct {
|
|
740
|
+
const char *v; /* the fiber's values, contiguous */
|
|
741
|
+
const boolean8_t *m; /* its mask, or NULL */
|
|
742
|
+
int8_t type;
|
|
743
|
+
ca_size_t bytes;
|
|
744
|
+
int desc;
|
|
745
|
+
} ca_topk_fiber_t;
|
|
746
|
+
|
|
747
|
+
/* 0 a value, 1 NaN, 2 masked: the classes come in that order. */
|
|
748
|
+
static int
|
|
749
|
+
ca_topk_class (const ca_topk_fiber_t *f, ca_size_t i)
|
|
750
|
+
{
|
|
751
|
+
if ( f->m && f->m[i] ) return 2;
|
|
752
|
+
if ( f->type == CA_FLOAT64 && isnan(((const double *) f->v)[i]) ) return 1;
|
|
753
|
+
if ( f->type == CA_FLOAT32 && isnan(((const float *) f->v)[i]) ) return 1;
|
|
754
|
+
return 0;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
#define CA_TOPK_CMP(T) \
|
|
758
|
+
{ T a_ = ((const T *) f->v)[i], b_ = ((const T *) f->v)[j]; \
|
|
759
|
+
r = (a_ < b_) ? -1 : (a_ > b_) ? 1 : 0; } \
|
|
760
|
+
break
|
|
761
|
+
|
|
762
|
+
/* The order of cells i and j, ties not broken. */
|
|
763
|
+
static int
|
|
764
|
+
ca_topk_cmp (const ca_topk_fiber_t *f, ca_size_t i, ca_size_t j)
|
|
765
|
+
{
|
|
766
|
+
int ci = ca_topk_class(f, i), cj = ca_topk_class(f, j), r = 0;
|
|
767
|
+
if ( ci != cj ) return ( ci < cj ) ? -1 : 1;
|
|
768
|
+
if ( ci != 0 ) return 0;
|
|
769
|
+
switch ( f->type ) {
|
|
770
|
+
case CA_BOOLEAN:
|
|
771
|
+
case CA_UINT8: CA_TOPK_CMP(uint8_t);
|
|
772
|
+
case CA_INT8: CA_TOPK_CMP(int8_t);
|
|
773
|
+
case CA_INT16: CA_TOPK_CMP(int16_t);
|
|
774
|
+
case CA_UINT16: CA_TOPK_CMP(uint16_t);
|
|
775
|
+
case CA_INT32: CA_TOPK_CMP(int32_t);
|
|
776
|
+
case CA_UINT32: CA_TOPK_CMP(uint32_t);
|
|
777
|
+
case CA_INT64: CA_TOPK_CMP(int64_t);
|
|
778
|
+
case CA_UINT64: CA_TOPK_CMP(uint64_t);
|
|
779
|
+
case CA_FLOAT32: CA_TOPK_CMP(float);
|
|
780
|
+
case CA_FLOAT64: CA_TOPK_CMP(double);
|
|
781
|
+
case CA_FIXLEN:
|
|
782
|
+
r = memcmp(f->v + i * f->bytes, f->v + j * f->bytes, (size_t) f->bytes);
|
|
783
|
+
r = ( r > 0 ) - ( r < 0 );
|
|
784
|
+
break;
|
|
785
|
+
default:
|
|
786
|
+
r = 0;
|
|
787
|
+
}
|
|
788
|
+
return f->desc ? -r : r;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
#undef CA_TOPK_CMP
|
|
792
|
+
|
|
793
|
+
/* Merge sort of positions by (order, position). */
|
|
794
|
+
static void
|
|
795
|
+
ca_topk_sort (const ca_topk_fiber_t *f, ca_size_t *p, ca_size_t *tmp, ca_size_t n)
|
|
796
|
+
{
|
|
797
|
+
ca_size_t h, i, j, k;
|
|
798
|
+
if ( n < 2 ) return;
|
|
799
|
+
h = n / 2;
|
|
800
|
+
ca_topk_sort(f, p, tmp, h);
|
|
801
|
+
ca_topk_sort(f, p + h, tmp, n - h);
|
|
802
|
+
i = 0; j = h; k = 0;
|
|
803
|
+
while ( i < h && j < n ) {
|
|
804
|
+
int r = ca_topk_cmp(f, p[i], p[j]);
|
|
805
|
+
if ( r < 0 || ( r == 0 && p[i] < p[j] ) ) tmp[k++] = p[i++];
|
|
806
|
+
else tmp[k++] = p[j++];
|
|
807
|
+
}
|
|
808
|
+
while ( i < h ) tmp[k++] = p[i++];
|
|
809
|
+
while ( j < n ) tmp[k++] = p[j++];
|
|
810
|
+
memcpy(p, tmp, (size_t) n * sizeof(ca_size_t));
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
#define CA_TOPK_SCAN_VALUE(T) \
|
|
814
|
+
{ const T *v_ = (const T *) f->v; T w_ = v_[worst]; \
|
|
815
|
+
for (j = 0; j <= end && ties > 0; j++) { \
|
|
816
|
+
if ( v_[j] == w_ && ! ( f->m && f->m[j] ) ) { c[n++] = j; ties--; } \
|
|
817
|
+
} } \
|
|
818
|
+
break
|
|
819
|
+
|
|
820
|
+
/* The earliest cells, up to `end`, that tie with cell `worst`, appended to
|
|
821
|
+
c[n..] until `ties` are found. A value compares by ==, which leaves out
|
|
822
|
+
NaN and the masked cells; those classes tie within themselves. */
|
|
823
|
+
static ca_size_t
|
|
824
|
+
ca_topk_scan_ties (const ca_topk_fiber_t *f, ca_size_t worst, ca_size_t end,
|
|
825
|
+
ca_size_t *c, ca_size_t n, ca_size_t ties)
|
|
826
|
+
{
|
|
827
|
+
ca_size_t j;
|
|
828
|
+
int cls = ca_topk_class(f, worst);
|
|
829
|
+
if ( cls != 0 || f->type == CA_FIXLEN ) {
|
|
830
|
+
for (j = 0; j <= end && ties > 0; j++) {
|
|
831
|
+
if ( ca_topk_cmp(f, j, worst) == 0 ) { c[n++] = j; ties--; }
|
|
832
|
+
}
|
|
833
|
+
return n;
|
|
834
|
+
}
|
|
835
|
+
switch ( f->type ) {
|
|
836
|
+
case CA_BOOLEAN:
|
|
837
|
+
case CA_UINT8: CA_TOPK_SCAN_VALUE(uint8_t);
|
|
838
|
+
case CA_INT8: CA_TOPK_SCAN_VALUE(int8_t);
|
|
839
|
+
case CA_INT16: CA_TOPK_SCAN_VALUE(int16_t);
|
|
840
|
+
case CA_UINT16: CA_TOPK_SCAN_VALUE(uint16_t);
|
|
841
|
+
case CA_INT32: CA_TOPK_SCAN_VALUE(int32_t);
|
|
842
|
+
case CA_UINT32: CA_TOPK_SCAN_VALUE(uint32_t);
|
|
843
|
+
case CA_INT64: CA_TOPK_SCAN_VALUE(int64_t);
|
|
844
|
+
case CA_UINT64: CA_TOPK_SCAN_VALUE(uint64_t);
|
|
845
|
+
case CA_FLOAT32: CA_TOPK_SCAN_VALUE(float);
|
|
846
|
+
case CA_FLOAT64: CA_TOPK_SCAN_VALUE(double);
|
|
847
|
+
}
|
|
848
|
+
return n;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
#undef CA_TOPK_SCAN_VALUE
|
|
852
|
+
|
|
853
|
+
static void
|
|
854
|
+
ca_topk_fix_fiber (const ca_topk_fiber_t *f, ca_size_t len,
|
|
855
|
+
ca_size_t *c, ca_size_t cap, ca_size_t *tmp)
|
|
856
|
+
{
|
|
857
|
+
ca_size_t worst = c[0], ties = 0, n = 0, end = 0, k;
|
|
858
|
+
for (k = 1; k < cap; k++) {
|
|
859
|
+
if ( ca_topk_cmp(f, c[k], worst) > 0 ) worst = c[k];
|
|
860
|
+
}
|
|
861
|
+
/* Keep the candidates before the worst; the ones tied with it are
|
|
862
|
+
replaced by the earliest cells that tie, which lie no later than the
|
|
863
|
+
last tied candidate. */
|
|
864
|
+
for (k = 0; k < cap; k++) {
|
|
865
|
+
if ( ca_topk_cmp(f, c[k], worst) < 0 ) {
|
|
866
|
+
c[n++] = c[k];
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
ties++;
|
|
870
|
+
if ( c[k] > end ) end = c[k];
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
(void) len;
|
|
874
|
+
n = ca_topk_scan_ties(f, worst, end, c, n, ties);
|
|
875
|
+
ca_topk_sort(f, c, tmp, cap);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/* `picked` holds cap candidate positions per fiber along axis; returns the
|
|
879
|
+
stable answer of the same shape. The fibers are read with the axis
|
|
880
|
+
moved last, from a contiguous copy. */
|
|
881
|
+
static VALUE
|
|
882
|
+
ca_topk_stable (VALUE self, VALUE picked, long axis, int desc)
|
|
883
|
+
{
|
|
884
|
+
volatile VALUE key = ca_order_key(self, desc ? "nlargest" : "nsmallest");
|
|
885
|
+
volatile VALUE vals, pk, holder = 0;
|
|
886
|
+
VALUE perm[CA_RANK_MAX], inv[CA_RANK_MAX];
|
|
887
|
+
CArray *cv, *cp;
|
|
888
|
+
ca_topk_fiber_t f;
|
|
889
|
+
ca_size_t len, cap, outer, o, *tmp;
|
|
890
|
+
int ndim, k, last;
|
|
891
|
+
|
|
892
|
+
GetCArray(key, cv);
|
|
893
|
+
ndim = cv->ndim;
|
|
894
|
+
last = ( axis == ndim - 1 );
|
|
895
|
+
if ( ! last ) {
|
|
896
|
+
int n = 0;
|
|
897
|
+
for (k = 0; k < ndim; k++) if ( k != axis ) perm[n++] = INT2FIX(k);
|
|
898
|
+
perm[n] = LONG2NUM(axis);
|
|
899
|
+
for (k = 0; k < ndim; k++) inv[NUM2INT(perm[k])] = INT2FIX(k);
|
|
900
|
+
}
|
|
901
|
+
/* An entity whose fibers already lie along its last axis is read in
|
|
902
|
+
place; anything else is copied into that layout. */
|
|
903
|
+
if ( last && ca_is_entity(cv) ) {
|
|
904
|
+
vals = key;
|
|
905
|
+
ca_update_mask(cv);
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
vals = rb_ca_copy(last ? key : rb_funcallv(key, rb_intern("transpose"), ndim, perm));
|
|
909
|
+
}
|
|
910
|
+
pk = rb_ca_copy(last ? picked : rb_funcallv(picked, rb_intern("transpose"), ndim, perm));
|
|
911
|
+
GetCArray(vals, cv);
|
|
912
|
+
GetCArray(pk, cp);
|
|
913
|
+
|
|
914
|
+
len = cv->dim[ndim - 1];
|
|
915
|
+
cap = cp->dim[ndim - 1];
|
|
916
|
+
if ( len == 0 || cap == 0 ) return picked;
|
|
917
|
+
outer = cv->elements / len;
|
|
918
|
+
f.type = cv->data_type;
|
|
919
|
+
f.bytes = cv->bytes;
|
|
920
|
+
f.desc = desc;
|
|
921
|
+
tmp = ALLOCV_N(ca_size_t, holder, cap);
|
|
922
|
+
for (o = 0; o < outer; o++) {
|
|
923
|
+
f.v = cv->ptr + o * len * cv->bytes;
|
|
924
|
+
f.m = cv->mask ? (boolean8_t *) cv->mask->ptr + o * len : NULL;
|
|
925
|
+
ca_topk_fix_fiber(&f, len, (ca_size_t *) cp->ptr + o * cap, cap, tmp);
|
|
926
|
+
}
|
|
927
|
+
ALLOCV_END(holder);
|
|
928
|
+
return last ? pk : rb_ca_copy(rb_funcallv(pk, rb_intern("transpose"), ndim, inv));
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/* Whether self holds a cell the order puts after the numbers: a masked
|
|
932
|
+
cell, or a NaN. */
|
|
933
|
+
static int
|
|
934
|
+
ca_topk_has_cells_after_numbers (VALUE self, CArray *ca)
|
|
935
|
+
{
|
|
936
|
+
if ( ca_has_mask(ca) && ca_count_masked(ca) > 0 ) {
|
|
937
|
+
return 1;
|
|
938
|
+
}
|
|
939
|
+
if ( ca_is_float_type(ca) ) {
|
|
940
|
+
VALUE nan = rb_funcall(self, rb_intern("is_nan"), 0);
|
|
941
|
+
return RTEST(rb_funcall(nan, rb_intern("any"), 0));
|
|
942
|
+
}
|
|
943
|
+
return 0;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/* ---- the buffered walk ------------------------------------------------
|
|
947
|
+
Each fiber is read once. The cells that come before the cut (the last
|
|
948
|
+
of the cap cells kept so far, in (order, position)) are collected in a
|
|
949
|
+
buffer of a few times cap; when it fills, a selection keeps the first
|
|
950
|
+
cap and the cut moves up. The work area is the buffer, whatever the
|
|
951
|
+
length of the fiber, and a fiber in order costs one selection per
|
|
952
|
+
bufferful instead of a heap update per cell.
|
|
953
|
+
|
|
954
|
+
The walk is written once as a macro over three others: CLASS(i) (0 a
|
|
955
|
+
value, 1 NaN, 2 masked), AFTER(i, j) (cell i comes after cell j in
|
|
956
|
+
(order, position)) and ENTERS(j) (cell j comes before the cut), and
|
|
957
|
+
instantiated per type so that the comparisons
|
|
958
|
+
are inline. The fixlen type takes the generic comparison. */
|
|
959
|
+
|
|
960
|
+
/* Rearranges b[0..nb) so that b[0..cap) hold the cap cells first in
|
|
961
|
+
(order, position): quickselect with median-of-three pivots; past a
|
|
962
|
+
depth limit the remaining range is sorted (tmp holds nb positions). */
|
|
963
|
+
#define CA_TOPK_SELECT(AFTER) \
|
|
964
|
+
do { \
|
|
965
|
+
lo = 0; hi = nb; \
|
|
966
|
+
depth = 2; \
|
|
967
|
+
for (s = nb; s > 1; s >>= 1) depth += 2; \
|
|
968
|
+
while ( hi - lo > 16 ) { \
|
|
969
|
+
mid = lo + ( hi - lo ) / 2; last = hi - 1; \
|
|
970
|
+
if ( depth-- == 0 ) break; \
|
|
971
|
+
if ( AFTER(b[lo], b[mid]) ) { t = b[lo]; b[lo] = b[mid]; b[mid] = t; } \
|
|
972
|
+
if ( AFTER(b[mid], b[last]) ) { t = b[mid]; b[mid] = b[last]; b[last] = t; } \
|
|
973
|
+
if ( AFTER(b[lo], b[mid]) ) { t = b[lo]; b[lo] = b[mid]; b[mid] = t; } \
|
|
974
|
+
t = b[mid]; b[mid] = b[last]; b[last] = t; \
|
|
975
|
+
for (s = lo, i = lo; i < last; i++) { \
|
|
976
|
+
if ( AFTER(b[last], b[i]) ) { t = b[s]; b[s] = b[i]; b[i] = t; s++; } \
|
|
977
|
+
} \
|
|
978
|
+
t = b[s]; b[s] = b[last]; b[last] = t; \
|
|
979
|
+
if ( s == cap || s + 1 == cap ) { lo = hi; break; } \
|
|
980
|
+
if ( cap < s ) hi = s; \
|
|
981
|
+
else lo = s + 1; \
|
|
982
|
+
} \
|
|
983
|
+
if ( hi > lo ) ca_topk_sort(f, b + lo, tmp, hi - lo); \
|
|
984
|
+
} while (0)
|
|
985
|
+
|
|
986
|
+
#define CA_TOPK_DEFINE_BUF(NAME, T, CLASS, AFTER, ENTERS) \
|
|
987
|
+
static void \
|
|
988
|
+
NAME (const ca_topk_fiber_t *f, ca_size_t len, ca_size_t cap, \
|
|
989
|
+
ca_size_t *b, ca_size_t *tmp, ca_size_t size) \
|
|
990
|
+
{ \
|
|
991
|
+
const T *v_ = (const T *) f->v; \
|
|
992
|
+
ca_size_t j, nb = 0, cut = 0, lo, hi, i, s, t, mid, last; \
|
|
993
|
+
int rc_ = 3, depth; \
|
|
994
|
+
T rv_; \
|
|
995
|
+
(void) v_; \
|
|
996
|
+
memset(&rv_, 0, sizeof(rv_)); \
|
|
997
|
+
for (j = 0; j < len; j++) { \
|
|
998
|
+
if ( rc_ != 3 && ! ( ENTERS(j) ) ) continue; \
|
|
999
|
+
b[nb++] = j; \
|
|
1000
|
+
if ( nb == size ) { \
|
|
1001
|
+
CA_TOPK_SELECT(AFTER); \
|
|
1002
|
+
nb = cap; \
|
|
1003
|
+
} \
|
|
1004
|
+
else if ( ! ( nb == cap && rc_ == 3 ) ) { /* the first cap cells */ \
|
|
1005
|
+
continue; \
|
|
1006
|
+
} \
|
|
1007
|
+
cut = b[0]; \
|
|
1008
|
+
for (i = 1; i < cap; i++) if ( AFTER(b[i], cut) ) cut = b[i]; \
|
|
1009
|
+
rc_ = CLASS(cut); \
|
|
1010
|
+
if ( rc_ == 0 ) memcpy(&rv_, f->v + cut * f->bytes, sizeof(rv_)); \
|
|
1011
|
+
} \
|
|
1012
|
+
if ( nb > cap ) CA_TOPK_SELECT(AFTER); \
|
|
1013
|
+
ca_topk_sort(f, b, tmp, cap); \
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/* The typed comparisons. A cell enters when it comes before the cut;
|
|
1017
|
+
one that ties the cut does not, it lies later. */
|
|
1018
|
+
#define CA_TOPK_CLASS_INT(i) ( ( f->m && f->m[i] ) ? 2 : 0 )
|
|
1019
|
+
#define CA_TOPK_CLASS_FLT(i) ( ( f->m && f->m[i] ) ? 2 : ( isnan(v_[i]) ? 1 : 0 ) )
|
|
1020
|
+
#define CA_TOPK_AFTER_V(CLASS, i, j) \
|
|
1021
|
+
( CLASS(i) != CLASS(j) ? CLASS(i) > CLASS(j) \
|
|
1022
|
+
: ( CLASS(i) == 0 && v_[i] != v_[j] ) \
|
|
1023
|
+
? ( f->desc ? v_[i] < v_[j] : v_[i] > v_[j] ) \
|
|
1024
|
+
: (i) > (j) )
|
|
1025
|
+
#define CA_TOPK_AFTER_INT(i, j) CA_TOPK_AFTER_V(CA_TOPK_CLASS_INT, i, j)
|
|
1026
|
+
#define CA_TOPK_AFTER_FLT(i, j) CA_TOPK_AFTER_V(CA_TOPK_CLASS_FLT, i, j)
|
|
1027
|
+
#define CA_TOPK_ENTERS_V(CLASS, j) \
|
|
1028
|
+
( CLASS(j) < rc_ || \
|
|
1029
|
+
( CLASS(j) == 0 && rc_ == 0 && \
|
|
1030
|
+
( f->desc ? v_[j] > rv_ : v_[j] < rv_ ) ) )
|
|
1031
|
+
#define CA_TOPK_ENTERS_INT(j) CA_TOPK_ENTERS_V(CA_TOPK_CLASS_INT, j)
|
|
1032
|
+
#define CA_TOPK_ENTERS_FLT(j) CA_TOPK_ENTERS_V(CA_TOPK_CLASS_FLT, j)
|
|
1033
|
+
|
|
1034
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_u8, uint8_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
|
|
1035
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_i8, int8_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
|
|
1036
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_i16, int16_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
|
|
1037
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_u16, uint16_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
|
|
1038
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_i32, int32_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
|
|
1039
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_u32, uint32_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
|
|
1040
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_i64, int64_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
|
|
1041
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_u64, uint64_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
|
|
1042
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_f32, float, CA_TOPK_CLASS_FLT, CA_TOPK_AFTER_FLT, CA_TOPK_ENTERS_FLT)
|
|
1043
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_f64, double, CA_TOPK_CLASS_FLT, CA_TOPK_AFTER_FLT, CA_TOPK_ENTERS_FLT)
|
|
1044
|
+
|
|
1045
|
+
/* fixlen: the generic comparison; v_ is unused. */
|
|
1046
|
+
#define CA_TOPK_CLASS_GEN(i) ca_topk_class(f, i)
|
|
1047
|
+
#define CA_TOPK_AFTER_GEN(i, j) ca_topk_after(f, i, j)
|
|
1048
|
+
#define CA_TOPK_ENTERS_GEN(j) ( ca_topk_cmp(f, j, cut) < 0 )
|
|
1049
|
+
|
|
1050
|
+
/* Whether cell i comes after cell j in (order, position). */
|
|
1051
|
+
static int
|
|
1052
|
+
ca_topk_after (const ca_topk_fiber_t *f, ca_size_t i, ca_size_t j)
|
|
1053
|
+
{
|
|
1054
|
+
int r = ca_topk_cmp(f, i, j);
|
|
1055
|
+
return r > 0 || ( r == 0 && i > j );
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
CA_TOPK_DEFINE_BUF(ca_topk_buf_gen, char, CA_TOPK_CLASS_GEN, CA_TOPK_AFTER_GEN, CA_TOPK_ENTERS_GEN)
|
|
1059
|
+
|
|
1060
|
+
#undef CA_TOPK_DEFINE_BUF
|
|
1061
|
+
#undef CA_TOPK_SELECT
|
|
1062
|
+
#undef CA_TOPK_CLASS_INT
|
|
1063
|
+
#undef CA_TOPK_CLASS_FLT
|
|
1064
|
+
#undef CA_TOPK_AFTER_V
|
|
1065
|
+
#undef CA_TOPK_AFTER_INT
|
|
1066
|
+
#undef CA_TOPK_AFTER_FLT
|
|
1067
|
+
#undef CA_TOPK_ENTERS_V
|
|
1068
|
+
#undef CA_TOPK_ENTERS_INT
|
|
1069
|
+
#undef CA_TOPK_ENTERS_FLT
|
|
1070
|
+
#undef CA_TOPK_CLASS_GEN
|
|
1071
|
+
#undef CA_TOPK_AFTER_GEN
|
|
1072
|
+
#undef CA_TOPK_ENTERS_GEN
|
|
1073
|
+
|
|
1074
|
+
/* The first cap cells of one fiber in (order, position), sorted, into
|
|
1075
|
+
b[0..cap). b and tmp hold size positions, cap < size <= len. */
|
|
1076
|
+
static void
|
|
1077
|
+
ca_topk_buf_fiber (const ca_topk_fiber_t *f, ca_size_t len, ca_size_t cap,
|
|
1078
|
+
ca_size_t *b, ca_size_t *tmp, ca_size_t size)
|
|
1079
|
+
{
|
|
1080
|
+
switch ( f->type ) {
|
|
1081
|
+
case CA_BOOLEAN:
|
|
1082
|
+
case CA_UINT8: ca_topk_buf_u8(f, len, cap, b, tmp, size); break;
|
|
1083
|
+
case CA_INT8: ca_topk_buf_i8(f, len, cap, b, tmp, size); break;
|
|
1084
|
+
case CA_INT16: ca_topk_buf_i16(f, len, cap, b, tmp, size); break;
|
|
1085
|
+
case CA_UINT16: ca_topk_buf_u16(f, len, cap, b, tmp, size); break;
|
|
1086
|
+
case CA_INT32: ca_topk_buf_i32(f, len, cap, b, tmp, size); break;
|
|
1087
|
+
case CA_UINT32: ca_topk_buf_u32(f, len, cap, b, tmp, size); break;
|
|
1088
|
+
case CA_INT64: ca_topk_buf_i64(f, len, cap, b, tmp, size); break;
|
|
1089
|
+
case CA_UINT64: ca_topk_buf_u64(f, len, cap, b, tmp, size); break;
|
|
1090
|
+
case CA_FLOAT32: ca_topk_buf_f32(f, len, cap, b, tmp, size); break;
|
|
1091
|
+
case CA_FLOAT64: ca_topk_buf_f64(f, len, cap, b, tmp, size); break;
|
|
1092
|
+
default: ca_topk_buf_gen(f, len, cap, b, tmp, size); break;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/* Per-axis top-k positions of `key` (an order key: not object, not
|
|
1097
|
+
complex) by the buffered walk, for cap < the length of the axis. The
|
|
1098
|
+
fibers come in the row-major order of the other axes, so the answer is
|
|
1099
|
+
filled with the axis last and moved into place at the end (cap cells
|
|
1100
|
+
per fiber). */
|
|
1101
|
+
static VALUE
|
|
1102
|
+
ca_topk_buffered_positions (VALUE key, long cap, long axis, int desc)
|
|
1103
|
+
{
|
|
1104
|
+
volatile VALUE holder = 0, out;
|
|
1105
|
+
VALUE perm[CA_RANK_MAX];
|
|
1106
|
+
ca_size_t odim[CA_RANK_MAX];
|
|
1107
|
+
CArray *ck, *co;
|
|
1108
|
+
ca_iter_state st;
|
|
1109
|
+
ca_topk_fiber_t f;
|
|
1110
|
+
ca_size_t *buf, *tmp, n, o = 0, k, len, size;
|
|
1111
|
+
int64_t *op;
|
|
1112
|
+
boolean8_t *m;
|
|
1113
|
+
char *p;
|
|
1114
|
+
int ndim, i, j = 0;
|
|
1115
|
+
|
|
1116
|
+
GetCArray(key, ck);
|
|
1117
|
+
ndim = ck->ndim;
|
|
1118
|
+
for (i = 0; i < ndim; i++) if ( i != axis ) odim[j++] = ck->dim[i];
|
|
1119
|
+
odim[ndim - 1] = cap;
|
|
1120
|
+
len = ck->dim[axis];
|
|
1121
|
+
out = rb_carray_new(CA_INT64, ndim, odim, 0, NULL);
|
|
1122
|
+
GetCArray(out, co);
|
|
1123
|
+
op = (int64_t *) co->ptr;
|
|
1124
|
+
|
|
1125
|
+
f.type = ck->data_type;
|
|
1126
|
+
f.bytes = ck->bytes;
|
|
1127
|
+
f.desc = desc;
|
|
1128
|
+
size = ( len < 4 * cap + 256 ) ? len : 4 * cap + 256;
|
|
1129
|
+
buf = ALLOCV_N(ca_size_t, holder, 2 * size);
|
|
1130
|
+
tmp = buf + size;
|
|
1131
|
+
CA_FOR_EACH_FIBER_MASKED(st, ck, axis, 0, p, n, m) {
|
|
1132
|
+
f.v = p;
|
|
1133
|
+
f.m = m;
|
|
1134
|
+
ca_topk_buf_fiber(&f, n, cap, buf, tmp, size);
|
|
1135
|
+
for (k = 0; k < cap; k++) op[o * cap + k] = (int64_t) buf[k];
|
|
1136
|
+
o++;
|
|
1137
|
+
}
|
|
1138
|
+
ALLOCV_END(holder);
|
|
1139
|
+
if ( axis == ndim - 1 ) return out;
|
|
1140
|
+
for (i = 0; i < ndim; i++) {
|
|
1141
|
+
perm[i] = INT2FIX( i < axis ? i : ( i == axis ? ndim - 1 : i - 1 ) );
|
|
1142
|
+
}
|
|
1143
|
+
return rb_ca_copy(rb_funcallv(out, rb_intern("transpose"), ndim, perm));
|
|
1144
|
+
}
|
|
1145
|
+
|
|
664
1146
|
/* Per-axis top-k positions (cap clamped, axis normalized).
|
|
665
1147
|
* Called by rb_ca_topk_index below. */
|
|
666
1148
|
static VALUE
|
|
@@ -671,12 +1153,44 @@ rb_ca_topk_positions (VALUE self, long cap, long axis_norm, int desc)
|
|
|
671
1153
|
long dim_axis = (long) ca->dim[axis_norm];
|
|
672
1154
|
VALUE raxis_norm = LONG2NUM(axis_norm);
|
|
673
1155
|
|
|
1156
|
+
/* The buffered walk reads any key but an object (its cells compare in
|
|
1157
|
+
Ruby) or a complex one (no order), and keeps cap cells per fiber. */
|
|
1158
|
+
if ( ca->data_type != CA_OBJECT && ! ca_is_complex_type(ca) ) {
|
|
1159
|
+
VALUE key = ca_order_key(self, desc ? "nlargest" : "nsmallest");
|
|
1160
|
+
CArray *ck;
|
|
1161
|
+
GetCArray(key, ck);
|
|
1162
|
+
if ( ck->data_type != CA_OBJECT && ! ca_is_complex_type(ck) &&
|
|
1163
|
+
cap < dim_axis ) {
|
|
1164
|
+
return ca_topk_buffered_positions(key, cap, axis_norm, desc);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/* The largest: reversing the ascending order would put the NaN and the
|
|
1169
|
+
masked cells first, so rank them descending (NaN, then the masked
|
|
1170
|
+
cells, last) when there are any. An object array is ranked by
|
|
1171
|
+
`order` both ways (its cells compare in Ruby). */
|
|
1172
|
+
if ( ca->data_type == CA_OBJECT ||
|
|
1173
|
+
( desc && ca_topk_has_cells_after_numbers(self, ca) ) ) {
|
|
1174
|
+
VALUE kw = rb_hash_new();
|
|
1175
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("axis")), raxis_norm);
|
|
1176
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("descending")), desc ? Qtrue : Qfalse);
|
|
1177
|
+
VALUE rank = rb_funcallv_kw(self, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
|
|
1178
|
+
VALUE full = rb_ca_sort_index_ki_quick(rank, raxis_norm);
|
|
1179
|
+
if ( cap >= dim_axis ) {
|
|
1180
|
+
return rb_ca_copy(full);
|
|
1181
|
+
}
|
|
1182
|
+
int ndim = (int) ca->ndim;
|
|
1183
|
+
VALUE spec[CA_RANK_MAX];
|
|
1184
|
+
for ( int k = 0; k < ndim; k++ ) {
|
|
1185
|
+
spec[k] = Qnil;
|
|
1186
|
+
}
|
|
1187
|
+
spec[axis_norm] = rb_range_new(INT2FIX(0), LONG2NUM(cap), 1);
|
|
1188
|
+
return rb_ca_copy(rb_ca_fetch2(full, ndim, spec));
|
|
1189
|
+
}
|
|
1190
|
+
|
|
674
1191
|
if ( cap >= dim_axis ) {
|
|
675
1192
|
VALUE full = rb_ca_sort_index_ki_quick(self, raxis_norm);
|
|
676
|
-
|
|
677
|
-
full = rb_ca_flip_axis(full, NUM2LONG(raxis_norm));
|
|
678
|
-
}
|
|
679
|
-
return rb_ca_copy(full);
|
|
1193
|
+
return ca_topk_stable(self, full, axis_norm, desc);
|
|
680
1194
|
}
|
|
681
1195
|
|
|
682
1196
|
long pivot = desc ? (dim_axis - cap) : (cap - 1);
|
|
@@ -703,7 +1217,7 @@ rb_ca_topk_positions (VALUE self, long cap, long axis_norm, int desc)
|
|
|
703
1217
|
order = rb_ca_flip_axis(order, NUM2LONG(raxis_norm));
|
|
704
1218
|
}
|
|
705
1219
|
VALUE picked = rb_ca_take_along_axis_c(partidx_top, order, raxis_norm);
|
|
706
|
-
return
|
|
1220
|
+
return ca_topk_stable(self, picked, axis_norm, desc);
|
|
707
1221
|
}
|
|
708
1222
|
|
|
709
1223
|
/* Dispatcher: normalize axis, clamp cap, empty-cap corner.
|
|
@@ -807,11 +1321,10 @@ rb_ca_nsmallest (int argc, VALUE *argv, VALUE self)
|
|
|
807
1321
|
* ever compare equal), so a later sort_addr key never gets consulted;
|
|
808
1322
|
* dense ranks preserve ties, so it does.
|
|
809
1323
|
*
|
|
810
|
-
* descending:
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
814
|
-
* order still reverses correctly. */
|
|
1324
|
+
* descending: ranks the largest value 0, with NaN after the numbers as
|
|
1325
|
+
* ascending. The kernel gives the descending dense rank; the ordinal rank
|
|
1326
|
+
* is the ascending ordinal rank of it, so tied cells keep their original
|
|
1327
|
+
* order in both directions. */
|
|
815
1328
|
|
|
816
1329
|
static VALUE
|
|
817
1330
|
rb_ca_order (int argc, VALUE *argv, VALUE self)
|
|
@@ -826,11 +1339,24 @@ rb_ca_order (int argc, VALUE *argv, VALUE self)
|
|
|
826
1339
|
int do_stable = NIL_P(rkind) ? 0
|
|
827
1340
|
: ca_symbol_choice(rkind, "kind", "quick", "stable", NULL);
|
|
828
1341
|
|
|
829
|
-
|
|
1342
|
+
if ( RTEST(rdesc) && ! dense ) {
|
|
1343
|
+
VALUE kw = rb_hash_new();
|
|
1344
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("axis")), raxis);
|
|
1345
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("descending")), Qtrue);
|
|
1346
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("method")), ID2SYM(rb_intern("dense")));
|
|
1347
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("kind")), do_stable ? ID2SYM(rb_intern("stable")) : ID2SYM(rb_intern("quick")));
|
|
1348
|
+
VALUE ranked = rb_funcallv_kw(self, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
|
|
1349
|
+
rb_hash_delete(kw, ID2SYM(rb_intern("descending")));
|
|
1350
|
+
rb_hash_delete(kw, ID2SYM(rb_intern("method")));
|
|
1351
|
+
return rb_funcallv_kw(ranked, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
/* The kernel's dense == 2 is the descending dense rank. */
|
|
1355
|
+
int kmode = ( dense && RTEST(rdesc) ) ? 2 : dense;
|
|
830
1356
|
if ( NIL_P(raxis) ) {
|
|
831
1357
|
VALUE flat = rb_ca_flatten(self);
|
|
832
1358
|
/* Direct C-level dispatch via c_callable: true extern (mkkernel). */
|
|
833
|
-
VALUE ranked = rb_ca_rank_index_ki_kind_dense(flat, INT2FIX(0), do_stable,
|
|
1359
|
+
VALUE ranked = rb_ca_rank_index_ki_kind_dense(flat, INT2FIX(0), do_stable, kmode);
|
|
834
1360
|
/* reshape(*shape) */
|
|
835
1361
|
CArray *ca;
|
|
836
1362
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
@@ -838,43 +1364,12 @@ rb_ca_order (int argc, VALUE *argv, VALUE self)
|
|
|
838
1364
|
for ( long k = 0; k < (long) ca->ndim; k++ ) {
|
|
839
1365
|
shape_argv[k] = LONG2NUM((long) ca->dim[k]);
|
|
840
1366
|
}
|
|
841
|
-
|
|
842
|
-
n = RTEST(rb_ca_has_mask(self))
|
|
843
|
-
? SIZE2NUM(ca_count_not_masked(ca)) /* = elements - count_masked */
|
|
844
|
-
: rb_ca_elements(self);
|
|
845
|
-
} else {
|
|
846
|
-
long axis_norm = rb_ca_normalize_axis_value(self, raxis, "order");
|
|
847
|
-
VALUE raxis_norm = LONG2NUM(axis_norm);
|
|
848
|
-
asc = rb_ca_rank_index_ki_kind_dense(self, raxis_norm, do_stable, dense);
|
|
849
|
-
if ( RTEST(rb_ca_has_mask(self)) ) {
|
|
850
|
-
n = rb_ca_count_not_masked_c(self, raxis_norm);
|
|
851
|
-
} else {
|
|
852
|
-
CArray *ca;
|
|
853
|
-
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
854
|
-
n = LONG2NUM((long) ca->dim[axis_norm]);
|
|
855
|
-
}
|
|
856
|
-
if ( RTEST(rb_obj_is_kind_of(n, rb_cCArray)) ) {
|
|
857
|
-
VALUE ia_argv[1] = { raxis_norm };
|
|
858
|
-
n = rb_ca_insert_axis(1, ia_argv, n);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
VALUE diff;
|
|
863
|
-
if ( RTEST(rdesc) ) {
|
|
864
|
-
/* `-` via rb_funcall handles both Integer-Integer and
|
|
865
|
-
CArray/Integer mixed receivers uniformly via Ruby's operator
|
|
866
|
-
dispatch. No single C entry covers both. */
|
|
867
|
-
VALUE n_minus_1 = rb_funcall(n, id_sub, 1, INT2FIX(1));
|
|
868
|
-
diff = rb_funcall(n_minus_1, id_sub, 1, asc);
|
|
869
|
-
} else {
|
|
870
|
-
diff = asc;
|
|
1367
|
+
return rb_ca_reshape((int) ca->ndim, shape_argv, ranked);
|
|
871
1368
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
promotion in the descending branch preserves it. */
|
|
877
|
-
return diff;
|
|
1369
|
+
long axis_norm = rb_ca_normalize_axis_value(self, raxis, "order");
|
|
1370
|
+
/* Output data_type: CA_SIZE (int64 on 64-bit builds), as the rest of the
|
|
1371
|
+
*_index family. */
|
|
1372
|
+
return rb_ca_rank_index_ki_kind_dense(self, LONG2NUM(axis_norm), do_stable, kmode);
|
|
878
1373
|
}
|
|
879
1374
|
|
|
880
1375
|
/* ---- linear_section / linear_fetch ----------------------------------- */
|
|
@@ -896,9 +1391,7 @@ ca_linear_prep (VALUE self, VALUE raxis, const char *name, long *axis_out)
|
|
|
896
1391
|
Face) is caught by the rb_ca_to_float64 cast below, so no separate
|
|
897
1392
|
float-numeric flag is needed. */
|
|
898
1393
|
if ( ca_is_face(ca) ) {
|
|
899
|
-
|
|
900
|
-
ca_face_refuse_order(name, self, "order");
|
|
901
|
-
}
|
|
1394
|
+
ca_face_require_orderable(self, name, "order");
|
|
902
1395
|
self = rb_ca_strip_face_value(self);
|
|
903
1396
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
904
1397
|
}
|
|
@@ -925,13 +1418,26 @@ rb_ca_linear_section_m (int argc, VALUE *argv, VALUE self)
|
|
|
925
1418
|
rb_check_arity(argc, 1, 1);
|
|
926
1419
|
VALUE val = argv[0];
|
|
927
1420
|
|
|
928
|
-
/* Query gate: the reference axis, still a Face here,
|
|
929
|
-
into
|
|
1421
|
+
/* Query gate: the reference axis, still a Face here, and the query are
|
|
1422
|
+
brought into one space (ca_face_reconcile_pair). */
|
|
930
1423
|
if ( rb_obj_is_carray(self) ) {
|
|
931
1424
|
CArray *sca;
|
|
932
1425
|
TypedData_Get_Struct(self, CArray, &carray_data_type, sca);
|
|
933
1426
|
if ( ca_is_face(sca) ) {
|
|
934
|
-
|
|
1427
|
+
volatile VALUE ref = self, query = val;
|
|
1428
|
+
ca_face_reconcile_pair(&ref, &query, "linear_section");
|
|
1429
|
+
self = ref;
|
|
1430
|
+
val = query;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
{
|
|
1435
|
+
CArray *ca;
|
|
1436
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1437
|
+
if ( ca_is_complex_type(ca) ) {
|
|
1438
|
+
rb_raise(rb_eCADataTypeError,
|
|
1439
|
+
"linear_section: a %s array has no order (complex numbers are "
|
|
1440
|
+
"not ordered)", ca_type_name[ca->data_type]);
|
|
935
1441
|
}
|
|
936
1442
|
}
|
|
937
1443
|
|
|
@@ -960,6 +1466,27 @@ rb_ca_linear_fetch_m (int argc, VALUE *argv, VALUE self)
|
|
|
960
1466
|
rb_check_arity(argc, 1, 1);
|
|
961
1467
|
VALUE addr = argv[0];
|
|
962
1468
|
|
|
1469
|
+
/* A complex array interpolates its real and imaginary parts: the
|
|
1470
|
+
weights are real, so each part is a real interpolation. */
|
|
1471
|
+
{
|
|
1472
|
+
CArray *ca;
|
|
1473
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1474
|
+
if ( ca_is_complex_type(ca) ) {
|
|
1475
|
+
VALUE args[2], re, im;
|
|
1476
|
+
int n = 1;
|
|
1477
|
+
args[0] = addr;
|
|
1478
|
+
if ( ! NIL_P(ropt) ) args[n++] = ropt;
|
|
1479
|
+
re = rb_funcallv_kw(rb_funcall(self, rb_intern("real"), 0),
|
|
1480
|
+
rb_intern("linear_fetch"), n, args,
|
|
1481
|
+
NIL_P(ropt) ? RB_NO_KEYWORDS : RB_PASS_KEYWORDS);
|
|
1482
|
+
im = rb_funcallv_kw(rb_funcall(self, rb_intern("imag"), 0),
|
|
1483
|
+
rb_intern("linear_fetch"), n, args,
|
|
1484
|
+
NIL_P(ropt) ? RB_NO_KEYWORDS : RB_PASS_KEYWORDS);
|
|
1485
|
+
return rb_funcall(re, '+', 1,
|
|
1486
|
+
rb_funcall(im, '*', 1, rb_Complex(INT2FIX(0), INT2FIX(1))));
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
963
1490
|
long axis_norm = 0;
|
|
964
1491
|
VALUE sc = ca_linear_prep(self, raxis, "linear_fetch", &axis_norm);
|
|
965
1492
|
return rb_ca_linear_fetch_ki(sc, addr, LONG2NUM(axis_norm));
|