carray 3.0.3 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
data/ext/ca_obj_face.c CHANGED
@@ -132,10 +132,18 @@ rb_ca_face_template (VALUE original_face, CArray *new_parent,
132
132
  TypedData_Get_Struct(original_face, CArray, &carray_data_type, src);
133
133
  tdata = ca_typeddata[src->obj_type];
134
134
 
135
- /* Actual subclass size (incl. tail) — via TypedData's dsize callback */
136
- size = (tdata && tdata->function.dsize)
137
- ? tdata->function.dsize(src)
138
- : sizeof(CAView);
135
+ /* The struct with its subclass tail. struct_size is the struct alone;
136
+ a dsize callback also counts the buffers the struct points to (dim),
137
+ so it is only the fallback for a type registered without struct_size,
138
+ and copying that many bytes reads past the struct. */
139
+ if ( ca_func[src->obj_type].struct_size ) {
140
+ size = ca_func[src->obj_type].struct_size;
141
+ }
142
+ else {
143
+ size = (tdata && tdata->function.dsize)
144
+ ? tdata->function.dsize(src)
145
+ : sizeof(CAView);
146
+ }
139
147
 
140
148
  dst = (CArray *) xmalloc(size);
141
149
  /* CAREFUL: the whole-struct memcpy is how a C-tail Face
@@ -198,6 +206,23 @@ ca_face_relift_entity (VALUE face, VALUE result)
198
206
  return lifted;
199
207
  }
200
208
 
209
+ /* A lift moves a view built on a wrapper (a Face or the lazy marker) one
210
+ * level down, onto what the wrapper wraps, so the view no longer keeps the
211
+ * wrapper alive. The view's mask was built over the wrapper's mask, which
212
+ * the wrapper owns and frees with itself; move it down the same level.
213
+ * The wrapper's mask is a same-shape refer of its parent's mask, so the
214
+ * mask view addresses the same cells there. */
215
+ static void
216
+ ca_lift_move_mask_down (CArray *view_ca, CArray *wp)
217
+ {
218
+ CArray *below = ((CAView *) wp)->parent;
219
+ if ( view_ca->mask && wp->mask && ca_is_view(wp->mask)
220
+ && ((CAView *) wp->mask)->parent == below->mask
221
+ && ((CAView *) view_ca->mask)->parent == wp->mask ) {
222
+ ((CAView *) view_ca->mask)->parent = below->mask;
223
+ }
224
+ }
225
+
201
226
  /* ca_face_lift(view, face_parent) — at the tail of user-facing entry
202
227
  * points (`ca[...]` etc.), when the operand was a Face, re-wrap the
203
228
  * resulting view as a Face of the same subclass. Thin wrapper over
@@ -260,6 +285,7 @@ ca_face_lift (VALUE view, VALUE face_parent)
260
285
  && ! ca_test_flag(view_ca, CA_FLAG_MULTI_PARENTS) ) {
261
286
  ((CAView *) view_ca)->parent = ((CAView *) fp)->parent; /* C pointer: one level */
262
287
  rb_ca_set_parent(view, rb_ca_parent(face_parent)); /* @parent ivar: lockstep */
288
+ ca_lift_move_mask_down(view_ca, fp);
263
289
  }
264
290
  }
265
291
 
@@ -269,6 +295,12 @@ ca_face_lift (VALUE view, VALUE face_parent)
269
295
  lifted = rb_ca_face_template(face_parent, view_ca, view_ca->dim);
270
296
  /* Pin view as a GC root of lifted so the slice is not collected. */
271
297
  rb_ca_set_parent(lifted, view);
298
+ /* The Face the view was taken from is off the parent chain now; keep it
299
+ for the attributes set on it. A fresh result the ordering kernels
300
+ re-wrap is not a view of it and takes none. */
301
+ if ( ca_is_view(view_ca) ) {
302
+ rb_ca_set_lifted_from(lifted, face_parent);
303
+ }
272
304
 
273
305
  /* Optional Ruby callback for ivar / state carry. Faces holding
274
306
  semantic state in the struct tail (CATime / CATimedelta
@@ -346,6 +378,7 @@ ca_lazy_marker_lift (VALUE view, VALUE marker)
346
378
  && ! ca_test_flag(view_ca, CA_FLAG_MULTI_PARENTS) ) {
347
379
  ((CAView *) view_ca)->parent = ((CAView *) mp)->parent; /* C pointer */
