carray 1.5.7 → 1.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53f410eef744cd7d2c38db76556f150cbb1820054bc938fd39d6f45c09dce148
4
- data.tar.gz: a3a8994279e0d7b56adbf5a5427390867c6f8fcc4bef5adacdd792a7865efc6b
3
+ metadata.gz: 536e992ddb82a26f4e24f300892a303306bff88fcd1596b872f418a17879f84c
4
+ data.tar.gz: e41b0740aaf2319feea6c795a93b1e36c0f4138257429a224e33602c21175b99
5
5
  SHA512:
6
- metadata.gz: 38a07507ad136d73bc1cbc136853577546187fb5852450471d5ca149d8b8e5edebbd28a688391c0f27666327f90b69421885cea17cbdab6e78cb92ed6ca8844b
7
- data.tar.gz: f62fedda0c4d535ed8686cc5a21c96db5e670eb5710eece004d75fc22fcbb89a8dc74b51d412eef65f55454b6885b699ff46b0d8d5c78cdf1413374613218b83
6
+ metadata.gz: 2ec6a48b80522cd0e583ee0c7feeda490dab810c3c1a8670fd106c654b50c8af3595aaef9dc848082b8d0bd79ee250d7f39d363b123024e93894c385ed93cfd8
7
+ data.tar.gz: 59e38a1e1d8dc1030b1de81840c7c215df768f1ad15cdaa19e54015ffc6344fc6f2ac381d70509a2f74ba4f1d5dc9588b4b47340ca9e98b8cbb4456c51ce742e
data/NEWS.md CHANGED
@@ -1,12 +1,23 @@
1
1
  ChangeLog of Ruby/CArray
2
2
  ========================
3
3
 
4
+ 1.5.7 -> 1.5.8
5
+ --------------
6
+
7
+ * [New] Add new method 'CArray#vectorized_section'
8
+ * [New] Add new method 'CArray#fetch_linear_addr'
9
+ * [New] Add new method 'CArray#vectorized_fetch_linear_addr'
10
+ * [New] Add new method 'CArray#str_format'
11
+ * [Fix] Fixed SEGV in loading binary data from the piped input in CArray#load_binary
12
+
4
13
  1.5.6 -> 1.5.7
14
+ --------------
5
15
 
6
16
  * [Mod] Modify the methods 'CArray#first' and 'CArray#last' to return nil when the number of elements is zero
7
17
  * [Fix] Added check of having ArithmeticSeuqence in ruby_carray.c
8
18
 
9
19
  1.5.5 -> 1.5.6
20
+ --------------
10
21
 
11
22
  * [Mod] Modify CArray access methods to accept Enumerator::ArithmeticSequence for index, address
12
23
  * [Mod] Remove some gem autoloading (io_csv, io_sqlite3, rmagick, cairo, opencv, ffi)
@@ -14,6 +25,7 @@ ChangeLog of Ruby/CArray
14
25
  * [Mod] Modify the methods 'CArray#double' etc not to raise error for invalid string in conversion into float
15
26
 
16
27
  1.5.4 -> 1.5.5
28
+ --------------
17
29
 
18
30
  * [Fix] Fix recognition of "atan2", ... in "math.h"
19
31
  * [Fix] Fix serialize.rb of keyword parameters for Ruby2.7
data/carray.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification::new do |s|
2
2
 
3
- version = "1.5.7"
3
+ version = "1.5.8"
4
4
 
5
5
  files = Dir.glob("**/*") + [".yardopts"] -
