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.
- data/LICENSE +20 -0
- data/README.rdoc +9 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/bin/bb.rb +12 -0
- data/bin/statistics2-0.53/ext/extconf.rb +11 -0
- data/bin/statistics2-0.53/ext/show.rb +11 -0
- data/bin/statistics2-0.53/ext/t.rb +46 -0
- data/bin/statistics2-0.53/mklist.rb +26 -0
- data/bin/statistics2-0.53/sample-tbl.rb +129 -0
- data/bin/statistics2-0.53/statistics2.rb +532 -0
- data/bin/statistics2-0.53/t-inv.rb +54 -0
- data/bin/statistics2.rb +532 -0
- data/data/aafreq.txt +20 -0
- data/data/cars.txt +50 -0
- data/data/century.txt +1 -0
- data/data/colors.txt +64 -0
- data/data/earth.yaml +15 -0
- data/data/fruit.txt +45 -0
- data/data/hacodes.txt +35 -0
- data/data/hafreq.txt +16 -0
- data/data/hvfreq.txt +5 -0
- data/data/nbody.R +23 -0
- data/data/nbody.out +731 -0
- data/data/nbody.pdf +3111 -0
- data/data/nbody.png +0 -0
- data/data/nbody3d.pdf +3201 -0
- data/data/outer.pdf +182785 -0
- data/data/solar.dat +36501 -0
- data/data/solarsystem.txt +17 -0
- data/data/suits.txt +1 -0
- data/data/wordlist.txt +210653 -0
- data/lib/bitlab.rb +624 -0
- data/lib/elizalab.rb +523 -0
- data/lib/encryptionlab.rb +42 -0
- data/lib/hashlab.rb +224 -0
- data/lib/introlab.rb +14 -0
- data/lib/iterationlab.rb +130 -0
- data/lib/randomlab.rb +294 -0
- data/lib/recursionlab.rb +228 -0
- data/lib/rubylabs.rb +507 -0
- data/lib/sievelab.rb +58 -0
- data/lib/sortlab.rb +213 -0
- data/lib/spherelab.rb +352 -0
- data/lib/temps.rb +41 -0
- data/lib/tsplab.rb +416 -0
- data/lib/viewer.rb +65 -0
- data/test/bit_test.rb +175 -0
- data/test/encryption_test.rb +20 -0
- data/test/iteration_test.rb +40 -0
- data/test/random_test.rb +64 -0
- data/test/recursion_test.rb +47 -0
- data/test/rubylabs_test.rb +18 -0
- data/test/sieve_test.rb +28 -0
- data/test/sphere_test.rb +130 -0
- data/test/temps_test.rb +24 -0
- data/test/test_helper.rb +18 -0
- 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
|