rubylabs 0.9.0 → 0.9.1
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/README.rdoc +15 -6
- data/Rakefile +3 -0
- data/VERSION +1 -1
- data/lib/bitlab.rb +593 -328
- data/lib/demos.rb +20 -9
- data/lib/elizalab.rb +660 -507
- data/lib/hashlab.rb +289 -192
- data/lib/introlab.rb +33 -38
- data/lib/iterationlab.rb +117 -61
- data/lib/marslab.rb +608 -475
- data/lib/randomlab.rb +227 -121
- data/lib/recursionlab.rb +197 -140
- data/lib/rubylabs.rb +936 -390
- data/lib/sievelab.rb +32 -24
- data/lib/spherelab.rb +308 -220
- data/lib/tsplab.rb +634 -312
- data/test/bit_test.rb +4 -4
- data/test/tsp_test.rb +18 -0
- metadata +2 -2
data/lib/bitlab.rb
CHANGED
@@ -1,23 +1,60 @@
|
|
1
|
+
module RubyLabs
|
2
|
+
|
1
3
|
=begin rdoc
|
2
4
|
|
3
5
|
== BitLab
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
+
The BitLab module has definitions of classes and methods used in the projects for Chapter 7
|
8
|
+
of <em>Explorations in Computing</em>. The module has methods used to experiment
|
9
|
+
with binary codes, including fixed-width codes and Huffman codes. Methods create
|
10
|
+
messages by encoding strings, decode messages back to the original text, and run experiments
|
11
|
+
that introduce errors into messages and test for errors with simple parity checks.
|
12
|
+
|
13
|
+
The file bitlab.rb also adds a method to the PriorityQueue class, which is defined in
|
14
|
+
the top level RubyLabs module. The methods that insert and remove items have a "hook" that can be called called when
|
15
|
+
when the queue is being displayed on the canvas.
|
16
|
+
The +update+ method defined here will move nodes of a Huffman tree around on the screen when
|
17
|
+
they are removed from or added to the queue.
|
7
18
|
=end
|
8
19
|
|
9
|
-
module RubyLabs
|
10
|
-
|
11
20
|
module BitLab
|
12
21
|
|
13
22
|
QueueView = Struct.new(:queue, :options)
|
14
23
|
NodeView = Struct.new(:circle, :text, :ftext, :lseg, :rseg)
|
15
24
|
NodeCoords = Struct.new(:x, :y, :leftedge, :leftdepth, :rightedge, :rightdepth)
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
# def test_setup
|
27
|
+
# vf = read_frequencies(:hvfreq)
|
28
|
+
# pq = init_queue(vf)
|
29
|
+
# view_queue(pq)
|
30
|
+
# return pq
|
31
|
+
# end
|
32
|
+
|
33
|
+
# Options for drawing trees on the canvas:
|
34
|
+
|
35
|
+
@@unit = 24 # pixels per "tree unit"
|
36
|
+
|
37
|
+
@@queueViewOptions = {
|
38
|
+
:width => 42 * @@unit,
|
39
|
+
:height => 15 * @@unit,
|
40
|
+
:qy => 50,
|
41
|
+
:qx => 50,
|
42
|
+
}
|
43
|
+
|
44
|
+
# Data directory for BitLab:
|
45
|
+
|
46
|
+
@@bitsDirectory = File.join(File.dirname(__FILE__), '..', 'data', 'huffman')
|
47
|
+
|
48
|
+
# Make a unique binary code for each item in array +a+, returning a Hash that
|
49
|
+
# associates each item with a unique Code object. The codes are fixed-size
|
50
|
+
# binary codes, with a number of bits that depends on the number of items in +a+.
|
51
|
+
#
|
52
|
+
# Example -- make a 2-bit encoding for each of the 4 objects in this array:
|
53
|
+
#
|
54
|
+
# >> nt_code = make_codes( ["a", "t", "c", "g"] )
|
55
|
+
# => {"a"=>00, "c"=>10, "g"=>11, "t"=>01}
|
56
|
+
# >> nt_code["c"]
|
57
|
+
# => 10
|
21
58
|
|
22
59
|
def make_codes(a)
|
23
60
|
n = log2(a.length).ceil
|
@@ -28,20 +65,37 @@ module BitLab
|
|
28
65
|
return res
|
29
66
|
end
|
30
67
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
output, either
|
35
|
-
|
36
|
-
|
68
|
+
# Print the codes in +a+, which should be an associative array made by +make_codes+ or
|
69
|
+
# +assign_codes+, the method that creates binary codes for a Huffman tree.
|
70
|
+
# An option specifies how to order the
|
71
|
+
# output, either <tt>:by_code</tt> (the default) or <tt>:by_name</tt>.
|
72
|
+
#
|
73
|
+
# Example:
|
74
|
+
# >> nt_code
|
75
|
+
# => {"a"=>00, "c"=>10, "g"=>11, "t"=>01}
|
76
|
+
#
|
77
|
+
# >> print_codes(nt_code)
|
78
|
+
# 00 a
|
79
|
+
# 01 t
|
80
|
+
# 10 c
|
81
|
+
# 11 g
|
82
|
+
# => true
|
83
|
+
#
|
84
|
+
# >> print_codes(nt_code, :by_name)
|
85
|
+
# a 00
|
86
|
+
# c 10
|
87
|
+
# g 11
|
88
|
+
# t 01
|
89
|
+
# => true
|
90
|
+
|
37
91
|
def print_codes(a, mode = :by_code)
|
38
92
|
if mode == :by_code
|
39
93
|
a.sort { |x,y| x[1] <=> y[1] }.each do |sym, code|
|
40
94
|
printf "%s %s\n", code, sym
|
41
95
|
end
|
42
96
|
elsif mode == :by_name
|
43
|
-
width = a.keys.map{ |x| x.length }.max
|
44
|
-
a.keys.sort.each do |x|
|
97
|
+
width = a.keys.map{ |x| x.to_s.length }.max
|
98
|
+
a.keys.sort { |x,y| x.to_s <=> y.to_s }.each do |x|
|
45
99
|
printf "%-#{width}s %s\n", x, a[x]
|
46
100
|
end
|
47
101
|
else
|
@@ -50,15 +104,21 @@ module BitLab
|
|
50
104
|
return true
|
51
105
|
end
|
52
106
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
107
|
+
# Make a Message object for the characters in string +s+. The second
|
108
|
+
# parameter determines the code to use. It can be <tt>:ascii</tt>, <tt>:parity</tt>, a hash
|
109
|
+
# object that has code mappings for letters, or a Huffman tree. If a hash
|
110
|
+
# or tree is passed, the message type is set to <tt>:packed</tt>, otherwise it's <tt>:unpacked</tt>.
|
111
|
+
# The encoding type is saved so it can be used later in decoding.
|
112
|
+
#
|
113
|
+
# Example:
|
114
|
+
# >> encode("atg", :ascii)
|
115
|
+
# => 01100001 01110100 01100111
|
116
|
+
#
|
117
|
+
# >> nt_code
|
118
|
+
# => {"a"=>00, "c"=>10, "g"=>11, "t"=>01}
|
119
|
+
# >> encode("atg", nt_code)
|
120
|
+
# => 000111
|
121
|
+
|
62
122
|
def encode(s, type, opt = nil)
|
63
123
|
if (type.class == Hash || type.class == Node)
|
64
124
|
code = (type.class == Node) ? assign_codes(type) : type
|
@@ -76,33 +136,41 @@ module BitLab
|
|
76
136
|
printf("%s: %s\n", ch.chr, code) if opt == :trace
|
77
137
|
end
|
78
138
|
end
|
79
|
-
msg.encoding = type
|
80
139
|
return msg
|
81
140
|
end
|
82
141
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
Note:
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
142
|
+
# Decode a sequence of bits (represented by Message object +m+) using the
|
143
|
+
# specified decoding scheme.
|
144
|
+
#
|
145
|
+
# Note: if the decoding scheme is not the same as the one used to create
|
146
|
+
# the message the results are unpredictable.
|
147
|
+
#
|
148
|
+
# See also Message#decode, which generates a string using the coding scheme
|
149
|
+
# specified when the message was created. Message#decode always gives the
|
150
|
+
# right result -- the point of this stand-alone decode method
|
151
|
+
# is to show what happens if the decoding scheme does not match the
|
152
|
+
# encoding scheme.
|
153
|
+
#
|
154
|
+
# Example:
|
155
|
+
# >> msg = encode("aloha", :ascii)
|
156
|
+
# => 01100001 01101100 01101111 01101000 01100001
|
157
|
+
# >> decode(msg, :ascii)
|
158
|
+
# => "aloha"
|
159
|
+
# >> decode(msg, :parity)
|
160
|
+
# => "?67??"
|
161
|
+
|
162
|
+
def decode(m, scheme)
|
95
163
|
raise "not a message" unless m.class == Message
|
96
164
|
res = ""
|
97
|
-
if
|
98
|
-
res = huffman_decode(m,
|
99
|
-
elsif
|
165
|
+
if scheme.class == Node
|
166
|
+
res = huffman_decode(m, scheme)
|
167
|
+
elsif scheme.class == Hash
|
100
168
|
raise "packed decode not implemented"
|
101
|
-
elsif
|
169
|
+
elsif scheme == :ascii
|
102
170
|
m.array.each do |x|
|
103
171
|
res << x.value.chr
|
104
172
|
end
|
105
|
-
elsif
|
173
|
+
elsif scheme == :parity
|
106
174
|
m.array.each do |x|
|
107
175
|
if x.even_parity?
|
108
176
|
res << (x.value >> 1).chr
|
@@ -113,16 +181,23 @@ module BitLab
|
|
113
181
|
end
|
114
182
|
end
|
115
183
|
else
|
116
|
-
raise "unknown
|
184
|
+
raise "unknown scheme: #{scheme}"
|
117
185
|
end
|
118
186
|
return res
|
119
187
|
end
|
120
188
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
189
|
+
# Simulate noisy transmission of a message represented by a Message object +m+.
|
190
|
+
# Returns a copy of +m+ after making +n+ calls to the +flip+ method (which changes
|
191
|
+
# a random bit in the message).
|
192
|
+
#
|
193
|
+
# Example:
|
194
|
+
# >> msg = encode("hola", :ascii)
|
195
|
+
# => 01101000 01101111 01101100 01100001
|
196
|
+
# >> recvd = garbled(msg, 3)
|
197
|
+
# => 01001000 01000111 01101100 01100001
|
198
|
+
# >> decode(recvd, :ascii)
|
199
|
+
# => "HGla"
|
200
|
+
|
126
201
|
def garbled(m, n)
|
127
202
|
res = m.copy
|
128
203
|
n.times do
|
@@ -132,16 +207,20 @@ module BitLab
|
|
132
207
|
return res
|
133
208
|
end
|
134
209
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
210
|
+
# Read letters and frequencies from file +fn+, save them in a hash indexed by letter name.
|
211
|
+
# The hash can be passed to build_tree, the method that creates a Huffman tree from
|
212
|
+
# a set of letter frequencies. If +fn+ is a symbol it should be the name of one of the
|
213
|
+
# letter frequency files included with RubyLabs; if it's a string is should be the name
|
214
|
+
# of a file in the current working directory.
|
215
|
+
#
|
216
|
+
# Example:
|
217
|
+
# >> read_frequencies( :hvfreq )
|
218
|
+
# => {"A"=>0.45, "O"=>0.18, "E"=>0.12, "I"=>0.15, "U"=>0.1}
|
140
219
|
|
141
220
|
def read_frequencies(fn)
|
142
221
|
a = Hash.new
|
143
|
-
|
144
|
-
|
222
|
+
if fn.class == Symbol
|
223
|
+
fn = File.join(@@bitsDirectory, fn.to_s + ".txt")
|
145
224
|
end
|
146
225
|
File.open(fn).each do |line|
|
147
226
|
line.chomp!
|
@@ -154,11 +233,18 @@ module BitLab
|
|
154
233
|
return a
|
155
234
|
end
|
156
235
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
# :
|
236
|
+
# Build a Huffane tree using frequencies in hash +f+ (typically created
|
237
|
+
# by calling read_frequencies). The return value is a Node object that
|
238
|
+
# represents the root of the tree.
|
239
|
+
#
|
240
|
+
# Example:
|
241
|
+
# >> f = read_frequencies( :hvfreq )
|
242
|
+
# => {"A"=>0.45, "O"=>0.18, "E"=>0.12, "I"=>0.15, "U"=>0.1}
|
243
|
+
# >> build_tree(f)
|
244
|
+
# => ( 1.000 ( A: 0.450 ) ( ... ) )
|
245
|
+
#
|
246
|
+
#--
|
247
|
+
# :begin :build_tree
|
162
248
|
def build_tree(f)
|
163
249
|
pq = init_queue(f)
|
164
250
|
|
@@ -170,18 +256,45 @@ module BitLab
|
|
170
256
|
|
171
257
|
return pq[0]
|
172
258
|
end
|
173
|
-
# :end :build_tree
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
259
|
+
# :end :build_tree
|
260
|
+
|
261
|
+
# Helper method for build_tree: initialize a priority queue with Node
|
262
|
+
# objects for each letter in hash +a+, returning the queue as the result
|
263
|
+
# of the call.
|
264
|
+
#
|
265
|
+
# Example:
|
266
|
+
# >> f
|
267
|
+
# => {"A"=>0.45, "O"=>0.18, "E"=>0.12, "I"=>0.15, "U"=>0.1}
|
268
|
+
# >> init_queue(f)
|
269
|
+
# => [( U: 0.100 ), ( E: 0.120 ), ( I: 0.150 ), ( O: 0.180 ), ( A: 0.450 )]
|
270
|
+
#--
|
271
|
+
# :begin :init_queue
|
272
|
+
def init_queue(a)
|
273
|
+
q = PriorityQueue.new
|
274
|
+
a.each do |x,f|
|
275
|
+
q << Node.new(x,f)
|
276
|
+
end
|
277
|
+
return q
|
278
|
+
end
|
279
|
+
# :end :init_queue
|
280
|
+
|
281
|
+
# Traverse a Huffman tree to make a Code object for each
|
282
|
+
# leaf node, returning the codes in a Hash object. When users call this
|
283
|
+
# method, they should pass only one argument (+tree+). On recursive calls
|
284
|
+
# the other two arguments will be the set of codes defined so far and the
|
285
|
+
# binary prefix for the path to the current node.
|
286
|
+
#
|
287
|
+
# Users normally pass a tree to +encode+, which will call this method to make the code.
|
288
|
+
# The same tree should also be passed to +decode+ to decode the message.
|
289
|
+
#
|
290
|
+
# Example:
|
291
|
+
# >> t = build_tree(f)
|
292
|
+
# => ( 1.000 ( A: 0.450 ) ( ... ) )
|
293
|
+
# >> assign_codes(t)
|
294
|
+
# => {"A"=>0, "O"=>111, "E"=>101, "I"=>110, "U"=>100}
|
295
|
+
#
|
296
|
+
#--
|
297
|
+
# :begin :assign_codes
|
185
298
|
def assign_codes(tree, code = {}, prefix = Code.new(0,0))
|
186
299
|
if tree.char != nil
|
187
300
|
code[tree.char] = prefix
|
@@ -191,33 +304,15 @@ module BitLab
|
|
191
304
|
end
|
192
305
|
return code
|
193
306
|
end
|
194
|
-
# :end :assign_codes
|
195
|
-
|
196
|
-
=begin rdoc
|
197
|
-
Huffman tree helper procedure: initialize a priority queue with Node
|
198
|
-
objects for each letter in Hash +a+.
|
199
|
-
=end
|
200
|
-
|
201
|
-
# :begin :init_queue
|
202
|
-
def init_queue(a)
|
203
|
-
q = PriorityQueue.new
|
204
|
-
a.each do |x,f|
|
205
|
-
q << Node.new(x,f)
|
206
|
-
end
|
207
|
-
return q
|
208
|
-
end
|
209
|
-
# :end :init_queue
|
307
|
+
# :end :assign_codes
|
210
308
|
|
211
|
-
|
212
|
-
|
213
|
-
Not intended to be called by students; they will use the top level +decode+ method
|
214
|
-
or the +decode+ method defined in the Message class.
|
215
|
-
=end
|
309
|
+
# Helper method used by decode and Message#decode: decode the binary codes in message +m+,
|
310
|
+
# using +tree+ as a guide.
|
216
311
|
|
217
312
|
def huffman_decode(m, tree)
|
218
313
|
res = ""
|
219
314
|
path = tree
|
220
|
-
m.
|
315
|
+
m.each_bit do |bit|
|
221
316
|
if path.leaf?
|
222
317
|
res << path.char
|
223
318
|
path = tree
|
@@ -228,10 +323,17 @@ module BitLab
|
|
228
323
|
return res
|
229
324
|
end
|
230
325
|
|
231
|
-
|
232
|
-
|
233
|
-
can
|
234
|
-
|
326
|
+
# The data directory BitLabs has a file named "testcodes.txt" that contains a set
|
327
|
+
# of binary encodings of Hawaiian words. Call this method to read the encodings and return
|
328
|
+
# and an array of Message objects, one for each encoding. Users can try decoding the messages
|
329
|
+
# by hand before verifying their answer by passing them to decode. The messages are ordered
|
330
|
+
# from shortest to longest.
|
331
|
+
#
|
332
|
+
# Example (assuming +t+ is the Huffman tree for the full Hawaiian alphabet):
|
333
|
+
# >> msgs = read_codes
|
334
|
+
# => [011010, ... 000101101100001100001011011000011011100110001011011100110001011010110011011010011110]
|
335
|
+
# >> decode(msgs[-1], t)
|
336
|
+
# => "HUMUHUMUNUKUNUKUAPUA'A"
|
235
337
|
|
236
338
|
def read_codes
|
237
339
|
codes = Array.new
|
@@ -249,65 +351,119 @@ module BitLab
|
|
249
351
|
return codes
|
250
352
|
end
|
251
353
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
354
|
+
# The data directory for BitLabs has a file named "testwords.txt" with a few Hawaiian words.
|
355
|
+
# Call this method to read the file and return a list of strings for experiments with encoding
|
356
|
+
# Hawaiian words with a Huffman code.
|
357
|
+
|
257
358
|
def read_words
|
258
359
|
fn = File.join(@@bitsDirectory, "testwords.txt")
|
259
360
|
words = File.open(fn).readlines
|
260
361
|
return words.map { |x| x.chomp }
|
261
362
|
end
|
262
363
|
|
263
|
-
|
264
364
|
=begin rdoc
|
265
|
-
|
365
|
+
Initialize the canvas with a drawing of a priority queue.
|
266
366
|
=end
|
267
367
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
368
|
+
# Initialize the RubyLabs Canvas and draw a picture of priority queue +pq+.
|
369
|
+
# Future calls to the queue's <tt><<</tt> and <tt>shift</tt> methods will update the drawing.
|
370
|
+
|
371
|
+
def view_queue(pq, userOptions = {} )
|
372
|
+
options = @@queueViewOptions.merge(userOptions)
|
373
|
+
Canvas.init(options[:width], options[:height], "BitLab")
|
374
|
+
@@drawing = QueueView.new(pq, options)
|
375
|
+
options[:nodefill] = "lightgray"
|
376
|
+
options[:freqfont] = Canvas::Font.new('freqfont', :family => 'Helvetica', :size => 10)
|
377
|
+
pq.on_canvas = true
|
378
|
+
x = options[:qx]
|
379
|
+
pq.each_with_index do |node, i|
|
380
|
+
draw_node(node, x, options[:qy])
|
381
|
+
x += 3 * @@unit
|
280
382
|
end
|
281
|
-
return
|
383
|
+
return true
|
282
384
|
end
|
283
385
|
|
386
|
+
def move_tree(tree, dx, dy) # :nodoc:
|
387
|
+
tree.coords.x += dx
|
388
|
+
tree.coords.y += dy
|
389
|
+
Canvas.move(tree.drawing.circle, dx, dy)
|
390
|
+
Canvas.move(tree.drawing.text, dx, dy)
|
391
|
+
Canvas.move(tree.drawing.ftext, dx, dy) if tree.drawing.ftext
|
392
|
+
if tree.left
|
393
|
+
Canvas.move(tree.drawing.lseg, dx, dy)
|
394
|
+
move_tree(tree.left, dx, dy)
|
395
|
+
end
|
396
|
+
if tree.right
|
397
|
+
Canvas.move(tree.drawing.rseg, dx, dy)
|
398
|
+
move_tree(tree.right, dx, dy)
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def draw_root(node, left, right) # :nodoc:
|
403
|
+
opts = @@drawing.options
|
404
|
+
x = (left.coords.x + right.coords.x) / 2
|
405
|
+
y = left.coords.y - 2 * @@unit
|
406
|
+
draw_node(node, x, y)
|
407
|
+
node.drawing.lseg = Canvas::Line.new(x, y, left.coords.x, left.coords.y)
|
408
|
+
node.drawing.rseg = Canvas::Line.new(x, y, right.coords.x, right.coords.y)
|
409
|
+
node.drawing.lseg.lower
|
410
|
+
node.drawing.rseg.lower
|
411
|
+
# [left, right].each do |desc|
|
412
|
+
# desc.drawing.ftext.delete
|
413
|
+
# desc.drawing.ftext = nil
|
414
|
+
# end
|
415
|
+
end
|
416
|
+
|
417
|
+
def draw_node(node, x, y) # :nodoc:
|
418
|
+
return nil unless @@drawing
|
419
|
+
opts = @@drawing.options
|
420
|
+
d = @@unit
|
421
|
+
circ = Canvas::Circle.new( x, y, d / 2, :fill => opts[:nodefill] )
|
422
|
+
text = Canvas::Text.new( node.char, x, y, :anchor => :center )
|
423
|
+
ftext = Canvas::Text.new( node.freq.to_s, x, y-d, {:font => opts[:freqfont].name, :anchor => :center} )
|
424
|
+
node.drawing = NodeView.new(circ, text, ftext, nil, nil)
|
425
|
+
node.coords = NodeCoords.new(x, y, x, 0, x, 0)
|
426
|
+
end
|
427
|
+
|
428
|
+
|
284
429
|
=begin rdoc
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
430
|
+
|
431
|
+
== Node
|
432
|
+
|
433
|
+
A Node object represents a node of a Huffman tree. All nodes have an
|
434
|
+
attribute named +freq+, the frequency used to
|
435
|
+
determine the node's place in a priority queue as the tree is built.
|
436
|
+
The +char+ attribute is +nil+ for interior nodes, or the character stored at
|
437
|
+
a leaf node.
|
438
|
+
Descendants of a node can be found by calling the +left+ or +right+ accessor methods
|
439
|
+
(which return +nil+ for leaf nodes). The remaining attributes (+drawing+, +coords+, etc)
|
440
|
+
are used by methods that display a tree on the RubyLabs Canvas.
|
441
|
+
|
442
|
+
Use +new+ to create a new leaf node. Call the class method +combine+ (an alternative
|
443
|
+
constructor) to make a new interior node from two existing nodes. If the tree is
|
444
|
+
being displayed on the Canvas, a call to +combine+ will
|
445
|
+
make a graphic for the new interior node directly above its two descandants.
|
446
|
+
|
296
447
|
=end
|
297
448
|
|
298
449
|
class Node
|
299
450
|
|
300
451
|
attr_accessor :freq, :char, :left, :right, :drawing, :coords, :lfchain, :rfchain, :depth
|
301
452
|
|
453
|
+
# Create a new leaf node for +char+ with frequency +freq+.
|
454
|
+
|
302
455
|
def initialize(char,freq)
|
303
456
|
@char = char
|
304
457
|
@freq = freq
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
458
|
+
@left = @right = nil
|
459
|
+
@drawing = @coords = nil
|
460
|
+
@lfchain = @rfchain = self
|
461
|
+
@depth = 0
|
309
462
|
end
|
310
463
|
|
464
|
+
# Create a new interior node with descendants +leftdesc+ and +rightdesc+. If the
|
465
|
+
# tree is being displayed, draw the new node above and between its two descendants.
|
466
|
+
#--
|
311
467
|
# todo -- need to follow chains to end when updating shallower tree
|
312
468
|
|
313
469
|
def Node.combine(leftdesc, rightdesc)
|
@@ -326,78 +482,116 @@ module BitLab
|
|
326
482
|
return node
|
327
483
|
end
|
328
484
|
|
485
|
+
# Compare this node and node +x+ according to their frequency values
|
486
|
+
# so they can be ordered in a priority queue.
|
487
|
+
|
329
488
|
def <(x)
|
330
489
|
x.class == Node && @freq < x.freq
|
331
490
|
end
|
491
|
+
|
492
|
+
# Return +true+ if this node is a leaf (i.e. it has no descendants).
|
332
493
|
|
333
494
|
def leaf?
|
334
495
|
return @char != nil
|
335
496
|
end
|
497
|
+
|
498
|
+
# Return a String with a concise representation of this node, including its
|
499
|
+
# character and frequency if it's a leaf, or it's frequency and two descendants
|
500
|
+
# if it's an interior node.
|
336
501
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
502
|
+
def inspect
|
503
|
+
if leaf?
|
504
|
+
sprintf "( %s: %.3f )", @char, @freq
|
505
|
+
else
|
506
|
+
sprintf "( %.3f %s %s )", @freq, @left.to_s, @right.to_s
|
507
|
+
end
|
508
|
+
end
|
344
509
|
|
345
510
|
alias to_s inspect
|
346
511
|
|
347
512
|
end # Node
|
348
513
|
|
349
514
|
=begin rdoc
|
350
|
-
Code objects are variable-length binary numbers representing individual letters or members
|
351
|
-
of a set. The main reason to create a Code object is so to have the value of the integer
|
352
|
-
displayed in binary or hex. To make it easier for advanced students to understand the
|
353
|
-
Huffman tree "assign_codes" method this class defines a method that attaches a bit to the
|
354
|
-
end of a code and returns a new Code object.
|
355
|
-
|
356
|
-
Students will not create Code objects directly -- instead they are created by methods in
|
357
|
-
other classes, e.g. the +code+ method added to Fixnum or the top level encode method.
|
358
|
-
|
359
|
-
There are two methods for attaching bits: << appends a bit to a code (used by the method
|
360
|
-
that makes a parity-encoded message) and +, which returns a copy of a code with the bit
|
361
|
-
added (used by the recursive method that assigns Huffman codes).
|
362
|
-
|
363
|
-
<< can also be used to extend a code by appending a second code.
|
364
515
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
516
|
+
== Code
|
517
|
+
|
518
|
+
Code objects are variable-length binary numbers representing integers, letters, or members
|
519
|
+
of a set.
|
520
|
+
|
521
|
+
In the projects described in the text readers do not create Code objects directly --
|
522
|
+
instead Codes are created by methods in
|
523
|
+
other classes, e.g. the +code+ method added to the Fixnum class or the top level +encode+ method
|
524
|
+
defined in the BitLab module.
|
370
525
|
|
371
|
-
|
526
|
+
See also HexCode, a derived class that has the same operations and attributes but displays
|
527
|
+
values with hexadecimal (base 16) digits.
|
372
528
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
529
|
+
#--
|
530
|
+
Way cool -- this class used to have a maxcodesize (set to 60) to make sure codes fit within
|
531
|
+
a single 64-bit word. But a typo during one experiment created an 80-bit code. Turns out
|
532
|
+
indexing etc work just fine on Bignums:
|
533
|
+
|
534
|
+
>> c1 = s[1].code(80)
|
535
|
+
=> 00000000000000000000000000000000000000000000000000000000000000000000000001101000
|
536
|
+
>> c1.flip(0)
|
537
|
+
=> 10000000000000000000000000000000000000000000000000000000000000000000000001101000
|
538
|
+
=end
|
381
539
|
|
382
540
|
class Code
|
383
|
-
attr_accessor :value, :length
|
541
|
+
attr_accessor :value, :length
|
384
542
|
|
385
|
-
|
386
|
-
|
387
|
-
|
543
|
+
# Create a new code for the number +x+. An optional argument specifies the number
|
544
|
+
# of bits in the code, in which case the code will be padded with leading 0s (if the
|
545
|
+
# value is small enough to fit in that number of bits) or truncated (if the number
|
546
|
+
# is too big to fit in that number of bits).
|
547
|
+
#
|
548
|
+
# Examples:
|
549
|
+
# >> Code.new(26)
|
550
|
+
# => 11010
|
551
|
+
# >> Code.new(26, 8)
|
552
|
+
# => 00011010
|
553
|
+
# >> Code.new(26,4)
|
554
|
+
# => 1010
|
555
|
+
|
556
|
+
def initialize(x, length = log2(x+1).ceil)
|
557
|
+
@value = x % (1 << length)
|
388
558
|
@length = length
|
389
|
-
@base = base
|
390
559
|
@has_parity_bit = false
|
391
560
|
end
|
392
|
-
|
561
|
+
|
562
|
+
# Create a new code by copying this code and extending it by one bit. The extra bit is appended
|
563
|
+
# on the right. The argument on the right side of the <tt>+</tt> operator should be an integer;
|
564
|
+
# the bit appended to the code is the least significant bit of this number.
|
565
|
+
#
|
566
|
+
# Examples:
|
567
|
+
# >> x = Code.new(4)
|
568
|
+
# => 100
|
569
|
+
# >> x + 0
|
570
|
+
# => 1000
|
571
|
+
# >> x + 1
|
572
|
+
# => 1001
|
573
|
+
|
393
574
|
def +(bit)
|
394
|
-
raise "operation not defined for hex codes" unless @base == :binary
|
395
575
|
val = (@value << 1) | bit[0]
|
396
576
|
return Code.new(val, @length+1)
|
397
577
|
end
|
398
578
|
|
579
|
+
# Extend this code by attaching bits in +x+ to the end of this code. The argument
|
580
|
+
# +x+ can either be an integer, in which case one bit, corresponding to the least significant
|
581
|
+
# bit of +x+, is added to the end of the this code, or another Code object, in which case
|
582
|
+
# all the bits in +x+ are added to the end of the this code.
|
583
|
+
#
|
584
|
+
# Examples:
|
585
|
+
# >> x = Code.new(4)
|
586
|
+
# => 100
|
587
|
+
# >> y = Code.new(5)
|
588
|
+
# => 101
|
589
|
+
# >> x << 1
|
590
|
+
# => 1001
|
591
|
+
# >> x << y
|
592
|
+
# => 1001101
|
593
|
+
|
399
594
|
def <<(x)
|
400
|
-
raise "operation not defined for hex codes" unless @base == :binary
|
401
595
|
if x.class == Code
|
402
596
|
val = x.value # val known to fit in x.length bits
|
403
597
|
n = x.length
|
@@ -410,7 +604,25 @@ module BitLab
|
|
410
604
|
@length += n
|
411
605
|
return self
|
412
606
|
end
|
413
|
-
|
607
|
+
|
608
|
+
# Return one or more bits from this code.
|
609
|
+
# If the argument passed to this method is an integer, the method returns a single integer,
|
610
|
+
# either 0 or 1.
|
611
|
+
# If the argument is a Range, the method returns a new Code object with the specified bits
|
612
|
+
# from this code.
|
613
|
+
#
|
614
|
+
# Note: The index operator for Fixnums orders bits from right to left, consistent with standard
|
615
|
+
# usage but the opposite of Strings and Arrays. In this module bits are ordered from
|
616
|
+
# left to right to be consistent with Strings and Arrays.
|
617
|
+
#
|
618
|
+
# Example:
|
619
|
+
# >> x = Code.new(117)
|
620
|
+
# => 1110101
|
621
|
+
# >> x[1]
|
622
|
+
# => 1
|
623
|
+
# >> x[1..3]
|
624
|
+
# => 110
|
625
|
+
|
414
626
|
def [](i)
|
415
627
|
if i.class == Range
|
416
628
|
res = 0
|
@@ -421,7 +633,20 @@ module BitLab
|
|
421
633
|
return @value[@length-i-1]
|
422
634
|
end
|
423
635
|
end
|
424
|
-
|
636
|
+
|
637
|
+
# Return the bit value that would give this code an even parity, i.e. if this code
|
638
|
+
# already has an even number of 1s return 0, if it has an odd number of 1s return 1.
|
639
|
+
#
|
640
|
+
# Example:
|
641
|
+
# >> x = Code.new(17)
|
642
|
+
# => 10001
|
643
|
+
# >> x.parity_bit
|
644
|
+
# => 0
|
645
|
+
# >> x << 1
|
646
|
+
# => 100011
|
647
|
+
# >> x.parity_bit
|
648
|
+
# => 1
|
649
|
+
|
425
650
|
def parity_bit
|
426
651
|
bit = 0
|
427
652
|
for i in 0...@length
|
@@ -430,22 +655,47 @@ module BitLab
|
|
430
655
|
return bit
|
431
656
|
end
|
432
657
|
|
658
|
+
# Extend this code by adding a new bit, chosen so that this code will now have even parity.
|
659
|
+
#
|
660
|
+
# Example:
|
661
|
+
# >> x = Code.new(17)
|
662
|
+
# => 10001
|
663
|
+
# >> x.add_parity_bit
|
664
|
+
# => 100010
|
665
|
+
|
433
666
|
def add_parity_bit
|
434
667
|
@has_parity_bit = true
|
435
668
|
return self << parity_bit
|
436
669
|
end
|
437
670
|
|
671
|
+
# Return +true+ or +false+, depending on whether this code has an even or odd number of 1 bits.
|
672
|
+
|
438
673
|
def even_parity?
|
439
674
|
return parity_bit() == 0
|
440
675
|
end
|
676
|
+
|
677
|
+
# Invert bit +i+ of this code object, where bit 0 is the leftmost bit
|
678
|
+
# (see the note about bit order in the documentation for the [] operator
|
679
|
+
# for Code objects).
|
680
|
+
#
|
681
|
+
# Example:
|
682
|
+
# >> x = Code.new(17)
|
683
|
+
# => 10001
|
684
|
+
# >> x.flip(3)
|
685
|
+
# => 10011
|
441
686
|
|
442
687
|
def flip(i)
|
443
688
|
raise "bit index out of range" unless i < @length
|
444
689
|
@value ^= (1 << (@length - i - 1))
|
445
690
|
return self
|
446
691
|
end
|
692
|
+
|
693
|
+
# Return a one-letter string containing the character encoded by this Code object,
|
694
|
+
# which must have fewer than 8 bits.
|
695
|
+
# Ignores the parity bit if it has been attached.
|
447
696
|
|
448
697
|
def chr
|
698
|
+
raise "code must have fewer than 8 bits" unless @value < 256
|
449
699
|
if @has_parity_bit
|
450
700
|
if even_parity?
|
451
701
|
return (@value >> 1).chr
|
@@ -457,43 +707,94 @@ module BitLab
|
|
457
707
|
end
|
458
708
|
end
|
459
709
|
|
710
|
+
# Compare the numeric values of this object and Code object +x+.
|
711
|
+
|
460
712
|
def <=>(x)
|
461
|
-
return @value <=> x.value
|
713
|
+
return x.class == Code && @value <=> x.value
|
462
714
|
end
|
715
|
+
|
716
|
+
# Return a string with the binary digits of the value represented by this Code object.
|
463
717
|
|
464
718
|
def inspect
|
465
|
-
|
466
|
-
|
467
|
-
|
719
|
+
if @length == 0
|
720
|
+
return ""
|
721
|
+
else
|
468
722
|
return sprintf "%0#{@length}b", @value
|
469
|
-
when :hex
|
470
|
-
return sprintf "%0#{@length/4}X", @value
|
471
723
|
end
|
472
724
|
end
|
473
725
|
|
474
726
|
alias to_s inspect
|
475
727
|
|
476
728
|
end # Code
|
477
|
-
|
729
|
+
|
478
730
|
=begin rdoc
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
731
|
+
|
732
|
+
== HexCode
|
733
|
+
|
734
|
+
HexCodes are Code objects that are printed in hexadecimal. All of the attributes
|
735
|
+
and methods of the Code class are applicable to HexCodes -- the only differences
|
736
|
+
are in <tt>to_s</tt>, which generates the string of digits used to print a number,
|
737
|
+
and in the method that compares objects (Codes can only be compared to other Codes,
|
738
|
+
HexCodes to other HexCodes).
|
739
|
+
|
487
740
|
=end
|
741
|
+
|
742
|
+
class HexCode < Code
|
743
|
+
|
744
|
+
# Create a new HexCode object for the number +x+. The optional second
|
745
|
+
# argument specifies the number of bits to use in the encoding.
|
746
|
+
|
747
|
+
def initialize(x, length = log2(x+1).ceil)
|
748
|
+
super
|
749
|
+
end
|
750
|
+
|
751
|
+
# Compare the numeric values of this object and HexCode object +x+.
|
752
|
+
|
753
|
+
def <=>(x)
|
754
|
+
return x.class == HexCode && @value <=> x.value
|
755
|
+
end
|
756
|
+
|
757
|
+
# Return a string with the hexadecimal digits of the value represented by this Code object.
|
488
758
|
|
489
|
-
|
759
|
+
def inspect
|
760
|
+
if @length == 0
|
761
|
+
return ""
|
762
|
+
else
|
763
|
+
return sprintf "%0#{@length/4}X", @value
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
alias to_s inspect
|
768
|
+
|
769
|
+
end
|
770
|
+
|
771
|
+
=begin rdoc
|
772
|
+
|
773
|
+
== Message
|
774
|
+
|
775
|
+
Message objects are arrays of Code objects.
|
776
|
+
There are two types of messages: packed and unpacked.
|
777
|
+
If the message is unpacked, each code is stored in its own array element.
|
778
|
+
This is the standard form for messages created with the
|
779
|
+
<tt>:ascii</tt> or <tt>:parity</tt> encodings.
|
780
|
+
If the message is packed,
|
781
|
+
codes are repackaged into 8-bit bytes, and individual codes may cross
|
782
|
+
array item boundaries. This form is used by the method that uses a
|
783
|
+
Huffman code to encode a string.
|
784
|
+
#--
|
785
|
+
TODO: allow for the possibility that a code word being added to a packed code can be longer than 8 bits
|
786
|
+
|
787
|
+
=end
|
490
788
|
|
491
789
|
class Message
|
492
790
|
|
493
|
-
attr_accessor :packed, :array
|
791
|
+
attr_accessor :packed, :array
|
494
792
|
|
495
793
|
@@packsize = 8 # number of bits to pack into single code
|
496
794
|
|
795
|
+
# Create a new Message of the specified type (<tt>:packed</tt> or <tt>:unpacked</tt>).
|
796
|
+
# The message is initially empty; new characters are added by with the <tt><<</tt> operator.
|
797
|
+
|
497
798
|
def initialize(type)
|
498
799
|
raise "Message: unknown type" unless [:packed, :unpacked].include? type
|
499
800
|
if type == :packed
|
@@ -505,14 +806,29 @@ module BitLab
|
|
505
806
|
end
|
506
807
|
end
|
507
808
|
|
809
|
+
# Create a new Message object that is a copy of this message.
|
810
|
+
|
508
811
|
def copy
|
509
|
-
dup = self.clone # copies @packed
|
812
|
+
dup = self.clone # copies @packed
|
510
813
|
dup.array = Array.new
|
511
814
|
@array.each { |x| dup.array << x.clone } # deep copy of @array
|
512
815
|
return dup
|
513
816
|
end
|
514
817
|
|
515
|
-
|
818
|
+
# Iterate over each bit in a message, without regard for code boundaries.
|
819
|
+
#
|
820
|
+
# Example:
|
821
|
+
# >> m
|
822
|
+
# => 10011
|
823
|
+
# >> m.each_bit { |b| puts b }
|
824
|
+
# 1
|
825
|
+
# 0
|
826
|
+
# 0
|
827
|
+
# 1
|
828
|
+
# 1
|
829
|
+
# => 10011
|
830
|
+
|
831
|
+
def each_bit
|
516
832
|
@array.each do |byte|
|
517
833
|
for i in 0...byte.length
|
518
834
|
yield(byte[i])
|
@@ -520,6 +836,16 @@ module BitLab
|
|
520
836
|
end
|
521
837
|
return self
|
522
838
|
end
|
839
|
+
|
840
|
+
# Append the bits in Code object +x+ to the end of this message.
|
841
|
+
#
|
842
|
+
# Example:
|
843
|
+
# >> m = Message.new(:packed)
|
844
|
+
# =>
|
845
|
+
# >> m << Code.new(4)
|
846
|
+
# => 100
|
847
|
+
# >> m << Code.new(3)
|
848
|
+
# => 10011
|
523
849
|
|
524
850
|
def <<(x)
|
525
851
|
raise "Message#<<: not a code" unless x.class == Code
|
@@ -538,32 +864,34 @@ module BitLab
|
|
538
864
|
return self
|
539
865
|
end
|
540
866
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
end
|
867
|
+
# Deprecated -- use standalone BitLab#decode instead
|
868
|
+
|
869
|
+
# def decode :nodoc:
|
870
|
+
# res = ""
|
871
|
+
# if @encoding.class == Symbol
|
872
|
+
# @array.each do |code|
|
873
|
+
# res << code.chr
|
874
|
+
# end
|
875
|
+
# elsif @encoding.class == Node
|
876
|
+
# res = huffman_decode(self, @encoding)
|
877
|
+
# else
|
878
|
+
# res = unpack
|
879
|
+
# end
|
880
|
+
# return res
|
881
|
+
# end
|
554
882
|
|
555
|
-
def unpack
|
556
|
-
|
557
|
-
end
|
883
|
+
# def unpack
|
884
|
+
# "unpack: not implemented"
|
885
|
+
# end
|
558
886
|
|
887
|
+
# Return the length (in bits) of this message.
|
888
|
+
|
559
889
|
def length
|
560
890
|
@array.inject(0) { |sum, code| sum += code.length }
|
561
891
|
end
|
562
892
|
|
563
|
-
|
564
|
-
|
565
|
-
end
|
566
|
-
|
893
|
+
# Create a string of binary digits representing the Codes in this message.
|
894
|
+
|
567
895
|
def inspect
|
568
896
|
if @packed
|
569
897
|
return @array.join("")
|
@@ -575,106 +903,21 @@ module BitLab
|
|
575
903
|
alias to_s inspect
|
576
904
|
|
577
905
|
end # Message
|
578
|
-
|
579
|
-
def move_tree(tree, dx, dy)
|
580
|
-
tree.coords.x += dx
|
581
|
-
tree.coords.y += dy
|
582
|
-
Canvas.move(tree.drawing.circle, dx, dy)
|
583
|
-
Canvas.move(tree.drawing.text, dx, dy)
|
584
|
-
Canvas.move(tree.drawing.ftext, dx, dy) if tree.drawing.ftext
|
585
|
-
if tree.left
|
586
|
-
Canvas.move(tree.drawing.lseg, dx, dy)
|
587
|
-
move_tree(tree.left, dx, dy)
|
588
|
-
end
|
589
|
-
if tree.right
|
590
|
-
Canvas.move(tree.drawing.rseg, dx, dy)
|
591
|
-
move_tree(tree.right, dx, dy)
|
592
|
-
end
|
593
|
-
end
|
594
|
-
|
595
|
-
def draw_root(node, left, right)
|
596
|
-
opts = @@drawing.options
|
597
|
-
x = (left.coords.x + right.coords.x) / 2
|
598
|
-
y = left.coords.y - 2 * @@unit
|
599
|
-
draw_node(node, x, y)
|
600
|
-
node.drawing.lseg = Canvas::Line.new(x, y, left.coords.x, left.coords.y)
|
601
|
-
node.drawing.rseg = Canvas::Line.new(x, y, right.coords.x, right.coords.y)
|
602
|
-
node.drawing.lseg.lower
|
603
|
-
node.drawing.rseg.lower
|
604
|
-
# [left, right].each do |desc|
|
605
|
-
# desc.drawing.ftext.delete
|
606
|
-
# desc.drawing.ftext = nil
|
607
|
-
# end
|
608
|
-
end
|
609
|
-
|
610
|
-
def draw_node(node, x, y)
|
611
|
-
return nil unless @@drawing
|
612
|
-
opts = @@drawing.options
|
613
|
-
d = @@unit
|
614
|
-
circ = Canvas::Circle.new( x, y, d / 2, :fill => opts[:nodefill] )
|
615
|
-
text = Canvas::Text.new( node.char, x, y, :anchor => :center )
|
616
|
-
ftext = Canvas::Text.new( node.freq.to_s, x, y-d, {:font => opts[:freqfont].name, :anchor => :center} )
|
617
|
-
node.drawing = NodeView.new(circ, text, ftext, nil, nil)
|
618
|
-
node.coords = NodeCoords.new(x, y, x, 0, x, 0)
|
619
|
-
end
|
620
|
-
|
621
|
-
=begin rdoc
|
622
|
-
Initialize the canvas with a drawing of a priority queue.
|
623
|
-
=end
|
624
|
-
|
625
|
-
def view_queue(pq, userOptions = {} )
|
626
|
-
options = @@queueViewOptions.merge(userOptions)
|
627
|
-
Canvas.init(options[:width], options[:height], "BitLab")
|
628
|
-
@@drawing = QueueView.new(pq, options)
|
629
|
-
options[:nodefill] = "lightgray"
|
630
|
-
options[:freqfont] = Canvas::Font.new('freqfont', :family => 'Helvetica', :size => 10)
|
631
|
-
pq.on_canvas = true
|
632
|
-
x = options[:qx]
|
633
|
-
pq.each_with_index do |node, i|
|
634
|
-
draw_node(node, x, options[:qy])
|
635
|
-
x += 3 * @@unit
|
636
|
-
end
|
637
|
-
return true
|
638
|
-
end
|
639
|
-
|
640
|
-
# def test_setup
|
641
|
-
# vf = read_frequencies(:hvfreq)
|
642
|
-
# pq = init_queue(vf)
|
643
|
-
# view_queue(pq)
|
644
|
-
# return pq
|
645
|
-
# end
|
646
|
-
|
647
|
-
@@unit = 24 # pixels per "tree unit"
|
648
|
-
|
649
|
-
@@queueViewOptions = {
|
650
|
-
:width => 42 * @@unit,
|
651
|
-
:height => 15 * @@unit,
|
652
|
-
:qy => 50,
|
653
|
-
:qx => 50,
|
654
|
-
}
|
655
|
-
|
656
|
-
@@bitsDirectory = File.join(File.dirname(__FILE__), '..', 'data', 'huffman')
|
657
906
|
|
658
907
|
end # BitLab
|
659
908
|
|
660
|
-
end # RubyLabs
|
661
|
-
|
662
|
-
=begin rdoc
|
663
|
-
Add an update method to the PriorityQueue so nodes are moved around on the screen when
|
664
|
-
they are removed from or added to the queue (if the queue is on the canvas).
|
665
|
-
Note: the << and shift methods call this method when @on_canvas is true....
|
666
|
-
=end
|
667
|
-
|
668
|
-
=begin
|
669
|
-
TODO make time to sleep a parameter
|
670
|
-
todo add comments, rdoc
|
671
|
-
todo distance in units (down, up, spacing) should also be params
|
672
|
-
todo clean up def of Node -- coords not being used, combine with graphics
|
673
|
-
=end
|
674
909
|
class PriorityQueue
|
675
910
|
|
676
911
|
attr_accessor :on_canvas
|
677
912
|
|
913
|
+
# Update the drawing of the priority queue after operation +op+ has been performed
|
914
|
+
# on +node+. Operation is either <tt>:shift</tt> or <tt><<</tt>, and +node+ is a
|
915
|
+
# reference to the Node object being removed or inserted into the queue.
|
916
|
+
#
|
917
|
+
# Calls helper methods <tt>left_edge</tt>, <tt>right_edge</tt>, and <tt>tree_sep</tt>
|
918
|
+
# to figure out how to place subtrees to use the minimum amount of horizontal space
|
919
|
+
# (see bitlab.rb).
|
920
|
+
|
678
921
|
def update(op, node)
|
679
922
|
if op == :shift
|
680
923
|
move_tree(node, 0, 4 * @@unit) # move subtree rooted at node down 4 units
|
@@ -702,7 +945,7 @@ class PriorityQueue
|
|
702
945
|
end
|
703
946
|
end
|
704
947
|
|
705
|
-
def left_edge(tree)
|
948
|
+
def left_edge(tree) # :nodoc:
|
706
949
|
x = tree.coords.x
|
707
950
|
while tree.lfchain != tree
|
708
951
|
tree = tree.lfchain
|
@@ -711,7 +954,7 @@ class PriorityQueue
|
|
711
954
|
return x
|
712
955
|
end
|
713
956
|
|
714
|
-
def right_edge(tree)
|
957
|
+
def right_edge(tree) # :nodoc:
|
715
958
|
x = tree.coords.x
|
716
959
|
while tree.rfchain != tree
|
717
960
|
tree = tree.rfchain
|
@@ -720,7 +963,7 @@ class PriorityQueue
|
|
720
963
|
return x
|
721
964
|
end
|
722
965
|
|
723
|
-
def tree_sep(left, right)
|
966
|
+
def tree_sep(left, right) # :nodoc:
|
724
967
|
res = right.coords.x - left.coords.x
|
725
968
|
while (left.rfchain != left && right.lfchain != right)
|
726
969
|
left = left.rfchain
|
@@ -742,19 +985,39 @@ class PriorityQueue
|
|
742
985
|
|
743
986
|
end
|
744
987
|
|
988
|
+
end # RubyLabs
|
989
|
+
|
745
990
|
class Fixnum
|
746
991
|
|
747
992
|
=begin rdoc
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
x.code(:hex) hex, size = log2 bits
|
755
|
-
x.code(:hex,n) hex, size = 4*n bits
|
993
|
+
|
994
|
+
== Fixnum
|
995
|
+
|
996
|
+
Add a method to +Fixnum+ to make a +Code+ object showing the binary or
|
997
|
+
hexadecimal representation of a number. Intended mainly to show codes for
|
998
|
+
characters in +String+ objects.
|
756
999
|
=end
|
757
|
-
|
1000
|
+
|
1001
|
+
# Create a Code object showing the binary or hexadecimal representation of
|
1002
|
+
# this number. The two arguments, both optional, define the type of
|
1003
|
+
# representation and the number of digits:
|
1004
|
+
# x.code make a binary code for x, using as many bits as necessary
|
1005
|
+
# x.code(n) make a binary code for x, using n bits
|
1006
|
+
# x.code(:hex) make a hex code for x, using as many digits as necessary
|
1007
|
+
# x.code(:hex,n) make a hex code for x, using n digits (i.e. 4*n bits)
|
1008
|
+
#
|
1009
|
+
# Example:
|
1010
|
+
# >> x = 26
|
1011
|
+
# => 26
|
1012
|
+
# >> x.code
|
1013
|
+
# => 11010
|
1014
|
+
# >> x.code(8)
|
1015
|
+
# => 00011010
|
1016
|
+
# >> x.code(:hex)
|
1017
|
+
# => 1A
|
1018
|
+
# >> x.code(:hex, 3)
|
1019
|
+
# => 01A
|
1020
|
+
|
758
1021
|
def code(*args)
|
759
1022
|
if args.first == :hex
|
760
1023
|
base = args.shift
|
@@ -769,11 +1032,13 @@ class Fixnum
|
|
769
1032
|
else
|
770
1033
|
raise "code: can't understand #{args}"
|
771
1034
|
end
|
772
|
-
|
773
|
-
|
1035
|
+
if base == :hex
|
1036
|
+
return HexCode.new(self, 4*digits)
|
1037
|
+
else
|
1038
|
+
return Code.new(self, digits)
|
1039
|
+
end
|
1040
|
+
# bits = (base == :hex) ? digits * 4 : digits
|
1041
|
+
# return Code.new(self, bits, base)
|
774
1042
|
end
|
775
1043
|
|
776
1044
|
end
|
777
|
-
|
778
|
-
|
779
|
-
|