carray 3.0.1 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/ca_obj_face.c CHANGED
@@ -19,16 +19,18 @@
19
19
  ca_face_lift re-wrap a view as a Face when the source was
20
20
  ca_strip_face walk down through Face parents to storage
21
21
  CAFace abstract class organisational relay in the class hierarchy
22
- storage_to_scalar / per-obj_type dispatch for element decode (read)
23
- scalar_to_storage and encode (write) between surface and storage
22
+ storage_to_element / per-obj_type dispatch for element decode (read)
23
+ element_to_storage and encode (write) between surface and storage
24
24
  state-compatible / homogeneity + portability registries used by
25
- state-portable promote_list / CAStack / MV producer
25
+ state-portable promote_list / CAStack
26
+ memory_view which Faces the MemoryView producer exports
26
27
 
27
28
  ---------------------------------------------------------------------------- */
28
29
 
29
30
  #include "ruby.h"
30
31
  #include "carray.h"
31
32
  #include "ca_obj_face.h"
33
+ #include "carray_internal.h"
32
34
 
33
35
  /* ---- shared op helpers (thin-forward) ---- */
34
36
 
@@ -37,7 +39,7 @@ ca_face_attach (void *ap)
37
39
  {
38
40
  CAView *cav = (CAView *) ap;
39
41
  CArray *ca = (CArray *) ap;
40
- ca_attach(cav->parent);
42
+ ca_attach(cav->parent); /* window: a Face's attach slot; its detach slot releases */
41
43
  /* alias parent->ptr (Face has identical data layout, so copy is unneeded) */
42
44
  ca->ptr = cav->parent->ptr;
43
45
  }
@@ -100,6 +102,7 @@ void
100
102
  ca_face_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
101
103
  ca_size_t *strides, void *data, int dir)
102
104
  {
105
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
103
106
  CAView *cav = (CAView *) ap;
104
107
  ca_xfer_stride(cav->parent, starts, counts, strides, data, dir);
105
108
  }
@@ -174,6 +177,27 @@ rb_ca_face_template (VALUE original_face, CArray *new_parent,
174
177
  dst);
175
178
  }
176
179
 
180
+ VALUE
181
+ ca_face_relift_entity (VALUE face, VALUE result)
182
+ {
183
+ static ID id_copy_state = 0;
184
+ CArray *rc, *lc;
185
+ VALUE lifted;
186
+ if ( id_copy_state == 0 ) id_copy_state = rb_intern("copy_state");
187
+ TypedData_Get_Struct(result, CArray, &carray_data_type, rc);
188
+ lifted = rb_ca_face_template(face, rc, rc->dim);
189
+ rb_ca_set_parent(lifted, result);
190
+ if ( ca_has_mask(rc) ) {
191
+ /* The template starts without a mask. */
192
+ TypedData_Get_Struct(lifted, CArray, &carray_data_type, lc);
193
+ ca_copy_mask(lc, rc);
194
+ }
195
+ if ( rb_respond_to(face, id_copy_state) ) {
196
+ rb_funcall(lifted, id_copy_state, 1, face);
197
+ }
198
+ return lifted;
199
+ }
200
+
177
201
  /* ca_face_lift(view, face_parent) — at the tail of user-facing entry
178
202
  * points (`ca[...]` etc.), when the operand was a Face, re-wrap the
179
203
  * resulting view as a Face of the same subclass. Thin wrapper over
@@ -419,37 +443,62 @@ rb_ca_face_s_allocate_forbidden (VALUE klass)
419
443
  "(CATime / CATimedelta / ...)");
420
444
  }
421
445
 
422
- /* Face-local C dispatch table for storage_to_scalar (the read/decode
446
+ VALUE
447
+ ca_face_storage_value (CArray *ca, VALUE raw)
448
+ {
449
+ CArray *storage;
450
+ CArray as_storage;
451
+ if ( ca->data_type != CA_FIXLEN || TYPE(raw) != T_STRING ) {
452
+ return raw;
453
+ }
454
+ storage = ca_strip_face(ca);
455
+ if ( storage == NULL
456
+ || storage->data_type == CA_FIXLEN
457
+ || ca_is_object_type(storage)
458
+ || RSTRING_LEN(raw) != storage->bytes ) {
459
+ return raw;
460
+ }
461
+ if ( storage->data_type == CA_BOOLEAN ) {
462
+ return *(uint8_t *) RSTRING_PTR(raw) ? Qtrue : Qfalse;
463
+ }
464
+ /* The cell lives in a String's buffer, not in the storage: read it
465
+ through a stand-in that carries only the storage's type. */
466
+ as_storage.data_type = storage->data_type;
467
+ as_storage.bytes = storage->bytes;
468
+ return ca_ptr2obj(&as_storage, RSTRING_PTR(raw));
469
+ }
470
+
471
+ /* Face-local C dispatch table for storage_to_element (the read/decode
423
472
  direction). Sized to CA_OBJ_TYPE_MAX so any registered obj_type can be
424
473
  indexed directly; file-scope zero-init leaves unregistered slots NULL. */
