rubylabs 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +9 -0
  3. data/Rakefile +63 -0
  4. data/VERSION +1 -0
  5. data/bin/bb.rb +12 -0
  6. data/bin/statistics2-0.53/ext/extconf.rb +11 -0
  7. data/bin/statistics2-0.53/ext/show.rb +11 -0
  8. data/bin/statistics2-0.53/ext/t.rb +46 -0
  9. data/bin/statistics2-0.53/mklist.rb +26 -0
  10. data/bin/statistics2-0.53/sample-tbl.rb +129 -0
  11. data/bin/statistics2-0.53/statistics2.rb +532 -0
  12. data/bin/statistics2-0.53/t-inv.rb +54 -0
  13. data/bin/statistics2.rb +532 -0
  14. data/data/aafreq.txt +20 -0
  15. data/data/cars.txt +50 -0
  16. data/data/century.txt +1 -0
  17. data/data/colors.txt +64 -0
  18. data/data/earth.yaml +15 -0
  19. data/data/fruit.txt +45 -0
  20. data/data/hacodes.txt +35 -0
  21. data/data/hafreq.txt +16 -0
  22. data/data/hvfreq.txt +5 -0
  23. data/data/nbody.R +23 -0
  24. data/data/nbody.out +731 -0
  25. data/data/nbody.pdf +3111 -0
  26. data/data/nbody.png +0 -0
  27. data/data/nbody3d.pdf +3201 -0
  28. data/data/outer.pdf +182785 -0
  29. data/data/solar.dat +36501 -0
  30. data/data/solarsystem.txt +17 -0
  31. data/data/suits.txt +1 -0
  32. data/data/wordlist.txt +210653 -0
  33. data/lib/bitlab.rb +624 -0
  34. data/lib/elizalab.rb +523 -0
  35. data/lib/encryptionlab.rb +42 -0
  36. data/lib/hashlab.rb +224 -0
  37. data/lib/introlab.rb +14 -0
  38. data/lib/iterationlab.rb +130 -0
  39. data/lib/randomlab.rb +294 -0
  40. data/lib/recursionlab.rb +228 -0
  41. data/lib/rubylabs.rb +507 -0
  42. data/lib/sievelab.rb +58 -0
  43. data/lib/sortlab.rb +213 -0
  44. data/lib/spherelab.rb +352 -0
  45. data/lib/temps.rb +41 -0
  46. data/lib/tsplab.rb +416 -0
  47. data/lib/viewer.rb +65 -0
  48. data/test/bit_test.rb +175 -0
  49. data/test/encryption_test.rb +20 -0
  50. data/test/iteration_test.rb +40 -0
  51. data/test/random_test.rb +64 -0
  52. data/test/recursion_test.rb +47 -0
  53. data/test/rubylabs_test.rb +18 -0
  54. data/test/sieve_test.rb +28 -0
  55. data/test/sphere_test.rb +130 -0
  56. data/test/temps_test.rb +24 -0
  57. data/test/test_helper.rb +18 -0
  58. metadata +112 -0
