rubylabs 0.9.6 → 0.9.7
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/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
|