425
- ca_face_storage_to_scalar_fn ca_face_storage_to_scalar_table[CA_OBJ_TYPE_MAX];
474
+ ca_face_storage_to_element_fn ca_face_storage_to_element_table[CA_OBJ_TYPE_MAX];
426
475
 
427
476
  void
428
- ca_face_register_storage_to_scalar (int obj_type, ca_face_storage_to_scalar_fn fn)
477
+ ca_face_register_storage_to_element (int obj_type, ca_face_storage_to_element_fn fn)
429
478
  {
430
479
  if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
431
480
  rb_raise(rb_eArgError,
432
- "ca_face_register_storage_to_scalar: obj_type %d out of range",
481
+ "ca_face_register_storage_to_element: obj_type %d out of range",
433
482
  obj_type);
434
483
  }
435
- ca_face_storage_to_scalar_table[obj_type] = fn;
484
+ ca_face_storage_to_element_table[obj_type] = fn;
436
485
  }
437
486
 
438
- /* Face-local C dispatch table for scalar_to_storage (the write/encode
487
+ /* Face-local C dispatch table for element_to_storage (the write/encode
439
488
  direction), the mirror of the decode table above. Unregistered slots
440
489
  stay NULL; a Face without a C fast path relies on the Ruby fallback in
441
- ca_face_scalar_to_storage (carray_cast.c). */
442
- ca_face_scalar_to_storage_fn ca_face_scalar_to_storage_table[CA_OBJ_TYPE_MAX];
490
+ ca_face_element_to_storage (carray_cast.c). */
491
+ ca_face_element_to_storage_fn ca_face_element_to_storage_table[CA_OBJ_TYPE_MAX];
443
492
 
444
493
  void
445
- ca_face_register_scalar_to_storage (int obj_type, ca_face_scalar_to_storage_fn fn)
494
+ ca_face_register_element_to_storage (int obj_type, ca_face_element_to_storage_fn fn)
446
495
  {
447
496
  if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
448
497
  rb_raise(rb_eArgError,
449
- "ca_face_register_scalar_to_storage: obj_type %d out of range",
498
+ "ca_face_register_element_to_storage: obj_type %d out of range",
450
499
  obj_type);
451
500
  }
452
- ca_face_scalar_to_storage_table[obj_type] = fn;
501
+ ca_face_element_to_storage_table[obj_type] = fn;
453
502
  }
454
503
 
455
504
  /* -- Face state homogeneity check -- */
@@ -524,6 +573,31 @@ rb_ca_face_state_compatible_p (VALUE self, VALUE other)
524
573
  return ca_face_state_compatible(self, a, other, b) ? Qtrue : Qfalse;
525
574
  }
526
575
 
576
+ /* -- Face MemoryView export -- */
577
+
578
+ /* A Face is exported through MemoryView only when its storage bytes carry
579
+ its values without the Face (a struct record, fixed-width bytes). Zero
580
+ (the default) refuses the export: the consumer would see the storage
581
+ with nothing to say what it means. */
582
+ static int8_t ca_face_memory_view_table[CA_OBJ_TYPE_MAX];
583
+
584
+ void
585
+ ca_face_register_memory_view (int obj_type, int exportable)
586
+ {
587
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
588
+ rb_raise(rb_eArgError,
589
+ "ca_face_register_memory_view: obj_type %d out of range",
590
+ obj_type);
591
+ }
592
+ ca_face_memory_view_table[obj_type] = exportable ? 1 : 0;
593
+ }
594
+
595
+ int
596
+ ca_face_memory_view_exportable (CArray *ca)
597
+ {
598
+ return ca_face_memory_view_table[ca->obj_type] == 1;
599
+ }
600
+
527
601
  /* -- Face state portability -- */
