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.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -0
  3. data/README.md +24 -3
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_group.c +6 -6
  6. data/ext/ca_binop_dispatch.c +8 -0
  7. data/ext/ca_binop_dispatch.h +1 -0
  8. data/ext/ca_categorical_iterator.c +7 -7
  9. data/ext/ca_kernel_iterator.h +6 -4
  10. data/ext/ca_obj_array.c +4 -4
  11. data/ext/ca_obj_bincmp.c +19 -3
  12. data/ext/ca_obj_binop.c +36 -11
  13. data/ext/ca_obj_bitarray.c +2 -4
  14. data/ext/ca_obj_bitfield.c +86 -4
  15. data/ext/ca_obj_byte_swap.c +2 -4
  16. data/ext/ca_obj_const_string.c +422 -257
  17. data/ext/ca_obj_face.c +71 -33
  18. data/ext/ca_obj_fake.c +112 -13
  19. data/ext/ca_obj_farray.c +7 -1
  20. data/ext/ca_obj_fixlen_string.c +51 -17
  21. data/ext/ca_obj_grid.c +12 -7
  22. data/ext/ca_obj_meld.c +5 -5
  23. data/ext/ca_obj_monop.c +65 -39
  24. data/ext/ca_obj_object.c +9 -9
  25. data/ext/ca_obj_reduce.c +2 -4
  26. data/ext/ca_obj_refer.c +104 -10
  27. data/ext/ca_obj_repeat.c +1 -1
  28. data/ext/ca_obj_roll.c +2 -4
  29. data/ext/ca_obj_select.c +8 -6
  30. data/ext/ca_obj_select_axis.c +8 -6
  31. data/ext/ca_obj_stack.c +5 -5
  32. data/ext/ca_obj_stride.c +46 -8
  33. data/ext/ca_obj_string.c +2 -2
  34. data/ext/ca_obj_tile.c +2 -4
  35. data/ext/ca_obj_triop.c +15 -12
  36. data/ext/ca_obj_window.c +22 -15
  37. data/ext/ca_op_ipower.c +63 -15
  38. data/ext/ca_op_powi.h +6 -29
  39. data/ext/ca_sort_kernels.h +3 -0
  40. data/ext/ca_sweep_engine.c +90 -23
  41. data/ext/ca_sweep_engine.h +5 -6
  42. data/ext/caframe_csv_reader.c +1 -1
  43. data/ext/carray.h +3 -1
  44. data/ext/carray_access.c +98 -39
  45. data/ext/carray_address_basis.c +98 -24
  46. data/ext/carray_broadcast.c +2 -2
  47. data/ext/carray_call_cfunc.c +5 -8
  48. data/ext/carray_cast.c +311 -37
  49. data/ext/carray_cast_func.rb +37 -5
  50. data/ext/carray_conversion.c +56 -2
  51. data/ext/carray_copy.c +24 -0
  52. data/ext/carray_core.c +69 -6
  53. data/ext/carray_count.c +28 -9
  54. data/ext/carray_element.c +9 -3
  55. data/ext/carray_factorize.c +11 -11
  56. data/ext/carray_histogram.c +2 -2
  57. data/ext/carray_index_classifier.c +7 -5
  58. data/ext/carray_internal.h +94 -0
  59. data/ext/carray_kernels_binop.c +55 -55
  60. data/ext/carray_kernels_init.c +14 -14
  61. data/ext/carray_kernels_monop.c +90 -140
  62. data/ext/carray_lazy.c +16 -1
  63. data/ext/carray_loop.c +9 -2
  64. data/ext/carray_mask.c +118 -11
  65. data/ext/carray_median_percentile.c +13 -8
  66. data/ext/carray_memory_view.c +41 -22
  67. data/ext/carray_operator.c +72 -11
  68. data/ext/carray_order.c +52 -9
  69. data/ext/carray_partition.c +1 -1
  70. data/ext/carray_random.c +1 -1
  71. data/ext/carray_round.c +24 -1
  72. data/ext/carray_scatter.c +4 -0
  73. data/ext/carray_segment.c +1 -1
  74. data/ext/carray_slab.c +13 -4
  75. data/ext/carray_sort.c +6 -3
  76. data/ext/carray_sort_kernel.c +24 -0
  77. data/ext/carray_struct.c +21 -6
  78. data/ext/carray_test.c +85 -39
  79. data/ext/carray_time_text.c +6 -0
  80. data/ext/carray_utils.c +124 -54
  81. data/ext/mk_call_cfunc.rb +6 -11
  82. data/ext/mkkernel.rb +37 -27
  83. data/ext/version.h +4 -4
  84. data/lib/carray/axis_group.rb +9 -6
  85. data/lib/carray/basics.rb +26 -8
  86. data/lib/carray/bincount_nd.rb +8 -2
  87. data/lib/carray/block_iterator.rb +48 -21
  88. data/lib/carray/categorical.rb +21 -8
  89. data/lib/carray/categorical_iterator.rb +2 -1
  90. data/lib/carray/conditional.rb +52 -4
  91. data/lib/carray/const_string.rb +74 -0
  92. data/lib/carray/construct.rb +2 -2
  93. data/lib/carray/core_extensions.rb +19 -68
  94. data/lib/carray/data_type_extension.rb +1 -0
  95. data/lib/carray/fixlen_string.rb +83 -1
  96. data/lib/carray/frame/column_verbs.rb +61 -6
  97. data/lib/carray/frame/concat.rb +50 -6
  98. data/lib/carray/frame/csv_parser.rb +13 -4
  99. data/lib/carray/frame/frame.rb +38 -6
  100. data/lib/carray/frame/group.rb +67 -40
  101. data/lib/carray/frame/io.rb +10 -5
  102. data/lib/carray/frame/join.rb +20 -6
  103. data/lib/carray/frame/records.rb +6 -4
  104. data/lib/carray/frame/reshape.rb +25 -5
  105. data/lib/carray/frame/verbs.rb +78 -25
  106. data/lib/carray/frame/windows.rb +30 -6
  107. data/lib/carray/fuse_source.rb +4 -2
  108. data/lib/carray/iterator.rb +8 -0
  109. data/lib/carray/key_windows.rb +10 -4
  110. data/lib/carray/lazy.rb +12 -17
  111. data/lib/carray/methods/bin.rb +3 -0
  112. data/lib/carray/methods/composition.rb +1 -1
  113. data/lib/carray/methods/factorize.rb +2 -0
  114. data/lib/carray/methods/gather_nd.rb +4 -1
  115. data/lib/carray/methods/is_in.rb +23 -3
  116. data/lib/carray/methods/locate_addr.rb +63 -12
  117. data/lib/carray/methods/repeat.rb +11 -1
  118. data/lib/carray/record_order.rb +35 -6
  119. data/lib/carray/runtime.rb +10 -0
  120. data/lib/carray/segment_iterator.rb +19 -16
  121. data/lib/carray/serialize.rb +22 -0
  122. data/lib/carray/slab_iterator.rb +1 -1
  123. data/lib/carray/stack.rb +3 -3
  124. data/lib/carray/string.rb +6 -6
  125. data/lib/carray/string_operation_extension.rb +25 -4
  126. data/lib/carray/struct_builder.rb +4 -1
  127. data/lib/carray/time.rb +190 -46
  128. data/lib/carray/window_iterator.rb +48 -12
  129. data/yard-stubs/ca_obj_bitfield.rb +4 -1
  130. data/yard-stubs/ca_obj_refer.rb +3 -0
  131. data/yard-stubs/ca_obj_window.rb +2 -1
  132. data/yard-stubs/carray_access.rb +4 -2
  133. data/yard-stubs/carray_cast.rb +6 -0
  134. data/yard-stubs/carray_count.rb +6 -3
  135. data/yard-stubs/carray_math.rb +5 -0
  136. data/yard-stubs/carray_order.rb +6 -0
  137. data/yard-stubs/carray_scatter.rb +2 -1
  138. data/yard-stubs/carray_test.rb +18 -5
  139. metadata +2 -2