348
380
  rb_ca_set_parent(view, rb_ca_parent(marker)); /* @parent ivar */
381
+ ca_lift_move_mask_down(view_ca, mp);
349
382
  }
350
383
 
351
384
  return rb_ca_lazy_marker_new(view);
@@ -522,15 +555,10 @@ ca_face_register_state_compatible (int obj_type, ca_face_state_compatible_fn fn)
522
555
  static int
523
556
  ca_face_has_ruby_state_check (VALUE va)
524
557
  {
525
- static ID id_face_state_compatible_p = 0;
526
- static ID id_method = 0;
527
- static ID id_owner = 0;
558
+ ID id_face_state_compatible_p = rb_intern("face_state_compatible?");
559
+ ID id_method = rb_intern("method");
560
+ ID id_owner = rb_intern("owner");
528
561
  VALUE m, owner;
529
- if ( id_face_state_compatible_p == 0 ) {
530
- id_face_state_compatible_p = rb_intern("face_state_compatible?");
531
- id_method = rb_intern("method");
532
- id_owner = rb_intern("owner");
533
- }
534
562
  m = rb_funcall(va, id_method, 1, ID2SYM(id_face_state_compatible_p));
535
563
  owner = rb_funcall(m, id_owner, 0);
536
564
  return ( owner != rb_cCArray ) ? 1 : 0;
@@ -650,16 +678,11 @@ ca_face_register_state_portable (int obj_type, int portable)
650
678
  static int
651
679
  ca_face_class_has_portable_method (VALUE klass)
652
680
  {
653
- static ID id_face_state_portable_p = 0;
654
- static ID id_method = 0;
655
- static ID id_owner = 0;
681
+ ID id_face_state_portable_p = rb_intern("face_state_portable?");
682
+ ID id_method = rb_intern("method");
683
+ ID id_owner = rb_intern("owner");
656
684
  VALUE m, owner;
657
685
  if ( NIL_P(klass) ) return 0;
658
- if ( id_face_state_portable_p == 0 ) {
659
- id_face_state_portable_p = rb_intern("face_state_portable?");
660
- id_method = rb_intern("method");
661
- id_owner = rb_intern("owner");
662
- }
663
686
  if ( !rb_respond_to(klass, id_face_state_portable_p) ) return 0;
664
687
  m = rb_funcall(klass, id_method, 1, ID2SYM(id_face_state_portable_p));
665
688
  owner = rb_funcall(m, id_owner, 0);
@@ -680,18 +703,88 @@ ca_face_conversion_hint (VALUE face)
680
703
  : "to_type(:object) gives its values";
681
704
  }
682
705
 
706
+ /* The method that gives the storage under a Face, for a refusal to point
707
+ at: #ticks for a time or a duration, #codes for a categorical. NULL for
708
+ a Face that names its storage some other way, or not at all. */
709
+ const char *
710
+ ca_face_storage_method (VALUE face)
711
+ {
712
+ static const char *names[] = { "ticks", "codes" };
713
+ size_t i;
714
+ for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
715
+ if ( rb_respond_to(face, rb_intern(names[i])) ) {
716
+ return names[i];
717
+ }
718
+ }
719
+ return NULL;
720
+ }
721
+
722
+ /* ca.face_storage_method -- the name of the method that gives the storage
723
+ under ca (ca_face_storage_method), as a String, or nil. For refusals
724
+ written in Ruby. */
725
+ static VALUE
726
+ rb_ca_face_storage_method (VALUE self)
727
+ {
728
+ const char *name = ca_face_storage_method(self);
729
+ return name ? rb_str_new_cstr(name) : Qnil;
730
+ }
731
+
683
732
  /* Refuse to order (or, with verb "count", to count by value) a Face whose
684
733
  storage does not hold its values in order, pointing at what does: its
685
734
  values, or its storage when that is what is meant. */
686
735
  void
687
736
  ca_face_refuse_order (const char *name, VALUE face, const char *verb)