6
6
  [
@@ -45,7 +45,7 @@ rb_ca_data_type (VALUE self)
45
45
  /* @overload ndim
46
46
 
47
47
  (Attribute)
48
- Returns the rank (e.g. 1 for 1D array, 3 for 3D array, ...).
48
+ Returns the number of dimensions (e.g. 1 for 1D array, 3 for 3D array, ...).
49
49
  */
50
50
 
51
51
  VALUE
@@ -60,7 +60,7 @@ rb_ca_ndim (VALUE self)
60
60
 
61
61
  (Attribute)
62
62
  Returns the byte size of each element (e.g. 4 for CA_INT32, 8 for CA_FLOAT64).
63
- The byte size can be known using CArray.sizeof(data_type)
63
+ The byte size can be obtained using CArray.sizeof(data_type)
64
64
  for the numerical data types, but
65
65
  the byte size of fixed-length data type can be known
66
66
  only by this method.
@@ -168,7 +168,7 @@ rb_ca_dump_binary (int argc, VALUE *argv, VALUE self)
168
168
  #endif
169
169
  default:
170
170
  if ( rb_respond_to(io, rb_intern("write") ) ) {
171
- VALUE buf = rb_str_new(NULL, ca_length(ca));
171
+ volatile VALUE buf = rb_str_new(NULL, ca_length(ca));
172
172
  ca_copy_data(ca, StringValuePtr(buf));
173
173
  OBJ_INFECT(buf, self);
174
174
  rb_funcall(io, rb_intern("write"), 1, buf);
@@ -210,8 +210,6 @@ rb_ca_load_binary (VALUE self, VALUE io)
210
210
  rb_raise(rb_eCADataTypeError, "don't load object array");
211
211
  }
212
212
 
213
- ca_allocate(ca);
214
-
215
213
  switch ( TYPE(io) ) {
216
214
  case T_STRING:
217
215
  if ( ca_length(ca) > RSTRING_LEN(io) ) {
@@ -219,23 +217,23 @@ rb_ca_load_binary (VALUE self, VALUE io)
219
217
  "data size mismatch (%lld for %lld)",
220
218
  (ca_size_t) RSTRING_LEN(io), (ca_size_t) ca_length(ca));
221
219
  }
220
+ ca_allocate(ca);
222
221
  memcpy(ca->ptr, StringValuePtr(io), ca_length(ca));
222
+ ca_sync(ca);
223
+ ca_detach(ca);
223
224
  OBJ_INFECT(self, io);
225
+ return self;
224
226
  break;
225
227
  default:
226
228
  if ( rb_respond_to(io, rb_intern("read") ) ) {
227
- VALUE buf = rb_funcall(io, rb_intern("read"), 1, SIZE2NUM(ca_length(ca)));
228
- memcpy(ca->ptr, StringValuePtr(buf), ca_length(ca));
229
- OBJ_INFECT(self, io);
229
+ volatile VALUE buf = rb_funcall(io, rb_intern("read"), 1, SIZE2NUM(ca_length(ca)));
230
+ return rb_ca_load_binary(self, buf);
230
231
  }
231
232
  else {
232
233
  rb_raise(rb_eRuntimeError, "IO like object should have 'read' method");
233
234
  }
234
235
  }
235
236
 
236
- ca_sync(ca);
237
- ca_detach(ca);
238
-
239
237
  return self;
240
238
  }
241
239
 
@@ -371,7 +371,7 @@ rb_ca_iter_prepare_output (int argc, VALUE *argv, VALUE self)
371
371
 
372
372
  /* yard:
373
373
  class CAIterator
374
- def calculate
374
+ def calculate (data_type=nil, )
375
375
  end
376
376
  end
377
377
  */
data/ext/carray_order.c CHANGED
@@ -1009,7 +1009,7 @@ rb_ca_binary_search_linear_index (volatile VALUE self, volatile VALUE vx)
1009
1009
  else {
1010
1010
  for (i=0; i<cx->elements; i++) {
1011
1011
  linear_index(n, x, *px, po);
1012
- px++, po++;
1012
+ px++; po++;
1013
1013
  }
1014
1014
  }
1015
1015
 
@@ -1025,6 +1025,320 @@ rb_ca_binary_search_linear_index (volatile VALUE self, volatile VALUE vx)
1025
1025
  }
1026
1026
 
1027
1027
 
