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/bitlab.rb ADDED
@@ -0,0 +1,624 @@
1
+ =begin rdoc
2
+
3
+ == BitLab
4
+
5
+ Classes used in experiments on binary representations, including Huffman trees.
6
+
7
+ =end
8
+
9
+ module RubyLabs
10
+
11
+ module BitLab
12
+
13
+ =begin rdoc
14
+ Make a unique binary code for each item in array +a+, returning a Hash that
15
+ associates each item with its code.
16
+ =end
17
+
18
+ def make_codes(a)
19
+ n = log2(a.length).ceil
20
+ res = Hash.new
21
+ a.each_with_index do |x,i|
22
+ res[x] = i.code(n)
23
+ end
24
+ return res
25
+ end
26
+
27
+ =begin rdoc
28
+ Print the codes in +a+ (an associative array made by +make_codes+ or the
29
+ Huffman tree +assign_codes+ method). An option specifies how to order the
30
+ output, either +:by_code+ or +:by_name+.
31
+ =end
32
+
33
+ def print_codes(a, mode = :by_code)
34
+ if mode == :by_code
35
+ a.sort { |x,y| x[1] <=> y[1] }.each do |sym, code|
36
+ printf "%s %s\n", code, sym
37
+ end
38
+ elsif mode == :by_name
39
+ width = a.keys.map{ |x| x.length }.max
40
+ a.keys.sort.each do |x|
41
+ printf "%-#{width}s %s\n", x, a[x]
42
+ end
43
+ else
44
+ raise "print_codes: mode must be :by_code or :by_name"
45
+ end
46
+ return true
47
+ end
48
+
49
+ =begin rdoc
50
+ Make a Message object for the characters in string s. The second
51
+ parameter determines the code to use. It can be :ascii, :parity, a hash
52
+ object that has code mappings for letters, or a Huffman tree. If a hash
53
+ or tree is passed, the message type is set to :packed, otherwise it's :unpacked.
54
+ The encoding type is saved so it can be used later in decoding (but see note in
55
+ documentation of +decode+).
56
+ =end
57
+
58
+ def encode(s, type, opt = nil)
59
+ if (type.class == Hash || type.class == Node)
60
+ code = (type.class == Node) ? assign_codes(type) : type
61
+ msg = Message.new(:packed)
62
+ s.each_byte do |ch|
63
+ msg << code[ch.chr]
64
+ printf("%s: %s\n", ch.chr, code[ch.chr]) if opt == :trace
65
+ end
66
+ else
67
+ msg = Message.new(:unpacked)
68
+ s.each_byte do |ch|
69
+ code = ch.code(8)
70
+ code.add_parity_bit if type == :parity
71
+ msg << code
72
+ printf("%s: %s\n", ch.chr, code) if opt == :trace
73
+ end
74
+ end
75
+ msg.encoding = type
76
+ return msg
77
+ end
78
+
79
+ =begin rdoc
80
+ Decode a message using the specified decoding scheme.
81
+
82
+ Note: see also the +decode+ method in the Message class, which assembles
83
+ a string using the coding scheme specified when the message was created.
84
+
85
+ +Message#decode+ always gives the right result -- the point of the method
86
+ here is to show what happens if the decoding scheme does not match the
87
+ encoding scheme.
88
+ =end
89
+
90
+ def decode(m, type)
91
+ raise "not a message" unless m.class == Message
92
+ res = ""
93
+ if type.class == Node # weird -- it appears case labels can't be class names...
94
+ res = huffman_decode(m, type)
95
+ elsif type.class == Code
96
+ raise "packed decode not implemented"
97
+ elsif type == :ascii
98
+ m.array.each do |x|
99
+ res << x.value.chr
100
+ end
101
+ elsif type == :parity
102
+ m.array.each do |x|
103
+ if x.even_parity?
104
+ res << (x.value >> 1).chr
105
+ else
106
+ res << "?"
107
+ end
108
+ end
109
+ else
110
+ raise "unknown option: #{type}"
111
+ end
112
+ return res
113
+ end
114
+
115
+ =begin rdoc
116
+ Simulate transmission of message +m+, adding +n+ random errors. Returns a
117
+ copy of the Message object after making +n+ calls to the +flip+ method.
118
+ =end
119
+
120
+ def garbled(m, n)
121
+ res = m.copy
122
+ n.times do
123
+ c = res.array[ rand(res.array.length) ]
124
+ c.flip( rand(c.length) )
125
+ end
126
+ return res
127
+ end
128
+
129
+
130
+ =begin rdoc
131
+ Huffman tree interface: Read letters and frequencies from file +fn+, save them in a hash indexed
132
+ by letter name
133
+ =end
134
+
135
+ def read_frequencies(fn)
136
+ a = Hash.new
137
+ File.open(fn).each do |line|
138
+ line.chomp!
139
+ next if line.length == 0
140
+ next if line[0] == ?#
141
+ x = line[/^./]
142
+ f = line[/\d+\.\d+/].to_f
143
+ a[x] = f
144
+ end
145
+ return a
146
+ end
147
+
148
+ =begin rdoc
149
+ Huffman tree interface: Build a tree using frequencies in Hash +f+.
150
+ =end
151
+
152
+ # :begin :build_tree
153
+ def build_tree(f)
154
+ pq = init_queue(f)
155
+
156
+ while pq.length > 1
157
+ n1 = pq.shift
158
+ n2 = pq.shift
159
+ pq << Node.combine( n1, n2)
160
+ end
161
+
162
+ return pq[0]
163
+ end
164
+ # :end :build_tree
165
+
166
+ =begin rdoc
167
+ Huffman tree helper procedure: Traverse +tree+, making a +Code+ object for each
168
+ leaf node, returning the codes in a Hash object. On recursive calls the
169
+ +prefix+ parameter is the set of codes on the path so far.
170
+
171
+ Students should pass a tree to +encode+, which will call this method to make the code.
172
+ The same tree should also be passed to +decode+ when they want to decode a message.
173
+ =end
174
+
175
+ # :begin :assign_codes
176
+ def assign_codes(tree, code = {}, prefix = Code.new(0,0))
177
+ if tree.char != nil
178
+ code[tree.char] = prefix
179
+ else
180
+ assign_codes(tree.left, code, prefix + 0)
181
+ assign_codes(tree.right, code, prefix + 1)
182
+ end
183
+ return code
184
+ end
185
+ # :end :assign_codes
186
+
187
+ =begin rdoc
188
+ Huffman tree helper procedure: initialize a priority queue with Node
189
+ objects for each letter in Hash +a+.
190
+ =end
191
+
192
+ # :begin :init_queue
193
+ def init_queue(a)
194
+ q = PriorityQueue.new
195
+ a.each do |x,f|
196
+ q << Node.new(x,f)
197
+ end
198
+ return q
199
+ end
200
+ # :end :init_queue
201
+
202
+ =begin rdoc
203
+ Huffman tree helper procedure: decode the binary codes in message +m+, using +tree+ as a guide.
204
+ Not intended to be called by students; they will use the top level +decode+ method
205
+ or the +decode+ method defined in the Message class.
206
+ =end
207
+
208
+ def huffman_decode(m, tree)
209
+ res = ""
210
+ path = tree
211
+ m.each do |bit|
212
+ if path.leaf?
213
+ res << path.char
214
+ path = tree
215
+ end
216
+ path = (bit == 0) ? path.left : path.right
217
+ end
218
+ res << path.char if path.leaf?
219
+ return res
220
+ end
221
+
222
+ =begin rdoc
223
+ Huffman tree utility: read strings of binary digits from a file. Each line has a binary
224
+ sequence, a tab, and an English word. Return two arrays, one a list of Message objects built
225
+ from the binary sequences and the other a parallel list of words. Since the input is sorted
226
+ by code length (shortest codes first) returning them in order allows student to choose a
227
+ word to decode based on length.
228
+ =end
229
+
230
+ def read_codes(fn)
231
+ codes = Array.new
232
+ words = Array.new
233
+ File.open(fn).each do |line|
234
+ line.chomp!
235
+ next if line.length == 0
236
+ next if line[0] == ?#
237
+ code = Code.new(0,0)
238
+ line[/[01]+/].each_byte do |byte|
239
+ code << byte[0] # add least significant digit of ASCII "0" or "1"
240
+ end
241
+ msg = Message.new(:packed)
242
+ msg << code
243
+ codes << msg
244
+ words << line[/[a-z]+/]
245
+ end
246
+ return codes, words
247
+ end
248
+
249
+ =begin rdoc
250
+ Huffman tree utility: generate a random string of length +n+ using the letter frequencies +f+.
251
+ =end
252
+
253
+ def generate_string(n, f)
254
+ s = ""
255
+ n.times do
256
+ r = rand
257
+ sum = 0
258
+ f.each do |ch,x|
259
+ sum += x
260
+ if r < sum
261
+ s += ch
262
+ break
263
+ end
264
+ end
265
+ end
266
+ return s
267
+ end
268
+
269
+ =begin rdoc
270
+ Class for nodes of a Huffman tree. All nodes have a frequency (+freq+) used to
271
+ determine its place in a priority queue. Leaf nodes have a character (+char+).
272
+ Interior nodes have a +nil+ character value, in which case +left+ and +right+
273
+ are Nodes for the roots of subtrees.
274
+
275
+ Use +new+ to create a new leaf node. Call the class method +combine+ (an alternative
276
+ constructor) to make a new interior node from two existing nodes.
277
+
278
+ The +<+ method allows Nodes to be compared so they can be ordered in a priority
279
+ queue.
280
+ =end
281
+
282
+ class Node
283
+
284
+ attr_accessor :freq, :char, :left, :right
285
+
286
+ def initialize(char,freq)
287
+ @char = char
288
+ @freq = freq
289
+ @left = @right = nil
290
+ end
291
+
292
+ def Node.combine(n1,n2)
293
+ node = Node.new(nil, n1.freq + n2.freq)
294
+ node.left = n1
295
+ node.right = n2
296
+ return node
297
+ end
298
+
299
+ def <(x)
300
+ x.class == Node && @freq < x.freq
301
+ end
302
+
303
+ def leaf?
304
+ return @char != nil
305
+ end
306
+
307
+ def inspect
308
+ if leaf?
309
+ sprintf "( %s: %.3f )", @char, @freq
310
+ else
311
+ sprintf "( %.3f %s %s )", @freq, @left.to_s, @right.to_s
312
+ end
313
+ end
314
+
315
+ alias to_s inspect
316
+
317
+ end # Node
318
+
319
+ =begin rdoc
320
+ Code objects are variable-length binary numbers representing individual letters or members
321
+ of a set. The main reason to create a Code object is so to have the value of the integer
322
+ displayed in binary or hex. To make it easier for advanced students to understand the
323
+ Huffman tree "assign_codes" method this class defines a method that attaches a bit to the
324
+ end of a code and returns a new Code object.
325
+
326
+ Students will not create Code objects directly -- instead they are created by methods in
327
+ other classes, e.g. the +code+ method added to Fixnum or the top level encode method.
328
+
329
+ There are two methods for attaching bits: << appends a bit to a code (used by the method
330
+ that makes a parity-encoded message) and +, which returns a copy of a code with the bit
331
+ added (used by the recursive method that assigns Huffman codes).
332
+
333
+ << can also be used to extend a code by appending a second code.
334
+
335
+ The index operator for Fixnums orders bits from right to left, consistent with standard
336
+ usage but the opposite of Strings and Arrays. In this module bits will be ordered from
337
+ left to right to be consistent with Strings and Arrays. The modified ordering is used
338
+ in the index operator and the +flip+ method.
339
+ =end
340
+
341
+ # TBD allow + and << on hex codes; they should have same internal rep, as binary codes, just show as hex when displaying
342
+
343
+ # Way cool -- this class used to have a maxcodesize (set to 60) to make sure codes fit within
344
+ # a single 64-bit word. But a typo during one experiment created an 80-bit code. Turns out
345
+ # indexing etc work just fine on Bignums:
346
+ #
347
+ # >> c1 = s[1].code(80)
348
+ # => 00000000000000000000000000000000000000000000000000000000000000000000000001101000
349
+ # >> c1.flip(0)
350
+ # => 10000000000000000000000000000000000000000000000000000000000000000000000001101000
351
+
352
+ class Code
353
+ attr_accessor :value, :length, :base
354
+
355
+ def initialize(value, length = 0, base = :binary)
356
+ raise "Code: unknown base: #{base}" unless [:binary, :hex].include?(base)
357
+ @value = value
358
+ @length = length
359
+ @base = base
360
+ @has_parity_bit = false
361
+ end
362
+
363
+ def +(bit)
364
+ raise "operation not defined for hex codes" unless @base == :binary
365
+ val = (@value << 1) | bit[0]
366
+ return Code.new(val, @length+1)
367
+ end
368
+
369
+ def <<(x)
370
+ raise "operation not defined for hex codes" unless @base == :binary
371
+ if x.class == Code
372
+ val = x.value # val known to fit in x.length bits
373
+ n = x.length
374
+ else
375
+ val = x[0] # val is a single bit
376
+ n = 1
377
+ end
378
+ @value <<= n
379
+ @value |= val # val can't have any higher order bits set -- see above
380
+ @length += n
381
+ return self
382
+ end
383
+
384
+ def [](i)
385
+ if i.class == Range
386
+ res = 0
387
+ n = 0
388
+ i.each { |j| res <<= 1; res |= @value[@length-j-1]; n += 1 }
389
+ return Code.new(res, n)
390
+ else
391
+ return @value[@length-i-1]
392
+ end
393
+ end
394
+
395
+ def parity_bit
396
+ bit = 0
397
+ for i in 0...@length
398
+ bit ^= @value[i]
399
+ end
400
+ return bit
401
+ end
402
+
403
+ def add_parity_bit
404
+ @has_parity_bit = true
405
+ return self << parity_bit
406
+ end
407
+
408
+ def even_parity?
409
+ return parity_bit() == 0
410
+ end
411
+
412
+ def flip(i)
413
+ raise "bit index out of range" unless i < @length
414
+ @value ^= (1 << (@length - i - 1))
415
+ return self
416
+ end
417
+
418
+ def chr
419
+ if @has_parity_bit
420
+ if even_parity?
421
+ return (@value >> 1).chr
422
+ else
423
+ return "?"
424
+ end
425
+ else
426
+ return @value.chr
427
+ end
428
+ end
429
+
430
+ def <=>(x)
431
+ return @value <=> x.value
432
+ end
433
+
434
+ def inspect
435
+ return "" if @length == 0
436
+ case @base
437
+ when :binary
438
+ return sprintf "%0#{@length}b", @value
439
+ when :hex
440
+ return sprintf "%0#{@length/4}X", @value
441
+ end
442
+ end
443
+
444
+ alias to_s inspect
445
+
446
+ end # Code
447
+
448
+ =begin rdoc
449
+ Message objects are arrays of Codes. New codes are added by a call to <<.
450
+ If the message is unpacked, each code is stored in its own array element
451
+ (use this for :ascii or :parity encodings). If the message is packed,
452
+ codes are repackaged into 8-bit codes, and individual codes may cross
453
+ array item boundaries.
454
+
455
+ The +each+ method iterates over each bit in a message. +copy+ makes a
456
+ deep copy, copying each code object (but sharing a reference to +encoding+).
457
+ =end
458
+
459
+ # TODO: allow for the possibility that a code word being added to a packed code can be longer than 8 bits
460
+
461
+ class Message
462
+
463
+ attr_accessor :packed, :array, :encoding
464
+
465
+ @@packsize = 8 # number of bits to pack into single code
466
+
467
+ def initialize(type)
468
+ raise "Message: unknown type" unless [:packed, :unpacked].include? type
469
+ if type == :packed
470
+ @packed = true
471
+ @array = [ Code.new(0,0) ]
472
+ else
473
+ @packed = false
474
+ @array = [ ]
475
+ end
476
+ end
477
+
478
+ def copy
479
+ dup = self.clone # copies @packed, @encoding
480
+ dup.array = Array.new
481
+ @array.each { |x| dup.array << x.clone } # deep copy of @array
482
+ return dup
483
+ end
484
+
485
+ def each
486
+ @array.each do |byte|
487
+ for i in 0...byte.length
488
+ yield(byte[i])
489
+ end
490
+ end
491
+ return self
492
+ end
493
+
494
+ def <<(x)
495
+ raise "Message#<<: not a code" unless x.class == Code
496
+ if @packed
497
+ if @array[-1].length + x.length <= @@packsize
498
+ @array[-1] << x
499
+ else
500
+ n = @@packsize - @array[-1].length
501
+ m = x.length - n
502
+ @array[-1] << x[0...n]
503
+ @array << x[n...x.length]
504
+ end
505
+ else
506
+ @array << x
507
+ end
508
+ return self
509
+ end
510
+
511
+ def decode
512
+ res = ""
513
+ if @encoding.class == Symbol
514
+ @array.each do |code|
515
+ res << code.chr
516
+ end
517
+ elsif @encoding.class == Node
518
+ res = huffman_decode(self, @encoding)
519
+ else
520
+ res = unpack
521
+ end
522
+ return res
523
+ end
524
+
525
+ def unpack
526
+ "unpack: not implemented"
527
+ end
528
+
529
+ def length
530
+ @array.inject(0) { |sum, code| sum += code.length }
531
+ end
532
+
533
+ def print(option)
534
+ "a message"
535
+ end
536
+
537
+ def inspect
538
+ if @packed
539
+ return @array.join("")
540
+ else
541
+ return @array.join(" ")
542
+ end
543
+ end
544
+
545
+ alias to_s inspect
546
+
547
+ end # Message
548
+
549
+ end # BitLab
550
+
551
+ end # RubyLabs
552
+
553
+
554
+ class Fixnum
555
+
556
+ =begin rdoc
557
+ Add a method to +Fixnum+ to make a +Code+ object showing the binary or
558
+ hexadecimal representation of a number. Intended mainly to show codes for
559
+ characters in +String+ objects.
560
+ Usage:
561
+ x.code binary, size = log2 bits
562
+ x.code(n) binary, size = n bits
563
+ x.code(:hex) hex, size = log2 bits
564
+ x.code(:hex,n) hex, size = 4*n bits
565
+ =end
566
+
567
+ def code(*args)
568
+ if args.first == :hex
569
+ base = args.shift
570
+ else
571
+ base = :binary
572
+ end
573
+ if args.first.kind_of? Integer
574
+ digits = args.shift
575
+ elsif args.empty?
576
+ b = (self == 0) ? 1 : log2(self+1).ceil
577
+ digits = (base == :hex) ? (b/4.0).ceil : b
578
+ else
579
+ raise "code: can't understand #{args}"
580
+ end
581
+ bits = (base == :hex) ? digits * 4 : digits
582
+ return Code.new(self, bits, base)
583
+ end
584
+
585
+ end
586
+
587
+ =begin rdoc
588
+ Priority queue class -- simple wrapper for an array that can only be updated via
589
+ +<<+ and +shift+ operations. Also responds to +length+, +first+, and +last+,
590
+ and allows direct access to an item through an index expression, but does not allow
591
+ assignment via an index or any other array operation.
592
+ The +<<+ method checks to make sure an object is comparable (responds to <) before
593
+ adding it to the queue.
594
+ =end
595
+
596
+
597
+ # Note: defined outside rubylabs, so it's a top level class -- move to own file, require here?
598
+
599
+ class PriorityQueue
600
+
601
+ def initialize
602
+ @q = Array.new
603
+ end
604
+
605
+ def <<(obj)
606
+ raise "Object cannot be inserted into priority queue" unless obj.respond_to?(:<)
607
+ i = 0
608
+ while (i < @q.length)
609
+ break if obj < @q[i]
610
+ i += 1
611
+ end
612
+ @q.insert(i, obj)
613
+ end
614
+
615
+ %w{shift length first last to_s inspect}.each do |name|
616
+ eval "def #{name}() @q.#{name} end"
617
+ end
618
+
619
+ def [](i)
620
+ @q[i]
621
+ end
622
+
623
+ end # PriorityQueue
624
+