688
737
  {
738
+ const char *storage = ca_face_storage_method(face);
739
+ if ( storage ) {
740
+ rb_raise(rb_eArgError,
741
+ "%s: %s is not orderable by storage (a cell encodes the value "
742
+ "rather than being it); %s its values instead -- %s -- or %s "
743
+ "its storage, which #%s gives",
744
+ name, rb_obj_classname(face), verb,
745
+ ca_face_conversion_hint(face), verb, storage);
746
+ }
689
747
  rb_raise(rb_eArgError,
690
748
  "%s: %s is not orderable by storage (a cell encodes the value "
691
- "rather than being it); %s its values instead -- %s -- or pass "
692
- ".parent to %s its storage",
749
+ "rather than being it); %s its values instead -- %s",
693
750
  name, rb_obj_classname(face), verb,
694
- ca_face_conversion_hint(face), verb);
751
+ ca_face_conversion_hint(face));
752
+ }
753
+
754
+ /* The rule every family that orders, compares, searches or hashes the cells
755
+ of a Face reference keeps: the storage stands for the values only when the
756
+ Face declares ORDERABLE_STORAGE. A fixlen storage is no exception -- its
757
+ bytes are an order only when the Face says so (CAFixlenString). Returns
758
+ when the Face is ORDERABLE; refuses otherwise (ca_face_refuse_order). A
759
+ family that can order by a key asks ca_face_orders_by_itself first. */
760
+ void
761
+ ca_face_require_orderable (VALUE face, const char *name, const char *verb)
762
+ {
763
+ CArray *ca;
764
+ TypedData_Get_Struct(face, CArray, &carray_data_type, ca);
765
+ if ( ! ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
766
+ ca_face_refuse_order(name, face, verb);
767
+ }
768
+ }
769
+
770
+ /* Whether a Face is ordered by its own #order rather than by its storage:
771
+ it is not ORDERABLE and answers #order itself (CAConstString, CARecord),
772
+ so a family that orders by a key can take that order. CArray#order is
773
+ not one: it sorts the storage and comes back to the gate. */
774
+ int
775
+ ca_face_orders_by_itself (VALUE face)
776
+ {
777
+ ID id_order = rb_intern("order");
778
+ ID id_method = rb_intern("method");
779
+ ID id_owner = rb_intern("owner");
780
+ CArray *ca;
781
+ VALUE m;
782
+ TypedData_Get_Struct(face, CArray, &carray_data_type, ca);
783
+ if ( ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
784
+ return 0;
785
+ }
786
+ m = rb_funcall(face, id_method, 1, ID2SYM(id_order));
787
+ return ( rb_funcall(m, id_owner, 0) != rb_cCArray ) ? 1 : 0;
695
788
  }
696
789
 
697
790
  /* Refuse a numeric operation (arithmetic, math, a reduction) on a Face,
@@ -708,10 +801,20 @@ ca_face_refuse_numeric (VALUE self, const char *name, VALUE eclass)
708
801
  if ( ! ca_is_face(ca) ) {
709
802
  return;
710
803
  }
804
+ {
805
+ const char *storage = ca_face_storage_method(self);
806
+ if ( storage ) {
807
+ rb_raise(eclass,
808
+ "%s: %s does not define it (a cell encodes the value rather "
809
+ "than being a number); compute on its values instead -- %s -- "
810
+ "or on its storage, which #%s gives",
811
+ name, rb_obj_classname(self), ca_face_conversion_hint(self),
812
+ storage);
813
+ }
814
+ }
711
815
  rb_raise(eclass,
712
816
  "%s: %s does not define it (a cell encodes the value rather than "
713
- "being a number); compute on its values instead -- %s -- or pass "
714
- ".parent to compute on its storage",
817
+ "being a number); compute on its values instead -- %s",
715
818
  name, rb_obj_classname(self), ca_face_conversion_hint(self));
716
819
  }
717
820
 
@@ -729,12 +832,24 @@ ca_face_operand_descend (VALUE operand, const char *name)
729
832
  if ( ca_test_flag(op, CA_FLAG_FACE_COMPARABLE_STORAGE) ) {
730
833
  return rb_ca_strip_face_value(operand);
731
834
  }
835
+ {
836
+ const char *storage = ca_face_storage_method(operand);
837
+ if ( storage ) {
838
+ rb_raise(rb_eArgError,
839
+ "%s: cannot take a %s operand: its storage is not its surface "
840
+ "(a cell encodes the value rather than being it), so comparing "
841
+ "the storage would compare the encoding. Convert the operand "
842
+ "to the receiver's space first -- %s -- or compare the storage "
843
+ "of both sides, which #%s gives",
844
+ name, rb_obj_classname(operand),
845
+ ca_face_conversion_hint(operand), storage);
846
+ }
847
+ }
732
848
  rb_raise(rb_eArgError,
733
849
  "%s: cannot take a %s operand: its storage is not its surface "
734
850
  "(a cell encodes the value rather than being it), so comparing "
735
851
  "the storage would compare the encoding. Convert the operand to "
736
- "the receiver's space first -- %s -- "
737
- "or pass .parent on both sides to work in storage space",
852
+ "the receiver's space first -- %s",
738
853
  name, rb_obj_classname(operand), ca_face_conversion_hint(operand));
739
854
  return Qnil; /* not reached */
