sieve 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +20 -7
- data/Rakefile +33 -0
- data/ext/sieve/sieve.c +11 -11
- data/features/sieve.feature +8 -4
- data/features/step_definitions/sieve_steps.rb +19 -8
- data/lib/sieve.rb +1 -0
- metadata +4 -8
- data/benchmark.rb +0 -32
data/README.markdown
CHANGED
@@ -13,7 +13,7 @@ Install with Rubygems:
|
|
13
13
|
A method is added to the Numeric class.
|
14
14
|
|
15
15
|
5.sieve # [2, 3, 5]
|
16
|
-
20.sieve # [2, 3, 5, 7, 11, 13, 17]
|
16
|
+
20.sieve # [2, 3, 5, 7, 11, 13, 17, 19]
|
17
17
|
|
18
18
|
## Benchmarks
|
19
19
|
|
@@ -37,17 +37,30 @@ As far as I know, this is the most optimized pure Ruby sieve written.
|
|
37
37
|
|
38
38
|
The benchmarks were run on a 2.8GHz Intel Core 2 Duo MacBook Pro with 8 GB memory.
|
39
39
|
|
40
|
-
|
40
|
+
ruby 1.8.6 (2010-02-05 patchlevel 399) [i686-darwin10.4.0]
|
41
41
|
|
42
42
|
user system total real
|
43
|
-
sieve method
|
44
|
-
Numeric#sieve 1.
|
43
|
+
sieve method 41.430000 0.600000 42.030000 ( 42.261872)
|
44
|
+
Numeric#sieve 1.010000 0.250000 1.260000 ( 1.342326)
|
45
45
|
|
46
|
-
|
46
|
+
ruby 1.8.7 (2010-08-16 patchlevel 302) [i686-darwin10.4.0]
|
47
47
|
|
48
48
|
user system total real
|
49
|
-
sieve method
|
50
|
-
Numeric#sieve 0.
|
49
|
+
sieve method 41.720000 0.730000 42.450000 ( 43.640117)
|
50
|
+
Numeric#sieve 0.960000 0.380000 1.340000 ( 1.385924)
|
51
|
+
|
52
|
+
ruby 1.8.7 (2010-04-19 patchlevel 253) [i686-darwin10.4.0], MBARI 0x6770, Ruby Enterprise Edition 2010.02
|
53
|
+
|
54
|
+
user system total real
|
55
|
+
sieve method 42.800000 0.910000 43.710000 ( 45.105879)
|
56
|
+
Numeric#sieve 1.090000 0.370000 1.460000 ( 1.517832)
|
57
|
+
|
58
|
+
|
59
|
+
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]
|
60
|
+
|
61
|
+
user system total real
|
62
|
+
sieve method 22.800000 0.670000 23.470000 ( 24.132390)
|
63
|
+
Numeric#sieve 1.000000 0.380000 1.380000 ( 1.422877)
|
51
64
|
|
52
65
|
## Author
|
53
66
|
|
data/Rakefile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require "rake"
|
3
|
+
require "benchmark"
|
4
|
+
require "sieve"
|
3
5
|
|
4
6
|
require "rake/extensiontask"
|
5
7
|
Rake::ExtensionTask.new("sieve") do |extension|
|
@@ -12,3 +14,34 @@ Cucumber::Rake::Task.new(:cucumber => [:clean, :compile]) do |t|
|
|
12
14
|
end
|
13
15
|
|
14
16
|
task :default => :cucumber
|
17
|
+
|
18
|
+
desc "Benchmark C implementation against pure Ruby implementation of the Sieve"
|
19
|
+
task(:benchmark => [:clean, :compile]) do
|
20
|
+
def sieve(n)
|
21
|
+
numbers = (0..n).map {|i| i }
|
22
|
+
numbers[0] = numbers[1] = nil
|
23
|
+
numbers.each do |num|
|
24
|
+
next unless num
|
25
|
+
break if num**2 > n
|
26
|
+
(num**2).step(n, num) {|idx| numbers[idx] = nil }
|
27
|
+
end
|
28
|
+
numbers.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
Benchmark.bmbm(15) do |benchmark|
|
32
|
+
range = (0..1000000)
|
33
|
+
step = 10000
|
34
|
+
|
35
|
+
benchmark.report("sieve method") do
|
36
|
+
range.step(step) do |i|
|
37
|
+
sieve(i)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
benchmark.report("Numeric#sieve") do
|
42
|
+
range.step(step) do |i|
|
43
|
+
i.sieve
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/ext/sieve/sieve.c
CHANGED
@@ -10,22 +10,23 @@ void Init_sieve() {
|
|
10
10
|
}
|
11
11
|
|
12
12
|
static VALUE sieve(const VALUE self) {
|
13
|
-
if(
|
14
|
-
long number =
|
13
|
+
if(NUM2LONG(self) < 2) { return Qnil; }
|
14
|
+
long number = NUM2LONG(self) + 1,
|
15
15
|
* numbers = malloc(number * sizeof(long));
|
16
16
|
|
17
|
-
if(numbers == NULL) {
|
18
|
-
|
19
|
-
long i;
|
20
|
-
for(i = 0; i < number; i++) {
|
21
|
-
numbers[i] = i;
|
17
|
+
if(numbers == NULL) {
|
18
|
+
rb_raise(rb_eNoMemError, "Can't allocate enough memory.");
|
22
19
|
}
|
20
|
+
|
23
21
|
numbers[0] = numbers[1] = -1;
|
22
|
+
long i;
|
23
|
+
for(i = 2; i < number; i++) { numbers[i] = i; }
|
24
24
|
|
25
25
|
long current_square;
|
26
26
|
for(i = 0; i < number; i++) {
|
27
|
+
if(numbers[i] == -1) { continue; }
|
28
|
+
|
27
29
|
current_square = powl(i, 2);
|
28
|
-
if(numbers[i] == -1) { continue; }
|
29
30
|
if(current_square > number) { break; }
|
30
31
|
|
31
32
|
long n;
|
@@ -36,9 +37,8 @@ static VALUE sieve(const VALUE self) {
|
|
36
37
|
|
37
38
|
VALUE primes_array = rb_ary_new();
|
38
39
|
for(i = 0; i < number; i++) {
|
39
|
-
if(numbers[i]
|
40
|
-
|
41
|
-
}
|
40
|
+
if(numbers[i] == -1) { continue; }
|
41
|
+
rb_ary_push(primes_array, LONG2FIX(numbers[i]));
|
42
42
|
}
|
43
43
|
|
44
44
|
free(numbers);
|
data/features/sieve.feature
CHANGED
@@ -4,15 +4,15 @@ Feature: Sieve of Eratosthenes
|
|
4
4
|
I should be able to call a sieve on a numeric to find its primes
|
5
5
|
|
6
6
|
Scenario Outline: Find primes effectively
|
7
|
-
When I
|
7
|
+
When I calculate the sieve for <number>
|
8
8
|
Then I should have the primes <primes>
|
9
9
|
Examples:
|
10
10
|
| number | primes |
|
11
11
|
| -12345 | nil |
|
12
12
|
| -5 | nil |
|
13
13
|
| -1 | nil |
|
14
|
-
| 0 |
|
15
|
-
| 1 |
|
14
|
+
| 0 | nil |
|
15
|
+
| 1 | nil |
|
16
16
|
| 2 | 2 |
|
17
17
|
| 3 | 2,3 |
|
18
18
|
| 4 | 2,3 |
|
@@ -23,7 +23,11 @@ Feature: Sieve of Eratosthenes
|
|
23
23
|
| 30 | 2,3,5,7,11,13,17,19,23,29 |
|
24
24
|
| 110 | 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109 |
|
25
25
|
|
26
|
+
Scenario: Allocating memory for the sieve raises an exception if it fails
|
27
|
+
When I calculate the sieve for 1000000000000000000
|
28
|
+
Then a NoMemoryError should be raised
|
29
|
+
|
26
30
|
Scenario: Prime tables
|
27
31
|
When I load all the primes from "features/support/primes.txt"
|
28
|
-
And I
|
32
|
+
And I calculate the sieve for the last number
|
29
33
|
Then I should have all primes from "features/support/primes.txt"
|
@@ -1,15 +1,16 @@
|
|
1
|
-
When /^I
|
2
|
-
|
1
|
+
When /^I calculate the sieve for (\-?\d+)$/ do |number|
|
2
|
+
begin
|
3
|
+
@result = number.to_i.sieve
|
4
|
+
rescue Exception => @sieve_exception
|
5
|
+
end
|
3
6
|
end
|
4
7
|
|
5
8
|
Then /^I should have the primes (.*)$/ do |primes|
|
6
9
|
case primes
|
7
10
|
when "nil"
|
8
11
|
@result.should be_nil
|
9
|
-
when ""
|
10
|
-
@result.should be_empty
|
11
12
|
else
|
12
|
-
@primes = primes.split(",").map
|
13
|
+
@primes = primes.split(",").map {|prime| prime.to_i }
|
13
14
|
@result.should == @primes
|
14
15
|
end
|
15
16
|
end
|
@@ -19,17 +20,27 @@ When /^I load all the primes from "([^"]*)"$/ do |path|
|
|
19
20
|
@primes.should_not be_empty
|
20
21
|
end
|
21
22
|
|
22
|
-
When /^I
|
23
|
-
|
23
|
+
When /^I calculate the sieve for the last number$/ do
|
24
|
+
When %{I calculate the sieve for #{@primes.last}}
|
24
25
|
end
|
25
26
|
|
26
27
|
Then /^I should have all primes from "([^"]*)"$/ do |path|
|
27
28
|
Then %{I should have the primes #{prime_file_process(path).join(",")}}
|
28
29
|
end
|
29
30
|
|
31
|
+
Then /^a NoMemoryError should be raised$/ do
|
32
|
+
@sieve_exception.should be_a(NoMemoryError)
|
33
|
+
end
|
34
|
+
|
30
35
|
module PrimeFileProcessor
|
31
36
|
def prime_file_process(path)
|
32
|
-
|
37
|
+
@processed_cache ||= {}
|
38
|
+
|
39
|
+
if @processed_cache[path]
|
40
|
+
@processed_cache[path]
|
41
|
+
else
|
42
|
+
@processed_cache[path] = File.new(path).read.strip.split(/\s+/).map {|prime| prime.to_i }
|
43
|
+
end
|
33
44
|
end
|
34
45
|
end
|
35
46
|
|
data/lib/sieve.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sieve
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 27
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
7
|
+
- 1
|
8
8
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.2
|
9
|
+
version: 0.1.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Josh Clayton
|
@@ -15,11 +14,11 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-08-
|
17
|
+
date: 2010-08-29 00:00:00 -04:00
|
19
18
|
default_executable:
|
20
19
|
dependencies: []
|
21
20
|
|
22
|
-
description:
|
21
|
+
description: Ruby C Extension for the Sieve of Eratosthenes
|
23
22
|
email: joshua.clayton@gmail.com
|
24
23
|
executables: []
|
25
24
|
|
@@ -32,7 +31,6 @@ files:
|
|
32
31
|
- MIT-LICENSE
|
33
32
|
- README.markdown
|
34
33
|
- Rakefile
|
35
|
-
- benchmark.rb
|
36
34
|
- ext/sieve/extconf.rb
|
37
35
|
- ext/sieve/sieve.c
|
38
36
|
- ext/sieve/sieve.h
|
@@ -56,7 +54,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
54
|
requirements:
|
57
55
|
- - ">="
|
58
56
|
- !ruby/object:Gem::Version
|
59
|
-
hash: 3
|
60
57
|
segments:
|
61
58
|
- 0
|
62
59
|
version: "0"
|
@@ -65,7 +62,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
62
|
requirements:
|
66
63
|
- - ">="
|
67
64
|
- !ruby/object:Gem::Version
|
68
|
-
hash: 3
|
69
65
|
segments:
|
70
66
|
- 0
|
71
67
|
version: "0"
|
data/benchmark.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "rubygems"
|
4
|
-
require "benchmark"
|
5
|
-
require "sieve"
|
6
|
-
|
7
|
-
def sieve(n)
|
8
|
-
numbers = (0..n).map(&:to_i)
|
9
|
-
numbers[0] = numbers[1] = nil
|
10
|
-
numbers.each do |num|
|
11
|
-
next unless num
|
12
|
-
break if num**2 > n
|
13
|
-
(num**2).step(n, num) {|idx| numbers[idx] = nil }
|
14
|
-
end
|
15
|
-
numbers.compact
|
16
|
-
end
|
17
|
-
|
18
|
-
Benchmark.bm(15) do |benchmark|
|
19
|
-
range = (0..1000000)
|
20
|
-
step = 10000
|
21
|
-
benchmark.report("sieve method") do
|
22
|
-
range.step(step).each do |i|
|
23
|
-
sieve(i)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
benchmark.report("Numeric#sieve") do
|
28
|
-
range.step(step).each do |i|
|
29
|
-
i.sieve
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|