528
602
 
529
603
  /* int8_t per slot to distinguish "unregistered" (-1, dispatch falls
@@ -595,6 +669,207 @@ ca_face_class_has_portable_method (VALUE klass)
595
669
  return ( owner != rb_singleton_class(rb_cCArray) ) ? 1 : 0;
596
670
  }
597
671
 
672
+ /* The conversion a refusal of a Face whose cells encode its values points
673
+ at: a string Face gives its strings with #to_string, any other its values
674
+ with to_type(:object). */
675
+ const char *
676
+ ca_face_conversion_hint (VALUE face)
677
+ {
678
+ return rb_respond_to(face, rb_intern("to_string"))
679
+ ? "#to_string gives its strings"
680
+ : "to_type(:object) gives its values";
681
+ }
682
+
683
+ /* Refuse to order (or, with verb "count", to count by value) a Face whose
684
+ storage does not hold its values in order, pointing at what does: its
685
+ values, or its storage when that is what is meant. */
686
+ void
687
+ ca_face_refuse_order (const char *name, VALUE face, const char *verb)
688
+ {
689
+ rb_raise(rb_eArgError,
690
+ "%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",
693
+ name, rb_obj_classname(face), verb,
694
+ ca_face_conversion_hint(face), verb);
695
+ }
696
+
697
+ /* Refuse a numeric operation (arithmetic, math, a reduction) on a Face,
698
+ whose cells encode its values rather than being numbers, pointing at what
699
+ gives numbers. Returns when `self` is not a Face. */
700
+ void
701
+ ca_face_refuse_numeric (VALUE self, const char *name, VALUE eclass)
702
+ {
703
+ CArray *ca;
704
+ if ( ! rb_obj_is_carray(self) ) {
705
+ return;
706
+ }
707
+ GetCArray(self, ca);
708
+ if ( ! ca_is_face(ca) ) {
709
+ return;
710
+ }
711
+ rb_raise(eclass,
712
+ "%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",
715
+ name, rb_obj_classname(self), ca_face_conversion_hint(self));
716
+ }
717
+
718
+ VALUE
719
+ ca_face_operand_descend (VALUE operand, const char *name)
720
+ {
721
+ CArray *op;
722
+ if ( ! RTEST(rb_obj_is_carray(operand)) ) {
723
+ return operand;
724
+ }
725
+ TypedData_Get_Struct(operand, CArray, &carray_data_type, op);
726
+ if ( ! ca_is_face(op) ) {
727
+ return operand;
728
+ }
729
+ if ( ca_test_flag(op, CA_FLAG_FACE_COMPARABLE_STORAGE) ) {
730
+ return rb_ca_strip_face_value(operand);
731
+ }
732
+ rb_raise(rb_eArgError,
733
+ "%s: cannot take a %s operand: its storage is not its surface "
734
+ "(a cell encodes the value rather than being it), so comparing "
735
+ "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",
738
+ name, rb_obj_classname(operand), ca_face_conversion_hint(operand));
739
+ return Qnil; /* not reached */
740
+ }
741
+
742
+ /* Whether two arrays, at least one a Face other than a record array, are
743
+ equal (`==`, strict = 0) or the same key (`eql?`, strict = 1).
744
+
745
+ A Face equals only a Face of its own class. When the two share their
746
+ state (a time array's unit) and the storage holds the values in their
747
+ order, equal storage is equal values, so the storage is compared.
748
+ Otherwise `==` compares the values themselves (two time arrays of
749
+ different units, two const strings over different pools, two
750
+ categoricals with differently ordered labels), and `eql?`, which has to
751
+ agree with #hash, answers false. */
752
+ int
753
+ ca_face_equal (VALUE va, VALUE vb, int strict)
754
+ {
755
+ CArray *a, *b;
756
+ int8_t i;
757
+ TypedData_Get_Struct(va, CArray, &carray_data_type, a);
758
+ TypedData_Get_Struct(vb, CArray, &carray_data_type, b);
759
+ if ( ! ca_is_face(a) || ! ca_is_face(b) ) {
760
+ return 0;
761
+ }
762
+ if ( rb_obj_class(va) != rb_obj_class(vb) ) {
763
+ return 0;
764
+ }
765
+ if ( a->ndim != b->ndim ) {
766
+ return 0;
767
+ }
768
+ for (i = 0; i < a->ndim; i++) {
769
+ if ( a->dim[i] != b->dim[i] ) {
770
+ return 0;
771
+ }
772
+ }
773
+ if ( ca_face_state_compatible(va, a, vb, b)
774
+ && ( strict || ca_test_flag(a, CA_FLAG_FACE_ORDERABLE_STORAGE) ) ) {
775
+ return strict ? ca_eql_storage(ca_strip_face(a), ca_strip_face(b))
776
+ : ca_equal(ca_strip_face(a), ca_strip_face(b));
777
+ }
778
+ if ( strict ) {
779
+ return 0;
780
+ }
781
+ return RTEST(rb_equal(rb_funcall(va, rb_intern("to_a"), 0),
782
+ rb_funcall(vb, rb_intern("to_a"), 0)));
783
+ }
784
+
785
+ /* An operand Face whose storage is not even in the order of its values
786
+ (a CAConstString, a CACategorical) is read by its values: its strings
787
+ (#to_string) or, for any other, its values (to_type(:object)). Any
788
+ other operand is returned as it is. */
789
+ static VALUE
790
+ ca_face_operand_values (VALUE operand)
791
+ {
792
+ CArray *op;
793
+ if ( ! RTEST(rb_obj_is_carray(operand)) ) {
794
+ return operand;
795
+ }
796
+ TypedData_Get_Struct(operand, CArray, &carray_data_type, op);
797
+ if ( ! ca_is_face(op) || op->obj_type == CA_OBJ_RECORD
798
+ || ca_test_flag(op, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
799
+ return operand;
800
+ }
801
+ if ( rb_respond_to(operand, rb_intern("to_string")) ) {
802
+ return rb_funcall(operand, rb_intern("to_string"), 0);
803
+ }
804
+ return rb_funcall(operand, rb_intern("to_type"), 1, INT2NUM(CA_OBJECT));
805
+ }
806
+
807
+ /* Bring `operand` into the storage space of `reference`, a Face, so the
808
+ two can be compared, searched, counted or hashed cell by cell. Every
809
+ family that takes an operand against a Face goes through here; the
810
+ family keeps its own gate on the reference (ORDERABLE, a record's own
811
+ rules) and its own handling of a scalar operand afterwards.
812
+
813
+ - A record reference takes records of its own data_class (stripped to
814
+ their bytes) and plain operands as they are; any other Face operand is
815
+ refused with TypeError.
816
+ - A COMPARABLE reference stores its values as they are, so a plain
817
+ operand is taken as it is, a Face whose cells are its values is
818
+ stripped, a string or categorical Face is read by its values, and a
819
+ Face whose cells encode its values in order (a time) is refused
820
+ (ca_face_operand_descend).
821
+ - A reference whose storage is not in the order of its values refuses,
822
+ naming the conversion that gives its values.
823
+ - A reference with to_comparable brings any operand into its own space
824
+ (a unit, an instant), and refuses what it cannot convert.
825
+ - Any other reference has no way to read the operand in its space, and
826
+ refuses it. */
827
+ VALUE
828
+ ca_face_reconcile_operand (VALUE reference, VALUE operand, const char *name)
829
+ {
830
+ CArray *ca;
831
+ TypedData_Get_Struct(reference, CArray, &carray_data_type, ca);
832
+ if ( ca->obj_type == CA_OBJ_RECORD ) {
833
+ if ( RTEST(rb_obj_is_carray(operand)) ) {
834
+ CArray *op;
835
+ TypedData_Get_Struct(operand, CArray, &carray_data_type, op);
836
+ if ( ca_is_face(op) ) {
837
+ if ( op->obj_type != CA_OBJ_RECORD ) {
838
+ rb_raise(rb_eTypeError,
839
+ "%s: can not take a %s operand for a record array of "
840
+ "%"PRIsVALUE, name, rb_obj_classname(operand),
841
+ rb_ca_data_class(reference));
842
+ }
843
+ if ( rb_ca_data_class(operand) != rb_ca_data_class(reference) ) {
844
+ rb_raise(rb_eTypeError,
845
+ "%s: can not take a record array of %"PRIsVALUE
846
+ " for a record array of %"PRIsVALUE, name,
847
+ rb_ca_data_class(operand), rb_ca_data_class(reference));
848
+ }
849
+ return rb_ca_strip_face_value(operand);
850
+ }
851
+ }
852
+ return operand;
853
+ }
854
+ if ( ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE) ) {
855
+ return ca_face_operand_descend(ca_face_operand_values(operand), name);
856
+ }
857
+ if ( ! ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
858
+ ca_face_refuse_order(name, reference, "compare");
859
+ }
860
+ if ( rb_respond_to(reference, rb_intern("to_comparable")) ) {
861
+ return rb_ca_strip_face_value(rb_funcall(reference,
862
+ rb_intern("to_comparable"),
863
+ 1, operand));
864
+ }
865
+ rb_raise(rb_eArgError,
866
+ "%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",
869
+ name, rb_obj_classname(reference));
870
+ return Qnil; /* not reached */
871
+ }
872
+
598
873
  int