740
855
  }
@@ -823,11 +938,16 @@ ca_face_operand_values (VALUE operand)
823
938
  - A reference with to_comparable brings any operand into its own space
824
939
  (a unit, an instant), and refuses what it cannot convert.
825
940
  - Any other reference has no way to read the operand in its space, and
826
- refuses it. */
941
+ refuses it.
942
+ UNDEF is undefined in every space, so it passes as it is and the entry
943
+ answers it as it does for a plain array. */
827
944
  VALUE
828
945
  ca_face_reconcile_operand (VALUE reference, VALUE operand, const char *name)
829
946
  {
830
947
  CArray *ca;
948
+ if ( operand == CA_UNDEF ) {
949
+ return operand;
950
+ }
831
951
  TypedData_Get_Struct(reference, CArray, &carray_data_type, ca);
832
952
  if ( ca->obj_type == CA_OBJ_RECORD ) {
833
953
  if ( RTEST(rb_obj_is_carray(operand)) ) {
@@ -854,22 +974,61 @@ ca_face_reconcile_operand (VALUE reference, VALUE operand, const char *name)
854
974
  if ( ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE) ) {
855
975
  return ca_face_operand_descend(ca_face_operand_values(operand), name);
856
976
  }
857
- if ( ! ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
858
- ca_face_refuse_order(name, reference, "compare");
859
- }
977
+ ca_face_require_orderable(reference, name, "compare");
860
978
  if ( rb_respond_to(reference, rb_intern("to_comparable")) ) {
861
979
  return rb_ca_strip_face_value(rb_funcall(reference,
862
980
  rb_intern("to_comparable"),
863
981
  1, operand));
864
982
  }
983
+ {
984
+ const char *storage = ca_face_storage_method(reference);
985
+ if ( storage ) {
986
+ rb_raise(rb_eArgError,
987
+ "%s: %s is not comparable by storage and has no to_comparable "
988
+ "to bring the operand into its space; compare the storage of "
989
+ "both sides, which #%s gives",
990
+ name, rb_obj_classname(reference), storage);
991
+ }
992
+ }
865
993
  rb_raise(rb_eArgError,
866
994
  "%s: %s is not comparable by storage and has no to_comparable "
867
- "to bring the operand into its space; pass .parent on both "
868
- "sides to work in storage space",
995
+ "to bring the operand into its space",
869
996
  name, rb_obj_classname(reference));
870
997
  return Qnil; /* not reached */
871
998
  }
872
999
 
1000
+ /* The reference and the operand of a comparison (an operator, count(v), a
1001
+ search, linear_section) brought into one space. A time reference answers
1002
+ both through comparable_pair: it keeps itself when the operand sits on its
1003
+ grid, and otherwise meets the operand in the finer unit the two share,
1004
+ where to_comparable would refuse a value off its grid. Every other
1005
+ reference keeps itself and reconciles the operand
1006
+ (ca_face_reconcile_operand). An assignment, which has to land on the
1007
+ reference's grid, uses ca_face_reconcile_operand. */
1008
+ void
1009
+ ca_face_reconcile_pair (volatile VALUE *pref, volatile VALUE *poperand,
1010
+ const char *name)
1011
+ {
1012
+ static ID id_comparable_pair = 0;
1013
+ VALUE ref = *pref, operand = *poperand;
1014
+ CArray *ca;
1015
+ if ( id_comparable_pair == 0 ) {
1016
+ id_comparable_pair = rb_intern("comparable_pair");
1017
+ }
1018
+ TypedData_Get_Struct(ref, CArray, &carray_data_type, ca);
1019
+ if ( operand != CA_UNDEF
1020
+ && ca->obj_type != CA_OBJ_RECORD
1021
+ && ! ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE)
1022
+ && ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE)
1023
+ && rb_respond_to(ref, id_comparable_pair) ) {
1024
+ VALUE pair = rb_funcall(ref, id_comparable_pair, 1, operand);
1025
+ *pref = rb_ary_entry(pair, 0);
1026
+ *poperand = rb_ca_strip_face_value(rb_ary_entry(pair, 1));
1027
+ return;
1028
+ }
1029
+ *poperand = ca_face_reconcile_operand(ref, operand, name);
1030
+ }
1031
+
873
1032
  int
