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 CHANGED
@@ -1 +1 @@
1
- 0.9.6
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
- # x = a.slice!(i) # remove the item at location i
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.class == ArrayView
149
- dist = (j - i) * @@drawing.options[:dx]
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
- amax = a.max
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
- def touch(loc)
223
- rect = @@drawing.rects[loc]
224
- pmax = @@drawing.palette.length
225
- @@drawing.history.pop if @@drawing.history.length >= pmax
226
- @@drawing.history.insert(0, rect)
227
- @@drawing.history.each_with_index do |r, i|
228
- r.fill = @@drawing.palette[i]
229
- end
230
- end
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) # sort small items (range from p to 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 - 1
267
- j = r + 1
268
- while true # squeeze i, j until they point at items to exchange
269
- loop { j = j - 1; break if a[j] <= x }
270
- loop { i = i + 1; break if a[i] >= x }
271
- if i < j
272
- a[i], a[j] = a[j], a[i] # exchange items at locations i and j
273
- else
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
- # Equivalent to calling <tt>TestArray.new</tt>.
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: 55
4
+ hash: 53
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 6
10
- version: 0.9.6
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-10 00:00:00 Z
18
+ date: 2012-10-14 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rubygems-test