599
874
  ca_face_state_portable (int obj_type, VALUE klass)
600
875
  {
@@ -611,6 +886,66 @@ ca_face_state_portable (int obj_type, VALUE klass)
611
886
  return 1; /* default portable */
612
887
  }
613
888
 
889
+ VALUE
890
+ ca_face_list_class (VALUE list, const char *who)
891
+ {
892
+ long n = RARRAY_LEN(list), i;
893
+ int any_face = 0, all_face = 1;
894
+ VALUE face_class = Qnil;
895
+ CArray *ref = NULL;
896
+
897
+ for ( i = 0; i < n; i++ ) {
898
+ VALUE p = rb_ary_entry(list, i);
899
+ CArray *ca;
900
+ rb_check_carray_object(p);
901
+ TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
902
+ if ( ! ca_is_face(ca) ) { all_face = 0; continue; }
903
+ any_face = 1;
904
+ if ( NIL_P(face_class) ) {
905
+ face_class = rb_obj_class(p);
906
+ ref = ca;
907
+ }
908
+ else if ( rb_obj_class(p) != face_class ) {
909
+ all_face = 0;
910
+ }
911
+ }
912
+ if ( ! any_face ) return Qnil;
913
+ if ( ! all_face ) {
914
+ rb_raise(rb_eArgError,
915
+ "%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",
918
+ who);
919
+ }
920
+ if ( n > 1 && ! ca_face_state_portable(ref->obj_type, face_class) ) {
921
+ rb_raise(rb_eArgError,
922
+ "%s: %s state is not portable across several arrays (each "
923
+ "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));
927
+ }
928
+ for ( i = 1; i < n; i++ ) {
929
+ VALUE p = rb_ary_entry(list, i);
930
+ CArray *ca;
931
+ TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
932
+ if ( ! ca_face_state_compatible(rb_ary_entry(list, 0), ref, p, ca) ) {
933
+ rb_raise(rb_eArgError,
934
+ "%s: Face state mismatch (the %s at index %ld differs from "
935
+ "index 0)", who, rb_class2name(face_class), i);
936
+ }
937
+ }
938
+ return face_class;
939
+ }
940
+
941
+ VALUE
942
+ ca_face_strip_one (VALUE v)
943
+ {
944
+ CArray *c;
945
+ TypedData_Get_Struct(v, CArray, &carray_data_type, c);
946
+ return ca_is_face(c) ? rb_ca_parent(v) : v;
947
+ }
948
+
614
949
  /* CArray.face_state_portable? — class method on CArray. Subclasses
615
950
  (Face classes) override by defining their own `def self.face_state_portable?`.
616
951
  This default returns true (= 1) for non-Face CArray classes; the actual
@@ -621,6 +956,57 @@ rb_ca_s_face_state_portable_p (VALUE klass)
621
956
  return Qtrue;
622
957
  }
623
958
 
959
+ /* ca.__orderable_storage__ -- whether ca's storage holds its values, in
960
+ their order, so that a lookup may compare the storage: true for an array
961
+ that is not a Face and for a Face that declares
962
+ CA_FLAG_FACE_ORDERABLE_STORAGE, false for a Face whose cells encode the
963
+ value (a const string's ranges, a categorical's codes). */
964
+ static VALUE
965
+ rb_ca_orderable_storage_p (VALUE self)
966
+ {
967
+ CArray *ca;
968
+ GetCArray(self, ca);
969
+ if ( ! ca_is_face(ca) ) return Qtrue;
970
+ return ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ? Qtrue : Qfalse;
971
+ }
972
+
973
+ /* ca.__comparable_storage__ -- whether ca's storage holds its values as
974
+ they are, so that a plain operand may be compared with the storage: true
975
+ for an array that is not a Face and for a Face that declares
976
+ CA_FLAG_FACE_COMPARABLE_STORAGE. */
977
+ static VALUE
978
+ rb_ca_comparable_storage_p (VALUE self)
979
+ {
980
+ CArray *ca;
981
+ GetCArray(self, ca);
982
+ if ( ! ca_is_face(ca) ) return Qtrue;
983
+ return ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE) ? Qtrue : Qfalse;
984
+ }
985
+
986
+ /* ca.__face_storage__ -- the storage under a Face (ca itself when it is not
987
+ a Face). */
988
+ static VALUE
989
+ rb_ca_face_storage (VALUE self)
990
+ {
991
+ return rb_ca_strip_face_value(self);
992
+ }
993
+
994
+ /* ca.__reconcile_operand__(operand, name) -- operand brought into ca's
995
+ storage space by the rule every operand family follows
996
+ (ca_face_reconcile_operand for a Face, ca_face_operand_descend for an
997
+ array that is not one). For the families written in Ruby. */
998
+ static VALUE
999
+ rb_ca_reconcile_operand (VALUE self, VALUE operand, VALUE rname)
1000
+ {
1001
+ CArray *ca;
1002
+ const char *name = StringValueCStr(rname);
1003
+ GetCArray(self, ca);
1004
+ if ( ca_is_face(ca) ) {
1005
+ return ca_face_reconcile_operand(self, operand, name);
1006
+ }
1007
+ return ca_face_operand_descend(operand, name);
1008
+ }
1009
+
624
1010
  /* CArray#face_lift(face_parent) — Ruby surface for ca_face_lift.
625
1011
  Used by CArray.stack to re-wrap a raw CAStack with the homogeneous
626
1012
  Face class carried by face_parent (typically list[0] after the
@@ -642,21 +1028,19 @@ rb_ca_face_lift_method (VALUE self, VALUE face_parent)
642
1028
 
643
1029
  /* ca_face_reconcile_comparison(pself, pother) — Face gate for the element-
644
1030
  * wise comparison operators (< <= > >=, eq, ne; <=> composes from > and <).
645
- * Mirrors the search query gate (PROPOSAL_FACE_ORDERING_GATE): when the LHS
1031
+ * Mirrors the search query gate (docs/authoring/FaceOrderingSearch.md): when the LHS
646
1032
  * is a Face over NUMERIC storage, memcmp on the surface fixlen bytes would
647
1033
  * mis-order the storage, so descend to storage (ORDERABLE licenses that the
648
1034
  * numeric order equals the surface order) and reconcile the RHS:
649
1035
  *
650
- * - RHS is a Face: COMPARABLE self strips it; else it must respond to
651
- * to_comparable (reconcile to self's reference, e.g. unit alignment,
652
- * which may raise on loss); else raise.
653
- * - RHS is plain (bare number / non-Face array / Scalar): a direct storage
654
- * compare, which only a COMPARABLE self licenses -- a non-COMPARABLE
655
- * Face (e.g. a unit-bearing CATime) rejects it.
1036
+ * the RHS goes through ca_face_reconcile_operand: a COMPARABLE self takes
1037
+ * a plain RHS or a COMPARABLE Face, a self with to_comparable brings any
1038
+ * RHS into its space, and anything else is refused.
656
1039
  *
657
- * A Face over FIXLEN storage (CARecord / string faces) is left untouched:
1040
+ * A Face over FIXLEN storage (CARecord / string faces) keeps its Face:
658
1041
  * memcmp on its bytes is already the correct order/equality, same as a plain
659
- * fixlen array. Both operands are rebound to storage via *pself / *pother.
1042
+ * fixlen array; only the RHS is reconciled. Both operands are rebound via
1043
+ * *pself / *pother.
660
1044
  * No-op when self is not a Face. */
