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/sortlab.rb ADDED
@@ -0,0 +1,213 @@
1
+
2
+ =begin rdoc
3
+
4
+ == SortLab
5
+
6
+ Instrumented versions of the sorting algorithms described in the chapters
7
+ on iterative algorithms and recursive algorithms. The versions here are
8
+ not intended to be viewed by students -- those implementations are in the
9
+ modules named IterationLab and RecursionLab.
10
+
11
+ The three public methods implemented here are +isort+ (insertion sort),
12
+ +msort+ (mergesort), and +qsort+ (quicksort). The only required parameter
13
+ is an array to sort. A method will make a copy of the parameter and return
14
+ a sorted version of the copy.
15
+
16
+ An optional second parameter can be used when running experiments:
17
+ :trace print the state of the array after each iteration of the outer loop
18
+ :count return a count of the number of comparisons made instead of the sorted array
19
+ :timer return the execution time in seconds instead of the sorted array
20
+
21
+ =end
22
+
23
+ module RubyLabs
24
+
25
+ module SortLab
26
+
27
+ =begin rdoc
28
+
29
+ The Timer class implements a simpler timer. Call +Timer.start+ to start
30
+ the timer, and +Timer.stop+ to get the elapsed time since the call to
31
+ +Timer.start+.
32
+ =end
33
+
34
+ class Timer
35
+
36
+ def Timer.start
37
+ @@tstart = Time.now
38
+ end
39
+
40
+ def Timer.stop
41
+ return Time.now - @@tstart
42
+ end
43
+
44
+ end
45
+
46
+ =begin rdoc
47
+ Insertion sort.
48
+ =end
49
+
50
+ def isort(a, mode = nil)
51
+ a = a.clone
52
+ Timer.start if mode == :timer
53
+ count = 0
54
+ for i in 1..a.length - 1
55
+ puts isort_brackets(a,i) if mode == :trace
56
+ x = a.slice!(i)
57
+ j = i - 1
58
+ while j >= 0 && (count += 1) > 0 && a[j] > x
59
+ j = j - 1
60
+ end
61
+ a.insert(j + 1, x)
62
+ end
63
+ return case mode
64
+ when :count : count
65
+ when :timer : Timer.stop
66
+ else a
67
+ end
68
+ end
69
+
70
+ =begin rdoc
71
+ Helper method for printing the state of the array during insertion sort.
72
+ =end
73
+
74
+ def isort_brackets(a, i)
75
+ pre = (i == 0) ? [] : a.slice(0..(i-1))
76
+ post = (i <= a.length) ? a.slice(i..-1) : []
77
+ return "[" + pre.join(", ") + "] [" + post.join(", ") + "]"
78
+ end
79
+
80
+
81
+ # Merge sort. Makes a copy of the input Array, returns a sorted
82
+ # version of the copy. Uses a "helper function" named merge to
83
+ # combine successively bigger pieces of the input array.
84
+
85
+ def msort(a, mode = nil)
86
+ a = a.clone # don't modify the input array!
87
+ Timer.start
88
+ n = 1 # size of "pile" at each step
89
+ count = 0
90
+ while n < a.length
91
+ i = 0 # first pile starts here
92
+ while i < a.length
93
+ count += merge(a,i,n) # merge piles at a[i] and i+n, put at a[i]
94
+ print " [" + a[i..i+2*n-1].join(" ") + "] " if mode == :trace
95
+ i += 2*n # next pile starts at i+2n
96
+ end
97
+ puts if mode == :trace
98
+ n *= 2 # double the pile size
99
+ end
100
+ if mode == :timer
101
+ return Timer.stop
102
+ elsif mode == :count
103
+ return count
104
+ else
105
+ return a
106
+ end
107
+ end
108
+
109
+ # "Helper function" to merge two "piles" in place. A call to this method
110
+ # merges n-element lists at a[i] and a[i+n] and stores the merged result
111
+ # in the array starting at a[i]. Uses an auxiliary list to hold items moved
112
+ # from a[i..i+n-1], then merges those into place at a[i+n].
113
+
114
+ def merge(a, i, n)
115
+ aux = []
116
+ j = k = i + n
117
+ kmax = i + 2*n
118
+ kmax = a.length if kmax > a.length
119
+ count = 0
120
+ # phase 1 -- copy items from a[i..i+n-1] to aux
121
+ while i < k
122
+ if a[j] && a[j] < a[i] && (aux.empty? || a[j] < aux[0])
123
+ aux << a[i]
124
+ a[i] = a[j]
125
+ j += 1
126
+ count += 1
127
+ elsif !aux.empty? && aux[0] < a[i]
128
+ aux << a[i]
129
+ a[i] = aux.shift
130
+ count += 1
131
+ end
132
+ i += 1
133
+ end
134
+ # phase 2 -- merge aux into empty slots in a[i+n..i+2n]
135
+ while k < kmax && ! aux.empty?
136
+ if j == kmax || a[j] > aux[0]
137
+ a[k] = aux.shift
138
+ else
139
+ a[k] = a[j]
140
+ j += 1
141
+ end
142
+ k += 1
143
+ count += 1
144
+ end
145
+ return count
146
+ end
147
+
148
+ # QuickSort, based on the description from Cormen et al. The interface is
149
+ # a method qsort, called with the array to sort and an optional mode parameter
150
+ # that specifies whether to count comparisons or measure execution time.
151
+
152
+ # The actual sorting is done by qs. The parameters (using notation from
153
+ # Cormen et al): p is the left boundary of the region to sort, and r is
154
+ # right boundary. The call to partition sets q, the new boundary between
155
+ # two sub-regions.
156
+
157
+ def qsort(a, mode = nil)
158
+ dup = a.clone
159
+ Timer.start
160
+ if mode == :timer
161
+ return Timer.stop
162
+ else
163
+ count = qs(dup, 0, a.length-1, mode)
164
+ return mode == :count ? count : dup
165
+ end
166
+ end
167
+
168
+ def qs(a, p, r, mode)
169
+ puts bracketed(a,p,r) if mode == :trace
170
+ if p < r
171
+ q, count = partition(a, p, r)
172
+ count += qs(a, p, q, mode)
173
+ count += qs(a, q+1, r, mode)
174
+ else
175
+ count = 0
176
+ end
177
+ return count
178
+ end
179
+
180
+ # Set the pivot (called x here) to the item on the left edge of the
181
+ # region, then extend the regions until they meet in the middle
182
+
183
+ def partition(a, p, r)
184
+ x = a[p]
185
+ i = p - 1
186
+ j = r + 1
187
+ count = 0
188
+ while true
189
+ loop { j = j - 1; count += 1; break if a[j] <= x }
190
+ loop { i = i + 1; count += 1; break if a[i] >= x }
191
+ if i < j
192
+ a[i], a[j] = a[j], a[i]
193
+ else
194
+ return j, count
195
+ end
196
+ end
197
+ end
198
+
199
+ def bracketed(a, left, right)
200
+ # puts "#{left}..#{right}"
201
+ tmp = []
202
+ tmp += a[ 0 .. (left-1) ] if left > 0
203
+ tmp << "["
204
+ tmp += a[ left .. right ] if right >= 0
205
+ tmp << "]"
206
+ tmp += a[ (right+1) .. (a.length-1) ] if right < a.length
207
+ return tmp.join(" ")
208
+ end
209
+
210
+ end # RecursionLab
211
+
212
+ end # RubyLabs
213
+
data/lib/spherelab.rb ADDED
@@ -0,0 +1,352 @@
1
+
2
+ =begin rdoc
3
+
4
+ == SphereLab
5
+
6
+ Definition of Vector and Body objects used for n-body simulations.
7
+
8
+ =end
9
+
10
+ module RubyLabs
11
+
12
+ module SphereLab
13
+
14
+ =begin rdoc
15
+ The constant G is the universal gravitational constant, assuming mass is
16
+ in units of kilograms, distances are in meters, and time is in seconds.
17
+ =end
18
+
19
+ G = 6.67E-11
20
+
21
+
22
+ class Vector
23
+ attr_accessor :x, :y, :z
24
+
25
+ =begin rdoc
26
+ Make a new vector with the specified x, y, and z components.
27
+ =end
28
+
29
+ def initialize(*args)
30
+ @x, @y, @z = args
31
+ end
32
+
33
+ def inspect
34
+ sprintf "<%s, %s, %s>", @x.to_s, @y.to_s, @z.to_s
35
+ end
36
+
37
+ =begin rdoc
38
+ v1 == v2 if the three components are the same
39
+ =end
40
+
41
+ def ==(v)
42
+ return (@x == v.x) && (@y == v.y) && (@z == v.z)
43
+ end
44
+
45
+ =begin rdoc
46
+ Arithmetic methods are invoked for a vector v1 when Ruby evaluates an expression of the
47
+ form v1 <op> v2 where <op> is +, -, or *. They create a a new vector containing the
48
+ result of the operation. + and - do element-wise addition or and subtraction, *
49
+ is a vector-scalar multiplication.
50
+ =end
51
+
52
+ def +(v)
53
+ Vector.new(@x + v.x, @y + v.y, @z + v.z)
54
+ end
55
+
56
+ def -(v)
57
+ Vector.new(@x - v.x, @y - v.y, @z - v.z)
58
+ end
59
+
60
+ def *(a)
61
+ Vector.new(@x * a, @y * a, @z * a)
62
+ end
63
+
64
+ =begin rdoc
65
+ v1.add(v2) adds the components of v2 to v1 -- would be v1 += v2 if Ruby
66
+ allowed us to overload +=
67
+ =end
68
+
69
+ def add(v)
70
+ @x += v.x
71
+ @y += v.y
72
+ @z += v.z
73
+ self
74
+ end
75
+
76
+ =begin rdoc
77
+ v1.sub(v2) subtracts the components of v2 from v1 -- would be v1 -= v2 if Ruby
78
+ allowed us to overload -=
79
+ =end
80
+
81
+ def sub(v)
82
+ @x -= v.x
83
+ @y -= v.y
84
+ @z -= v.z
85
+ self
86
+ end
87
+
88
+ =begin rdoc
89
+ The magnitude of a vector is the Euclidean norm: sqrt(x**2 + y**2 + z**2)
90
+ =end
91
+
92
+ def norm
93
+ Math.sqrt(@x*@x + @y*@y + @z*@z)
94
+ end
95
+
96
+ end # Vector
97
+
98
+
99
+ =begin rdoc
100
+ A Body object represents the state of a celestial body. A body has mass (a scalar),
101
+ position (a vector), and velocity (a vector). A third vector, named force, is used
102
+ when calculating forces acting on a body. The size and color attributes are used by
103
+ the visualization methods.
104
+ =end
105
+
106
+ class Body
107
+ attr_accessor :mass, :position, :velocity, :force, :name, :size, :color
108
+
109
+ =begin rdoc
110
+ The constructor can be called with a variety of different arguments:
111
+ * for the main n-body simulation, initial conditions are read from a data file,
112
+ and the constructor is passed three parameters: mass, position vector, and
113
+ velocity vector; an optional 4th parameter is a name string
114
+ * for interactive experiments, pass two floats, which become the mass and the
115
+ x component of the velocity; an optional 3rd parameter is the name
116
+ * also for interactive experiments pass :rand to make a body with random
117
+ mass, position, and velocity
118
+ * pass no parameters to get a body with all attributes set to 0
119
+ =end
120
+
121
+ def initialize(*args)
122
+ if args[1].class == Vector
123
+ @mass, @position, @velocity, @name = args
124
+ elsif args[0] == :random
125
+ @mass = rand(10000) * 1e6
126
+ @position = Vector.new(rand(300)-150, rand(300)-150, 0)
127
+ @velocity = Vector.new(rand(20)-10, rand(10)-5, 0)
128
+ @name = "b" + self.object_id.to_s
129
+ elsif args[0].is_a?(Numeric) && args[1].is_a?(Numeric)
130
+ @mass = args[0]
131
+ @position = Vector.new(args[1], 0.0, 0.0)
132
+ @velocity = Vector.new(0.0, 0.0, 0.0)
133
+ @name = args[2]
134
+ @linear = true
135
+ else
136
+ @mass = 0.0
137
+ @position = Vector.new(0.0, 0.0, 0.0)
138
+ @velocity = Vector.new(0.0, 0.0, 0.0)
139
+ @name = nil
140
+ end
141
+ @force = Vector.new(0.0, 0.0, 0.0)
142
+ end
143
+
144
+ def inspect
145
+ s = ""
146
+ s << @name + ": " if @name
147
+ s << @mass.to_s + "g "
148
+ if @linear
149
+ s << "x: " + @position.x.to_s
150
+ else
151
+ s << @position.inspect + " " + @velocity.inspect
152
+ end
153
+ return s
154
+ end
155
+
156
+ =begin rdoc
157
+ Reset the force vector to 0 (to get ready for a new round of interaction calculations).
158
+ =end
159
+
160
+ def clear_force
161
+ @force.x = 0.0
162
+ @force.y = 0.0
163
+ @force.z = 0.0
164
+ end
165
+
166
+ =begin rdoc
167
+ Compute force acting on this body wrt body b
168
+ =end
169
+
170
+ def add_force(b)
171
+ r = @position - b.position
172
+ nr = r.norm ** 3
173
+ mr = b.mass / nr
174
+ @force.add(r * mr)
175
+ end
176
+
177
+ =begin rdoc
178
+ Move this body by applying current force vector for dt seconds
179
+ =end
180
+
181
+ def move(dt)
182
+ acc = @force * G * -1.0
183
+ @velocity.add( acc * dt )
184
+ @position.add( @velocity * dt )
185
+ end
186
+
187
+ =begin rdoc
188
+ Class method to compute the interaction between bodies b1 and b2 and update
189
+ their force vectors.
190
+ =end
191
+
192
+ def Body.interaction(b1, b2)
193
+ r = b1.position - b2.position
194
+ a = r.norm ** 3
195
+ b1.force.add(r * (b2.mass / a))
196
+ b2.force.add(r * (-b1.mass / a))
197
+ end
198
+
199
+ end # Body
200
+
201
+ =begin rdoc
202
+ Make a list of Body objects using data in the file +fn+
203
+ =end
204
+
205
+ def read_bodies(fn)
206
+ bodies = []
207
+ File.open(fn).each do |line|
208
+ line.strip!
209
+ next if line.length == 0
210
+ next if line[0] == ?#
211
+ a = line.chomp.split
212
+ for i in 1..7
213
+ a[i] = a[i].to_f
214
+ end
215
+ b = Body.new( a[1], Vector.new(a[2],a[3],a[4]), Vector.new(a[5],a[6],a[7]), a[0] )
216
+ b.size = a[-2].to_i
217
+ b.color = a[-1]
218
+ bodies << b
219
+ end
220
+ return bodies
221
+ end
222
+
223
+ =begin rdoc
224
+ Call SphereLab::step(bodies, time) to calculate the pairwise interactions between all
225
+ Body objects in the array +bodies+ and then compute their new positions after +time+ seconds.
226
+ =end
227
+
228
+ # :begin :step
229
+ def step(bodies, time)
230
+ nb = bodies.length
231
+
232
+ for i in 0...nb # compute all pairwise interactions
233
+ for j in (i+1)...nb
234
+ Body.interaction( bodies[i], bodies[j] )
235
+ end
236
+ end
237
+
238
+ bodies.each do |b|
239
+ b.move(time) # apply the accumulated forces
240
+ b.clear_force # reset force to 0 for next round
241
+ end
242
+ end
243
+ # :end :step
244
+
245
+ def random_vectors(r, i, n)
246
+ theta = (2 * Math::PI / n) * i + (Math::PI * rand / n)
247
+ radius = r + (r/3)*(rand-0.5)
248
+ x = radius * Math.cos(theta)
249
+ y = radius * Math.sin(theta)
250
+ vtheta = (Math::PI - theta) * -1 + Math::PI * (rand-0.5)
251
+ vx = radius/20 * Math.cos(vtheta)
252
+ vy = radius/20 * Math.sin(vtheta)
253
+ return Vector.new(x, y, 0), Vector.new(vx, vy, 0)
254
+ end
255
+
256
+ def random_velocity(v, r)
257
+ res = Vector.new(-r * Math.cos)
258
+ end
259
+
260
+ def random_bodies(n, big = 1)
261
+ res = []
262
+ mm = 1e12 # average mass
263
+ mr = 150 # average distance from origin
264
+ write_tuple( :scale, [ 1.0 ] )
265
+ big.times do |i|
266
+ r0, v0 = random_vectors(mr/2, i, big)
267
+ res << Body.new(mm*100/big, r0, v0)
268
+ write_tuple( :view, [i, 10, "#0080ff"] )
269
+ end
270
+ (n-big).times do |i|
271
+ r, v = random_vectors(mr, i, n-big)
272
+ b = Body.new(mm, r, v)
273
+ b.name = "b" + (i+1).to_s
274
+ write_tuple( :view, [i+big, 5, "#0080ff"] )
275
+ res << b
276
+ end
277
+ return res
278
+ end
279
+
280
+ def urey_hall
281
+ write_tuple( :scale, [ 1e-7 ] )
282
+ # a 6.6 lb watermelon 7 stories (84 feet) above the surface of the earth:
283
+ w = Body.new(3000, 6.37101e6 + 25)
284
+ w.name = "watermelon"
285
+ w.size = 2
286
+ w.color = "#ff6666"
287
+ write_tuple( :view, [1, w.size, w.color] )
288
+ # the earth:
289
+ e = Body.new(5.9736E+24, 0)
290
+ e.name = "earth"
291
+ e.size = 100
292
+ e.color = "#004080"
293
+ write_tuple( :view, [0, e.size, e.color] )
294
+ return [e, w]
295
+ end
296
+
297
+ def four_body
298
+ write_tuple( :scale, [ 0.5 ] )
299
+ a = Body.new(1.4e18, Vector.new(50, 70, 0), Vector.new(0, 1, 0))
300
+ write_tuple( :view, [0, 10, "#ff6666"] )
301
+ b = Body.new(1.2e16, Vector.new(50, 225, 0), Vector.new(-1000, 2, 0))
302
+ write_tuple( :view, [1, 3, "#ff6666"] )
303
+ c = Body.new(2.1e16, Vector.new(175, -75, 0), Vector.new(-1, -5, 0))
304
+ write_tuple( :view, [2, 5, "#ff6666"] )
305
+ d = Body.new(4.5e16, Vector.new(300, -75, 0), Vector.new(-5, 2, 0))
306
+ write_tuple( :view, [3, 6, "#ff6666"] )
307
+ return [a,b,c,d]
308
+ end
309
+
310
+ =begin rdoc
311
+ Attach a probe to the step method, have it call this method to show the
312
+ current state of the simulation.
313
+ =end
314
+
315
+ def display(bodies)
316
+ for i in 0...bodies.length
317
+ write_tuple( :body, [i, bodies[i].position.x, bodies[i].position.y] )
318
+ end
319
+ write_tuple( :timestep, [] )
320
+ return true
321
+ end
322
+
323
+ =begin rdoc
324
+ Call this method to create a "scope" for viewing the state of an n-body simulation.
325
+ Also creates a proc that will be executed when IRB exits to shut down the tuple
326
+ space and viewer.
327
+ =end
328
+
329
+ def make_viewer
330
+ include Viewer
331
+ launch_viewer("nbview.rb")
332
+ end
333
+
334
+ =begin rdoc
335
+ Methods used in IRB sessions.
336
+ =end
337
+
338
+ # :begin :dist
339
+ def dist(r, t)
340
+ return r * t
341
+ end
342
+ # :end :dist
343
+
344
+ # :begin :falling
345
+ def falling(t)
346
+ return 0.5 * 9.8 * t**2
347
+ end
348
+ # :end :falling
349
+
350
+ end # SphereLab
351
+
352
+ end # RubyLabs
data/lib/temps.rb ADDED
@@ -0,0 +1,41 @@
1
+
2
+ =begin rdoc
3
+
4
+ == Ruby Workbench
5
+
6
+ Chapter 2, <em>The Ruby Workbench</em>, is an introduction to Ruby and IRB. There
7
+ are no "experiments," but the chapter does describe two methods. The code here is
8
+ available so students can make a copy by calling the +checkout+ method.
9
+
10
+ =end
11
+
12
+ module RubyLabs
13
+
14
+ module Temps
15
+
16
+ =begin rdoc
17
+ Convert the temperature +f+ (in degrees Fahrenheit) into the equivalent
18
+ temperature in degrees Celsius.
19
+ =end
20
+
21
+ # :begin :celsius
22
+ def celsius(f)
23
+ (f - 32) * 5 / 9
24
+ end
25
+ # :end :celsius
26
+
27
+ =begin rdoc
28
+ Fill in the body of this method with an equation that converts a number of
29
+ degrees on the Celsius scale to the equivalent on the Fahrenheit scale.
30
+ =end
31
+
32
+ # :begin :fahrenheit
33
+ def fahrenheit(c)
34
+
35
+ end
36
+ # :end :fahrenheit
37
+
38
+
39
+ end # Temp
40
+
41
+ end # RubyLabs