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/elizalab.rb ADDED
@@ -0,0 +1,523 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ =begin rdoc
4
+
5
+ == ELIZA
6
+
7
+ Joseph Weizenbaum's ELIZA program in Ruby. Students can play with the Doctor script,
8
+ which mimics a Rogerian psychiatrist, and experiment by adding new rules to Doctor or
9
+ writing their own scripts.
10
+
11
+ =end
12
+
13
+ require 'priorityqueue'
14
+
15
+ require 'readline'
16
+ include Readline
17
+
18
+
19
+ class String
20
+
21
+ =begin rdoc
22
+ A useful operation on strings -- call +s.unquote+ to remove double quotes from
23
+ the beginning and end of string +s+.
24
+ =end
25
+
26
+ def unquote
27
+ if self[0] == ?" && self[-1] == ?"
28
+ return self.slice(1..-2)
29
+ else
30
+ return self
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ =begin rdoc
37
+
38
+ A transformation rule is associated with a key word, and is triggered
39
+ when that word is found in an input sentence. Rules have integer
40
+ priorities, and if more than one rule is enabled ELIZA applies the one
41
+ with the highest priority. Each rule has an ordered list of patterns,
42
+ and each pattern has a list of reassembly rules.
43
+
44
+ To apply a rule, scan the patterns, and for the first pattern that matches
45
+ a sentence, build the output using the current reassembly rule.
46
+
47
+ =end
48
+
49
+ class Rule
50
+
51
+ attr_accessor :key, :priority, :patterns
52
+
53
+ =begin rdoc
54
+ Specify the key word for a rule when the rule is created.
55
+ =end
56
+ def initialize(key, priority = 1)
57
+ @key = key
58
+ @priority = priority
59
+ @patterns = Array.new
60
+ end
61
+
62
+ def addPattern(expr)
63
+ if expr.class == Pattern
64
+ @patterns << expr
65
+ else
66
+ if expr.class == String
67
+ expr = Regexp.new(expr.slice(1..-2))
68
+ end
69
+ @patterns << Pattern.new(expr)
70
+ end
71
+ end
72
+
73
+ def [](n)
74
+ @patterns[n]
75
+ end
76
+
77
+ def addReassembly(line, n = -1)
78
+ @patterns[n].addText(line)
79
+ end
80
+
81
+
82
+ =begin rdoc
83
+ Rule application -- try the patterns in order. When the line matches a pattern,
84
+ return the next reassembly for that pattern. Apply variable substitutions to both
85
+ the patterns and the reassemblies if they contain variables.
86
+ =end
87
+
88
+
89
+ def apply(s, post = {}, verbose = false)
90
+ @patterns.each do |p|
91
+ if verbose
92
+ print "trying pattern "
93
+ p p.regexp
94
+ end
95
+ res = p.apply( s, post, verbose )
96
+ return res if ! res.nil?
97
+ end
98
+ return nil
99
+ end
100
+
101
+ def to_s
102
+ s = @key + " / " + @priority.to_s + "\n"
103
+ @patterns.each { |r| s += " " + r.to_s + "\n" }
104
+ return s
105
+ end
106
+
107
+ def inspect
108
+ s = @key.inspect
109
+ s += " [#{@priority}]" if @priority > 1
110
+ s += " --> [\n" + @patterns.join("\n") + "]"
111
+ return s
112
+ end
113
+
114
+ end
115
+
116
+ =begin rdoc
117
+
118
+ A Pattern represents one way to transform an input sentence into a
119
+ response. A Pattern instance has a regular expression and a list of
120
+ one or more reassembly strings that can refer to groups in the expression.
121
+ There is also an index to record the last reassembly string used, so
122
+ the application can cycle through the strings.
123
+
124
+ For convenience the constructor inserts word break anchors and attaches
125
+ a /i to the expression as needed. NOTE: the inspect method removes these
126
+ automatic items so the printed string is cleaner; to see the real Regexp
127
+ call the regexp accessor. Example:
128
+
129
+ >> p = Pattern.new(/hi/,"hello")
130
+ => /hi/ -> ["hello"] [0]
131
+ >> p.regexp
132
+ => /\bhi\b/i
133
+
134
+ =end
135
+
136
+
137
+ class Pattern
138
+ attr_accessor :regexp, :list, :index
139
+
140
+ def initialize(expr, text = nil)
141
+ re = expr.inspect
142
+ re.insert(1,'\b') if re =~ /^\/\w/
143
+ re.insert(-2,'\b') if re =~ /\w\/$/
144
+ re += "i" unless re =~ /\/i$/
145
+ @regexp = eval(re)
146
+
147
+ if text.nil?
148
+ @list = Array.new
149
+ elsif text.class == String
150
+ @list = [text]
151
+ elsif text.class == Array
152
+ @list = text
153
+ else
154
+ raise "Pattern.initialize: text argument must be nil, String, or Array"
155
+ end
156
+
157
+ @index = 0
158
+ end
159
+
160
+ def addText(line)
161
+ @list << line
162
+ end
163
+
164
+ def inc
165
+ n = @index
166
+ @index = (@index + 1) % @list.length
167
+ return n
168
+ end
169
+
170
+ def apply(s, post = {}, verbose = false)
171
+ md = s.match(@regexp)
172
+ return nil if @list.empty? || md == nil
173
+ res = @list[inc()].clone
174
+ return res if res[0] == ?@
175
+ puts "reassembling '#{res}'" if verbose
176
+ res.gsub!(/\$\d+/) do |ns|
177
+ n = ns.slice(1..-1).to_i # strip leading $, convert to int
178
+ if n && md[n]
179
+ puts "postprocess #{md[n]}" if verbose
180
+ md[n].gsub(/[a-z\-$']+/i) do |w|
181
+ (post.has_key?(w) && post[w][0] != ?$) ? post[w] : w
182
+ end
183
+ else
184
+ warn "Pattern.apply: bad argument #{ns} in #{res}"
185
+ ""
186
+ end
187
+ end
188
+ return res
189
+ end
190
+
191
+ def cleanRegexp
192
+ res = @regexp.inspect
193
+ res.gsub!(/\\b/,"")
194
+ res.gsub!(/i$/,"")
195
+ return res
196
+ end
197
+
198
+ def to_s
199
+ s = cleanRegexp + "\n"
200
+ @list.each { |x| s += " \"" + x + "\"\n" }
201
+ return s
202
+ end
203
+
204
+ def inspect
205
+ return cleanRegexp + ": " + @list.inspect
206
+ end
207
+
208
+ end
209
+
210
+
211
+ module Eliza
212
+
213
+ # These top level constants define the "application" processed by ELIZA -- the rule
214
+ # sets used to transform inputs to outputs. The arrays are all initially empty.
215
+ # The 'load' method reads and parses rules and fills in these arrays. The 'reset'
216
+ # method empties them.
217
+
218
+ @@script = nil
219
+ @@aliases = Hash.new
220
+ @@vars = Hash.new
221
+ @@starts = Array.new
222
+ @@stops = Array.new
223
+ @@pre = Hash.new
224
+ @@post = Hash.new
225
+ @@default = nil
226
+ @@rules = Hash.new
227
+ @@queue = PriorityQueue.new
228
+
229
+ @@word = /[a-z\-$']+/i # pattern for a "word" in the input language
230
+ @@iword = /^[a-z\-$']+/i # same, but must be the first item on the line
231
+ @@var = /\$\d+/ # variable name in reassembly string
232
+
233
+ @@verbose = false
234
+
235
+ def Eliza.rules
236
+ return @@rules
237
+ end
238
+
239
+ def Eliza.queue
240
+ return @@queue
241
+ end
242
+
243
+ def Eliza.aliases
244
+ return @@aliases
245
+ end
246
+
247
+ def Eliza.vars
248
+ return @@vars
249
+ end
250
+
251
+ def Eliza.pre
252
+ return @@pre
253
+ end
254
+
255
+ def Eliza.post
256
+ return @@post
257
+ end
258
+
259
+ def Eliza.verbose
260
+ @@verbose = true
261
+ end
262
+
263
+ def Eliza.quiet
264
+ @@verbose = false
265
+ end
266
+
267
+ # First pass over the input -- scan each word, apply preprocessing substitutions,
268
+ # add rule names to the priority queue. NOTE: this method does a destructive
269
+ # update to the input line....
270
+
271
+ def Eliza.scan(line, queue)
272
+ line.gsub!(@@word) { |w| @@pre.has_key?(w) ? @@pre[w] : w }
273
+ puts "preprocess: line = '#{line}'" if @@verbose
274
+ line.scan(@@word) do |w|
275
+ w.downcase!
276
+ if r = @@rules[w]
277
+ queue.insert(r)
278
+ puts "add rule for '#{w}' to queue" if @@verbose
279
+ end
280
+ if (x = @@aliases[w]) && (r = @@rules[x])
281
+ queue.insert(r)
282
+ puts "add rule for '#{x}' to queue" if @@verbose
283
+ end
284
+ end
285
+ # queue.each { |r| puts r.key }
286
+ end
287
+
288
+ def Eliza.apply(line, rule)
289
+ puts "applying rule: key = '#{rule.key}'" if @@verbose
290
+ if res = rule.apply(line, @@post, @@verbose)
291
+ if res[0] == ?@
292
+ rulename = res.slice(1..-1)
293
+ if @@rules[rulename]
294
+ return Eliza.apply( line, @@rules[rulename] )
295
+ else
296
+ warn "Eliza.apply: no rule for #{rulename}"
297
+ return nil
298
+ end
299
+ else
300
+ return res
301
+ end
302
+ else
303
+ return nil
304
+ end
305
+ end
306
+
307
+ # The heart of the program -- apply transformation rules to an input sentence.
308
+
309
+ def Eliza.transform(s)
310
+ s.sub!(/[\n\.\?!\-]*$/,"") # strip trailing punctuation
311
+ s.downcase!
312
+
313
+ @@queue = PriorityQueue.new
314
+ @@queue << @@default # initialize queue with default rule
315
+
316
+ Eliza.scan(s, @@queue) # add rules for recognized key words
317
+
318
+ while @@queue.length > 0 # apply rules in order of priority
319
+ p @@queue.collect { |r| r.key } if @@verbose
320
+ rule = @@queue.shift
321
+ if result = Eliza.apply(s, rule)
322
+ return result
323
+ end
324
+ end
325
+
326
+ warn "No rules applied" if @@queue.empty?
327
+ return nil
328
+ end
329
+
330
+ # The parser calls this method to deal with directives (lines where the first
331
+ # word begins with a colon)
332
+
333
+ def Eliza.parseDirective(line)
334
+ word = Eliza.detachWord(line)
335
+ case word
336
+ when "alias"
337
+ if line.empty? || line[0] != ?$
338
+ warn "symbol after :alias must be a variable name; ignoring '#{word} #{line}'"
339
+ return
340
+ else
341
+ sym = Eliza.detachWord(line)
342
+ @@vars[sym] = Array.new
343
+ line.split.each do |s|
344
+ @@aliases[s] = sym
345
+ @@vars[sym] << s
346
+ end
347
+ end
348
+ when "start"
349
+ @@starts << line.unquote
350
+ when "stop"
351
+ @@stops << line.unquote
352
+ when "pre"
353
+ sym = Eliza.detachWord(line)
354
+ @@pre[sym] = line.unquote
355
+ when "post"
356
+ sym = Eliza.detachWord(line)
357
+ @@post[sym] = line.unquote
358
+ when "default"
359
+ @@default = line[@@word]
360
+ else
361
+ warn "unknown directive: :#{word} (ignored)"
362
+ end
363
+ end
364
+
365
+ # Remove a word from the front of a line
366
+
367
+ def Eliza.detachWord(line)
368
+ word = line[@@word] # pattern matches the first word
369
+ if line.index(" ")
370
+ line.slice!(0..line.index(" ")) # delete up to end of the word
371
+ line.lstrip! # in case there are extra spaces after word
372
+ else
373
+ line.slice!(0..-1) # line just had the one word
374
+ end
375
+ return word
376
+ end
377
+
378
+ # Check each pattern's regular expression and replace var names by alternation
379
+ # constructs. If the script specified a default rule name look up that
380
+ # rule and save it as the default.
381
+
382
+ def Eliza.compileRules
383
+ @@rules.each do |key,val|
384
+ a = val.patterns()
385
+ a.each do |p|
386
+ expr = p.regexp.inspect
387
+ expr.gsub!(/\$\w+/) { |x| @@vars[x].join("|") }
388
+ p.regexp = eval(expr)
389
+ end
390
+ end
391
+ if @@default.class == String
392
+ @@default = @@rules[@@default]
393
+ end
394
+ end
395
+
396
+ # Parse rules in file f, store them in global arrays. Strategy: use a local
397
+ # var named 'rule', initially set to nil. New rules start with a single word
398
+ # at the start of a line. When such a line is found in the input file, create a
399
+ # new Rule object and store it in 'rule'. Subsequent lines that are part of the
400
+ # current rule (lines that contain regular expressions or strings) are added to
401
+ # current Rule object. Directives indicate the end of a rule, so 'rule' is reset
402
+ # to nil when a directive is seen.
403
+
404
+ def Eliza.load(filename)
405
+ begin
406
+ Eliza.reset
407
+ rule = nil
408
+ File.open(filename).each do |line|
409
+ line.strip!
410
+ next if line.empty? || line[0] == ?#
411
+ if line[0] == ?:
412
+ Eliza.parseDirective(line)
413
+ rule = nil
414
+ else
415
+ if line =~ @@iword
416
+ rulename, priority = line.split
417
+ rule = priority ? Rule.new(rulename, priority.to_i) : Rule.new(rulename)
418
+ @@rules[rule.key] = rule
419
+ elsif rule.nil?
420
+ warn "missing rule name? unexpected input '#{line}'"
421
+ elsif line[0] == ?/
422
+ if line[-1] == ?/
423
+ rule.addPattern(line)
424
+ else
425
+ warn "badly formed expression (missing /): '#{line}'"
426
+ end
427
+ elsif line[0] == ?"
428
+ if line[-1] == ?"
429
+ rule.addReassembly(line.unquote)
430
+ else
431
+ warn "badly formed string (missing \"): '#{line}'"
432
+ end
433
+ elsif line[0] == ?@
434
+ rule.addReassembly(line)
435
+ else
436
+ warn "unexpected line in rule for #{rulename}: '#{line}'"
437
+ end
438
+ end
439
+ end
440
+ Eliza.compileRules
441
+ @@script = filename
442
+ rescue
443
+ puts "Error processing #{filename}: #{$!}"
444
+ return false
445
+ end
446
+ return true
447
+ end
448
+
449
+ def Eliza.dump
450
+ puts "Script: #{@@script}"
451
+ print "Starts:\n "; p @@starts
452
+ print "Stops:\n "; p @@stops
453
+ print "Vars:\n "; p @@vars
454
+ print "Aliases:\n "; p @@aliases
455
+ print "Pre:\n "; p @@pre
456
+ print "Post:\n "; p @@post
457
+ print "Default:\n "; p @@default
458
+ print "Queue:\n "; p @@queue.collect { |r| r.key }
459
+ puts
460
+ @@rules.each { |key,val| puts val }
461
+ return nil
462
+ end
463
+
464
+ def Eliza.info
465
+ puts "Script: #{@@script}"
466
+ puts "#{@@rules.size} rules"
467
+ puts "Keys: "
468
+
469
+ keys = Array.new
470
+ @@rules.each_key { |k| keys << k unless k[0] == ?$ }
471
+ keys += @@aliases.keys
472
+ puts " " + keys.sort.join(", ")
473
+ end
474
+
475
+ # Initialization -- reset all the aliases, etc, and make a single rule
476
+ # that echoes the input.
477
+
478
+ def Eliza.reset
479
+ @@script = nil
480
+ @@aliases.clear
481
+ @@vars.clear
482
+ @@starts.clear
483
+ @@stops.clear
484
+ @@pre.clear
485
+ @@post.clear
486
+ @@rules.clear
487
+ @@queue.clear
488
+
489
+ @@default = Rule.new(:default)
490
+ @@default.addPattern(/(.*)/)
491
+ @@default.addReassembly("$1")
492
+ end
493
+
494
+ def Eliza.run
495
+ puts @@starts[rand(@@starts.length)] if ! @@starts.empty?
496
+ loop do
497
+ s = readline(" H: ", true)
498
+ return if s.nil?
499
+ s.chomp!
500
+ next if s.empty?
501
+ if s == "bye" || s == "quit"
502
+ puts @@stops[rand(@@stops.length)] if ! @@stops.empty?
503
+ return
504
+ end
505
+ puts " C: " + Eliza.transform(s)
506
+ end
507
+ end
508
+
509
+ end
510
+
511
+ # When invoked from command line load the script specified by the command line
512
+ # argument and then start ELIZA
513
+
514
+ if !defined? IRB
515
+ filename = ARGV.shift or abort "Usage: eliza.rb F\n Run ELIZA using rules in file F"
516
+ Eliza.verbose if ARGV[0] == "-v"
517
+ if Eliza.load(filename)
518
+ Eliza.run
519
+ end
520
+ else
521
+ Eliza.reset
522
+ end
523
+
@@ -0,0 +1,42 @@
1
+
2
+ =begin rdoc
3
+
4
+ == Encryption Lab
5
+
6
+ Ruby implementation of Caesar cypher and RSA encryption.
7
+
8
+ =end
9
+
10
+ module RubyLabs
11
+
12
+ module EncryptionLab
13
+
14
+ =begin rdoc
15
+ Caesar cipher -- single letter substitution based on "rotating" each letter +n+ places.
16
+ =end
17
+
18
+ # :begin :caesar
19
+ def caesar(s, n = 3)
20
+ res = String.new
21
+ s.each_byte do |byte|
22
+ if byte >= ?a && byte <= ?z
23
+ res << rot(byte, ?a, n)
24
+ elsif byte >= ?A && byte <= ?Z
25
+ res << rot(byte, ?A, n)
26
+ else
27
+ res << byte
28
+ end
29
+ end
30
+ return res
31
+ end
32
+ # :end :caesar
33
+
34
+ def rot(char, base, n)
35
+ return ((char - base) + n) % 26 + base
36
+ end
37
+
38
+
39
+
40
+ end # EncryptionLab
41
+
42
+ end # RubyLabs