1028
+ static VALUE
1029
+ rb_ca_binary_search_linear_index_vectorized (volatile VALUE self, volatile VALUE vx)
1030
+ {
1031
+ volatile VALUE out, out0;
1032
+ CArray *ca, *sc, *cx, *co0, *co;
1033
+ double *x;
1034
+ double *px;
1035
+ double *po;
1036
+ ca_size_t nseri, nlist;
1037
+ ca_size_t odim[CA_DIM_MAX];
1038
+ ca_size_t i, k;
1039
+
1040
+ Data_Get_Struct(self, CArray, ca);
1041
+
1042
+ if ( rb_ca_is_any_masked(self) ) {
1043
+ rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1044
+ }
1045
+
1046
+ sc = ca_wrap_readonly(self, CA_FLOAT64);
1047
+ cx = ca_wrap_readonly(vx, CA_FLOAT64);
1048
+
1049
+ if ( sc->ndim < 2 ) {
1050
+ rb_raise(rb_eRuntimeError, "ndim of self should be larger than 2");
1051
+ }
1052
+
1053
+ if ( cx->ndim > CA_DIM_MAX ) {
1054
+ rb_raise(rb_eRuntimeError, "2nd argument carray has too large dimension");
1055
+ }
1056
+
1057
+ nseri = 1;
1058
+ for (i=0; i<sc->ndim-1; i++) {
1059
+ nseri *= sc->dim[i];
1060
+ }
1061
+ nlist = sc->dim[sc->ndim-1];
1062
+
1063
+ if ( rb_ca_is_scalar(vx) ) {
1064
+ for (i=0; i<sc->ndim-1; i++) {
1065
+ odim[i] = sc->dim[i];
1066
+ }
1067
+ co0 = carray_new(ca->data_type, sc->ndim-1, odim, 0, NULL);
1068
+ }
1069
+ else {
1070
+ for (i=0; i<sc->ndim-1; i++) {
1071
+ odim[i] = sc->dim[i];
1072
+ }
1073
+ memcpy(&odim[sc->ndim], cx->dim, cx->ndim*sizeof(ca_size_t));
1074
+ co0 = carray_new(ca->data_type, sc->ndim-1 + cx->ndim, odim, 0, NULL);
1075
+ }
1076
+
1077
+ out = out0 = ca_wrap_struct(co0);
1078
+ co = ca_wrap_writable(out, CA_FLOAT64);
1079
+
1080
+ ca_attach_n(3, sc, cx, co);
1081
+
1082
+ x = (double*) sc->ptr;
1083
+ po = (double*) co->ptr;
1084
+
1085
+ ca_update_mask(cx);
1086
+ if ( cx->mask ) {
1087
+ boolean8_t *mx, *mo;
1088
+ ca_create_mask(co);
1089
+ mx = (boolean8_t *) cx->mask->ptr;
1090
+ mo = (boolean8_t *) co->mask->ptr;
1091
+ for (k=0; k<nseri; k++) {
1092
+ px = (double*) cx->ptr;
1093
+ for (i=0; i<cx->elements; i++) {
1094
+ if ( ! *mx ) {
1095
+ linear_index(nlist, x, *px, po);
1096
+ }
1097
+ else {
1098
+ *mo = 1;
1099
+ }
1100
+ mx++; mo++; px++, po++;
1101
+ }
1102
+ x += nlist;
1103
+ }
1104
+ }
1105
+ else {
1106
+ for (k=0; k<nseri; k++) {
1107
+ px = (double*) cx->ptr;
1108
+ for (i=0; i<cx->elements; i++) {
1109
+ linear_index(nlist, x, *px, po);
1110
+ px++; po++;
1111
+ }
1112
+ x += nlist;
1113
+ }
1114
+ }
1115
+
1116
+ ca_sync(co);
1117
+ ca_detach_n(3, sc, cx, co);
1118
+
1119
+ return out0;
1120
+ }
1121
+
1122
+ /* ----------------------------------------------------------------- */
1123
+
1124
+ static int
1125
+ fetch_linear_addr (ca_size_t n, double *y, double idx, double *val)
1126
+ {
1127
+ ca_size_t il, iu;
1128
+ double w;
1129
+
1130
+ if ( idx < 0 || idx > n - 1 ) {
1131
+ return -1;
1132
+ }
1133
+
1134
+ il = (ca_size_t) floor(idx);
1135
+ iu = (ca_size_t) ceil(idx);
1136
+ w = idx - floor(idx);
1137
+
1138
+ *val = y[iu]*w + y[il]*(1.0-w);
1139
+
1140
+ /* printf("%g %i %i %g %g\n", idx, il, iu, w, *val); */
1141
+
1142
+ return 0;
1143
+ }
1144
+
1145
+ static VALUE
1146
+ rb_ca_fetch_linear_addr (volatile VALUE self, volatile VALUE vx)
1147
+ {
1148
+ volatile VALUE out, out0;
1149
+ CArray *ca, *sc, *cx, *co0, *co;
1150
+ double *x;
1151
+ double *px;
1152
+ double *po;
1153
+ ca_size_t nlist, nreq;
1154
+ ca_size_t i;
1155
+ boolean8_t *mx, *mo;
1156
+
1157
+ Data_Get_Struct(self, CArray, ca);
1158
+
1159
+ if ( rb_ca_is_any_masked(self) ) {
1160
+ rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1161
+ }
1162
+
1163
+ sc = ca_wrap_readonly(self, CA_FLOAT64);
1164
+ cx = ca_wrap_readonly(vx, CA_FLOAT64);
1165
+
1166
+ if ( sc->ndim != 1 ) {
1167
+ rb_raise(rb_eRuntimeError, "ndim of self should be 1");
1168
+ }
1169
+
1170
+ nlist = sc->dim[0];
1171
+
1172
+ nreq = 1;
1173
+ for (i=1; i<cx->ndim; i++) {
1174
+ nreq *= cx->dim[i];
1175
+ }
1176
+
1177
+ co0 = carray_new(ca->data_type, cx->ndim, cx->dim, 0, NULL);
1178
+ out = out0 = ca_wrap_struct(co0);
1179
+ co = ca_wrap_writable(out, CA_FLOAT64);
1180
+
1181
+ ca_attach_n(3, sc, cx, co);
1182
+
1183
+ x = (double*) sc->ptr;
1184
+ px = (double*) cx->ptr;
1185
+ po = (double*) co->ptr;
1186
+
1187
+ ca_create_mask(co);
1188
+ ca_update_mask(cx);
1189
+
1190
+ if ( cx->mask ) {
1191
+ mx = (boolean8_t *) cx->mask->ptr;
1192
+ mo = (boolean8_t *) co->mask->ptr;
1193
+ for (i=0; i<nreq; i++) {
1194
+ if ( ! *mx ) {
1195
+ if ( fetch_linear_addr(nlist, x, *px, po) ) {
1196
+ *mo = 1;
1197
+ }
1198
+ }
1199
+ else {
1200
+ *mo = 1;
1201
+ }
1202
+ mx++; mo++; px++, po++;
1203
+ }
1204
+ }
1205
+ else {
1206
+ mo = (boolean8_t *) co->mask->ptr;
1207
+ for (i=0; i<nreq; i++) {
1208
+ if ( fetch_linear_addr(nlist, x, *px, po) ) {
1209
+ *mo = 1;
1210
+ }
1211
+ mo++; px++; po++;
1212
+ }
1213
+ }
1214
+
1215
+ ca_sync(co);
1216
+ ca_detach_n(3, sc, cx, co);
1217
+
1218
+ if ( rb_ca_is_scalar(vx) ) {
1219
+ return rb_funcall(out0, rb_intern("[]"), 1, INT2NUM(0));
1220
+ }
1221
+ else {
1222
+ return out0;
1223
+ }
1224
+ }
1225
+
1226
+
1227
+ /*
1228
+
1229
+
1230
+ self: ndim >= 2
1231
+ 0...ndim : prev dimensions are vectorized elements
1232
+ -1: last dimension is used for fetch_addr (as self)
1233
+
1234
+ vx: ndim >= 2
1235
+ 0...ndim : prev dimensions are vectorized elements should be equal to self's
1236
+ -1: last dimension is used for fetch_addr (as addr)
1237
+
1238
+ */
1239
+
1240
+
1241
+ static VALUE
1242
+ rb_ca_fetch_linear_addr_vectorized (volatile VALUE self, volatile VALUE vx)
1243
+ {
1244
+ volatile VALUE out, out0;
1245
+ CArray *ca, *sc, *cx, *co0, *co;
1246
+ double *x;
1247
+ double *px;
1248
+ double *po;
1249
+ ca_size_t nseri, nlist, nreq, xnseri;
1250
+ ca_size_t i, k;
1251
+ boolean8_t *mx, *mo;
1252
+
1253
+ Data_Get_Struct(self, CArray, ca);
1254
+
1255
+ if ( rb_ca_is_any_masked(self) ) {
1256
+ rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1257
+ }
1258
+
1259
+ sc = ca_wrap_readonly(self, CA_FLOAT64);
1260
+ cx = ca_wrap_readonly(vx, CA_FLOAT64);
1261
+
1262
+ if ( sc->ndim < 2 ) {
1263
+ rb_raise(rb_eRuntimeError, "ndim of self should be larger than 2");
1264
+ }
1265
+
1266
+ nseri = 1;
1267
+ for (i=0; i<sc->ndim-1; i++) {
1268
+ nseri *= sc->dim[i];
1269
+ }
1270
+ nlist = sc->dim[sc->ndim-1];
1271
+
1272
+ if ( cx->ndim < sc->ndim - 1 ) {
1273
+ rb_raise(rb_eRuntimeError, "ndim of first argument should be larger than (ndim - 1) of self");
1274
+ }
1275
+
1276
+ xnseri = 1;
1277
+ for (i=0; i<sc->ndim-1; i++) {
1278
+ xnseri *= cx->dim[i];
1279
+ }
1280
+
1281
+ if ( xnseri != nseri ) {
1282
+ rb_raise(rb_eRuntimeError, "1st dimension should be same between self and 1st argument");
1283
+ }
1284
+
1285
+ if ( cx->ndim == sc->ndim - 1 ) {
1286
+ nreq = 1;
1287
+ }
1288
+ else {
1289
+ nreq = cx->dim[cx->ndim-1];
1290
+ }
1291
+
1292
+ co0 = carray_new(ca->data_type, cx->ndim, cx->dim, 0, NULL);
1293
+ out = out0 = ca_wrap_struct(co0);
1294
+ co = ca_wrap_writable(out, CA_FLOAT64);
1295
+
1296
+ ca_attach_n(3, sc, cx, co);
1297
+
1298
+ x = (double*) sc->ptr;
1299
+ px = (double*) cx->ptr;
1300
+ po = (double*) co->ptr;
1301
+
1302
+ ca_create_mask(co);
1303
+ ca_update_mask(cx);
1304
+
1305
+ if ( cx->mask ) {
1306
+ mx = (boolean8_t *) cx->mask->ptr;
1307
+ mo = (boolean8_t *) co->mask->ptr;
1308
+ for (k=0; k<nseri; k++) {
1309
+ for (i=0; i<nreq; i++) {
1310
+ if ( ! *mx ) {
1311
+ if ( fetch_linear_addr(nlist, x, *px, po) ) {
1312
+ *mo = 1;
1313
+ }
1314
+ }
1315
+ else {
1316
+ *mo = 1;
1317
+ }
1318
+ mx++; mo++; px++, po++;
1319
+ }
1320
+ x += nlist;
1321
+ }
1322
+ }
1323
+ else {
1324
+ mo = (boolean8_t *) co->mask->ptr;
1325
+ for (k=0; k<nseri; k++) {
1326
+ for (i=0; i<nreq; i++) {
1327
+ if ( fetch_linear_addr(nlist, x, *px, po) ) {
1328
+ *mo = 1;
1329
+ }
1330
+ mo++; px++; po++;
1331
+ }
1332
+ x += nlist;
1333
+ }
1334
+ }
1335
+
1336
+ ca_sync(co);
1337
+ ca_detach_n(3, sc, cx, co);
1338
+
1339
+ return out0;
1340
+ }
1341
+
1028
1342
  void