874
1033
  ca_face_state_portable (int obj_type, VALUE klass)
875
1034
  {
@@ -891,7 +1050,7 @@ ca_face_list_class (VALUE list, const char *who)
891
1050
  {
892
1051
  long n = RARRAY_LEN(list), i;
893
1052
  int any_face = 0, all_face = 1;
894
- VALUE face_class = Qnil;
1053
+ VALUE face_class = Qnil, face = Qnil;
895
1054
  CArray *ref = NULL;
896
1055
 
897
1056
  for ( i = 0; i < n; i++ ) {
@@ -903,6 +1062,7 @@ ca_face_list_class (VALUE list, const char *who)
903
1062
  any_face = 1;
904
1063
  if ( NIL_P(face_class) ) {
905
1064
  face_class = rb_obj_class(p);
1065
+ face = p;
906
1066
  ref = ca;
907
1067
  }
908
1068
  else if ( rb_obj_class(p) != face_class ) {
@@ -911,19 +1071,36 @@ ca_face_list_class (VALUE list, const char *who)
911
1071
  }
912
1072
  if ( ! any_face ) return Qnil;
913
1073
  if ( ! all_face ) {
1074
+ const char *storage = ca_face_storage_method(face);
1075
+ if ( ref->obj_type == CA_OBJ_STRING ) {
1076
+ rb_raise(rb_eArgError,
1077
+ "%s: cannot mix Face and non-Face arrays, or arrays of "
1078
+ "different Face classes; convert them first to one Face "
1079
+ "class or to no Face (CArray.string(a) makes a CAString of "
1080
+ "an object array of Strings)",
1081
+ who);
1082
+ }
1083
+ if ( storage ) {
1084
+ rb_raise(rb_eArgError,
1085
+ "%s: cannot mix Face and non-Face arrays, or arrays of "
1086
+ "different Face classes; convert them first to one Face "
1087
+ "class or to no Face, or combine their storage (#%s gives "
1088
+ "the storage of a %s)",
1089
+ who, storage, rb_obj_classname(face));
1090
+ }
914
1091
  rb_raise(rb_eArgError,
915
1092
  "%s: cannot mix Face and non-Face arrays, or arrays of "
916
- "different Face classes; convert them to one Face class, or "
917
- "none, first, or take .parent of each to combine the storage",
1093
+ "different Face classes; convert them first to one Face "
1094
+ "class or to no Face",
918
1095
  who);
919
1096
  }
920
1097
  if ( n > 1 && ! ca_face_state_portable(ref->obj_type, face_class) ) {
921
1098
  rb_raise(rb_eArgError,
922
1099
  "%s: %s state is not portable across several arrays (each "
923
1100
  "keeps its values in storage of its own); convert them first "
924
- "(a CAConstString with to_string), or take .parent of each to "
925
- "combine the storage",
926
- who, rb_class2name(face_class));
1101
+ "(%s)",
1102
+ who, rb_class2name(face_class),
1103
+ ca_face_conversion_hint(face));
927
1104
  }
928
1105
  for ( i = 1; i < n; i++ ) {
929
1106
  VALUE p = rb_ary_entry(list, i);
@@ -956,7 +1133,7 @@ rb_ca_s_face_state_portable_p (VALUE klass)
956
1133
  return Qtrue;
957
1134
  }
958
1135
 
959
- /* ca.__orderable_storage__ -- whether ca's storage holds its values, in
1136
+ /* ca.orderable_storage? -- whether ca's storage holds its values, in
960
1137
  their order, so that a lookup may compare the storage: true for an array
961
1138
  that is not a Face and for a Face that declares
962
1139
  CA_FLAG_FACE_ORDERABLE_STORAGE, false for a Face whose cells encode the
@@ -970,7 +1147,7 @@ rb_ca_orderable_storage_p (VALUE self)
970
1147
  return ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ? Qtrue : Qfalse;
971
1148
  }
972
1149
 
973
- /* ca.__comparable_storage__ -- whether ca's storage holds its values as
1150
+ /* ca.comparable_storage? -- whether ca's storage holds its values as
974
1151
  they are, so that a plain operand may be compared with the storage: true
975
1152
  for an array that is not a Face and for a Face that declares
976
1153
  CA_FLAG_FACE_COMPARABLE_STORAGE. */
@@ -983,7 +1160,7 @@ rb_ca_comparable_storage_p (VALUE self)
983
1160
  return ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE) ? Qtrue : Qfalse;
984
1161
  }