661
1045
  void
662
1046
  ca_face_reconcile_comparison (volatile VALUE *pself, volatile VALUE *pother)
@@ -674,52 +1058,26 @@ ca_face_reconcile_comparison (volatile VALUE *pself, volatile VALUE *pother)
674
1058
 
675
1059
  storage = ca_strip_face(sc);
676
1060
  if ( storage->data_type == CA_FIXLEN ) {
677
- /* genuine fixlen semantics: memcmp is the correct comparison, leave
678
- the Face(s) in place (same result as a plain fixlen array). */
1061
+ /* genuine fixlen semantics: memcmp is the correct comparison, so self
1062
+ keeps its Face (same result as a plain fixlen array) and only the
1063
+ operand is brought to bytes of the same meaning. */
1064
+ *pother = ca_face_reconcile_operand(self, other, "comparison");
679
1065
  return;
680
1066
  }
681
1067
 
682
1068
  if ( ! ca_test_flag(sc, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
683
- rb_raise(rb_eArgError,
684
- "comparison: Face operand (%s) is not orderable by storage; "
685
- "use ca.parent to descend to storage",
686
- rb_obj_classname(self));
1069
+ ca_face_refuse_order("comparison", self, "compare");
687
1070
  }
688
1071
 
689
1072
  {
690
- VALUE self_ref = self; /* reference before stripping (for to_comparable) */
691
- int comparable = ca_test_flag(sc, CA_FLAG_FACE_COMPARABLE_STORAGE);
692
- int other_is_face = 0;
693
-
694
- if ( rb_obj_is_kind_of(other, rb_cCArray) ) {
695
- CArray *oc;
696
- TypedData_Get_Struct(other, CArray, &carray_data_type, oc);
697
- other_is_face = ca_is_face(oc);
698
- }
699
-
700
- if ( comparable ) {
701
- /* directly comparable Face reference: strip a Face operand, take a
702
- plain operand as-is (storage compare is licensed). */
703
- if ( other_is_face ) {
704
- other = rb_ca_strip_face_value(other);
705
- }
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
1076
+ so the comparison stays array-vs-scalar instead of a shape check
1077
+ 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);
706
1080
  }
707
- else if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
708
- /* non-COMPARABLE Face reference: the reference brings the operand into
709
- its own space (unit alignment / instant lift), whatever the operand
710
- type -- Face CArray, our Scalar, a Ruby Time / DateTime, etc. The
711
- reference's to_comparable raises if it cannot reconcile it. */
712
- other = rb_funcall(self_ref, rb_intern("to_comparable"), 1, other);
713
- other = rb_ca_strip_face_value(other);
714
- }
715
- else {
716
- rb_raise(rb_eArgError,
717
- "comparison: non-comparable Face reference (%s) has no "
718
- "to_comparable to reconcile the operand; use ca.parent to "
719
- "compare the hidden storage explicitly",
720
- rb_obj_classname(self_ref));
721
- }
722
-
723
1081
  *pself = rb_ca_strip_face_value(self);
724
1082
  *pother = other;
725
1083
  }
@@ -747,4 +1105,12 @@ Init_ca_face (void)
747
1105
  rb_define_singleton_method(rb_cCArray, "face_state_portable?",
748
1106
  rb_ca_s_face_state_portable_p, 0);
749
1107
  rb_define_method(rb_cCArray, "face_lift", rb_ca_face_lift_method, 1);
1108
+ rb_define_private_method(rb_cCArray, "__orderable_storage__",
1109
+ rb_ca_orderable_storage_p, 0);
1110
+ rb_define_private_method(rb_cCArray, "__comparable_storage__",
1111
+ rb_ca_comparable_storage_p, 0);
1112
+ rb_define_private_method(rb_cCArray, "__face_storage__",
1113
+ rb_ca_face_storage, 0);
1114
+ rb_define_private_method(rb_cCArray, "__reconcile_operand__",
1115
+ rb_ca_reconcile_operand, 2);
750
1116
  }