rubylabs 0.9.6 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/iterationlab.rb +21 -47
- data/lib/recursionlab.rb +120 -10
- data/lib/rubylabs.rb +70 -3
- metadata +4 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.7
|
data/lib/iterationlab.rb
CHANGED
@@ -118,20 +118,9 @@ module IterationLab
|
|
118
118
|
# and reinsert it at a location between 0 and +i+ (i.e. move the item
|
119
119
|
# to the left in the array).
|
120
120
|
#--
|
121
|
-
# updated 10/9/2012 for visualization
|
122
121
|
# :begin :move_left
|
123
122
|
def move_left(a, i)
|
124
|
-
|
125
|
-
# j = i-1 # start scanning from the left of i
|
126
|
-
# while j >= 0 && less(x, a[j])
|
127
|
-
# j = j-1 # move left
|
128
|
-
# end
|
129
|
-
# a.insert(j+1, x) # insert x back into a at location j
|
130
|
-
if @@drawing
|
131
|
-
touch(i)
|
132
|
-
set_region(i+1)
|
133
|
-
sleep(@@drawing.options[:delay])
|
134
|
-
end
|
123
|
+
init_step(a, i) if @@drawing
|
135
124
|
while i > 0 && less(a[i], a[i-1])
|
136
125
|
swap(a, i-1, i)
|
137
126
|
i -= 1
|
@@ -140,18 +129,14 @@ module IterationLab
|
|
140
129
|
# :end :move_left
|
141
130
|
|
142
131
|
# Helper method called by +move_left+ to exchange the items at specified
|
143
|
-
# locations in an array.
|
132
|
+
# locations in an array. If there is a visualization on the screen call
|
133
|
+
# the helper method that swaps the locations of bars for a[i] and a[j].
|
144
134
|
#--
|
145
135
|
# :begin :swap
|
146
136
|
def swap(a, i, j)
|
147
137
|
a[i], a[j] = a[j], a[i]
|
148
|
-
if @@drawing
|
149
|
-
|
150
|
-
ri = @@drawing.rects[i]
|
151
|
-
rj = @@drawing.rects[j]
|
152
|
-
Canvas.move(ri, dist, 0)
|
153
|
-
Canvas.move(rj, -dist, 0)
|
154
|
-
@@drawing.rects[i], @@drawing.rects[j] = @@drawing.rects[j], @@drawing.rects[i]
|
138
|
+
if @@drawing
|
139
|
+
a.swap_bars(@@drawing.rects, i, j, @@drawing.options)
|
155
140
|
sleep(@@drawing.options[:delay])
|
156
141
|
end
|
157
142
|
end
|
@@ -198,41 +183,30 @@ module IterationLab
|
|
198
183
|
end
|
199
184
|
end
|
200
185
|
|
201
|
-
# Visualization
|
186
|
+
# Visualization for isort. Draw a vertical bar for each item in the array,
|
187
|
+
# setting the height of bar i according to the value of a[i]. Draw a horizontal
|
188
|
+
# progress bar below the array values.
|
202
189
|
|
203
190
|
def view_array(a, userOptions = {})
|
204
191
|
Canvas.init(800, 200, "IterationLab")
|
205
192
|
options = @@viewOptions.merge(userOptions)
|
193
|
+
|
194
|
+
rects = TestArray.draw_bars(a, options)
|
195
|
+
progress = Canvas::Rectangle.new( options[:x0], options[:y1] + 10, options[:x0] + a.length*options[:dx], options[:y1]+15, :fill => options[:bar_fill] )
|
206
196
|
|
207
|
-
|
208
|
-
rects = []
|
209
|
-
a.each_with_index do |val, i|
|
210
|
-
rx = options[:x0] + i * options[:dx]
|
211
|
-
y = Float(val)
|
212
|
-
dy = options[:y1] - options[:y0] - options[:ymin] # height of tallest bar
|
213
|
-
ry = options[:y1] - options[:ymin] - (y/amax)*dy # height of this bar
|
214
|
-
rects << Canvas::Rectangle.new( rx, ry, rx + options[:dx], options[:y1], :fill => options[:array_fill], :outline => options[:canvas_fill] )
|
215
|
-
end
|
216
|
-
bar = Canvas::Rectangle.new( options[:x0], options[:y1] + 10, options[:x0] + a.length*options[:dx], options[:y1]+15, :fill => options[:bar_fill] )
|
217
|
-
|
218
|
-
@@drawing = ArrayView.new(a, rects, bar, ['#000080','#BADFEB'], [], options)
|
197
|
+
@@drawing = ArrayView.new(a, rects, progress, ['#000080','#BADFEB'], [], options)
|
219
198
|
return true
|
220
199
|
end
|
221
200
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
@@drawing.
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
def set_region(loc)
|
233
|
-
x1, y1, x2, y2 = @@drawing.bar.coords
|
234
|
-
x1 = @@drawing.options[:x0] + loc * @@drawing.options[:dx]
|
235
|
-
@@drawing.bar.coords = [x1, y1, x2, y2]
|
201
|
+
# Helper method called at the start of each top level iteration if the
|
202
|
+
# array is on the canvas -- change the color of the bar for a[i] and resize
|
203
|
+
# the progress bar to show a[i] has been moved.
|
204
|
+
|
205
|
+
def init_step(a, i)
|
206
|
+
rect = @@drawing.rects[i]
|
207
|
+
a.touch(rect, @@drawing.history, @@drawing.palette)
|
208
|
+
a.set_region(i+1, a.length, @@drawing.bar, @@drawing.options)
|
209
|
+
sleep(@@drawing.options[:delay])
|
236
210
|
end
|
237
211
|
|
238
212
|
end # IterationLab
|
data/lib/recursionlab.rb
CHANGED
@@ -19,6 +19,24 @@ of the algorithms.
|
|
19
19
|
|
20
20
|
module RecursionLab
|
21
21
|
|
22
|
+
ArrayView = Struct.new(:array, :rects, :bar, :palette, :history, :groupstart, :group, :options)
|
23
|
+
|
24
|
+
@@viewOptions = {
|
25
|
+
:array_fill => 'lightblue',
|
26
|
+
:bar_fill => 'darkblue',
|
27
|
+
:canvas_fill => 'white',
|
28
|
+
:mark_color => 'blue',
|
29
|
+
:x0 => 10, # left edge of leftmost bar
|
30
|
+
:dx => 10, # distance between left edges of adjacent bars
|
31
|
+
:y0 => 50, # top edege of tallest bar
|
32
|
+
:y1 => 150, # bottom edge of array bars
|
33
|
+
:gdy => 150, # distance between array and temp area below
|
34
|
+
:ymin => 3, # minimum height of bar
|
35
|
+
:delay => 0.01,
|
36
|
+
}
|
37
|
+
|
38
|
+
@@drawing = nil
|
39
|
+
|
22
40
|
# The linear search method from iterationlab.rb is replicated here so it can be
|
23
41
|
# used for baseline tests.
|
24
42
|
#--
|
@@ -179,15 +197,19 @@ end
|
|
179
197
|
ix = j = min(i + gs, a.length)
|
180
198
|
jx = min(j + gs, a.length)
|
181
199
|
res = []
|
200
|
+
start_group(a,i,jx) if @@drawing
|
182
201
|
while i < ix || j < jx
|
183
202
|
if j == jx || i < ix && less( a[i], a[j] )
|
184
203
|
res << a[i]
|
204
|
+
move_down(a,i) if @@drawing
|
185
205
|
i += 1
|
186
206
|
else
|
187
207
|
res << a[j]
|
208
|
+
move_down(a,j) if @@drawing
|
188
209
|
j += 1
|
189
210
|
end
|
190
211
|
end
|
212
|
+
move_up(a) if @@drawing
|
191
213
|
return res
|
192
214
|
end
|
193
215
|
# :end :merge
|
@@ -246,8 +268,9 @@ end
|
|
246
268
|
a = a.dup if p == 0 && r == a.length-1 # don't modify the input array (top level only)
|
247
269
|
if p < r
|
248
270
|
q = partition(a, p, r) # q is boundary between small items and large items
|
249
|
-
qsort(a, p, q)
|
271
|
+
qsort(a, p, q-1) # sort small items (range from p to q)
|
250
272
|
qsort(a, q+1, r) # sort large items (range from q+1 to r)
|
273
|
+
mark(a, q, 'lightblue') if @@drawing
|
251
274
|
end
|
252
275
|
return a
|
253
276
|
end
|
@@ -263,20 +286,32 @@ end
|
|
263
286
|
# :begin :partition
|
264
287
|
def partition(a, p, r) # partition the region bounded by p and r
|
265
288
|
x = a[p] # x is the pivot value
|
266
|
-
i = p
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
a
|
273
|
-
|
274
|
-
return j # no more exchanges; return location that separates regions
|
289
|
+
i = p
|
290
|
+
mark(a, i, 'darkgreen') if @@drawing
|
291
|
+
for j in (p+1)..r do
|
292
|
+
touch(a, j) if @@drawing
|
293
|
+
if a[j] <= x
|
294
|
+
i += 1
|
295
|
+
touch(a, i) if @@drawing
|
296
|
+
swap(a, i, j)
|
275
297
|
end
|
276
298
|
end
|
299
|
+
swap(a, p, i)
|
300
|
+
return i
|
277
301
|
end
|
278
302
|
# :end :partition
|
279
303
|
|
304
|
+
# Helper method for partition -- exchange two items in the array, and if the
|
305
|
+
# array is on the canvas, exchange the locations of the two bars
|
306
|
+
|
307
|
+
def swap(a, i, j)
|
308
|
+
a[i], a[j] = a[j], a[i]
|
309
|
+
if @@drawing
|
310
|
+
a.swap_bars(@@drawing.rects, i, j, @@drawing.options)
|
311
|
+
sleep(@@drawing.options[:delay])
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
280
315
|
# Helper procedure used to trace the execution of qsort.
|
281
316
|
def qsort_brackets(a, left, right)
|
282
317
|
tmp = []
|
@@ -288,6 +323,81 @@ end
|
|
288
323
|
return tmp.join(" ")
|
289
324
|
end
|
290
325
|
|
326
|
+
# Visualization for msort and qsort. Draw a vertical bar for each item in the array,
|
327
|
+
# setting the height of bar i according to the value of a[i]. Merge sort uses auxilliary
|
328
|
+
# space for merges, so the window has room below the main array to show the merge steps.
|
329
|
+
|
330
|
+
def view_array(a, userOptions = {})
|
331
|
+
Canvas.init(800, 400, "RecursionLab")
|
332
|
+
options = @@viewOptions.merge(userOptions)
|
333
|
+
|
334
|
+
rects = TestArray.draw_bars(a, options)
|
335
|
+
progress = Canvas::Rectangle.new( options[:x0], options[:y1] + 10, options[:x0] + a.length*options[:dx], options[:y1]+15, :fill => options[:bar_fill] )
|
336
|
+
|
337
|
+
palette = Canvas.palette( [0,0,128], [182,224,234], 4)
|
338
|
+
palette[-1] = '#BADFEB'
|
339
|
+
|
340
|
+
@@drawing = ArrayView.new(a, rects, progress, palette, [], 0, [], options)
|
341
|
+
return true
|
342
|
+
end
|
343
|
+
|
344
|
+
# Visualization method for bsearch -- resize the progress bar so it's below
|
345
|
+
# the search region and color the bar at the midpoint of the region.
|
346
|
+
|
347
|
+
# Note this method is not called by bsearch itself. Instead attach a probe
|
348
|
+
# and run the method via trace.
|
349
|
+
|
350
|
+
def show_bsearch_region(a, lower, upper, mid)
|
351
|
+
return unless @@drawing
|
352
|
+
rect = @@drawing.rects[mid]
|
353
|
+
a.touch(rect, @@drawing.history, @@drawing.palette)
|
354
|
+
a.set_region(lower+1, upper-1, @@drawing.bar, @@drawing.options)
|
355
|
+
sleep(@@drawing.options[:delay])
|
356
|
+
end
|
357
|
+
|
358
|
+
# Method called at the start of each call to merge -- position the progress
|
359
|
+
# bar below the group and initialize the auxilliary region where merged
|
360
|
+
# groups are put.
|
361
|
+
|
362
|
+
def start_group(a,i,j)
|
363
|
+
@@drawing.groupstart = i
|
364
|
+
@@drawing.group = []
|
365
|
+
a.set_region(i, j, @@drawing.bar, @@drawing.options)
|
366
|
+
end
|
367
|
+
|
368
|
+
# Move one of the bars from the main array to the next location in the
|
369
|
+
# auxilliary area
|
370
|
+
|
371
|
+
def move_down(a,i)
|
372
|
+
rect = @@drawing.rects[i]
|
373
|
+
a.touch(rect, @@drawing.history, @@drawing.palette)
|
374
|
+
sleep(@@drawing.options[:delay])
|
375
|
+
a.move_down(rect, @@drawing.groupstart, @@drawing.group, @@drawing.options)
|
376
|
+
sleep(@@drawing.options[:delay])
|
377
|
+
end
|
378
|
+
|
379
|
+
# Move all the bars from auxilliary region back up to the main array.
|
380
|
+
|
381
|
+
def move_up(a)
|
382
|
+
a.move_up(@@drawing.rects, @@drawing.groupstart, @@drawing.group, @@drawing.options)
|
383
|
+
sleep(@@drawing.options[:delay])
|
384
|
+
end
|
385
|
+
|
386
|
+
# Set the fill color of the specified bar (called by qsort to indicate active regions)
|
387
|
+
|
388
|
+
def mark(a, i, color)
|
389
|
+
rect = @@drawing.rects[i]
|
390
|
+
rect.fill = color
|
391
|
+
sleep(@@drawing.options[:delay])
|
392
|
+
end
|
393
|
+
|
394
|
+
# Update the color of a bar (cycling through the palette of colors)
|
395
|
+
|
396
|
+
def touch(a, i)
|
397
|
+
rect = @@drawing.rects[i]
|
398
|
+
a.touch(rect, @@drawing.history, @@drawing.palette)
|
399
|
+
end
|
400
|
+
|
291
401
|
end # RecursionLab
|
292
402
|
|
293
403
|
end # RubyLabs
|
data/lib/rubylabs.rb
CHANGED
@@ -312,6 +312,74 @@ call <tt>a.random(:success)</tt> to get a value that is in the array +a+, or cal
|
|
312
312
|
return nil
|
313
313
|
end
|
314
314
|
end
|
315
|
+
|
316
|
+
# Visualization methods called by searching and sorting algorithms in
|
317
|
+
# IterationLab and RecursionLab.
|
318
|
+
|
319
|
+
def TestArray.draw_bars(a, options)
|
320
|
+
amax = a.max
|
321
|
+
rects = []
|
322
|
+
a.each_with_index do |val, i|
|
323
|
+
rx = options[:x0] + i * options[:dx]
|
324
|
+
y = Float(val)
|
325
|
+
dy = options[:y1] - options[:y0] - options[:ymin] # height of tallest bar
|
326
|
+
ry = options[:y1] - options[:ymin] - (y/amax)*dy # height of this bar
|
327
|
+
rects << Canvas::Rectangle.new( rx, ry, rx + options[:dx], options[:y1], :fill => options[:array_fill], :outline => options[:canvas_fill] )
|
328
|
+
end
|
329
|
+
return rects
|
330
|
+
end
|
331
|
+
|
332
|
+
# Add rectangle i to the history list, then set the color of each bar
|
333
|
+
# in the list to the corresponding palette color
|
334
|
+
|
335
|
+
def touch(rect, history, palette)
|
336
|
+
pmax = palette.length
|
337
|
+
history.pop if history.length >= pmax
|
338
|
+
history.insert(0, rect)
|
339
|
+
history.each_with_index do |r, i|
|
340
|
+
r.fill = palette[i]
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# Set the left and right ends of the progress bar
|
345
|
+
|
346
|
+
def set_region(i, j, bar, options)
|
347
|
+
x0, y0, x1, y1 = bar.coords
|
348
|
+
x0 = options[:x0] + i * options[:dx]
|
349
|
+
x1 = options[:x0] + j * options[:dx]
|
350
|
+
bar.coords = [x0, y0, x1, y1]
|
351
|
+
end
|
352
|
+
|
353
|
+
# Exchange the locations of rectangles i and j
|
354
|
+
|
355
|
+
def swap_bars(rects, i, j, options)
|
356
|
+
dist = (j - i) * options[:dx]
|
357
|
+
ri = rects[i]
|
358
|
+
rj = rects[j]
|
359
|
+
Canvas.move(ri, dist, 0)
|
360
|
+
Canvas.move(rj, -dist, 0)
|
361
|
+
rects[i], rects[j] = rects[j], rects[i]
|
362
|
+
end
|
363
|
+
|
364
|
+
# Move a bar down to the auxilliary area below the progress bar. Argument 'groupstart'
|
365
|
+
# is the index of the first bar in the area, group is the current set of bars in the area.
|
366
|
+
|
367
|
+
def move_down(rect, groupstart, group, options)
|
368
|
+
x0, y0, x1, y1 = rect.coords
|
369
|
+
newx = options[:x0] + (groupstart + group.length) * options[:dx]
|
370
|
+
Canvas.move(rect, newx - x0, options[:gdy])
|
371
|
+
group << rect
|
372
|
+
end
|
373
|
+
|
374
|
+
# Move all the bars in the auxilliary area straight up so they fill the space in the
|
375
|
+
# main array, update the array of rectangles so they correspond to the new order of bars
|
376
|
+
|
377
|
+
def move_up(rects, groupstart, group, options)
|
378
|
+
group.each do |rect|
|
379
|
+
Canvas.move(rect, 0, -options[:gdy])
|
380
|
+
end
|
381
|
+
rects[groupstart ... (groupstart + group.length)] = group
|
382
|
+
end
|
315
383
|
|
316
384
|
# Return a list of types of items that can be passed as arguments
|
317
385
|
# to <tt>TestArray.new</tt>
|
@@ -326,7 +394,7 @@ call <tt>a.random(:success)</tt> to get a value that is in the array +a+, or cal
|
|
326
394
|
|
327
395
|
end # class TestArray
|
328
396
|
|
329
|
-
#
|
397
|
+
# Make a new TestArray object (equivalent to calling <tt>TestArray.new</tt>).
|
330
398
|
#
|
331
399
|
# Examples:
|
332
400
|
# >> TestArray(5)
|
@@ -340,8 +408,7 @@ call <tt>a.random(:success)</tt> to get a value that is in the array +a+, or cal
|
|
340
408
|
|
341
409
|
def TestArray(n, type = nil)
|
342
410
|
TestArray.new(n, type)
|
343
|
-
end
|
344
|
-
|
411
|
+
end
|
345
412
|
|
346
413
|
=begin rdoc
|
347
414
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubylabs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 53
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
10
|
-
version: 0.9.
|
9
|
+
- 7
|
10
|
+
version: 0.9.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- conery
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-10-
|
18
|
+
date: 2012-10-14 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rubygems-test
|