rubylabs 0.5.4

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.
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