data/ext/carray_order.c CHANGED
@@ -169,7 +169,7 @@ ca_project (CArray *ca, CArray *ci, char *lfill, char *ufill)
169
169
  else CA_COUNT_BELOW_LOOP(T, <)
170
170
 
171
171
  static VALUE
172
- rb_ca_count_below (VALUE self, VALUE vq, VALUE vinclusive)
172
+ rb_ca_count_below (VALUE self, volatile VALUE vq, VALUE vinclusive)
173
173
  {
174
174
  volatile VALUE vout;
175
175
  CArray *cr, *cq, *co;
@@ -387,7 +387,7 @@ extern VALUE rb_ca_rank_index_ki_kind_dense (VALUE self, VALUE vaxis, int stable
387
387
  * search_kw / search_nearest_kw and their _addr siblings) on the
388
388
  * no-axis path. */
389
389
  static VALUE
390
- ca_reshape_search_result (VALUE query, VALUE rraw)
390
+ ca_reshape_search_result (volatile VALUE query, VALUE rraw)
391
391
  {
392
392
  CArray *cq;
393
393
  GetCArray(query, cq);
@@ -441,6 +441,28 @@ rb_ca_bsearch_addr_kw (int argc, VALUE *argv, VALUE self)
441
441
  return rb_ca_bsearch_addr_ki(self, argv[0], raxis);
442
442
  }
443
443
 
444
+ /* The query of search as the array reads it. true and false are 1 and 0
445
+ to a numeric array. A value the array can not hold (nil, a String)
446
+ finds nothing, as an Integer outside the array's type does. The query is asked of the
447
+ cells that matched (none), in the type that holds the answer. A Face that
448
+ can not be searched is refused as for any query, and a complex array,
449
+ which search does not take, goes on to its refusal. */
450
+ static void
451
+ ca_search_query_read (VALUE *pself, VALUE *pquery)
452
+ {
453
+ CArray *ca;
454
+ GetCArray(*pself, ca);
455
+ *pquery = ca_truth_as_number(ca, *pquery);
456
+ if ( ! ca_query_matches_nothing(ca, *pquery) || ca_is_complex_type(ca) ) {
457
+ return;
458
+ }
459
+ if ( ca_is_face(ca) ) {
460
+ ca_face_require_orderable(*pself, "search", "search");
461
+ }
462
+ *pself = rb_funcall(*pself, rb_intern("eq"), 1, *pquery);
463
+ *pquery = Qtrue;
464
+ }
465
+
444
466
  static VALUE
445
467
  rb_ca_search_kw (int argc, VALUE *argv, VALUE self)
446
468
  {
@@ -448,10 +470,12 @@ rb_ca_search_kw (int argc, VALUE *argv, VALUE self)
448
470
  VALUE raxis = Qnil;
449
471
  rb_scan_options(ropt, "axis", &raxis);
450
472
  rb_check_arity(argc, 1, 2);
473
+ VALUE rquery = argv[0];
474
+ ca_search_query_read(&self, &rquery);
451
475
 
452
476
  if ( NIL_P(raxis) ) {
453
477
  VALUE flat = rb_ca_flatten(self);
454
- VALUE q = rb_obj_is_carray(argv[0]) ? rb_ca_flatten(argv[0]) : argv[0];
478
+ VALUE q = rb_obj_is_carray(rquery) ? rb_ca_flatten(rquery) : rquery;
455
479
  VALUE r;
456
480
  VALUE ki_argv[3] = { q, INT2FIX(0), argv[1] };
457
481
  if ( argc >= 2 ) {
@@ -460,10 +484,10 @@ rb_ca_search_kw (int argc, VALUE *argv, VALUE self)
460
484
  else {
461
485
  r = rb_ca_search_ki(2, ki_argv, flat);
462
486
  }
463
- return rb_obj_is_carray(argv[0]) ? ca_reshape_search_result(argv[0], r) : r;
487
+ return rb_obj_is_carray(rquery) ? ca_reshape_search_result(rquery, r) : r;
464
488
  }
465
489
  /* search_ki(val, axis, eps) -- argc=2 (val,axis) or 3 (val,axis,eps) */
466
- VALUE ki_argv[3] = { argv[0], raxis, argv[1] };
490
+ VALUE ki_argv[3] = { rquery, raxis, argv[1] };
467
491
  if ( argc >= 2 ) {
468
492
  return rb_ca_search_ki(3, ki_argv, self);
469
493
  }
@@ -477,11 +501,13 @@ rb_ca_search_addr_kw (int argc, VALUE *argv, VALUE self)
477
501
  VALUE raxis = Qnil;
478
502
  rb_scan_options(ropt, "axis", &raxis);
479
503
  rb_check_arity(argc, 1, 2);
504
+ VALUE rquery = argv[0];
505
+ ca_search_query_read(&self, &rquery);
480
506
 
481
507
  if ( NIL_P(raxis) ) {
482
508
  /* 1-D self: addr == index reuses _ki (not _addr_ki). */
483
509
  VALUE flat = rb_ca_flatten(self);
484
- VALUE q = rb_obj_is_carray(argv[0]) ? rb_ca_flatten(argv[0]) : argv[0];
510
+ VALUE q = rb_obj_is_carray(rquery) ? rb_ca_flatten(rquery) : rquery;
485
511
  VALUE r;
486
512
  VALUE ki_argv[3] = { q, INT2FIX(0), argv[1] };
487
513
  if ( argc >= 2 ) {
@@ -490,9 +516,9 @@ rb_ca_search_addr_kw (int argc, VALUE *argv, VALUE self)
490
516
  else {
491
517
  r = rb_ca_search_ki(2, ki_argv, flat);
492
518
  }
493
- return rb_obj_is_carray(argv[0]) ? ca_reshape_search_result(argv[0], r) : r;
519
+ return rb_obj_is_carray(rquery) ? ca_reshape_search_result(rquery, r) : r;
494
520
  }
495
- VALUE ki_argv[3] = { argv[0], raxis, argv[1] };
521
+ VALUE ki_argv[3] = { rquery, raxis, argv[1] };
496
522
  if ( argc >= 2 ) {
497
523
  return rb_ca_search_addr_ki(3, ki_argv, self);
498
524
  }
@@ -597,6 +623,21 @@ rb_ca_sort_by_key (int argc, VALUE *argv, VALUE self)
597
623
  return rb_ca_fetch(self, addrs);
598
624
  }
599
625
 
626
+ /* The extreme kernels have no fixlen lane: a fixlen key is read through the
627
+ rank of its cells, which keeps ties and masked cells. */
628
+ static VALUE
629
+ ca_by_key_comparable (VALUE key)
630
+ {
631
+ CArray *ca;
632
+ TypedData_Get_Struct(key, CArray, &carray_data_type, ca);
633
+ if ( ca->data_type == CA_FIXLEN ) {
634
+ VALUE opts = rb_hash_new();
635
+ rb_hash_aset(opts, ID2SYM(rb_intern("method")), ID2SYM(rb_intern("dense")));
636
+ return rb_funcallv_kw(key, rb_intern("order"), 1, &opts, RB_PASS_KEYWORDS);
637
+ }
638
+ return key;
639
+ }
640
+
600
641
  /* max_by_key(key, axis: nil) -- self[key.max_addr(axis:)] (or UNDEF if empty). */
601
642
  static VALUE
602
643
  rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
@@ -609,6 +650,7 @@ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
609
650
  return CA_UNDEF;
610
651
  }
611
652
  rkey = ca_order_key(rkey, "max_by_key");
653
+ rkey = ca_by_key_comparable(rkey);
612
654
 
613
655
  VALUE addrs;
614
656
 
@@ -636,6 +678,7 @@ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
636
678
  return CA_UNDEF;
637
679
  }
638
680
  rkey = ca_order_key(rkey, "min_by_key");
681
+ rkey = ca_by_key_comparable(rkey);
639
682
 
640
683
  VALUE addrs;
641
684
 
@@ -1100,7 +1143,7 @@ ca_topk_buf_fiber (const ca_topk_fiber_t *f, ca_size_t len, ca_size_t cap,
1100
1143
  filled with the axis last and moved into place at the end (cap cells
1101
1144
  per fiber). */
1102
1145
  static VALUE
1103
- ca_topk_buffered_positions (VALUE key, long cap, long axis, int desc)
1146
+ ca_topk_buffered_positions (volatile VALUE key, long cap, long axis, int desc)
1104
1147
  {
1105
1148
  volatile VALUE holder = 0, out;
1106
1149
  VALUE perm[CA_RANK_MAX];
@@ -141,7 +141,7 @@ VALUE
141
141
  rb_ca_partition_copy_c (VALUE self, VALUE vkth, VALUE vaxis)
142
142
  {
143
143
  /* Mask handling (= same as partition view). */
144
- VALUE target = self;
144
+ volatile VALUE target = self; /* cat points into it; the walk can run a GC */
145
145
  CArray *cat;
146
146
  TypedData_Get_Struct(target, CArray, &carray_data_type, cat);
147
147
  if ( ca_has_mask(cat) ) {
data/ext/carray_random.c CHANGED
@@ -791,7 +791,7 @@ static CArray *
791
791
  ca_rng_cells (VALUE self, int64_t **cells)
792
792
  {
793
793
  CArray *ca;
794
- VALUE state = rb_ivar_get(self, rb_intern("@state"));
794
+ volatile VALUE state = rb_ivar_get(self, rb_intern("@state"));
795
795
  TypedData_Get_Struct(state, CArray, &carray_data_type, ca);
796
796
  ca_attach(ca); /* window: the state is an entity, whose attach holds nothing */
797
797
  *cells = (int64_t *) ca->ptr;
data/ext/carray_round.c CHANGED
@@ -196,6 +196,21 @@ ca_round_entity (CArray *ca, long ndigits)
196
196
  }
197
197
  }
198
198
 
199
+ typedef struct {
200
+ CArray *ca;
201
+ long ndigits;
202
+ } ca_round_call_t;
203
+
204
+ /* The cells of an object array are asked in turn, and a cell that is an
205
+ array asks its own (ca_computing_run). */
206
+ static VALUE
207
+ ca_round_cells (VALUE arg)
208
+ {
209
+ ca_round_call_t *c = (ca_round_call_t *) arg;
210
+ ca_round_entity(c->ca, c->ndigits);
211
+ return Qnil;
212
+ }
213
+
199
214
  /* The array to round: a boolean is rounded as its 0/1 int64 values, as
200
215
  round without digits does. */
201
216
  static VALUE
@@ -228,7 +243,15 @@ ca_round_copy (VALUE src, long ndigits)
228
243
  if ( ndigits > 0 && ca_is_integer_type(co) ) {
229
244
  return out;
230
245
  }
231
- ca_round_entity(co, ndigits);
246
+ if ( ca_is_object_type(co) ) {
247
+ ca_round_call_t c;
248
+ c.ca = co;
249
+ c.ndigits = ndigits;
250
+ ca_computing_run(1, &co, ca_round_cells, (VALUE) &c);
251
+ }
252
+ else {
253
+ ca_round_entity(co, ndigits);
254
+ }
232
255
  return out;
233
256
  }
234
257
 
data/ext/carray_scatter.c CHANGED
@@ -266,6 +266,10 @@ ca_scatter_is_scalar (VALUE v)
266
266
  } \
267
267
  } \
268
268
  else { \
269
+ if ( rb_obj_is_carray(rvals) ) { \
270
+ TypedData_Get_Struct(rvals, CArray, &carray_data_type, cv); \
271
+ ca_refuse_complex_into_real(ca, cv); \
272
+ } \
269
273
  /* coerce: a store into the receiver */ \
270
274
  rvals = rb_ca_wrap_readonly(rvals, INT2NUM(ca->data_type)); \
271
275
  TypedData_Get_Struct(rvals, CArray, &carray_data_type, cv); \
data/ext/carray_segment.c CHANGED
@@ -34,7 +34,7 @@ static ID id_lengths, id_offsets;
34
34
  data type and masked cells, since neither names a count or a
35
35
  position. */
36
36
  static VALUE
37
- segment_read_int64 (VALUE v, const char *name, int is_lengths,
37
+ segment_read_int64 (volatile VALUE v, const char *name, int is_lengths,
38
38
  ca_size_t pad_front, CArray **out_ca, ca_size_t *out_k)
39
39
  {
40
40
  CArray *src, *dst;
data/ext/carray_slab.c CHANGED
@@ -117,7 +117,7 @@ ca_slab_parse_axes (ca_slab_iter_state_t *st, VALUE axis_arg, int8_t src_ndim)
117
117
  init_val : VALUE — REDUCE_FIBER initial accumulator, or Qundef
118
118
  if not applicable. Stored as Qnil if Qundef. */
119
119
  static void
120
- slab_state_init (ca_slab_iter_state_t *st, VALUE source, int8_t form,
120
+ slab_state_init (ca_slab_iter_state_t *st, volatile VALUE source, int8_t form,
121
121
  VALUE axis_arg, VALUE data_type, VALUE init_val)
122
122
  {
123
123
  CArray *src;
@@ -422,8 +422,9 @@ ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
422
422
  ca_size_t exp = src->bytes;
423
423
  int8_t k;
424
424
  st->own_scratch_elements = elements;
425
- st->own_data_scratch = (char *) xmalloc(elements * src->bytes);
425
+ st->own_data_scratch = ca_alloc_cells_of(src->data_type, elements, src->bytes);
426
426
  if ( has_mask ) {
427
+ /* cells: boolean, which the collector does not read */
427
428
  st->own_mask_scratch = (boolean8_t *) xmalloc(elements);
428
429
  }
429
430
  /* Carrier wraps our own scratch. Direct field assignment of
@@ -516,6 +517,14 @@ ca_slab_advance_slab_view (ca_slab_iter_state_t *st,
516
517
  CArray *src, char *p_in, boolean8_t *m_in,
517
518
  int in_alias)
518
519
  {
520
+ /* A fiber of no cells has no address, but the view and its mask must
521
+ still read as attached. */
522
+ static char no_cells;
523
+ if ( st->t1->slab_elements == 0 ) {
524
+ if ( p_in == NULL ) p_in = &no_cells;
525
+ if ( m_in == NULL ) m_in = (boolean8_t *) &no_cells;
526
+ }
527
+
519
528
  if ( in_alias ) {
520
529
  /* ALIAS — single-axis OR multi-axis contig. ptr fast path uses
521
530
  ca_index2addr * bytes + ptr (= safe since slab is row-major
@@ -751,7 +760,7 @@ ca_slab_run_map (ca_slab_iter_state_t *st)
751
760
  st->t1->slab_dims);
752
761
  if ( st->own_data_scratch != NULL ) {
753
762
  st->own_out_scratch =
754
- (char *) xmalloc(st->own_scratch_elements * st->out_bytes);
763
+ ca_alloc_cells_of(st->out_data_type, st->own_scratch_elements, st->out_bytes);
755
764
  }
756
765
 
757
766
  /* Step 4: per-slab loop.
@@ -779,7 +788,7 @@ ca_slab_run_map (ca_slab_iter_state_t *st)
779
788
  piece = ca_slab_piece(st);
780
789
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) &&
781
790
  ca_iter_state_next_slab_axes(st->t1_out, &p_out, &m_out) ) {
782
- VALUE result;
791
+ volatile VALUE result;
783
792
 
784
793
  ca_slab_advance_slab_view(st, slab_cs, carrier_cw,
785
794
  src, p_in, m_in, in_alias);
data/ext/carray_sort.c CHANGED
@@ -621,7 +621,7 @@ axis2addr_body (void *user_data, void *ptr, ca_size_t n)
621
621
  * by rb_ca_take_along_axis_c / rb_ca_put_along_axis in
622
622
  * carray_order.c. */
623
623
  VALUE
624
- rb_ca_axis2addr_c (VALUE self, VALUE vindices, VALUE vaxis)
624
+ rb_ca_axis2addr_c (VALUE self, volatile VALUE vindices, VALUE vaxis)
625
625
  {
626
626
  rb_check_carray_object(vindices);
627
627
 
@@ -958,6 +958,9 @@ rb_ca_sort_copy (int argc, VALUE *argv, VALUE self)
958
958
  CA_FOR_EACH_FIBER_INOUT(st_in, st_out, cat, cao, (int8_t) axis,
959
959
  CA_KERNEL_NO_MASK, pi, po, n) {
960
960
  (void) fiber_n_for_check;
961
+ if ( n == 0 ) { /* an empty array's one fiber has no buffer */
962
+ continue;
963
+ }
961
964
  memcpy(po, pi, (size_t) n * (size_t) bytes);
962
965
  if ( do_stable ) {
963
966
  switch ( cat->data_type ) {
@@ -970,12 +973,12 @@ rb_ca_sort_copy (int argc, VALUE *argv, VALUE self)
970
973
  case CA_INT64: ca_sort_merge_i64((int64_t *) po, (int64_t *) aux, n); break;
971
974
  case CA_UINT64: ca_sort_merge_u64((uint64_t *) po, (uint64_t *) aux, n); break;
972
975
  case CA_FLOAT32: {
973
- ca_size_t fin = ca_partition_nan_f32((float32_t *) po, n);
976
+ ca_size_t fin = ca_partition_nan_stable_f32((float32_t *) po, n, (float32_t *) aux);
974
977
  ca_sort_merge_f32((float32_t *) po, (float32_t *) aux, fin);
975
978
  break;
976
979
  }
977
980
  case CA_FLOAT64: {
978
- ca_size_t fin = ca_partition_nan_f64((double *) po, n);
981
+ ca_size_t fin = ca_partition_nan_stable_f64((double *) po, n, (double *) aux);
979
982
  ca_sort_merge_f64((double *) po, (double *) aux, fin);
980
983
  break;
981
984
  }
@@ -446,6 +446,30 @@ struct ca_partition_nan_##SUFFIX##_eat_semicolon
446
446
  DEFINE_PARTITION_NAN(float32_t, f32);
447
447
  DEFINE_PARTITION_NAN(double, f64);
448
448
 
449
+ /* Stable variant for a stable value sort: the finite values, then the
450
+ NaNs, each in their original order. scratch holds n values, taken by
451
+ the caller before it opens its walks. */
452
+ #define DEFINE_PARTITION_NAN_STABLE(TYPE, SUFFIX) \
453
+ ca_size_t \
454
+ ca_partition_nan_stable_##SUFFIX (TYPE *a, ca_size_t n, TYPE *scratch) \
455
+ { \
456
+ ca_size_t fin = 0, k; \
457
+ for ( k = 0; k < n; k++ ) { \
458
+ if ( !isnan(a[k]) ) scratch[fin++] = a[k]; \
459
+ } \
460
+ if ( fin == n ) return n; \
461
+ ca_size_t m = fin; \
462
+ for ( k = 0; k < n; k++ ) { \
463
+ if ( isnan(a[k]) ) scratch[m++] = a[k]; \
464
+ } \
465
+ memcpy(a, scratch, (size_t) n * sizeof(TYPE)); \
466
+ return fin; \
467
+ } \
468
+ struct ca_partition_nan_stable_##SUFFIX##_eat_semicolon
469
+
470
+ DEFINE_PARTITION_NAN_STABLE(float32_t, f32);
471
+ DEFINE_PARTITION_NAN_STABLE(double, f64);
472
+
449
473
  /* Pair variant for argsort: separates ca_pair_<type> by
450
474
  isnan(v.value). Returns the finite count.
451
475
 
data/ext/carray_struct.c CHANGED
@@ -113,9 +113,15 @@ ca_struct_read_primitive_fast (const char *p, int type_code)
113
113
  }
114
114
  }
115
115
 
116
+ /* A String is read as a decimal number, as a store into the member's
117
+ field view reads it. */
116
118
  static void
117
119
  ca_struct_write_primitive_fast (char *p, int type_code, VALUE val)
118
120
  {
121
+ if ( type_code != CA_BOOLEAN && RB_TYPE_P(val, T_STRING) ) {
122
+ ca_decimal_string_store(val, (int8_t) type_code, p);
123
+ return;
124
+ }
119
125
  switch (type_code) {
120
126
  case CA_BOOLEAN: {
121
127
  /* The values a boolean array takes: true / false / 1 / 0. */
@@ -259,7 +265,7 @@ ca_struct_write_bitfield_fast (char *p, int view_bytes,
259
265
  }
260
266
  }
261
267
  else {
262
- v = NUM2ULL(val);
268
+ v = NUM2ULL(ca_integer_wrap_bits(val, bits, 0));
263
269
  }
264
270
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
265
271
  : ((uint64_t)1 << bits) - 1;
@@ -324,6 +330,15 @@ ca_struct_read_endian_swapped_fast (const char *p, int type_code)
324
330
  static void
325
331
  ca_struct_write_endian_swapped_fast (char *p, int type_code, VALUE val)
326
332
  {
333
+ if ( RB_TYPE_P(val, T_STRING) ) {
334
+ char host[8];
335
+ ca_decimal_string_store(val, (int8_t) type_code, host);
336
+ switch ( ca_sizeof[type_code] ) {
337
+ case 2: { uint16_t v; memcpy(&v, host, 2); v = CA_BSWAP16(v); memcpy(p, &v, 2); return; }
338
+ case 4: { uint32_t v; memcpy(&v, host, 4); v = CA_BSWAP32(v); memcpy(p, &v, 4); return; }
339
+ case 8: { uint64_t v; memcpy(&v, host, 8); v = CA_BSWAP64(v); memcpy(p, &v, 8); return; }
340
+ }
341
+ }
327
342
  switch (type_code) {
328
343
  case CA_INT16: {
329
344
  uint16_t v = (uint16_t) NUM2LL(ca_obj2integer_fitting(val, CA_INT16));
@@ -425,7 +440,7 @@ ca_struct_member_name (VALUE klass, VALUE name)
425
440
  if ( ! RB_TYPE_P(name, T_STRING) && ! RB_TYPE_P(name, T_FIXNUM) ) {
426
441
  rb_raise(rb_eTypeError,
427
442
  "a struct member is named by a String or an Integer position "
428
- "(got %s)", rb_obj_classname(name));
443
+ "(got %"PRIsVALUE")", rb_obj_class(name));
429
444
  }
430
445
  name_s = ca_struct_resolve_name(klass, name);
431
446
  if ( NIL_P(name_s)
@@ -708,8 +723,8 @@ rb_ca_struct_initialize (int argc, VALUE *argv, VALUE self)
708
723
  VALUE unknown_inspect = rb_inspect(state.unknown_keys);
709
724
  VALUE known_inspect = rb_inspect(members);
710
725
  rb_raise(rb_eArgError,
711
- "unknown member(s) for %s: %s (known: %s)",
712
- rb_class2name(klass),
726
+ "unknown member(s) for %"PRIsVALUE": %s (known: %s)",
727
+ klass,
713
728
  StringValueCStr(unknown_inspect),
714
729
  StringValueCStr(known_inspect));
715
730
  }
@@ -721,8 +736,8 @@ rb_ca_struct_initialize (int argc, VALUE *argv, VALUE self)
721
736
  long mems_size = RARRAY_LEN(members);
722
737
  if (argc > mems_size) {
723
738
  rb_raise(rb_eArgError,
724
- "too many arguments for %s.new (<%d> for <%ld>)",
725
- rb_class2name(klass), argc, mems_size);
739
+ "too many arguments for %"PRIsVALUE".new (<%d> for <%ld>)",
740
+ klass, argc, mems_size);
726
741
  }
727
742
  for (int i = 0; i < argc; i++) {
728
743
  VALUE name_s = rb_ary_entry(members, i);
data/ext/carray_test.c CHANGED
@@ -201,6 +201,23 @@ rb_ca_is_type (VALUE arg, int type)
201
201
  live in ca_obj_record.c; signatures stay in carray.h for cross-file
202
202
  extern use. */
203
203
 
204
+ /* Whether v, an index along a length of n, names a position: a negative one
205
+ * counts from the end. An Integer too large for ca_size_t is out of any
206
+ * range; what is not an Integer is not an index. */
207
+ static int
208
+ ca_index_arg_in_range (VALUE v, ca_size_t n, const char *arg)
209
+ {
210
+ ca_size_t idx;
211
+ if ( RB_TYPE_P(v, T_BIGNUM) ) {
212
+ return 0;
213
+ }
214
+ idx = ca_integer_arg(v, arg, NULL);
215
+ if ( idx < 0 ) {
216
+ idx += n;
217
+ }
218
+ return idx >= 0 && idx < n;
219
+ }
220
+
204
221
  /* CArray#valid_index?(*idx) -- whether self[*idx] names a cell: a negative
205
222
  * index counts from the end of its axis, as it does at every door. The
206
223
  * number of indices must equal ndim (raises otherwise). */
@@ -208,7 +225,6 @@ static VALUE
208
225
  rb_ca_is_valid_index (int argc, VALUE *argv, VALUE self)
209
226
  {
210
227
  CArray *ca;
211
- ca_size_t idx;
212
228
  int i;
213
229
 
214
230
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
@@ -218,11 +234,7 @@ rb_ca_is_valid_index (int argc, VALUE *argv, VALUE self)
218
234
  "invalid # of arguments (%i for %i)", argc, ca->ndim);
219
235
  }
220
236
  for (i=0; i<ca->ndim; i++) {
221
- idx = NUM2SIZE(argv[i]);
222
- if ( idx < 0 ) {
223
- idx += ca->dim[i];
224
- }
225
- if ( idx < 0 || idx >= ca->dim[i] ) {
237
+ if ( ! ca_index_arg_in_range(argv[i], ca->dim[i], "an index") ) {
226
238
  return Qfalse;
227
239
  }
228
240
  }
@@ -236,30 +248,26 @@ static VALUE
236
248
  rb_ca_is_valid_addr (VALUE self, VALUE raddr)
237
249
  {
238
250
  CArray *ca;
239
- ca_size_t addr;
240
251
 
241
252
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
242
- addr = NUM2SIZE(raddr);
243
- if ( addr < 0 ) {
244
- addr += ca->elements;
245
- }
246
- if ( addr < 0 || addr >= ca->elements ) {
247
- return Qfalse;
248
- }
249
- else {
250
- return Qtrue;
251
- }
253
+ return ca_index_arg_in_range(raddr, ca->elements, "an address") ? Qtrue : Qfalse;
252
254
  }
253
255
 
254
- /* CArray#same_shape?(other) — 1 if self and other share ndim and dim[].
255
- * Scalars compare true against any array (see ca_has_same_shape). */
256
+ /* CArray#same_shape?(other) — whether self and other share ndim and dim[].
257
+ * Scalars compare true against any array (see ca_has_same_shape). Only the
258
+ * shape is compared, so no value is converted: other that is not a CArray
259
+ * takes the shape it has as an object array. */
256
260
  static VALUE
257
261
  rb_ca_has_same_shape (VALUE self, VALUE other)
258
262
  {
259
263
  CArray *ca, *cb;
260
264
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
261
- /* coerce: only the shape is compared */
262
- cb = ca_wrap_readonly(other, ca->data_type);
265
+ if ( rb_obj_is_carray(other) ) {
266
+ TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
267
+ }
268
+ else {
269
+ cb = ca_wrap_readonly(other, CA_OBJECT);
270
+ }
263
271
  return ca_has_same_shape(ca, cb) ? Qtrue : Qfalse;
264
272
  }
265
273
 
@@ -276,10 +284,22 @@ eql_## type (const char *a, const char *b, ca_size_t bytes) \
276
284
 
277
285
  /* == compares contents: a NaN equals a NaN (whatever its payload), as the
278
286
  same array read back or copied holds the same NaN; -0.0 equals 0.0 as
279
- values do. eql? stays bitwise. */
287
+ values do. eql? stays bitwise. An object cell equals itself first, as
288
+ in Array#== and Array#eql?, so a copy of an array holding any NaN object
289
+ equals it. A cell holding a CArray is asked all the same, so an array
290
+ that holds itself is refused as cyclic. */
291
+ static int
292
+ ca_object_cells_identical (VALUE x, VALUE y)
293
+ {
294
+ return x == y && ! rb_obj_is_carray(x);
295
+ }
296
+
280
297
  static int
281
298
  ca_object_cells_equal (VALUE x, VALUE y)
282
299
  {
300
+ if ( ca_object_cells_identical(x, y) ) {
301
+ return 1;
302
+ }
283
303
  if ( RB_FLOAT_TYPE_P(x) && RB_FLOAT_TYPE_P(y) &&
284
304
  isnan(RFLOAT_VALUE(x)) && isnan(RFLOAT_VALUE(y)) ) {
285
305
  return 1;
@@ -287,6 +307,13 @@ ca_object_cells_equal (VALUE x, VALUE y)
287
307
  return RTEST(rb_funcall(x, id_equal, 1, y)) ? 1 : 0;
288
308
  }
289
309
 
310
+ static int
311
+ ca_object_cells_eql (VALUE x, VALUE y)
312
+ {
313
+ return ca_object_cells_identical(x, y)
314
+ || RTEST(rb_funcall(x, id_eql, 1, y));
315
+ }
316
+
290
317
  static int
291
318
  eql_VALUE (const char *a, const char *b, ca_size_t bytes)
292
319
  {
@@ -416,8 +443,7 @@ ca_compare_body (VALUE arg)
416
443
  for (i=0; i<ca->elements; i++) {
417
444
  volatile VALUE x = va[i];
418
445
  volatile VALUE y = vb[i];
419
- int eq = strict ? RTEST(rb_funcall(x, id_eql, 1, y))
420
- : ca_object_cells_equal(x, y);
446
+ int eq = strict ? ca_object_cells_eql(x, y) : ca_object_cells_equal(x, y);
421
447
  if ( ! eq ) {
422
448
  flag = 0;
423
449
  break;
@@ -459,8 +485,7 @@ ca_compare_body (VALUE arg)
459
485
  if ( is_object ) {
460
486
  volatile VALUE x = *(VALUE *) pa;
461
487
  volatile VALUE y = *(VALUE *) pb;
462
- eq = strict ? RTEST(rb_funcall(x, id_eql, 1, y))
463
- : ca_object_cells_equal(x, y);
488
+ eq = strict ? ca_object_cells_eql(x, y) : ca_object_cells_equal(x, y);
464
489
  }
465
490
  else if ( strict
466
491
  || ca->data_type <= CA_UINT64
@@ -507,7 +532,7 @@ ca_compare_run (VALUE arg)
507
532
  *
508
533
  * The only difference is the per-element comparator:
509
534
  * == : ca_eql[data_type] (by value: NaN equals NaN, -0.0 equals 0.0)
510
- * eql?: memcmp for numeric, rb_funcall(:eql?) for CA_OBJECT
535
+ * eql?: memcmp for numeric, ca_object_cells_eql for CA_OBJECT
511
536
  *
512
537
  * The unmasked-unmasked branch takes a bulk memcmp or per-cell
513
538
  * shortcut to amortise per-element function pointer dispatch. */
@@ -659,6 +684,24 @@ rb_ca_eql (VALUE self, VALUE other)
659
684
 
660
685
  #define CA_HASH_SAMPLE_BYTES 64
661
686
 
687
+ typedef struct {
688
+ st_index_t h;
689
+ ca_size_t n;
690
+ const VALUE *cells;
691
+ } ca_hash_cells_t;
692
+
693
+ static VALUE
694
+ ca_hash_cells (VALUE arg)
695
+ {
696
+ ca_hash_cells_t *hc = (ca_hash_cells_t *) arg;
697
+ ca_size_t i;
698
+
699
+ for (i = 0; i < hc->n; i++) {
700
+ hc->h = rb_hash_uint(hc->h, (st_index_t) NUM2LONG(rb_hash(hc->cells[i])));
701
+ }
702
+ return Qnil;
703
+ }
704
+
662
705
  static st_index_t
663
706
  ca_hash (CArray *ca)
664
707
  {
@@ -668,19 +711,21 @@ ca_hash (CArray *ca)
668
711
  ca_size_t data_len = ca_length(ca);
669
712
  ca_size_t sample = (data_len < CA_HASH_SAMPLE_BYTES) ? data_len : CA_HASH_SAMPLE_BYTES;
670
713
 
671
- h = rb_memhash(&ca->data_type, sizeof(ca->data_type));
672
- h ^= rb_memhash(&ca->ndim, sizeof(ca->ndim));
673
- h ^= rb_memhash(&ca->bytes, sizeof(ca->bytes));
674
- h ^= rb_memhash(&ca->elements, sizeof(ca->elements));
675
- h ^= rb_memhash(&scalar_flag, sizeof(scalar_flag));
714
+ /* Mixed in order: XOR would cancel equal fields, as a 1-D array's dim
715
+ and elements are. */
716
+ h = rb_hash_start(ca->data_type);
717
+ h = rb_hash_uint(h, (st_index_t) ca->ndim);
718
+ h = rb_hash_uint(h, (st_index_t) ca->bytes);
719
+ h = rb_hash_uint(h, (st_index_t) ca->elements);
720
+ h = rb_hash_uint(h, (st_index_t) scalar_flag);
676
721
  if ( ca->ndim > 0 ) {
677
- h ^= rb_memhash(ca->dim, sizeof(ca_size_t) * ca->ndim);
722
+ h = rb_hash_uint(h, rb_memhash(ca->dim, sizeof(ca_size_t) * ca->ndim));
678
723
  }
679
724
 
680
725
  /* ca_is_any_masked() attaches/detaches the mask internally and returns
681
726
  only a boolean, so we never touch ca->mask->ptr here. */
682
727
  masked_flag = ca_is_any_masked(ca) ? 1 : 0;
683
- h ^= rb_memhash(&masked_flag, sizeof(masked_flag));
728
+ h = rb_hash_uint(h, (st_index_t) masked_flag);
684
729
 
685
730
  if ( ! masked_flag && sample > 0 ) {
686
731
  ca_size_t addrs[CA_HASH_SAMPLE_BYTES];
@@ -700,22 +745,23 @@ ca_hash (CArray *ca)
700
745
  #hash, not the VALUE bits. The cells sit on the stack, where the
701
746
  GC sees them while #hash runs. */
702
747
  VALUE cells[CA_HASH_SAMPLE_BYTES / sizeof(VALUE)];
748
+ ca_hash_cells_t hc;
703
749
  ca_xfer_addrs(ca, ncell, addrs, cells, CA_XFER_GET);
704
- for (i = 0; i < ncell; i++) {
705
- h = rb_hash_uint(h, (st_index_t) NUM2LONG(rb_hash(cells[i])));
706
- }
750
+ hc.h = h; hc.n = ncell; hc.cells = cells;
751
+ ca_cell_walk_run(ca_hash_cells, (VALUE) &hc);
752
+ h = hc.h;
707
753
  RB_GC_GUARD(cells[0]);
708
754
  }
709
755
  else {
710
756
  volatile VALUE holder;
711
757
  char *buf = ALLOCV_N(char, holder, ncell * ca->bytes);
712
758
  ca_xfer_addrs(ca, ncell, addrs, buf, CA_XFER_GET);
713
- h ^= rb_memhash(buf, ncell * ca->bytes);
759
+ h = rb_hash_uint(h, rb_memhash(buf, ncell * ca->bytes));
714
760
  ALLOCV_END(holder);
715
761
  }
716
762
  }
717
763
 
718
- return h;
764
+ return rb_hash_end(h);
719
765
  }
720
766
 
721
767
  /* CArray#hash — Hash key value; wraps ca_hash into a Ruby Integer. */
@@ -602,6 +602,12 @@ rb_ca_s_time_ticks_from_fields (VALUE klass,
602
602
  other than Z, UTC and GMT, an offset written another way) is deferred to
603
603
  Date._strptime for that cell.
604
604
 
605
+ Three inputs read differently from Date._strptime. A format with both
606
+ a two-digit year and a four-digit year takes the four-digit year, where
607
+ Date._strptime adds the century to it. %Z ends at the zone's name, so
608
+ digits directly after it go to the next directive. A NUL in the text
609
+ is text, so it is left over.
610
+
605
611
  Strict reading, used to find the format a column is written in, holds
606
612
  the text to the shape of the format: %Y is four digits and %y two, %N
607
613
  one to nine digits, the other numbers one or two digits (two when a