data/lib/rubylabs.rb ADDED
@@ -0,0 +1,507 @@
1
+ =begin rdoc
2
+
3
+ = RubyLabs
4
+
5
+ Common methods used by two or more lab projects.
6
+
7
+ Methods used to monitor execution of programs during experiments.
8
+
9
+ =end
10
+
11
+ SCRIPT_LINES__ = Hash.new unless defined? SCRIPT_LINES__
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'bin'))
14
+
15
+ autoload :Temps, "temps.rb"
16
+ autoload :SieveLab, "sievelab.rb"
17
+ autoload :IterationLab, "iterationlab.rb"
18
+ autoload :RecursionLab, "recursionlab.rb"
19
+ autoload :HashLab, "hashlab.rb"
20
+ autoload :BitLab, "bitlab.rb"
21
+ autoload :RandomLab, "randomlab.rb"
22
+ autoload :EncryptionLab, "encryptionlab.rb"
23
+ autoload :ELIZALab, "elizalab.rb"
24
+ autoload :SphereLab, "spherelab.rb"
25
+ autoload :TSPLab, "tsplab.rb"
26
+
27
+ autoload :Viewer, "viewer.rb"
28
+
29
+ module RubyLabs
30
+
31
+ =begin rdoc
32
+
33
+ === hello
34
+
35
+ A simple 'hello world' method to test the installation.
36
+
37
+ =end
38
+
39
+ def hello
40
+ "Hello, you have successfully installed RubyLabs!"
41
+ end
42
+
43
+ =begin rdoc
44
+ Log base 2.
45
+ =end
46
+
47
+ def log2(x)
48
+ Math.log(x) / Math.log(2.0)
49
+ end
50
+
51
+ =begin rdoc
52
+ Return the larger of a and b
53
+ =end
54
+
55
+ def max(a,b)
56
+ a > b ? a : b
57
+ end
58
+
59
+ =begin rdoc
60
+ Return the smaller of a and b
61
+ =end
62
+
63
+ def min(a,b)
64
+ a < b ? a : b
65
+ end
66
+
67
+ =begin rdoc
68
+ Return a copy of object x with the elements in a new, scrambled order. The
69
+ parameter x can be any object that has an index operator (e.g. strings or
70
+ arrays).
71
+ =end
72
+
73
+ def permutation(x)
74
+ res = x.clone
75
+ for i in 0..res.length-2
76
+ r = rand(res.length-i) + i
77
+ res[i], res[r] = res[r], res[i]
78
+ end
79
+ return res
80
+ end
81
+
82
+ =begin rdoc
83
+
84
+ Call time { foo(...) } to measure the execution time of a call to foo. This
85
+ method will time any arbitrary Ruby expression.
86
+
87
+ =end
88
+
89
+ def time(&f)
90
+ tstart = Time.now
91
+ f.call
92
+ return Time.now - tstart
93
+ end
94
+
95
+ =begin rdoc
96
+
97
+ Call trace { foo(...) } to monitor the execution of the call to foo. Sets up
98
+ a callback method which checks to see if a probe has been attached to a line
99
+ via a call to Source.probe, and if so evaluates the expression associated with
100
+ the probe.
101
+
102
+ =end
103
+
104
+ # Debugging aid: put this line at the front of the trace_func:
105
+ # p [event, file, line, id, binding, classname]
106
+
107
+ def trace(&f)
108
+ set_trace_func proc { |event, file, line, id, binding, classname|
109
+ if expr = Source.probing(file, id, line)
110
+ eval(expr, binding) if expr != :count
111
+ end
112
+ }
113
+ res = f.call
114
+ set_trace_func nil
115
+ res
116
+ end
117
+
118
+ =begin rdoc
119
+
120
+ A call to count { foo(...) } is similar to a call to trace, but instead of evaluating
121
+ the expression associated with a probe it just counts the number of times the
122
+ lines are executed and returns the count.
123
+
124
+ Note: this version assumes there are two different kinds of probes. Probes monitored
125
+ by the trace method are expressions that are evaluated when the probed expression is
126
+ encountered, and probes monitored by the count method are :count tags.
127
+
128
+ =end
129
+
130
+ def count(&f)
131
+ counter = 0
132
+ set_trace_func proc { |event, file, line, id, binding, classname|
133
+ if expr = Source.probing(file, id, line)
134
+ counter += 1 if expr == :count
135
+ end
136
+ }
137
+ f.call
138
+ set_trace_func nil
139
+ return counter
140
+ end
141
+
142
+ =begin rdoc
143
+
144
+ === TestArray
145
+
146
+ A TestArray is an array of random numbers to use in testing searching and sorting
147
+ algorithms. Call TestArray.new(n) to make an array of n unique random numbers.
148
+
149
+ A method named +random+ will return a random element to use as a search target.
150
+ Call +a.random(:success)+ to get a value that is in the array +a+, or call
151
+ +a.random(:fail)+ to get a number that is not in the array.
152
+
153
+ =end
154
+
155
+ # The constructor uses a hash to create unique numbers -- it draws random numbers
156
+ # and uses them as keys to insert into the hash, and returns when the hash has n
157
+ # items. The hash is saved so it can be reused by a call to random(:fail) -- this
158
+ # time draw random numbers until one is not a key in the hash. A lot of machinery
159
+ # to keep around for very few calls, but it's efficient enough -- making an array
160
+ # of 100K items takes less than a second.
161
+
162
+ # The @spread variable controls the average spacing between items. The 6.667 for
163
+ # small arrays means an array of 15 will have numbers between 0 and 99.
164
+
165
+ # An earlier version used a method named test_array to make a regular Array object
166
+ # and augment it with the location method, but the singleton's methods were not passed
167
+ # on to copies made by a call to sort:
168
+ # >> x = test_array(3)
169
+ # => [16, 13, 4]
170
+ # >> x.sort.random(:fail)
171
+ # NoMethodError: undefined method `random' for [4, 13, 16]:Array
172
+
173
+ class TestArray < Array
174
+
175
+ def initialize(size)
176
+ @spread = (size > 50) ? 10 : 6.667
177
+ @h = Hash.new
178
+
179
+ while @h.size < size
180
+ @h[ rand( size * @spread ) ] = 1
181
+ end
182
+
183
+ @h.keys.each do |k|
184
+ self << k
185
+ end
186
+ end
187
+
188
+ def random(outcome)
189
+ if outcome == :success
190
+ return self[ rand(self.length) ]
191
+ elsif outcome == :fail
192
+ loop do
193
+ i = rand( self.length * @spread )
194
+ return i if @h[i] == nil
195
+ end
196
+ else
197
+ return nil
198
+ end
199
+ end
200
+
201
+ end # class TestArray
202
+
203
+ =begin rdoc
204
+
205
+ === RandomArray
206
+
207
+ Similar to TestArray, but draws random words from a file.
208
+
209
+ =end
210
+
211
+ class RandomArray < Array
212
+
213
+ data = File.join(File.dirname(__FILE__), '..', 'data')
214
+
215
+ @@sources = {
216
+ :cars => "#{data}/cars.txt",
217
+ :colors => "#{data}/colors.txt",
218
+ :fruit => "#{data}/fruit.txt",
219
+ :words => "#{data}/wordlist.txt",
220
+ }
221
+
222
+ def initialize(src, n)
223
+ fn = @@sources[src] or raise "RandomArray: undefined array type: #{src}"
224
+ words = File.open(fn).readlines
225
+ a = Hash.new
226
+ while a.size < n
227
+ w = words[rand(words.length)].chomp
228
+ a[w] = 1
229
+ end
230
+ a.keys.each do |w|
231
+ self << w
232
+ end
233
+ end
234
+
235
+ def RandomArray.sources
236
+ return @@sources.keys.sort { |a,b| a.to_s <=> b.to_s }
237
+ end
238
+
239
+ end # RandomArray
240
+
241
+
242
+ =begin
243
+ The Source module has methods that access the source code for a lab
244
+ project. Since the outer module (RubyLabs) assigns SCRIPT_LINES__ the
245
+ source code has already been read from the file -- these methods just have
246
+ to look for the code in memory. The methods assume the code students will
247
+ look at has been delimited by comments containing the strings :begin and :end.
248
+ =end
249
+
250
+ module Source
251
+
252
+ @@probes = Hash.new
253
+ @@file = Hash.new
254
+ @@size = Hash.new
255
+ @@base = Hash.new
256
+ @@helpers = Hash.new
257
+ @@line = nil
258
+
259
+ =begin
260
+ Print the source code for method +name+.
261
+ =end
262
+
263
+ def Source.listing(name)
264
+ begin
265
+ id = Source.find(name)
266
+ for i in @@base[id]..(@@base[id]+@@size[id]-1)
267
+ line = SCRIPT_LINES__[@@file[id]][i-1].chomp
268
+ printf "%3d: %s\n", i - @@base[id] + 1, line.gsub(/\t/," ")
269
+ end
270
+ rescue Exception => e
271
+ puts e
272
+ end
273
+ return true
274
+ end
275
+
276
+ =begin
277
+ Write a copy of the source code for method +name+. If a file name is not
278
+ specified, the output file name is the name of the method with ".rb" appended.
279
+ Prompts the user before overwriting an existing file.
280
+ =end
281
+
282
+ def Source.checkout(name, newfilename = nil)
283
+ begin
284
+ id = Source.find(name)
285
+ if newfilename.nil?
286
+ newfilename = id.to_s
287
+ end
288
+ if newfilename !~ /\.rb$/
289
+ newfilename += ".rb"
290
+ end
291
+ if File.exists?(newfilename)
292
+ print "Replace existing #{newfilename}? [yn] "
293
+ if STDIN.gets[0] != ?y
294
+ puts "File not written"
295
+ return false
296
+ end
297
+ end
298
+ File.open(newfilename, "w") do |f|
299
+ f.puts "# #{name} method exported from #{File.basename(@@file[id])}"
300
+ f.puts
301
+ Source.print_source(f, id)
302
+ @@helpers[id].each do |m|
303
+ f.puts
304
+ xid = Source.find(m)
305
+ Source.print_source(f, xid)
306
+ end
307
+ end
308
+ rescue Exception => e
309
+ puts e
310
+ end
311
+ puts "Saved a copy of source in #{newfilename}"
312
+ return true
313
+ end
314
+
315
+ def Source.print_source(f, id)
316
+ for i in @@base[id]..(@@base[id]+@@size[id]-1)
317
+ line = SCRIPT_LINES__[@@file[id]][i-1].chomp
318
+ f.puts line.gsub(/\t/," ")
319
+ end
320
+ end
321
+
322
+ =begin rdoc
323
+ Attach a probe to a line in method +name+. The line can be specified
324
+ by line number or a pattern. If a string is passed as a third parameter
325
+ that string will be evaluated when the method is called from a block passed
326
+ to trace, otherwise a count probe is created.
327
+ =end
328
+
329
+ def Source.probe(name, spec, expr = :count)
330
+ begin
331
+ id = Source.find(name)
332
+ Source.lines(spec, id).each do |n|
333
+ n += @@base[id] - 1
334
+ @@probes[id][n] = expr
335
+ end
336
+ rescue Exception => e
337
+ puts e
338
+ end
339
+ return true
340
+ end
341
+
342
+ =begin rdoc
343
+ Return probes (if any) attached to the specified file, method, and line number.
344
+ Intended to be called from a trace func callback (which is why the file is one
345
+ of the parameters).
346
+ =end
347
+
348
+ def Source.probing(filename, method, line)
349
+ return nil if line == @@line
350
+ @@line = line
351
+ return nil unless @@probes[method] && @@file[method] == filename
352
+ return @@probes[method][line]
353
+ end
354
+
355
+ =begin rdoc
356
+ Print the currently defined probes.
357
+ =end
358
+
359
+ def Source.probes
360
+ @@probes.each do |name, plist|
361
+ plist.each do |line, exprs|
362
+ n = line - @@base[name] + 1
363
+ printf "%s %2d: %s\n", name, n, exprs
364
+ end
365
+ end
366
+ return true
367
+ end
368
+
369
+ =begin rdoc
370
+ Clear the probes on a designated method (or all methods)
371
+ =end
372
+
373
+ def Source.clear(name = nil)
374
+ @@probes.each do |id, plist|
375
+ next if ! name.nil? && id != name
376
+ plist.clear
377
+ end
378
+ return true
379
+ end
380
+
381
+ =begin
382
+ Internal use only -- locate the filename, starting line number, and length
383
+ of a method named +name+ (+name+ can be a string or a symbol), record the
384
+ information for any methods that need it. This information only needs to
385
+ be found once, so it is recorded in a set of class variables. Revisit this
386
+ decision if monitoring user-defined methods....
387
+
388
+ TODO: save helper files listed on :begin line
389
+ =end
390
+
391
+ def Source.find(name)
392
+ id = name.to_sym
393
+ return id if @@file[id] # return if we looked for this source previously
394
+
395
+ filename, base, size = nil, nil, nil
396
+ names = []
397
+
398
+ catch (:found) do
399
+ SCRIPT_LINES__.each do |file, lines|
400
+ line_num = 0
401
+ lines.each do |s|
402
+ line_num += 1
403
+ if match = s.match(/:begin\s+(.*)/)
404
+ names = match[1].split.collect{|x| eval(x)}
405
+ if names[0] == id
406
+ filename = file
407
+ base = line_num + 1
408
+ next
409
+ end
410
+ end
411
+ if s =~ /:end\s+:#{id.to_s}/
412
+ size = line_num - base
413
+ throw :found
414
+ end
415
+ end
416
+ end
417
+ end
418
+
419
+ raise "Can't find method named '#{name}'" if size.nil?
420
+
421
+ @@file[id] = filename
422
+ @@size[id] = size
423
+ @@base[id] = base
424
+ @@probes[id] = Hash.new
425
+ @@helpers[id] = names[1..-1]
426
+ return id
427
+ end
428
+
429
+ =begin rdoc
430
+ Internal use only -- make an array of line numbers to use for probing method +name+.
431
+ Argument can be a single line number, an array of line numbers, or a pattern. Checks
432
+ to make sure line numbers are valid.
433
+ =end
434
+
435
+ def Source.lines(spec, id)
436
+ if spec.class == Fixnum && Source.range_check(spec, id)
437
+ return [spec]
438
+ elsif spec.class == Array
439
+ res = Array.new
440
+ spec.each do |line|
441
+ raise "line number must be an integer" unless line.class == Fixnum
442
+ res << line if Source.range_check(line, id)
443
+ end
444
+ return res
445
+ elsif spec.class == String
446
+ res = Array.new
447
+ for i in @@base[id]..(@@base[id]+@@size[id]-1)
448
+ line = SCRIPT_LINES__[@@file[id]][i-1].chomp
449
+ res << i - @@base[id] + 1 if line.index(spec)
450
+ end
451
+ return res
452
+ else
453
+ raise "invalid spec: '#{spec}' (must be an integer, array of integers, or a pattern)"
454
+ end
455
+ end
456
+
457
+ def Source.range_check(n, id)
458
+ max = @@size[id]
459
+ raise "line number must be between 1 and #{max}" unless n >= 1 && n <= max
460
+ return true
461
+ end
462
+
463
+ =begin rdoc
464
+ Internal use only -- show info about method
465
+ =end
466
+
467
+ def Source.info(name)
468
+ unless id = Source.find(name)
469
+ puts "Can't find method named '#{name}'"
470
+ return
471
+ end
472
+
473
+ printf "file: %s\n", @@file[name]
474
+ printf "size: %d\n", @@size[name]
475
+ printf "base: %d\n", @@base[name]
476
+ printf "probes: %s\n", @@probes[name].inspect
477
+ end
478
+
479
+ end # Source
480
+
481
+ end # RubyLabs
482
+
483
+ include RubyLabs
484
+
485
+ class Fixnum
486
+
487
+ =begin rdoc
488
+
489
+ An 'ord' method for the Fixnum class that maps ASCII codes for letters
490
+ to numbers between 0 and 25.
491
+
492
+ <b>NOTE:</b> +ord+ is built in to Ruby 1.9, and will be sligthly different; for
493
+ characters (1-letter strings) +ord+ will return the ASCII value.
494
+
495
+ =end
496
+
497
+ def ord
498
+ if self >= ?a && self <= ?z
499
+ self - ?a
500
+ elsif self >= ?A && self <= ?Z
501
+ self - ?A
502
+ else
503
+ self
504
+ end
505
+ end
506
+
507
+ end # Fixnum
data/lib/sievelab.rb ADDED
@@ -0,0 +1,58 @@
1
+
2
+ =begin rdoc
3
+
4
+ == Sieve of Eratosthenes
5
+
6
+ Use the Sieve of Eratosthenes algorithm to generate a list of prime
7
+ numbers. The method is an introduction to iteration, using iterators
8
+ to make and filter lists of numbers, and a while loop to repeat the
9
+ filtering step until no more composite numbers are left in the worklist.
10
+
11
+ =end
12
+
13
+ include Math
14
+
15
+ module RubyLabs
16
+
17
+ module SieveLab
18
+
19
+ # Call sieve(n) to create an array of prime numbers between 2 and n
20
+
21
+ # :begin :sieve
22
+ def sieve(n)
23
+ return [] if n < 2
24
+ worklist = []
25
+ (n-1).times { |i| worklist << i+2 }
26
+ primes = []
27
+
28
+ while worklist.first < sqrt(n)
29
+ primes << worklist.first
30
+ worklist.delete_if { |x| x % primes.last == 0 }
31
+ end
32
+
33
+ return primes + worklist
34
+ end
35
+ # :end :sieve
36
+
37
+
38
+ # A first version of the sieve, iterates until the worklist is empty
39
+
40
+ # :begin :proto_sieve
41
+ def proto_sieve(n)
42
+ return [] if n < 2
43
+ worklist = []
44
+ (n-1).times { |i| worklist << i+2 }
45
+ primes = []
46
+
47
+ while worklist.length > 0
48
+ primes << worklist.first
49
+ worklist.delete_if { |x| x % primes.last == 0 }
50
+ end
51
+
52
+ return primes
53
+ end
54
+ # :end :proto_sieve
55
+
56
+ end # SieveLab
57
+
58
+ end # RubyLabs