rural 0.1.0

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.
data/ext/vlerq_ext.c ADDED
@@ -0,0 +1,394 @@
1
+ /*
2
+ * vlerq_ext.c - Interface to the Ruby scripting language.
3
+ */
4
+
5
+ #include <ruby.h>
6
+
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+
10
+ #include "vlerq.h"
11
+
12
+ typedef struct CmdDispatch {
13
+ const char *name, *args;
14
+ ItemTypes (*proc) (Item_p);
15
+ } CmdDispatch;
16
+
17
+ static CmdDispatch f_commands[] = {
18
+ #include "vlerq_ext.h"
19
+ };
20
+
21
+ Shared *GetShared () {
22
+ static Shared f_shared;
23
+ return &f_shared;
24
+ }
25
+
26
+ void FailedAssert (const char *msg, const char *file, int line) {
27
+ rb_fatal("Failed assertion at %s, line %d: %s\n", file, line, msg);
28
+ }
29
+
30
+ typedef struct { VALUE view; int row; } Row, *Row_p;
31
+
32
+ static VALUE cRow;
33
+
34
+ static VALUE row_v (VALUE self) {
35
+ Row_p p;
36
+ Data_Get_Struct(self, Row, p);
37
+ return p->view;
38
+ }
39
+
40
+ static VALUE row_i (VALUE self) {
41
+ Row_p p;
42
+ Data_Get_Struct(self, Row, p);
43
+ return INT2NUM(p->row);
44
+ }
45
+
46
+ static void row_mark (Row_p p) {
47
+ rb_gc_mark(p->view);
48
+ }
49
+
50
+ static VALUE AtRow (VALUE view, int row) {
51
+ Row_p p = ALLOC(Row);
52
+ p->view = view;
53
+ p->row = row;
54
+ return Data_Wrap_Struct(cRow, row_mark, 0, p);
55
+ }
56
+
57
+ ItemTypes AtRowCmd_OI (Item_p args) {
58
+ args[0].o = AtRow(args[0].o, args[1].i);
59
+ return IT_object;
60
+ }
61
+
62
+ static VALUE cCol;
63
+
64
+ static ItemTypes ListObjGetter (int row, Item_p item) {
65
+ VALUE ary = (VALUE) item->c.seq->data[0].p;
66
+ item->o = rb_ary_entry(ary, row);
67
+ return IT_object;
68
+ }
69
+
70
+ static void col_mark (Column_p p) {
71
+ if (p->seq->getter == ListObjGetter)
72
+ rb_gc_mark((VALUE) p->seq->data[0].p);
73
+ }
74
+
75
+ static void col_free_keep (Column_p p) {
76
+ PUSH_KEEP_REFS
77
+ DecRefCount(p->seq);
78
+ POP_KEEP_REFS
79
+ }
80
+
81
+ static VALUE cView;
82
+
83
+ static void view_free_keep (View_p p) {
84
+ PUSH_KEEP_REFS
85
+ DecRefCount(p);
86
+ POP_KEEP_REFS
87
+ }
88
+
89
+ static VALUE view_alloc (VALUE klass) {
90
+ return Data_Wrap_Struct(klass, 0, view_free_keep, 0);
91
+ }
92
+
93
+ VALUE ItemAsObj (ItemTypes type, Item_p item) {
94
+ VALUE result;
95
+
96
+ switch (type) {
97
+
98
+ case IT_int:
99
+ result = INT2NUM(item->i);
100
+ break;
101
+
102
+ case IT_wide:
103
+ result = NUM2OFFT(item->w);
104
+ break;
105
+
106
+ case IT_float:
107
+ result = NUM2DBL(item->f);
108
+ break;
109
+
110
+ case IT_double:
111
+ result = NUM2DBL(item->d);
112
+ break;
113
+
114
+ case IT_string:
115
+ result = rb_tainted_str_new(item->s, strlen(item->s));
116
+ break;
117
+
118
+ case IT_bytes:
119
+ result = rb_tainted_str_new((const char*) item->u.ptr, item->u.len);
120
+ break;
121
+
122
+ case IT_column: {
123
+ Column_p p;
124
+ result = Data_Make_Struct(cCol, Column, col_mark, col_free_keep, p);
125
+ *p = item->c;
126
+ IncRefCount(p->seq);
127
+ break;
128
+ }
129
+
130
+ case IT_view:
131
+ result = Data_Wrap_Struct(cView, 0, view_free_keep, IncRefCount(item->v));
132
+ break;
133
+
134
+ case IT_object:
135
+ result = item->o;
136
+ break;
137
+
138
+ default:
139
+ rb_raise (rb_eRuntimeError, "unsupported type %d", type);
140
+ }
141
+
142
+ return result;
143
+ }
144
+
145
+ static VALUE col_to_a_keep (VALUE self) {
146
+ int i, count;
147
+ Column_p column;
148
+ Item item;
149
+ VALUE result;
150
+
151
+ Data_Get_Struct(self, Column, column);
152
+ count = S_Count(column->seq);
153
+
154
+ result = rb_ary_new2(count);
155
+
156
+ PUSH_KEEP_REFS
157
+
158
+ for (i = 0; i < count; ++i) {
159
+ item.c = *column;
160
+ rb_ary_store(result, i, ItemAsObj(GetItem(i, &item), &item));
161
+ }
162
+
163
+ POP_KEEP_REFS
164
+ return result;
165
+ }
166
+
167
+ ItemTypes OpenCmd_S (Item_p args) {
168
+ args[0].v = OpenDataFile(args[0].s);
169
+ return IT_view;
170
+ }
171
+
172
+ int ObjToItem (ItemTypes type, Item_p item) {
173
+ switch (type) {
174
+
175
+ case IT_int:
176
+ item->i = NUM2INT(item->o);
177
+ break;
178
+
179
+ case IT_wide:
180
+ item->w = NUM2LL(item->o);
181
+ break;
182
+
183
+ case IT_float:
184
+ item->f = (float) NUM2DBL(item->o);
185
+ break;
186
+
187
+ case IT_double:
188
+ item->d = NUM2DBL(item->o);
189
+ break;
190
+
191
+ case IT_string:
192
+ item->s = STR2CSTR(item->o);
193
+ break;
194
+
195
+ case IT_bytes:
196
+ item->u.ptr = (const void*) rb_str2cstr(item->o, (long*) &item->u.len);
197
+ break;
198
+
199
+ case IT_object:
200
+ break;
201
+
202
+ case IT_column:
203
+ if (rb_class_of(item->o) != cCol)
204
+ return 0;
205
+ Data_Get_Struct(item->o, Column, item->p);
206
+ item->c = *(Column_p) item->p;
207
+ break;
208
+
209
+ case IT_view:
210
+ if (TYPE(item->o) == T_FIXNUM)
211
+ item->v = NoColumnView(NUM2INT(item->o));
212
+ if (rb_class_of(item->o) == cView)
213
+ Data_Get_Struct(item->o, Sequence, item->v);
214
+ else
215
+ return 0;
216
+ break;
217
+
218
+ default:
219
+ return 0;
220
+ }
221
+
222
+ return 1;
223
+ }
224
+
225
+ int ColumnByName (View_p meta, Object_p obj) {
226
+ int colnum;
227
+
228
+ if (TYPE(obj) == T_FIXNUM) {
229
+ colnum = NUM2INT(obj);
230
+ if (colnum < 0)
231
+ colnum += ViewSize(meta);
232
+ if (colnum < 0 || colnum >= ViewSize(meta))
233
+ return -5;
234
+ } else
235
+ colnum = StringLookup(STR2CSTR(obj), ViewCol(meta, MC_name));
236
+
237
+ return colnum;
238
+ }
239
+
240
+ Column ObjAsColumn (VALUE obj) {
241
+ int objc;
242
+ Object_p *objv;
243
+ Column result;
244
+
245
+ if (rb_class_of(obj) == cCol) {
246
+ Item item;
247
+ item.o = obj;
248
+ if (!ObjToItem(IT_column, &item))
249
+ Assert(0);
250
+ return item.c;
251
+ }
252
+
253
+ Assert(TYPE(obj) == T_ARRAY);
254
+ objc = (int) RARRAY(obj)->len;
255
+
256
+ result = InitColumn(objc, ListObjGetter, 0);
257
+ result.seq->data[0].p = (void*) obj;
258
+
259
+ result.pos = -1;
260
+ return result;
261
+ }
262
+
263
+ static VALUE view_initialize_keep (VALUE self, VALUE arg) {
264
+ View_p view;
265
+ Item item;
266
+ PUSH_KEEP_REFS
267
+
268
+ item.o = arg;
269
+ if (!ObjToItem(IT_view, &item))
270
+ Assert(0);
271
+
272
+ DATA_PTR(self) = IncRefCount(item.v);
273
+
274
+ POP_KEEP_REFS
275
+ return self;
276
+ }
277
+
278
+ #define MAX_STACK 20
279
+
280
+ static VALUE vq_dispatch_keep (int argc, VALUE *argv, VALUE self) {
281
+ int i, index;
282
+ VALUE result;
283
+ Item stack [MAX_STACK];
284
+ ItemTypes type;
285
+ const char *args;
286
+
287
+ index = NUM2INT(argv[0]);
288
+
289
+ PUSH_KEEP_REFS
290
+
291
+ --argc; ++argv;
292
+ args = f_commands[index].args + 2; /* skip return type and ':' */
293
+
294
+ for (i = 0; args[i] != 0; ++i) {
295
+ Assert(i < MAX_STACK);
296
+ if (args[i] == 'X') {
297
+ Assert(args[i+1] == 0);
298
+ stack[i].u.ptr = (const void*) (argv+i);
299
+ stack[i].u.len = argc-i;
300
+ break;
301
+ }
302
+ if ((args[i] == 0 && i != argc) || (args[i] != 0 && i >= argc))
303
+ rb_raise(rb_eRuntimeError, "wrong number of arguments");
304
+ stack[i].o = argv[i];
305
+ if (!CastObjToItem(args[i], stack+i))
306
+ rb_raise(rb_eRuntimeError, "%s: error in arg %d",
307
+ f_commands[index].name, i);
308
+ }
309
+
310
+ type = f_commands[index].proc(stack);
311
+ if (type == IT_unknown)
312
+ rb_raise(rb_eRuntimeError, "%s: error in result", f_commands[index].name);
313
+
314
+ result = ItemAsObj(type, stack);
315
+
316
+ POP_KEEP_REFS
317
+ return result;
318
+ }
319
+
320
+ static VALUE vq_cmdlist (VALUE self) {
321
+ int i, ncmds = sizeof f_commands / sizeof f_commands[0];
322
+ VALUE result = rb_ary_new2(ncmds);
323
+
324
+ for (i = 0; i < ncmds; ++i)
325
+ rb_ary_store(result, i, rb_str_new2(f_commands[i].name));
326
+
327
+ return result;
328
+ }
329
+
330
+ static VALUE view_each_keep (VALUE self) {
331
+ int r, rows;
332
+ Item item;
333
+
334
+ PUSH_KEEP_REFS
335
+
336
+ item.o = self;
337
+ if (!ObjToItem(IT_view, &item))
338
+ Assert(0);
339
+
340
+ rows = ViewSize(item.v);
341
+
342
+ for (r = 0; r < rows; ++r)
343
+ rb_yield(AtRow(self, r));
344
+
345
+ POP_KEEP_REFS
346
+ return self;
347
+ }
348
+
349
+ static VALUE row_aref_keep (VALUE self, VALUE index) {
350
+ int col;
351
+ Row_p p;
352
+ View_p v;
353
+ Item item;
354
+ VALUE result;
355
+
356
+ Data_Get_Struct(self, Row, p);
357
+ Data_Get_Struct(p->view, Sequence, v);
358
+
359
+ PUSH_KEEP_REFS
360
+
361
+ if (TYPE(index) == T_FIXNUM)
362
+ col = NUM2INT(index);
363
+ else
364
+ col = StringLookup(STR2CSTR(index), ViewCol(V_Meta(v), MC_name));
365
+
366
+ Assert(col >= 0);
367
+ item.c = ViewCol(v, col);
368
+ result = ItemAsObj(GetItem(p->row, &item), &item);
369
+
370
+ POP_KEEP_REFS
371
+ return result;
372
+ }
373
+
374
+ void Init_vlerq () {
375
+ VALUE vlerq = rb_define_module("Vlerq");
376
+ rb_define_module_function(vlerq, "cmdlist", vq_cmdlist, 0);
377
+ rb_define_module_function(vlerq, "dispatch", vq_dispatch_keep, -1);
378
+
379
+ cRow = rb_define_class_under(vlerq, "Row", rb_cObject);
380
+ rb_define_method(cRow, "_v", row_v, 0);
381
+ rb_define_method(cRow, "_i", row_i, 0);
382
+ rb_define_method(cRow, "[]", row_aref_keep, 1);
383
+
384
+ cCol = rb_define_class_under(vlerq, "Col", rb_cObject);
385
+ rb_define_method(cCol, "to_a", col_to_a_keep, 0);
386
+
387
+ cView = rb_define_class("View", rb_cObject);
388
+ rb_include_module(cView, rb_mEnumerable);
389
+
390
+ rb_define_alloc_func(cView, view_alloc);
391
+
392
+ rb_define_method(cView, "initialize", view_initialize_keep, 1);
393
+ rb_define_method(cView, "each", view_each_keep, 0);
394
+ }
data/ext/vlerq_ext.h ADDED
@@ -0,0 +1,197 @@
1
+ /* vlerq_ext.h - generated code, do not edit */
2
+
3
+ /* 73 definitions generated for Ruby:
4
+
5
+ # name in out inline-code
6
+ : drop U {} {}
7
+ : dup U UU {$1 = $0;}
8
+ : imul II I {$0.i *= $1.i;}
9
+ : nip UU U {$0 = $1;}
10
+ : over UU UUU {$2 = $0;}
11
+ : rot UUU UUU {$3 = $0; $0 = $1; $1 = $2; $2 = $3;}
12
+ : rrot UUU UUU {$3 = $2; $2 = $1; $1 = $0; $0 = $3;}
13
+ : swap UU UU {$2 = $0; $0 = $1; $1 = $2;}
14
+ : tuck UU UUU {$1 = $2; $0 = $1; $2 = $0;}
15
+
16
+ # name in out
17
+ : Data VX V
18
+ : HashCol SO C
19
+ : Max VN O
20
+ : Min VN O
21
+ : Open S V
22
+ : Set VIX V
23
+ : Sum VN O
24
+
25
+ tcl {
26
+ # name in out
27
+ : Def OO V
28
+ : Emit V O
29
+ : Get VX U
30
+ : Load O V
31
+ : Loop X O
32
+ : Mdef O V
33
+ : Ref OX O
34
+ : Refs O I
35
+ : Rename VO V
36
+ : Save VS I
37
+ : StructDesc V S
38
+ : Structure V S
39
+ : To OO O
40
+ : Type O O
41
+ : View X O
42
+ : Write VS I
43
+ }
44
+
45
+ ruby {
46
+ # name in out
47
+ : AtRow OI O
48
+ }
49
+
50
+ # name in out internal-name
51
+ : Blocked V V BlockedView
52
+ : Clone V V CloneView
53
+ : ColMap Vn V ColMapView
54
+ : ColOmit Vn V ColOmitView
55
+ : Coerce OS C CoerceCmd
56
+ : Compare VV I ViewCompare
57
+ : Compat VV I ViewCompat
58
+ : Concat VV V ConcatView
59
+ : CountsCol C C NewCountsColumn
60
+ : CountView I V NoColumnView
61
+ : First VI V FirstView
62
+ : GetCol VN C ViewCol
63
+ : Group VnS V GroupCol
64
+ : HashFind VIViii I HashDoFind
65
+ : Ijoin VV V IjoinView
66
+ : GetInfo VVI C GetHashInfo
67
+ : Grouped ViiS V GroupedView
68
+ : HashView V C HashValues
69
+ : IsectMap VV C IntersectMap
70
+ : Iota I C NewIotaColumn
71
+ : Join VVS V JoinView
72
+ : Last VI V LastView
73
+ : Mdesc S V DescToMeta
74
+ : Meta V V V_Meta
75
+ : NameCol V V NameColView
76
+ : OmitMap iI C OmitColumn
77
+ : Pair VV V PairView
78
+ : RemapSub ViII V RemapSubview
79
+ : Replace VIIV V ViewReplace
80
+ : RowEq IVIV I RowEqual
81
+ : RowHash VI I RowHash
82
+ : Size V I ViewSize
83
+ : SortMap V C SortMap
84
+ : Step VIIII V StepView
85
+ : StrLookup Ss I StringLookup
86
+ : Tag VS V TagView
87
+ : Take VI V TakeView
88
+ : Ungroup VN V UngroupView
89
+ : UniqueMap V C UniqMap
90
+ : ViewAsCol V C ViewAsCol
91
+ : Width V I ViewWidth
92
+
93
+ # name in out compound-definition
94
+ : Append VV V {over size swap insert}
95
+ : ColConv C C { }
96
+ : Counts VN C {getcol countscol}
97
+ : Delete VII V {0 countview replace}
98
+ : Except VV V {over swap exceptmap remap}
99
+ : ExceptMap VV C {over swap isectmap swap size omitmap}
100
+ : GroupInfo V C {dup 1 getinfo}
101
+ : HashInfo V C {dup 0 getinfo}
102
+ : Insert VIV V {0 swap replace}
103
+ : JoinInfo VV C {2 getinfo}
104
+ : Intersect VV V {over swap isectmap remap}
105
+ : Names V C {meta 0 getcol}
106
+ : Product VV V {over over size spread rrot swap size repeat pair}
107
+ : Repeat VI V {over size imul 0 1 1 step}
108
+ : Remap Vi V {0 -1 remapsub}
109
+ : Reverse V V {dup size -1 1 -1 step}
110
+ : Slice VIII V {rrot 1 swap step}
111
+ : Sort V V {dup sortmap remap}
112
+ : Spread VI V {over size 0 rot 1 step}
113
+ : Types V C {meta 1 getcol}
114
+ : Unique V V {dup uniquemap remap}
115
+ : Union VV V {over except concat}
116
+ : UnionMap VV C {swap exceptmap}
117
+ : ViewConv V V { }
118
+
119
+ # some operators are omitted from restricted execution environments
120
+ unsafe Open Save
121
+ */
122
+
123
+ { "append", "V:VV", AppendCmd_VV },
124
+ { "atrow", "O:OI", AtRowCmd_OI },
125
+ { "blocked", "V:V", BlockedCmd_V },
126
+ { "clone", "V:V", CloneCmd_V },
127
+ { "coerce", "C:OS", CoerceCmd_OS },
128
+ { "colconv", "C:C", ColConvCmd_C },
129
+ { "colmap", "V:Vn", ColMapCmd_Vn },
130
+ { "colomit", "V:Vn", ColOmitCmd_Vn },
131
+ { "compare", "I:VV", CompareCmd_VV },
132
+ { "compat", "I:VV", CompatCmd_VV },
133
+ { "concat", "V:VV", ConcatCmd_VV },
134
+ { "counts", "C:VN", CountsCmd_VN },
135
+ { "countscol", "C:C", CountsColCmd_C },
136
+ { "countview", "V:I", CountViewCmd_I },
137
+ { "data", "V:VX", DataCmd_VX },
138
+ { "delete", "V:VII", DeleteCmd_VII },
139
+ { "except", "V:VV", ExceptCmd_VV },
140
+ { "exceptmap", "C:VV", ExceptMapCmd_VV },
141
+ { "first", "V:VI", FirstCmd_VI },
142
+ { "getcol", "C:VN", GetColCmd_VN },
143
+ { "getinfo", "C:VVI", GetInfoCmd_VVI },
144
+ { "group", "V:VnS", GroupCmd_VnS },
145
+ { "grouped", "V:ViiS", GroupedCmd_ViiS },
146
+ { "groupinfo", "C:V", GroupInfoCmd_V },
147
+ { "hashcol", "C:SO", HashColCmd_SO },
148
+ { "hashfind", "I:VIViii", HashFindCmd_VIViii },
149
+ { "hashinfo", "C:V", HashInfoCmd_V },
150
+ { "hashview", "C:V", HashViewCmd_V },
151
+ { "ijoin", "V:VV", IjoinCmd_VV },
152
+ { "insert", "V:VIV", InsertCmd_VIV },
153
+ { "intersect", "V:VV", IntersectCmd_VV },
154
+ { "iota", "C:I", IotaCmd_I },
155
+ { "isectmap", "C:VV", IsectMapCmd_VV },
156
+ { "join", "V:VVS", JoinCmd_VVS },
157
+ { "joininfo", "C:VV", JoinInfoCmd_VV },
158
+ { "last", "V:VI", LastCmd_VI },
159
+ { "max", "O:VN", MaxCmd_VN },
160
+ { "mdesc", "V:S", MdescCmd_S },
161
+ { "meta", "V:V", MetaCmd_V },
162
+ { "min", "O:VN", MinCmd_VN },
163
+ { "namecol", "V:V", NameColCmd_V },
164
+ { "names", "C:V", NamesCmd_V },
165
+ { "omitmap", "C:iI", OmitMapCmd_iI },
166
+ { "open", "V:S", OpenCmd_S },
167
+ { "pair", "V:VV", PairCmd_VV },
168
+ { "product", "V:VV", ProductCmd_VV },
169
+ { "remap", "V:Vi", RemapCmd_Vi },
170
+ { "remapsub", "V:ViII", RemapSubCmd_ViII },
171
+ { "repeat", "V:VI", RepeatCmd_VI },
172
+ { "replace", "V:VIIV", ReplaceCmd_VIIV },
173
+ { "reverse", "V:V", ReverseCmd_V },
174
+ { "roweq", "I:IVIV", RowEqCmd_IVIV },
175
+ { "rowhash", "I:VI", RowHashCmd_VI },
176
+ { "set", "V:VIX", SetCmd_VIX },
177
+ { "size", "I:V", SizeCmd_V },
178
+ { "slice", "V:VIII", SliceCmd_VIII },
179
+ { "sort", "V:V", SortCmd_V },
180
+ { "sortmap", "C:V", SortMapCmd_V },
181
+ { "spread", "V:VI", SpreadCmd_VI },
182
+ { "step", "V:VIIII", StepCmd_VIIII },
183
+ { "strlookup", "I:Ss", StrLookupCmd_Ss },
184
+ { "sum", "O:VN", SumCmd_VN },
185
+ { "tag", "V:VS", TagCmd_VS },
186
+ { "take", "V:VI", TakeCmd_VI },
187
+ { "types", "C:V", TypesCmd_V },
188
+ { "ungroup", "V:VN", UngroupCmd_VN },
189
+ { "union", "V:VV", UnionCmd_VV },
190
+ { "unionmap", "C:VV", UnionMapCmd_VV },
191
+ { "unique", "V:V", UniqueCmd_V },
192
+ { "uniquemap", "C:V", UniqueMapCmd_V },
193
+ { "viewascol", "C:V", ViewAsColCmd_V },
194
+ { "viewconv", "V:V", ViewConvCmd_V },
195
+ { "width", "I:V", WidthCmd_V },
196
+
197
+ /* end of generated code */
data/lib/rural.rb ADDED
@@ -0,0 +1,65 @@
1
+ require 'vlerq'
2
+
3
+ module Rural
4
+ VERSION = '0.1.0'
5
+ end
6
+
7
+ module Vlerq
8
+ class Row
9
+ undef_method :type
10
+
11
+ def method_missing(m, *args)
12
+ self[m.to_s]
13
+ end
14
+ end
15
+ end
16
+
17
+ class View
18
+ undef_method :clone
19
+
20
+ Map = Hash.new { |h,k| fail "Unknown view operator: #{k}" }
21
+ Vlerq.cmdlist.each_with_index { |s,i| Map[s.to_sym] = i }
22
+
23
+ Map[:[]] = Map[:atrow]
24
+ Map[:length] = Map[:size]
25
+
26
+ def method_missing(m, *args)
27
+ Vlerq.dispatch(Map[m], self, *args)
28
+ end
29
+
30
+ def self.method_missing(m, *args)
31
+ Vlerq.dispatch(Map[m], *args)
32
+ end
33
+
34
+ def dump
35
+ out = Array.new(3 + size, '')
36
+
37
+ for cname, ctype in names.to_a.zip(types.to_a)
38
+ values = [cname, '-']
39
+
40
+ case ctype
41
+ when 'B'
42
+ each { |r| values << "#{r[cname].size}b" }
43
+ when 'V'
44
+ each { |r| values << "##{r[cname].size}" }
45
+ else
46
+ each { |r| values << r[cname].to_s }
47
+ end
48
+
49
+ widths = []
50
+ values.each { |s| widths << s.size }
51
+ w = [widths.max, 50].min
52
+ right = /[BILFDV]/ =~ ctype
53
+ fmt = " %#{right ? '' : '-'}#{w}.#{w}s"
54
+
55
+ values[0], values[1] = cname.ljust(w), ''.ljust(w, '-')
56
+ values << values[1]
57
+ values.each_with_index { |s,i| out[i] += fmt % s }
58
+ end
59
+ out.join "\n"
60
+ end
61
+
62
+ def project(*names)
63
+ colmap(names).unique
64
+ end
65
+ end