rubylabs 0.5.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.
- data/LICENSE +20 -0
- data/README.rdoc +9 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/bin/bb.rb +12 -0
- data/bin/statistics2-0.53/ext/extconf.rb +11 -0
- data/bin/statistics2-0.53/ext/show.rb +11 -0
- data/bin/statistics2-0.53/ext/t.rb +46 -0
- data/bin/statistics2-0.53/mklist.rb +26 -0
- data/bin/statistics2-0.53/sample-tbl.rb +129 -0
- data/bin/statistics2-0.53/statistics2.rb +532 -0
- data/bin/statistics2-0.53/t-inv.rb +54 -0
- data/bin/statistics2.rb +532 -0
- data/data/aafreq.txt +20 -0
- data/data/cars.txt +50 -0
- data/data/century.txt +1 -0
- data/data/colors.txt +64 -0
- data/data/earth.yaml +15 -0
- data/data/fruit.txt +45 -0
- data/data/hacodes.txt +35 -0
- data/data/hafreq.txt +16 -0
- data/data/hvfreq.txt +5 -0
- data/data/nbody.R +23 -0
- data/data/nbody.out +731 -0
- data/data/nbody.pdf +3111 -0
- data/data/nbody.png +0 -0
- data/data/nbody3d.pdf +3201 -0
- data/data/outer.pdf +182785 -0
- data/data/solar.dat +36501 -0
- data/data/solarsystem.txt +17 -0
- data/data/suits.txt +1 -0
- data/data/wordlist.txt +210653 -0
- data/lib/bitlab.rb +624 -0
- data/lib/elizalab.rb +523 -0
- data/lib/encryptionlab.rb +42 -0
- data/lib/hashlab.rb +224 -0
- data/lib/introlab.rb +14 -0
- data/lib/iterationlab.rb +130 -0
- data/lib/randomlab.rb +294 -0
- data/lib/recursionlab.rb +228 -0
- data/lib/rubylabs.rb +507 -0
- data/lib/sievelab.rb +58 -0
- data/lib/sortlab.rb +213 -0
- data/lib/spherelab.rb +352 -0
- data/lib/temps.rb +41 -0
- data/lib/tsplab.rb +416 -0
- data/lib/viewer.rb +65 -0
- data/test/bit_test.rb +175 -0
- data/test/encryption_test.rb +20 -0
- data/test/iteration_test.rb +40 -0
- data/test/random_test.rb +64 -0
- data/test/recursion_test.rb +47 -0
- data/test/rubylabs_test.rb +18 -0
- data/test/sieve_test.rb +28 -0
- data/test/sphere_test.rb +130 -0
- data/test/temps_test.rb +24 -0
- data/test/test_helper.rb +18 -0
- metadata +112 -0
data/lib/bitlab.rb
ADDED
@@ -0,0 +1,624 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
== BitLab
|
4
|
+
|
5
|
+
Classes used in experiments on binary representations, including Huffman trees.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module RubyLabs
|
10
|
+
|
11
|
+
module BitLab
|
12
|
+
|
13
|
+
=begin rdoc
|
14
|
+
Make a unique binary code for each item in array +a+, returning a Hash that
|
15
|
+
associates each item with its code.
|
16
|
+
=end
|
17
|
+
|
18
|
+
def make_codes(a)
|
19
|
+
n = log2(a.length).ceil
|
20
|
+
res = Hash.new
|
21
|
+
a.each_with_index do |x,i|
|
22
|
+
res[x] = i.code(n)
|
23
|
+
end
|
24
|
+
return res
|
25
|
+
end
|
26
|
+
|
27
|
+
=begin rdoc
|
28
|
+
Print the codes in +a+ (an associative array made by +make_codes+ or the
|
29
|
+
Huffman tree +assign_codes+ method). An option specifies how to order the
|
30
|
+
output, either +:by_code+ or +:by_name+.
|
31
|
+
=end
|
32
|
+
|
33
|
+
def print_codes(a, mode = :by_code)
|
34
|
+
if mode == :by_code
|
35
|
+
a.sort { |x,y| x[1] <=> y[1] }.each do |sym, code|
|
36
|
+
printf "%s %s\n", code, sym
|
37
|
+
end
|
38
|
+
elsif mode == :by_name
|
39
|
+
width = a.keys.map{ |x| x.length }.max
|
40
|
+
a.keys.sort.each do |x|
|
41
|
+
printf "%-#{width}s %s\n", x, a[x]
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise "print_codes: mode must be :by_code or :by_name"
|
45
|
+
end
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
|
49
|
+
=begin rdoc
|
50
|
+
Make a Message object for the characters in string s. The second
|
51
|
+
parameter determines the code to use. It can be :ascii, :parity, a hash
|
52
|
+
object that has code mappings for letters, or a Huffman tree. If a hash
|
53
|
+
or tree is passed, the message type is set to :packed, otherwise it's :unpacked.
|
54
|
+
The encoding type is saved so it can be used later in decoding (but see note in
|
55
|
+
documentation of +decode+).
|
56
|
+
=end
|
57
|
+
|
58
|
+
def encode(s, type, opt = nil)
|
59
|
+
if (type.class == Hash || type.class == Node)
|
60
|
+
code = (type.class == Node) ? assign_codes(type) : type
|
61
|
+
msg = Message.new(:packed)
|
62
|
+
s.each_byte do |ch|
|
63
|
+
msg << code[ch.chr]
|
64
|
+
printf("%s: %s\n", ch.chr, code[ch.chr]) if opt == :trace
|
65
|
+
end
|
66
|
+
else
|
67
|
+
msg = Message.new(:unpacked)
|
68
|
+
s.each_byte do |ch|
|
69
|
+
code = ch.code(8)
|
70
|
+
code.add_parity_bit if type == :parity
|
71
|
+
msg << code
|
72
|
+
printf("%s: %s\n", ch.chr, code) if opt == :trace
|
73
|
+
end
|
74
|
+
end
|
75
|
+
msg.encoding = type
|
76
|
+
return msg
|
77
|
+
end
|
78
|
+
|
79
|
+
=begin rdoc
|
80
|
+
Decode a message using the specified decoding scheme.
|
81
|
+
|
82
|
+
Note: see also the +decode+ method in the Message class, which assembles
|
83
|
+
a string using the coding scheme specified when the message was created.
|
84
|
+
|
85
|
+
+Message#decode+ always gives the right result -- the point of the method
|
86
|
+
here is to show what happens if the decoding scheme does not match the
|
87
|
+
encoding scheme.
|
88
|
+
=end
|
89
|
+
|
90
|
+
def decode(m, type)
|
91
|
+
raise "not a message" unless m.class == Message
|
92
|
+
res = ""
|
93
|
+
if type.class == Node # weird -- it appears case labels can't be class names...
|
94
|
+
res = huffman_decode(m, type)
|
95
|
+
elsif type.class == Code
|
96
|
+
raise "packed decode not implemented"
|
97
|
+
elsif type == :ascii
|
98
|
+
m.array.each do |x|
|
99
|
+
res << x.value.chr
|
100
|
+
end
|
101
|
+
elsif type == :parity
|
102
|
+
m.array.each do |x|
|
103
|
+
if x.even_parity?
|
104
|
+
res << (x.value >> 1).chr
|
105
|
+
else
|
106
|
+
res << "?"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
else
|
110
|
+
raise "unknown option: #{type}"
|
111
|
+
end
|
112
|
+
return res
|
113
|
+
end
|
114
|
+
|
115
|
+
=begin rdoc
|
116
|
+
Simulate transmission of message +m+, adding +n+ random errors. Returns a
|
117
|
+
copy of the Message object after making +n+ calls to the +flip+ method.
|
118
|
+
=end
|
119
|
+
|
120
|
+
def garbled(m, n)
|
121
|
+
res = m.copy
|
122
|
+
n.times do
|
123
|
+
c = res.array[ rand(res.array.length) ]
|
124
|
+
c.flip( rand(c.length) )
|
125
|
+
end
|
126
|
+
return res
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
=begin rdoc
|
131
|
+
Huffman tree interface: Read letters and frequencies from file +fn+, save them in a hash indexed
|
132
|
+
by letter name
|
133
|
+
=end
|
134
|
+
|
135
|
+
def read_frequencies(fn)
|
136
|
+
a = Hash.new
|
137
|
+
File.open(fn).each do |line|
|
138
|
+
line.chomp!
|
139
|
+
next if line.length == 0
|
140
|
+
next if line[0] == ?#
|
141
|
+
x = line[/^./]
|
142
|
+
f = line[/\d+\.\d+/].to_f
|
143
|
+
a[x] = f
|
144
|
+
end
|
145
|
+
return a
|
146
|
+
end
|
147
|
+
|
148
|
+
=begin rdoc
|
149
|
+
Huffman tree interface: Build a tree using frequencies in Hash +f+.
|
150
|
+
=end
|
151
|
+
|
152
|
+
# :begin :build_tree
|
153
|
+
def build_tree(f)
|
154
|
+
pq = init_queue(f)
|
155
|
+
|
156
|
+
while pq.length > 1
|
157
|
+
n1 = pq.shift
|
158
|
+
n2 = pq.shift
|
159
|
+
pq << Node.combine( n1, n2)
|
160
|
+
end
|
161
|
+
|
162
|
+
return pq[0]
|
163
|
+
end
|
164
|
+
# :end :build_tree
|
165
|
+
|
166
|
+
=begin rdoc
|
167
|
+
Huffman tree helper procedure: Traverse +tree+, making a +Code+ object for each
|
168
|
+
leaf node, returning the codes in a Hash object. On recursive calls the
|
169
|
+
+prefix+ parameter is the set of codes on the path so far.
|
170
|
+
|
171
|
+
Students should pass a tree to +encode+, which will call this method to make the code.
|
172
|
+
The same tree should also be passed to +decode+ when they want to decode a message.
|
173
|
+
=end
|
174
|
+
|
175
|
+
# :begin :assign_codes
|
176
|
+
def assign_codes(tree, code = {}, prefix = Code.new(0,0))
|
177
|
+
if tree.char != nil
|
178
|
+
code[tree.char] = prefix
|
179
|
+
else
|
180
|
+
assign_codes(tree.left, code, prefix + 0)
|
181
|
+
assign_codes(tree.right, code, prefix + 1)
|
182
|
+
end
|
183
|
+
return code
|
184
|
+
end
|
185
|
+
# :end :assign_codes
|
186
|
+
|
187
|
+
=begin rdoc
|
188
|
+
Huffman tree helper procedure: initialize a priority queue with Node
|
189
|
+
objects for each letter in Hash +a+.
|
190
|
+
=end
|
191
|
+
|
192
|
+
# :begin :init_queue
|
193
|
+
def init_queue(a)
|
194
|
+
q = PriorityQueue.new
|
195
|
+
a.each do |x,f|
|
196
|
+
q << Node.new(x,f)
|
197
|
+
end
|
198
|
+
return q
|
199
|
+
end
|
200
|
+
# :end :init_queue
|
201
|
+
|
202
|
+
=begin rdoc
|
203
|
+
Huffman tree helper procedure: decode the binary codes in message +m+, using +tree+ as a guide.
|
204
|
+
Not intended to be called by students; they will use the top level +decode+ method
|
205
|
+
or the +decode+ method defined in the Message class.
|
206
|
+
=end
|
207
|
+
|
208
|
+
def huffman_decode(m, tree)
|
209
|
+
res = ""
|
210
|
+
path = tree
|
211
|
+
m.each do |bit|
|
212
|
+
if path.leaf?
|
213
|
+
res << path.char
|
214
|
+
path = tree
|
215
|
+
end
|
216
|
+
path = (bit == 0) ? path.left : path.right
|
217
|
+
end
|
218
|
+
res << path.char if path.leaf?
|
219
|
+
return res
|
220
|
+
end
|
221
|
+
|
222
|
+
=begin rdoc
|
223
|
+
Huffman tree utility: read strings of binary digits from a file. Each line has a binary
|
224
|
+
sequence, a tab, and an English word. Return two arrays, one a list of Message objects built
|
225
|
+
from the binary sequences and the other a parallel list of words. Since the input is sorted
|
226
|
+
by code length (shortest codes first) returning them in order allows student to choose a
|
227
|
+
word to decode based on length.
|
228
|
+
=end
|
229
|
+
|
230
|
+
def read_codes(fn)
|
231
|
+
codes = Array.new
|
232
|
+
words = Array.new
|
233
|
+
File.open(fn).each do |line|
|
234
|
+
line.chomp!
|
235
|
+
next if line.length == 0
|
236
|
+
next if line[0] == ?#
|
237
|
+
code = Code.new(0,0)
|
238
|
+
line[/[01]+/].each_byte do |byte|
|
239
|
+
code << byte[0] # add least significant digit of ASCII "0" or "1"
|
240
|
+
end
|
241
|
+
msg = Message.new(:packed)
|
242
|
+
msg << code
|
243
|
+
codes << msg
|
244
|
+
words << line[/[a-z]+/]
|
245
|
+
end
|
246
|
+
return codes, words
|
247
|
+
end
|
248
|
+
|
249
|
+
=begin rdoc
|
250
|
+
Huffman tree utility: generate a random string of length +n+ using the letter frequencies +f+.
|
251
|
+
=end
|
252
|
+
|
253
|
+
def generate_string(n, f)
|
254
|
+
s = ""
|
255
|
+
n.times do
|
256
|
+
r = rand
|
257
|
+
sum = 0
|
258
|
+
f.each do |ch,x|
|
259
|
+
sum += x
|
260
|
+
if r < sum
|
261
|
+
s += ch
|
262
|
+
break
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
return s
|
267
|
+
end
|
268
|
+
|
269
|
+
=begin rdoc
|
270
|
+
Class for nodes of a Huffman tree. All nodes have a frequency (+freq+) used to
|
271
|
+
determine its place in a priority queue. Leaf nodes have a character (+char+).
|
272
|
+
Interior nodes have a +nil+ character value, in which case +left+ and +right+
|
273
|
+
are Nodes for the roots of subtrees.
|
274
|
+
|
275
|
+
Use +new+ to create a new leaf node. Call the class method +combine+ (an alternative
|
276
|
+
constructor) to make a new interior node from two existing nodes.
|
277
|
+
|
278
|
+
The +<+ method allows Nodes to be compared so they can be ordered in a priority
|
279
|
+
queue.
|
280
|
+
=end
|
281
|
+
|
282
|
+
class Node
|
283
|
+
|
284
|
+
attr_accessor :freq, :char, :left, :right
|
285
|
+
|
286
|
+
def initialize(char,freq)
|
287
|
+
@char = char
|
288
|
+
@freq = freq
|
289
|
+
@left = @right = nil
|
290
|
+
end
|
291
|
+
|
292
|
+
def Node.combine(n1,n2)
|
293
|
+
node = Node.new(nil, n1.freq + n2.freq)
|
294
|
+
node.left = n1
|
295
|
+
node.right = n2
|
296
|
+
return node
|
297
|
+
end
|
298
|
+
|
299
|
+
def <(x)
|
300
|
+
x.class == Node && @freq < x.freq
|
301
|
+
end
|
302
|
+
|
303
|
+
def leaf?
|
304
|
+
return @char != nil
|
305
|
+
end
|
306
|
+
|
307
|
+
def inspect
|
308
|
+
if leaf?
|
309
|
+
sprintf "( %s: %.3f )", @char, @freq
|
310
|
+
else
|
311
|
+
sprintf "( %.3f %s %s )", @freq, @left.to_s, @right.to_s
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
alias to_s inspect
|
316
|
+
|
317
|
+
end # Node
|
318
|
+
|
319
|
+
=begin rdoc
|
320
|
+
Code objects are variable-length binary numbers representing individual letters or members
|
321
|
+
of a set. The main reason to create a Code object is so to have the value of the integer
|
322
|
+
displayed in binary or hex. To make it easier for advanced students to understand the
|
323
|
+
Huffman tree "assign_codes" method this class defines a method that attaches a bit to the
|
324
|
+
end of a code and returns a new Code object.
|
325
|
+
|
326
|
+
Students will not create Code objects directly -- instead they are created by methods in
|
327
|
+
other classes, e.g. the +code+ method added to Fixnum or the top level encode method.
|
328
|
+
|
329
|
+
There are two methods for attaching bits: << appends a bit to a code (used by the method
|
330
|
+
that makes a parity-encoded message) and +, which returns a copy of a code with the bit
|
331
|
+
added (used by the recursive method that assigns Huffman codes).
|
332
|
+
|
333
|
+
<< can also be used to extend a code by appending a second code.
|
334
|
+
|
335
|
+
The index operator for Fixnums orders bits from right to left, consistent with standard
|
336
|
+
usage but the opposite of Strings and Arrays. In this module bits will be ordered from
|
337
|
+
left to right to be consistent with Strings and Arrays. The modified ordering is used
|
338
|
+
in the index operator and the +flip+ method.
|
339
|
+
=end
|
340
|
+
|
341
|
+
# TBD allow + and << on hex codes; they should have same internal rep, as binary codes, just show as hex when displaying
|
342
|
+
|
343
|
+
# Way cool -- this class used to have a maxcodesize (set to 60) to make sure codes fit within
|
344
|
+
# a single 64-bit word. But a typo during one experiment created an 80-bit code. Turns out
|
345
|
+
# indexing etc work just fine on Bignums:
|
346
|
+
#
|
347
|
+
# >> c1 = s[1].code(80)
|
348
|
+
# => 00000000000000000000000000000000000000000000000000000000000000000000000001101000
|
349
|
+
# >> c1.flip(0)
|
350
|
+
# => 10000000000000000000000000000000000000000000000000000000000000000000000001101000
|
351
|
+
|
352
|
+
class Code
|
353
|
+
attr_accessor :value, :length, :base
|
354
|
+
|
355
|
+
def initialize(value, length = 0, base = :binary)
|
356
|
+
raise "Code: unknown base: #{base}" unless [:binary, :hex].include?(base)
|
357
|
+
@value = value
|
358
|
+
@length = length
|
359
|
+
@base = base
|
360
|
+
@has_parity_bit = false
|
361
|
+
end
|
362
|
+
|
363
|
+
def +(bit)
|
364
|
+
raise "operation not defined for hex codes" unless @base == :binary
|
365
|
+
val = (@value << 1) | bit[0]
|
366
|
+
return Code.new(val, @length+1)
|
367
|
+
end
|
368
|
+
|
369
|
+
def <<(x)
|
370
|
+
raise "operation not defined for hex codes" unless @base == :binary
|
371
|
+
if x.class == Code
|
372
|
+
val = x.value # val known to fit in x.length bits
|
373
|
+
n = x.length
|
374
|
+
else
|
375
|
+
val = x[0] # val is a single bit
|
376
|
+
n = 1
|
377
|
+
end
|
378
|
+
@value <<= n
|
379
|
+
@value |= val # val can't have any higher order bits set -- see above
|
380
|
+
@length += n
|
381
|
+
return self
|
382
|
+
end
|
383
|
+
|
384
|
+
def [](i)
|
385
|
+
if i.class == Range
|
386
|
+
res = 0
|
387
|
+
n = 0
|
388
|
+
i.each { |j| res <<= 1; res |= @value[@length-j-1]; n += 1 }
|
389
|
+
return Code.new(res, n)
|
390
|
+
else
|
391
|
+
return @value[@length-i-1]
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def parity_bit
|
396
|
+
bit = 0
|
397
|
+
for i in 0...@length
|
398
|
+
bit ^= @value[i]
|
399
|
+
end
|
400
|
+
return bit
|
401
|
+
end
|
402
|
+
|
403
|
+
def add_parity_bit
|
404
|
+
@has_parity_bit = true
|
405
|
+
return self << parity_bit
|
406
|
+
end
|
407
|
+
|
408
|
+
def even_parity?
|
409
|
+
return parity_bit() == 0
|
410
|
+
end
|
411
|
+
|
412
|
+
def flip(i)
|
413
|
+
raise "bit index out of range" unless i < @length
|
414
|
+
@value ^= (1 << (@length - i - 1))
|
415
|
+
return self
|
416
|
+
end
|
417
|
+
|
418
|
+
def chr
|
419
|
+
if @has_parity_bit
|
420
|
+
if even_parity?
|
421
|
+
return (@value >> 1).chr
|
422
|
+
else
|
423
|
+
return "?"
|
424
|
+
end
|
425
|
+
else
|
426
|
+
return @value.chr
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
def <=>(x)
|
431
|
+
return @value <=> x.value
|
432
|
+
end
|
433
|
+
|
434
|
+
def inspect
|
435
|
+
return "" if @length == 0
|
436
|
+
case @base
|
437
|
+
when :binary
|
438
|
+
return sprintf "%0#{@length}b", @value
|
439
|
+
when :hex
|
440
|
+
return sprintf "%0#{@length/4}X", @value
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
alias to_s inspect
|
445
|
+
|
446
|
+
end # Code
|
447
|
+
|
448
|
+
=begin rdoc
|
449
|
+
Message objects are arrays of Codes. New codes are added by a call to <<.
|
450
|
+
If the message is unpacked, each code is stored in its own array element
|
451
|
+
(use this for :ascii or :parity encodings). If the message is packed,
|
452
|
+
codes are repackaged into 8-bit codes, and individual codes may cross
|
453
|
+
array item boundaries.
|
454
|
+
|
455
|
+
The +each+ method iterates over each bit in a message. +copy+ makes a
|
456
|
+
deep copy, copying each code object (but sharing a reference to +encoding+).
|
457
|
+
=end
|
458
|
+
|
459
|
+
# TODO: allow for the possibility that a code word being added to a packed code can be longer than 8 bits
|
460
|
+
|
461
|
+
class Message
|
462
|
+
|
463
|
+
attr_accessor :packed, :array, :encoding
|
464
|
+
|
465
|
+
@@packsize = 8 # number of bits to pack into single code
|
466
|
+
|
467
|
+
def initialize(type)
|
468
|
+
raise "Message: unknown type" unless [:packed, :unpacked].include? type
|
469
|
+
if type == :packed
|
470
|
+
@packed = true
|
471
|
+
@array = [ Code.new(0,0) ]
|
472
|
+
else
|
473
|
+
@packed = false
|
474
|
+
@array = [ ]
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def copy
|
479
|
+
dup = self.clone # copies @packed, @encoding
|
480
|
+
dup.array = Array.new
|
481
|
+
@array.each { |x| dup.array << x.clone } # deep copy of @array
|
482
|
+
return dup
|
483
|
+
end
|
484
|
+
|
485
|
+
def each
|
486
|
+
@array.each do |byte|
|
487
|
+
for i in 0...byte.length
|
488
|
+
yield(byte[i])
|
489
|
+
end
|
490
|
+
end
|
491
|
+
return self
|
492
|
+
end
|
493
|
+
|
494
|
+
def <<(x)
|
495
|
+
raise "Message#<<: not a code" unless x.class == Code
|
496
|
+
if @packed
|
497
|
+
if @array[-1].length + x.length <= @@packsize
|
498
|
+
@array[-1] << x
|
499
|
+
else
|
500
|
+
n = @@packsize - @array[-1].length
|
501
|
+
m = x.length - n
|
502
|
+
@array[-1] << x[0...n]
|
503
|
+
@array << x[n...x.length]
|
504
|
+
end
|
505
|
+
else
|
506
|
+
@array << x
|
507
|
+
end
|
508
|
+
return self
|
509
|
+
end
|
510
|
+
|
511
|
+
def decode
|
512
|
+
res = ""
|
513
|
+
if @encoding.class == Symbol
|
514
|
+
@array.each do |code|
|
515
|
+
res << code.chr
|
516
|
+
end
|
517
|
+
elsif @encoding.class == Node
|
518
|
+
res = huffman_decode(self, @encoding)
|
519
|
+
else
|
520
|
+
res = unpack
|
521
|
+
end
|
522
|
+
return res
|
523
|
+
end
|
524
|
+
|
525
|
+
def unpack
|
526
|
+
"unpack: not implemented"
|
527
|
+
end
|
528
|
+
|
529
|
+
def length
|
530
|
+
@array.inject(0) { |sum, code| sum += code.length }
|
531
|
+
end
|
532
|
+
|
533
|
+
def print(option)
|
534
|
+
"a message"
|
535
|
+
end
|
536
|
+
|
537
|
+
def inspect
|
538
|
+
if @packed
|
539
|
+
return @array.join("")
|
540
|
+
else
|
541
|
+
return @array.join(" ")
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
alias to_s inspect
|
546
|
+
|
547
|
+
end # Message
|
548
|
+
|
549
|
+
end # BitLab
|
550
|
+
|
551
|
+
end # RubyLabs
|
552
|
+
|
553
|
+
|
554
|
+
class Fixnum
|
555
|
+
|
556
|
+
=begin rdoc
|
557
|
+
Add a method to +Fixnum+ to make a +Code+ object showing the binary or
|
558
|
+
hexadecimal representation of a number. Intended mainly to show codes for
|
559
|
+
characters in +String+ objects.
|
560
|
+
Usage:
|
561
|
+
x.code binary, size = log2 bits
|
562
|
+
x.code(n) binary, size = n bits
|
563
|
+
x.code(:hex) hex, size = log2 bits
|
564
|
+
x.code(:hex,n) hex, size = 4*n bits
|
565
|
+
=end
|
566
|
+
|
567
|
+
def code(*args)
|
568
|
+
if args.first == :hex
|
569
|
+
base = args.shift
|
570
|
+
else
|
571
|
+
base = :binary
|
572
|
+
end
|
573
|
+
if args.first.kind_of? Integer
|
574
|
+
digits = args.shift
|
575
|
+
elsif args.empty?
|
576
|
+
b = (self == 0) ? 1 : log2(self+1).ceil
|
577
|
+
digits = (base == :hex) ? (b/4.0).ceil : b
|
578
|
+
else
|
579
|
+
raise "code: can't understand #{args}"
|
580
|
+
end
|
581
|
+
bits = (base == :hex) ? digits * 4 : digits
|
582
|
+
return Code.new(self, bits, base)
|
583
|
+
end
|
584
|
+
|
585
|
+
end
|
586
|
+
|
587
|
+
=begin rdoc
|
588
|
+
Priority queue class -- simple wrapper for an array that can only be updated via
|
589
|
+
+<<+ and +shift+ operations. Also responds to +length+, +first+, and +last+,
|
590
|
+
and allows direct access to an item through an index expression, but does not allow
|
591
|
+
assignment via an index or any other array operation.
|
592
|
+
The +<<+ method checks to make sure an object is comparable (responds to <) before
|
593
|
+
adding it to the queue.
|
594
|
+
=end
|
595
|
+
|
596
|
+
|
597
|
+
# Note: defined outside rubylabs, so it's a top level class -- move to own file, require here?
|
598
|
+
|
599
|
+
class PriorityQueue
|
600
|
+
|
601
|
+
def initialize
|
602
|
+
@q = Array.new
|
603
|
+
end
|
604
|
+
|
605
|
+
def <<(obj)
|
606
|
+
raise "Object cannot be inserted into priority queue" unless obj.respond_to?(:<)
|
607
|
+
i = 0
|
608
|
+
while (i < @q.length)
|
609
|
+
break if obj < @q[i]
|
610
|
+
i += 1
|
611
|
+
end
|
612
|
+
@q.insert(i, obj)
|
613
|
+
end
|
614
|
+
|
615
|
+
%w{shift length first last to_s inspect}.each do |name|
|
616
|
+
eval "def #{name}() @q.#{name} end"
|
617
|
+
end
|
618
|
+
|
619
|
+
def [](i)
|
620
|
+
@q[i]
|
621
|
+
end
|
622
|
+
|
623
|
+
end # PriorityQueue
|
624
|
+
|