1require 'test/unit'
2require 'prime'
3require 'stringio'
4require 'timeout'
5
6class TestPrime < Test::Unit::TestCase
7  # The first 100 prime numbers
8  PRIMES = [
9    2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
10    41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
11    89, 97, 101, 103, 107, 109, 113, 127, 131,
12    137, 139, 149, 151, 157, 163, 167, 173, 179,
13    181, 191, 193, 197, 199, 211, 223, 227, 229,
14    233, 239, 241, 251, 257, 263, 269, 271, 277,
15    281, 283, 293, 307, 311, 313, 317, 331, 337,
16    347, 349, 353, 359, 367, 373, 379, 383, 389,
17    397, 401, 409, 419, 421, 431, 433, 439, 443,
18    449, 457, 461, 463, 467, 479, 487, 491, 499,
19    503, 509, 521, 523, 541,
20  ]
21  def test_each
22    primes = []
23    Prime.each do |p|
24      break if p > 541
25      primes << p
26    end
27    assert_equal PRIMES, primes
28  end
29
30  def test_each_by_prime_number_theorem
31    3.upto(15) do |i|
32      max = 2**i
33      primes = []
34      Prime.each do |p|
35        break if p >= max
36        primes << p
37      end
38
39      # Prime number theorem
40      assert primes.length >= max/Math.log(max)
41      delta = 0.05
42      li = (2..max).step(delta).inject(0){|sum,x| sum + delta/Math.log(x)}
43      assert primes.length <= li
44    end
45  end
46
47  def test_each_without_block
48    enum = Prime.each
49    assert enum.respond_to?(:each)
50    assert enum.kind_of?(Enumerable)
51    assert enum.respond_to?(:with_index)
52    assert enum.respond_to?(:next)
53    assert enum.respond_to?(:succ)
54    assert enum.respond_to?(:rewind)
55  end
56
57  def test_new
58    orig_stderr, orig_verbose = $stderr, $VERBOSE
59
60    $stderr = buf = StringIO.new('', 'w')
61    $VERBOSE = false
62
63    enum = Prime.new
64    assert_match("obsolete", buf.string)
65
66    assert enum.respond_to?(:each)
67    assert enum.kind_of?(Enumerable)
68    assert enum.respond_to?(:succ)
69
70    assert Prime === enum
71  ensure
72    $stderr = orig_stderr
73    $VERBOSE = orig_verbose
74  end
75
76  def test_enumerator_succ
77    enum = Prime.each
78    assert_equal PRIMES[0, 50], 50.times.map{ enum.succ }
79    assert_equal PRIMES[50, 50], 50.times.map{ enum.succ }
80    enum.rewind
81    assert_equal PRIMES[0, 100], 100.times.map{ enum.succ }
82  end
83
84  def test_enumerator_with_index
85    enum = Prime.each
86    last = -1
87    enum.with_index do |p,i|
88      break if i >= 100
89      assert_equal last+1, i
90      assert_equal PRIMES[i], p
91      last = i
92    end
93  end
94
95  def test_default_instance_does_not_have_compatibility_methods
96    assert !Prime.instance.respond_to?(:succ)
97    assert !Prime.instance.respond_to?(:next)
98  end
99
100  class TestInteger < Test::Unit::TestCase
101    def test_prime_division
102      pd = PRIMES.inject(&:*).prime_division
103      assert_equal PRIMES.map{|p| [p, 1]}, pd
104
105      pd = (-PRIMES.inject(&:*)).prime_division
106      assert_equal [-1, *PRIMES].map{|p| [p, 1]}, pd
107    end
108
109    def test_from_prime_division
110      assert_equal PRIMES.inject(&:*), Integer.from_prime_division(PRIMES.map{|p| [p,1]})
111
112      assert_equal(-PRIMES.inject(&:*), Integer.from_prime_division([[-1, 1]] + PRIMES.map{|p| [p,1]}))
113    end
114
115    def test_prime?
116      # zero and unit
117      assert !0.prime?
118      assert !1.prime?
119
120      # small primes
121      assert 2.prime?
122      assert 3.prime?
123
124      # squared prime
125      assert !4.prime?
126      assert !9.prime?
127
128      # mersenne numbers
129      assert((2**31-1).prime?)
130      assert !(2**32-1).prime?
131
132      # fermat numbers
133      assert((2**(2**4)+1).prime?)
134      assert !(2**(2**5)+1).prime? # Euler!
135
136      # large composite
137      assert !((2**13-1) * (2**17-1)).prime?
138
139      # factorial
140      assert !(2...100).inject(&:*).prime?
141
142      # negative
143      assert !-1.prime?
144      assert(-2.prime?)
145      assert(-3.prime?)
146      assert !-4.prime?
147    end
148  end
149
150  def test_eratosthenes_works_fine_after_timeout
151    sieve = Prime::EratosthenesSieve.instance
152    sieve.send(:initialize)
153    begin
154      # simulates that Timeout.timeout interrupts Prime::EratosthenesSieve#extend_table
155      def sieve.Integer(n)
156        n = super(n)
157        sleep 10 if /extend_table/ =~ caller.first
158        return n
159      end
160
161      begin
162	Timeout.timeout(0.5) { Prime.each(7*37){} }
163	flunk("timeout expected")
164      rescue Timeout::Error
165      end
166    ensure
167      class << sieve
168        remove_method :Integer
169      end
170    end
171
172    assert_not_include Prime.each(7*37).to_a, 7*37, "[ruby-dev:39465]"
173  end
174end
175