sieve 0.0.2 → 0.1.0
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/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
|