1029
1343
  Init_carray_order ()
1030
1344
  {
@@ -1052,4 +1366,13 @@ Init_carray_order ()
1052
1366
  rb_define_method(rb_cCArray, "section",
1053
1367
  rb_ca_binary_search_linear_index, 1);
1054
1368
 
1369
+ rb_define_method(rb_cCArray, "vectorized_section",
1370
+ rb_ca_binary_search_linear_index_vectorized, 1);
1371
+
1372
+ rb_define_method(rb_cCArray, "fetch_linear_addr",
1373
+ rb_ca_fetch_linear_addr, 1);
1374
+
1375
+ rb_define_method(rb_cCArray, "vectorized_fetch_linear_addr",
1376
+ rb_ca_fetch_linear_addr_vectorized, 1);
1377
+
1055
1378
  }
data/ext/ruby_carray.c CHANGED
@@ -78,6 +78,10 @@ void
78
78
  Init_carray_ext ()
79
79
  {
80
80
 
81
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
82
+ rb_ext_ractor_safe(true);
83
+ #endif
84
+
81
85
  /* Classes and Modules */
82
86
 
83
87
  #ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
data/ext/version.h CHANGED
@@ -8,9 +8,9 @@
8
8
 
9
9
  ---------------------------------------------------------------------------- */
10
10
 
11
- #define CA_VERSION "1.5.7"
12
- #define CA_VERSION_CODE 157
11
+ #define CA_VERSION "1.5.8"
12
+ #define CA_VERSION_CODE 158
13
13
  #define CA_VERSION_MAJOR 1
14
14
  #define CA_VERSION_MINOR 5
15
- #define CA_VERSION_TEENY 7
16
- #define CA_VERSION_DATE "2021/06/16"
15
+ #define CA_VERSION_TEENY 8
16
+ #define CA_VERSION_DATE "2023/01/13"
@@ -0,0 +1,8 @@
1
+
2
+ class CArray
3
+
4
+ def zip (*others)
5
+ return to_a.zip(*others.map(&:to_a))
6
+ end
7
+
8
+ end
data/lib/carray/string.rb CHANGED
@@ -12,6 +12,11 @@ require "date"
12
12
 
13
13
  class CArray
14
14
 
15
+ def self.format (fmt, *argv)
16
+ raise "no parameters given" if argv.empty?
17
+ return argv.shift.zip(*argv).map { |params| Kernel::format(fmt, *params) }.to_ca
18
+ end
19
+
15
20
  def str_len ()
16
21
  return convert(:int, &:length)
17
22
  end
data/lib/carray/time.rb CHANGED
@@ -30,7 +30,7 @@ class CArray
30
30
 
31
31
  def time_format (template = nil)
32
32
  if template
33
- return str_strftime(template)
33
+ return time_strftime(template)
34
34
  else
35
35
  return convert(&:to_s)
36
36
  end
data/lib/carray.rb CHANGED
@@ -36,7 +36,9 @@ require 'carray/math'
36
36
  require 'carray/iterator'
37
37
  require 'carray/struct'
38
38
  require 'carray/table'
39
+ require 'carray/array'
39
40
  require 'carray/string'
41
+ require 'carray/time'
40
42
 
41
43
  # obsolete methods
42
44
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carray
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.7
4
+ version: 1.5.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroki Motoyoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-16 00:00:00.000000000 Z
11
+ date: 2023-01-13 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Ruby/CArray is an extension library for the multi-dimensional numerical array
@@ -85,6 +85,7 @@ files:
85
85
  - ext/version.h
86
86
  - ext/version.rb
87
87
  - lib/carray.rb
88
+ - lib/carray/array.rb
88
89
  - lib/carray/autoload.rb
89
90
  - lib/carray/autoload/autoload_base.rb
90
91
  - lib/carray/autoload/autoload_gem_cairo.rb
@@ -221,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
221
222
  - !ruby/object:Gem::Version
222
223
  version: '0'
223
224
  requirements: []
224
- rubygems_version: 3.1.2
225
+ rubygems_version: 3.1.6
225
226
  signing_key:
226
227
  specification_version: 4
227
228
  summary: Multi-dimesional array class for Ruby