985
1162
 
986
- /* ca.__face_storage__ -- the storage under a Face (ca itself when it is not
1163
+ /* ca.face_storage -- the storage under a Face (ca itself when it is not
987
1164
  a Face). */
988
1165
  static VALUE
989
1166
  rb_ca_face_storage (VALUE self)
@@ -991,7 +1168,7 @@ rb_ca_face_storage (VALUE self)
991
1168
  return rb_ca_strip_face_value(self);
992
1169
  }
993
1170
 
994
- /* ca.__reconcile_operand__(operand, name) -- operand brought into ca's
1171
+ /* ca.reconcile_operand(operand, name) -- operand brought into ca's
995
1172
  storage space by the rule every operand family follows
996
1173
  (ca_face_reconcile_operand for a Face, ca_face_operand_descend for an
997
1174
  array that is not one). For the families written in Ruby. */
@@ -1065,21 +1242,23 @@ ca_face_reconcile_comparison (volatile VALUE *pself, volatile VALUE *pother)
1065
1242
  return;
1066
1243
  }
1067
1244
 
1068
- if ( ! ca_test_flag(sc, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
1069
- ca_face_refuse_order("comparison", self, "compare");
1070
- }
1245
+ ca_face_require_orderable(self, "comparison", "compare");
1071
1246
 
1072
1247
  {
1073
- int other_was_scalar = ! RTEST(rb_obj_is_carray(other));
1074
- other = ca_face_reconcile_operand(self, other, "comparison");
1075
- /* to_comparable lifts a scalar operand to a length-1 array; unwrap it
1248
+ /* A Ruby Array is a list of values (to_comparable reads it cell by
1249
+ cell), not one value, so it keeps the length it was given. */
1250
+ int other_was_scalar = ! RTEST(rb_obj_is_carray(other))
1251
+ && ! RB_TYPE_P(other, T_ARRAY);
1252
+ volatile VALUE ref = self, op = other;
1253
+ ca_face_reconcile_pair(&ref, &op, "comparison");
1254
+ /* The reconcile lifts a scalar operand to a length-1 array; unwrap it
1076
1255
  so the comparison stays array-vs-scalar instead of a shape check
1077
1256
  against [1] (which only a 1-D reference would pass). */
1078
- if ( other_was_scalar && RTEST(rb_obj_is_carray(other)) ) {
1079
- other = rb_ca_fetch_addr(other, 0);
1257
+ if ( other_was_scalar && RTEST(rb_obj_is_carray(op)) ) {
1258
+ op = rb_ca_fetch_addr(op, 0);
1080
1259
  }
1081
- *pself = rb_ca_strip_face_value(self);
1082
- *pother = other;
1260
+ *pself = rb_ca_strip_face_value(ref);
1261
+ *pother = op;
1083
1262
  }
1084
1263
  }
1085
1264
 
@@ -1105,12 +1284,14 @@ Init_ca_face (void)
1105
1284
  rb_define_singleton_method(rb_cCArray, "face_state_portable?",
1106
1285
  rb_ca_s_face_state_portable_p, 0);
1107
1286
  rb_define_method(rb_cCArray, "face_lift", rb_ca_face_lift_method, 1);
1108
- rb_define_private_method(rb_cCArray, "__orderable_storage__",
1287
+ rb_define_private_method(rb_cCArray, "orderable_storage?",
1109
1288
  rb_ca_orderable_storage_p, 0);
1110
- rb_define_private_method(rb_cCArray, "__comparable_storage__",
1289
+ rb_define_private_method(rb_cCArray, "comparable_storage?",
1111
1290
  rb_ca_comparable_storage_p, 0);
1112
- rb_define_private_method(rb_cCArray, "__face_storage__",
1291
+ rb_define_private_method(rb_cCArray, "face_storage",
1113
1292
  rb_ca_face_storage, 0);
1114
- rb_define_private_method(rb_cCArray, "__reconcile_operand__",
1293
+ rb_define_private_method(rb_cCArray, "reconcile_operand",
1115
1294
  rb_ca_reconcile_operand, 2);
1295
+ rb_define_private_method(rb_cCArray, "face_storage_method",
1296
+ rb_ca_face_storage_method, 0);
1116
1297
  }
data/ext/ca_obj_face.h CHANGED
@@ -104,6 +104,10 @@ void Init_ca_face (void);
104
104
  extern int8_t CA_OBJ_RECORD;
105
105
  VALUE rb_ca_record_get_data_class (CArray *ca);
106
106
 
107
+ /* CA_OBJ_STRING identifies CAString, read by the store path
108
+ (carray_access.c) to send an array source through the write hook. */
109
+ extern int8_t CA_OBJ_STRING;
110
+
107
111
  /* -- Convenience macro for view-creating method deployment --
108
112
  Insert at the tail of each view-creating Ruby method (= rb_ca_transpose
109
113
  / rb_ca_reshape / etc.) just before return. `ca` is self's CArray *,
@@ -288,10 +292,25 @@ VALUE ca_face_strip_one (VALUE v);
288
292
  NORETURN(void ca_face_refuse_order (const char *name, VALUE face,
289
293
  const char *verb));
290
294
 
295
+ /* The gate of every family that orders, compares, searches or hashes a Face
296
+ reference: returns when the Face is ORDERABLE_STORAGE, refuses otherwise
297
+ (ca_face_refuse_order). Call this rather than testing the flag. */
298
+ void ca_face_require_orderable (VALUE face, const char *name,
299
+ const char *verb);
300
+
301
+ /* Whether a Face is not ORDERABLE and answers #order itself (CAConstString,
302
+ CARecord), so a family that orders by a key takes that order instead of
303
+ the storage. */
304
+ int ca_face_orders_by_itself (VALUE face);
305
+
291
306
  /* The conversion a refusal of a Face whose cells encode its values points
292
307
  at (#to_string for a string Face, to_type(:object) for any other). */
293
308
  const char *ca_face_conversion_hint (VALUE face);
294
309
 
310
+ /* The method that gives the storage under a Face (#ticks, #codes), or NULL
311
+ when the Face has none of those. */
312
+ const char *ca_face_storage_method (VALUE face);
313
+
295
314
  /* Raise eclass naming a Face that does not define the numeric operation
296
315
  `name`; returns when self is not a Face. See ca_obj_face.c. */
297
316
  void ca_face_refuse_numeric (VALUE self, const char *name, VALUE eclass);
data/ext/ca_obj_fake.c CHANGED
@@ -78,8 +78,14 @@ ca_fake_setup (CAFake *ca, CArray *parent, int8_t data_type, ca_size_t bytes)
78
78
 
79
79
  /* check arguments */
80
80
 
81
- CA_CHECK_DATA_TYPE(data_type);
82
- CA_CHECK_BYTES(data_type, bytes);
81
+ CA_CHECK_DATA_TYPE_GIVEN(data_type);
82
+ CA_CHECK_BYTES_GIVEN(data_type, bytes);
83
+
84
+ /* ca == NULL asks for the checks above only, so that a constructor can
85
+ run them before it allocates. */
86
+ if ( ca == NULL ) {
87
+ return 0;
88
+ }
83
89
 
84
90
  ndim = parent->ndim;
85
91
  dim = parent->dim;
@@ -103,9 +109,6 @@ ca_fake_setup (CAFake *ca, CArray *parent, int8_t data_type, ca_size_t bytes)
103
109
 
104
110
  memcpy(ca->dim, dim, ndim * sizeof(ca_size_t));
105
111
 
106
- if ( ca_has_mask(parent) ) {
107
- ca_create_mask(ca);
108
- }
109
112
 
110
113
  if ( ca_is_scalar(parent) ) {
111
114
  ca_set_flag(ca, CA_FLAG_SCALAR);
@@ -117,8 +120,11 @@ ca_fake_setup (CAFake *ca, CArray *parent, int8_t data_type, ca_size_t bytes)
117
120
  CAFake *
118
121
  ca_fake_new (CArray *parent, int8_t data_type, ca_size_t bytes)
119
122
  {
120
- CAFake *ca = (CAFake *) ca_array_alloc(CA_OBJ_FAKE, parent->ndim);
123
+ CAFake *ca;
124
+ ca_fake_setup(NULL, parent, data_type, bytes);
125
+ ca = (CAFake *) ca_array_alloc(CA_OBJ_FAKE, parent->ndim);
121
126
  ca_fake_setup(ca, parent, data_type, bytes);
127
+ ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
122
128
  return ca;
123
129
  }
124
130
 
@@ -277,18 +283,7 @@ ca_fake_func_allocate (void *ap)
277
283
  {
278
284
  CAFake *ca = (CAFake *) ap;
279
285
  ca_attach(ca->parent);
280
- ca->ptr = xmalloc(ca_length(ca));
281
-
282
- /* CAREFUL: CA_OBJECT storage must contain valid VALUEs before GC can
283
- scan it, so seed with Fixnum 0 rather than leaving raw xmalloc bytes. */
284
- if ( ca->data_type == CA_OBJECT ) {
285
- VALUE *p = (VALUE *) ca->ptr;
286
- VALUE zero = SIZE2NUM(0);
287
- ca_size_t i;
288
- for (i=0; i<ca->elements; i++) {
289
- *p++ = zero;
290
- }
291
- }
286
+ ca->ptr = ca_alloc_cells((CArray *) ca);
292
287
  }
293
288
 
294
289
  static void
@@ -296,17 +291,7 @@ ca_fake_func_attach (void *ap)
296
291
  {
297
292
  CAFake *ca = (CAFake *) ap;
298
293
  ca_attach(ca->parent);
299
- ca->ptr = xmalloc(ca_length(ca));
300
-
301
- /* CAREFUL: same VALUE-seed rule as ca_fake_func_allocate. */
302
- if ( ca->data_type == CA_OBJECT ) {
303
- VALUE *p = (VALUE *) ca->ptr;
304
- VALUE zero = SIZE2NUM(0);
305
- ca_size_t i;
306
- for (i=0; i<ca->elements; i++) {
307
- *p++ = zero;
308
- }
309
- }
294
+ ca->ptr = ca_alloc_cells((CArray *) ca);
310
295
 
311
296
  if ( ca->parent->mask ) {
312
297
  ca_cast_block_with_mask(ca->elements, ca->parent, ca->parent->ptr,
@@ -499,6 +484,7 @@ rb_ca_fake_initialize_copy (VALUE self, VALUE other)
499
484
  ca_array_pool_alloc(ca, CA_OBJ_FAKE, cs->parent->ndim);
500
485
  }
501
486
  ca_fake_setup(ca, cs->parent, cs->data_type, cs->bytes);
487
+ ca_view_inherit_mask((VALUE) ca);
502
488
 
503
489
  return self;
504
490
  }
data/ext/ca_obj_farray.c CHANGED
@@ -7,6 +7,7 @@
7
7
  ---------------------------------------------------------------------------- */
8
8
 
9
9
  #include "carray.h"
10
+ #include "carray_internal.h" /* ca_view_inherit_mask */
10
11
 
11
12
  extern ca_operation_function_t ca_stride_func;
12
13
 
@@ -46,9 +47,9 @@ ca_farray_setup (CAStride *ca, CArray *parent)
46
47
  strides[i] = parent_byte_stride[ndim - 1 - i];
47
48
  }
48
49
 
49
- ca_stride_setup(ca, CA_OBJ_FARRAY, parent,
50
- parent->data_type, parent->bytes,
51
- ndim, newdim, strides, 0);
50
+ ca_stride_setup_view(ca, CA_OBJ_FARRAY, parent,
51
+ parent->data_type, parent->bytes,
52
+ ndim, newdim, strides, 0);
52
53
 
53
54
  if (ca_is_scalar(parent)) {
54
55
  ca_set_flag(ca, CA_FLAG_SCALAR);
@@ -59,8 +60,10 @@ ca_farray_setup (CAStride *ca, CArray *parent)
59
60
  CAStride *
60
61
  ca_farray_new (CArray *parent)
61
62
  {
63
+ /* check: the parent's shape, reversed, is already valid */
62
64
  CAStride *ca = (CAStride *) ca_array_alloc(CA_OBJ_FARRAY, parent->ndim);
63
65
  ca_farray_setup(ca, parent);
66
+ ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
64
67
  return ca;
65
68
  }
66
69