├── .github ├── dependabot.yml └── workflows │ ├── push_gem.yml │ └── test.yml ├── .gitignore ├── BSDL ├── COPYING ├── Gemfile ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── lib └── prime.rb ├── prime.gemspec ├── sig ├── integer-extension.rbs ├── manifest.yaml └── prime.rbs └── test ├── lib └── helper.rb ├── test_prime.rb └── test_rbs.rb /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | -------------------------------------------------------------------------------- /.github/workflows/push_gem.yml: -------------------------------------------------------------------------------- 1 | name: Publish gem to rubygems.org 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | push: 13 | if: github.repository == 'ruby/prime' 14 | runs-on: ubuntu-latest 15 | 16 | environment: 17 | name: rubygems.org 18 | url: https://rubygems.org/gems/prime 19 | 20 | permissions: 21 | contents: write 22 | id-token: write 23 | 24 | steps: 25 | - name: Harden Runner 26 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 27 | with: 28 | egress-policy: audit 29 | 30 | - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 31 | 32 | - name: Set up Ruby 33 | uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0 34 | with: 35 | bundler-cache: true 36 | ruby-version: ruby 37 | 38 | - name: Publish to RubyGems 39 | uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1 40 | 41 | - name: Create GitHub release 42 | run: | 43 | tag_name="$(git describe --tags --abbrev=0)" 44 | gh release create "${tag_name}" --verify-tag --generate-notes 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }} 47 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ruby-versions: 7 | uses: ruby/actions/.github/workflows/ruby_versions.yml@master 8 | with: 9 | min_version: 2.5 10 | engine: cruby 11 | 12 | build: 13 | needs: ruby-versions 14 | name: build (${{ matrix.ruby }} / ${{ matrix.os }}) 15 | strategy: 16 | matrix: 17 | ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} 18 | os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] 19 | exclude: 20 | - { os: macos-latest, ruby: '2.5' } 21 | 22 | runs-on: ${{ matrix.os }} 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Ruby 26 | uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: ${{ matrix.ruby }} 29 | bundler-cache: true 30 | - name: Run test 31 | run: bundle exec rake test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | Gemfile.lock 10 | -------------------------------------------------------------------------------- /BSDL: -------------------------------------------------------------------------------- 1 | Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 1. Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Ruby is copyrighted free software by Yukihiro Matsumoto . 2 | You can redistribute it and/or modify it under either the terms of the 3 | 2-clause BSDL (see the file BSDL), or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a. place your modifications in the Public Domain or otherwise 13 | make them Freely Available, such as by posting said 14 | modifications to Usenet or an equivalent medium, or by allowing 15 | the author to include your modifications in the software. 16 | 17 | b. use the modified software only within your corporation or 18 | organization. 19 | 20 | c. give non-standard binaries non-standard names, with 21 | instructions on where to get the original software distribution. 22 | 23 | d. make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or binary form, 26 | provided that you do at least ONE of the following: 27 | 28 | a. distribute the binaries and library files of the software, 29 | together with instructions (in the manual page or equivalent) 30 | on where to get the original distribution. 31 | 32 | b. accompany the distribution with the machine-readable source of 33 | the software. 34 | 35 | c. give non-standard binaries non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d. make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under these terms. 43 | 44 | For the list of those files and their copying conditions, see the 45 | file LEGAL. 46 | 47 | 5. The scripts and library files supplied as input to or produced as 48 | output from the software do not automatically fall under the 49 | copyright of the software, but belong to whomever generated them, 50 | and may be sold commercially, and may be aggregated with this 51 | software. 52 | 53 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 54 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 55 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 56 | PURPOSE. 57 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :development do 6 | gem "logger" 7 | gem "rake" 8 | gem "test-unit" 9 | gem "test-unit-ruby-core" 10 | 11 | # RBS requires Ruby >= 3.0 12 | if RUBY_VERSION >= "3.0.0" 13 | gem "rbs", "~> 3.4.0" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Prime 2 | 3 | [![CI](https://github.com/ruby/prime/actions/workflows/test.yml/badge.svg)](https://github.com/ruby/prime/actions/workflows/test.yml) 4 | 5 | Prime numbers and factorization library. 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'prime' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install prime 22 | 23 | ## Usage 24 | 25 | ```ruby 26 | require 'prime' 27 | 28 | # Prime is the set of all prime numbers, and it is Enumerable. 29 | Prime.take(4) #=> [2, 3, 5, 7] 30 | Prime.first(4) #=> [2, 3, 5, 7] 31 | Prime.each(7).to_a #=> [2, 3, 5, 7] 32 | 33 | # Determining whether an arbitrary integer is a prime number 34 | Prime.prime?(7) #=> true 35 | 8.prime? #=> false 36 | 37 | # Factorization in prime numbers 38 | Prime.prime_division(8959) #=> [[17, 2], [31, 1]] 39 | Prime.int_from_prime_division([[17, 2], [31, 1]]) #=> 8959 40 | 17**2 * 31 #=> 8959 41 | ``` 42 | 43 | ## Contributing 44 | 45 | Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/prime. 46 | 47 | ## License 48 | 49 | The gem is available as open source under the terms of the [BSD-2-Clause](LICENSE.txt). 50 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test/lib" 6 | t.ruby_opts << "-rhelper" 7 | t.test_files = FileList["test/**/test_*.rb"] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "prime" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /lib/prime.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | # 3 | # = prime.rb 4 | # 5 | # Prime numbers and factorization library. 6 | # 7 | # Copyright:: 8 | # Copyright (c) 1998-2008 Keiju ISHITSUKA(SHL Japan Inc.) 9 | # Copyright (c) 2008 Yuki Sonoda (Yugui) 10 | # 11 | # Documentation:: 12 | # Yuki Sonoda 13 | # 14 | 15 | require "singleton" 16 | require "forwardable" 17 | 18 | class Integer 19 | # Re-composes a prime factorization and returns the product. 20 | # 21 | # See Prime#int_from_prime_division for more details. 22 | def Integer.from_prime_division(pd) 23 | Prime.int_from_prime_division(pd) 24 | end 25 | 26 | # Returns the factorization of +self+. 27 | # 28 | # See Prime#prime_division for more details. 29 | def prime_division(generator = Prime::Generator23.new) 30 | Prime.prime_division(self, generator) 31 | end 32 | 33 | # Returns true if +self+ is a prime number, else returns false. 34 | # Not recommended for very big integers (> 10**23). 35 | def prime? 36 | return self >= 2 if self <= 3 37 | 38 | if (bases = miller_rabin_bases) 39 | return miller_rabin_test(bases) 40 | end 41 | 42 | return true if self == 5 43 | return false unless 30.gcd(self) == 1 44 | (7..Integer.sqrt(self)).step(30) do |p| 45 | return false if 46 | self%(p) == 0 || self%(p+4) == 0 || self%(p+6) == 0 || self%(p+10) == 0 || 47 | self%(p+12) == 0 || self%(p+16) == 0 || self%(p+22) == 0 || self%(p+24) == 0 48 | end 49 | true 50 | end 51 | 52 | MILLER_RABIN_BASES = [ 53 | [2], 54 | [2,3], 55 | [31,73], 56 | [2,3,5], 57 | [2,3,5,7], 58 | [2,7,61], 59 | [2,13,23,1662803], 60 | [2,3,5,7,11], 61 | [2,3,5,7,11,13], 62 | [2,3,5,7,11,13,17], 63 | [2,3,5,7,11,13,17,19,23], 64 | [2,3,5,7,11,13,17,19,23,29,31,37], 65 | [2,3,5,7,11,13,17,19,23,29,31,37,41], 66 | ].map!(&:freeze).freeze 67 | private_constant :MILLER_RABIN_BASES 68 | 69 | private def miller_rabin_bases 70 | # Miller-Rabin's complexity is O(k log^3n). 71 | # So we can reduce the complexity by reducing the number of bases tested. 72 | # Using values from https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test 73 | i = case 74 | when self < 0xffff then 75 | # For small integers, Miller Rabin can be slower 76 | # There is no mathematical significance to 0xffff 77 | return nil 78 | # when self < 2_047 then 0 79 | when self < 1_373_653 then 1 80 | when self < 9_080_191 then 2 81 | when self < 25_326_001 then 3 82 | when self < 3_215_031_751 then 4 83 | when self < 4_759_123_141 then 5 84 | when self < 1_122_004_669_633 then 6 85 | when self < 2_152_302_898_747 then 7 86 | when self < 3_474_749_660_383 then 8 87 | when self < 341_550_071_728_321 then 9 88 | when self < 3_825_123_056_546_413_051 then 10 89 | when self < 318_665_857_834_031_151_167_461 then 11 90 | when self < 3_317_044_064_679_887_385_961_981 then 12 91 | else return nil 92 | end 93 | MILLER_RABIN_BASES[i] 94 | end 95 | 96 | private def miller_rabin_test(bases) 97 | return false if even? 98 | 99 | r = 0 100 | d = self >> 1 101 | while d.even? 102 | d >>= 1 103 | r += 1 104 | end 105 | 106 | self_minus_1 = self-1 107 | bases.each do |a| 108 | x = a.pow(d, self) 109 | next if x == 1 || x == self_minus_1 || a == self 110 | 111 | return false if r.times do 112 | x = x.pow(2, self) 113 | break if x == self_minus_1 114 | end 115 | end 116 | true 117 | end 118 | 119 | # Iterates the given block over all prime numbers. 120 | # 121 | # See +Prime+#each for more details. 122 | def Integer.each_prime(ubound, &block) # :yields: prime 123 | Prime.each(ubound, &block) 124 | end 125 | end 126 | 127 | # 128 | # The set of all prime numbers. 129 | # 130 | # == Example 131 | # 132 | # Prime.each(100) do |prime| 133 | # p prime #=> 2, 3, 5, 7, 11, ...., 97 134 | # end 135 | # 136 | # Prime is Enumerable: 137 | # 138 | # Prime.first 5 # => [2, 3, 5, 7, 11] 139 | # 140 | # == Retrieving the instance 141 | # 142 | # For convenience, each instance method of +Prime+.instance can be accessed 143 | # as a class method of +Prime+. 144 | # 145 | # e.g. 146 | # Prime.instance.prime?(2) #=> true 147 | # Prime.prime?(2) #=> true 148 | # 149 | # == Generators 150 | # 151 | # A "generator" provides an implementation of enumerating pseudo-prime 152 | # numbers and it remembers the position of enumeration and upper bound. 153 | # Furthermore, it is an external iterator of prime enumeration which is 154 | # compatible with an Enumerator. 155 | # 156 | # +Prime+::+PseudoPrimeGenerator+ is the base class for generators. 157 | # There are few implementations of generator. 158 | # 159 | # [+Prime+::+EratosthenesGenerator+] 160 | # Uses Eratosthenes' sieve. 161 | # [+Prime+::+TrialDivisionGenerator+] 162 | # Uses the trial division method. 163 | # [+Prime+::+Generator23+] 164 | # Generates all positive integers which are not divisible by either 2 or 3. 165 | # This sequence is very bad as a pseudo-prime sequence. But this 166 | # is faster and uses much less memory than the other generators. So, 167 | # it is suitable for factorizing an integer which is not large but 168 | # has many prime factors. e.g. for Prime#prime? . 169 | 170 | class Prime 171 | 172 | VERSION = "0.1.3" 173 | 174 | include Enumerable 175 | include Singleton 176 | 177 | class << self 178 | extend Forwardable 179 | include Enumerable 180 | 181 | def method_added(method) # :nodoc: 182 | (class<< self;self;end).def_delegator :instance, method 183 | end 184 | end 185 | 186 | # Iterates the given block over all prime numbers. 187 | # 188 | # == Parameters 189 | # 190 | # +ubound+:: 191 | # Optional. An arbitrary positive number. 192 | # The upper bound of enumeration. The method enumerates 193 | # prime numbers infinitely if +ubound+ is nil. 194 | # +generator+:: 195 | # Optional. An implementation of pseudo-prime generator. 196 | # 197 | # == Return value 198 | # 199 | # An evaluated value of the given block at the last time. 200 | # Or an enumerator which is compatible to an +Enumerator+ 201 | # if no block given. 202 | # 203 | # == Description 204 | # 205 | # Calls +block+ once for each prime number, passing the prime as 206 | # a parameter. 207 | # 208 | # +ubound+:: 209 | # Upper bound of prime numbers. The iterator stops after it 210 | # yields all prime numbers p <= +ubound+. 211 | # 212 | def each(ubound = nil, generator = EratosthenesGenerator.new, &block) 213 | generator.upper_bound = ubound 214 | generator.each(&block) 215 | end 216 | 217 | # Returns true if +obj+ is an Integer and is prime. Also returns 218 | # true if +obj+ is a Module that is an ancestor of +Prime+. 219 | # Otherwise returns false. 220 | def include?(obj) 221 | case obj 222 | when Integer 223 | prime?(obj) 224 | when Module 225 | Module.instance_method(:include?).bind(Prime).call(obj) 226 | else 227 | false 228 | end 229 | end 230 | 231 | # Returns true if +value+ is a prime number, else returns false. 232 | # Integer#prime? is much more performant. 233 | # 234 | # == Parameters 235 | # 236 | # +value+:: an arbitrary integer to be checked. 237 | # +generator+:: optional. A pseudo-prime generator. 238 | def prime?(value, generator = Prime::Generator23.new) 239 | raise ArgumentError, "Expected a prime generator, got #{generator}" unless generator.respond_to? :each 240 | raise ArgumentError, "Expected an integer, got #{value}" unless value.respond_to?(:integer?) && value.integer? 241 | return false if value < 2 242 | generator.each do |num| 243 | q,r = value.divmod num 244 | return true if q < num 245 | return false if r == 0 246 | end 247 | end 248 | 249 | # Re-composes a prime factorization and returns the product. 250 | # 251 | # For the decomposition: 252 | # 253 | # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]], 254 | # 255 | # it returns: 256 | # 257 | # p_1**e_1 * p_2**e_2 * ... * p_n**e_n. 258 | # 259 | # == Parameters 260 | # +pd+:: Array of pairs of integers. 261 | # Each pair consists of a prime number -- a prime factor -- 262 | # and a natural number -- its exponent (multiplicity). 263 | # 264 | # == Example 265 | # Prime.int_from_prime_division([[3, 2], [5, 1]]) #=> 45 266 | # 3**2 * 5 #=> 45 267 | # 268 | def int_from_prime_division(pd) 269 | pd.inject(1){|value, (prime, index)| 270 | value * prime**index 271 | } 272 | end 273 | 274 | # Returns the factorization of +value+. 275 | # 276 | # For an arbitrary integer: 277 | # 278 | # p_1**e_1 * p_2**e_2 * ... * p_n**e_n, 279 | # 280 | # prime_division returns an array of pairs of integers: 281 | # 282 | # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]]. 283 | # 284 | # Each pair consists of a prime number -- a prime factor -- 285 | # and a natural number -- its exponent (multiplicity). 286 | # 287 | # == Parameters 288 | # +value+:: An arbitrary integer. 289 | # +generator+:: Optional. A pseudo-prime generator. 290 | # +generator+.succ must return the next 291 | # pseudo-prime number in ascending order. 292 | # It must generate all prime numbers, 293 | # but may also generate non-prime numbers, too. 294 | # 295 | # === Exceptions 296 | # +ZeroDivisionError+:: when +value+ is zero. 297 | # 298 | # == Example 299 | # 300 | # Prime.prime_division(45) #=> [[3, 2], [5, 1]] 301 | # 3**2 * 5 #=> 45 302 | # 303 | def prime_division(value, generator = Prime::Generator23.new) 304 | raise ZeroDivisionError if value == 0 305 | if value < 0 306 | value = -value 307 | pv = [[-1, 1]] 308 | else 309 | pv = [] 310 | end 311 | generator.each do |prime| 312 | count = 0 313 | while (value1, mod = value.divmod(prime) 314 | mod) == 0 315 | value = value1 316 | count += 1 317 | end 318 | if count != 0 319 | pv.push [prime, count] 320 | end 321 | break if value1 <= prime 322 | end 323 | if value > 1 324 | pv.push [value, 1] 325 | end 326 | pv 327 | end 328 | 329 | # An abstract class for enumerating pseudo-prime numbers. 330 | # 331 | # Concrete subclasses should override succ, next, rewind. 332 | class PseudoPrimeGenerator 333 | include Enumerable 334 | 335 | def initialize(ubound = nil) 336 | @ubound = ubound 337 | end 338 | 339 | def upper_bound=(ubound) 340 | @ubound = ubound 341 | end 342 | def upper_bound 343 | @ubound 344 | end 345 | 346 | # returns the next pseudo-prime number, and move the internal 347 | # position forward. 348 | # 349 | # +PseudoPrimeGenerator+#succ raises +NotImplementedError+. 350 | def succ 351 | raise NotImplementedError, "need to define `succ'" 352 | end 353 | 354 | # alias of +succ+. 355 | def next 356 | raise NotImplementedError, "need to define `next'" 357 | end 358 | 359 | # Rewinds the internal position for enumeration. 360 | # 361 | # See +Enumerator+#rewind. 362 | def rewind 363 | raise NotImplementedError, "need to define `rewind'" 364 | end 365 | 366 | # Iterates the given block for each prime number. 367 | def each 368 | return self.dup unless block_given? 369 | if @ubound 370 | last_value = nil 371 | loop do 372 | prime = succ 373 | break last_value if prime > @ubound 374 | last_value = yield prime 375 | end 376 | else 377 | loop do 378 | yield succ 379 | end 380 | end 381 | end 382 | 383 | # see +Enumerator+#with_index. 384 | def with_index(offset = 0, &block) 385 | return enum_for(:with_index, offset) { Float::INFINITY } unless block 386 | return each_with_index(&block) if offset == 0 387 | 388 | each do |prime| 389 | yield prime, offset 390 | offset += 1 391 | end 392 | end 393 | 394 | # see +Enumerator+#with_object. 395 | def with_object(obj) 396 | return enum_for(:with_object, obj) { Float::INFINITY } unless block_given? 397 | each do |prime| 398 | yield prime, obj 399 | end 400 | end 401 | 402 | def size 403 | Float::INFINITY 404 | end 405 | end 406 | 407 | # An implementation of +PseudoPrimeGenerator+. 408 | # 409 | # Uses +EratosthenesSieve+. 410 | class EratosthenesGenerator < PseudoPrimeGenerator 411 | def initialize 412 | @last_prime_index = -1 413 | super 414 | end 415 | 416 | def succ 417 | @last_prime_index += 1 418 | EratosthenesSieve.instance.get_nth_prime(@last_prime_index) 419 | end 420 | def rewind 421 | initialize 422 | end 423 | alias next succ 424 | end 425 | 426 | # An implementation of +PseudoPrimeGenerator+ which uses 427 | # a prime table generated by trial division. 428 | class TrialDivisionGenerator < PseudoPrimeGenerator 429 | def initialize 430 | @index = -1 431 | super 432 | end 433 | 434 | def succ 435 | TrialDivision.instance[@index += 1] 436 | end 437 | def rewind 438 | initialize 439 | end 440 | alias next succ 441 | end 442 | 443 | # Generates all integers which are greater than 2 and 444 | # are not divisible by either 2 or 3. 445 | # 446 | # This is a pseudo-prime generator, suitable on 447 | # checking primality of an integer by brute force 448 | # method. 449 | class Generator23 < PseudoPrimeGenerator 450 | def initialize 451 | @prime = 1 452 | @step = nil 453 | super 454 | end 455 | 456 | def succ 457 | if (@step) 458 | @prime += @step 459 | @step = 6 - @step 460 | else 461 | case @prime 462 | when 1; @prime = 2 463 | when 2; @prime = 3 464 | when 3; @prime = 5; @step = 2 465 | end 466 | end 467 | @prime 468 | end 469 | alias next succ 470 | def rewind 471 | initialize 472 | end 473 | end 474 | 475 | # Internal use. An implementation of prime table by trial division method. 476 | class TrialDivision 477 | include Singleton 478 | 479 | def initialize # :nodoc: 480 | # These are included as class variables to cache them for later uses. If memory 481 | # usage is a problem, they can be put in Prime#initialize as instance variables. 482 | 483 | # There must be no primes between @primes[-1] and @next_to_check. 484 | @primes = [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] 485 | # @next_to_check % 6 must be 1. 486 | @next_to_check = 103 # @primes[-1] - @primes[-1] % 6 + 7 487 | @ulticheck_index = 3 # @primes.index(@primes.reverse.find {|n| 488 | # n < Math.sqrt(@@next_to_check) }) 489 | @ulticheck_next_squared = 121 # @primes[@ulticheck_index + 1] ** 2 490 | end 491 | 492 | # Returns the +index+th prime number. 493 | # 494 | # +index+ is a 0-based index. 495 | def [](index) 496 | while index >= @primes.length 497 | # Only check for prime factors up to the square root of the potential primes, 498 | # but without the performance hit of an actual square root calculation. 499 | if @next_to_check + 4 > @ulticheck_next_squared 500 | @ulticheck_index += 1 501 | @ulticheck_next_squared = @primes.at(@ulticheck_index + 1) ** 2 502 | end 503 | # Only check numbers congruent to one and five, modulo six. All others 504 | 505 | # are divisible by two or three. This also allows us to skip checking against 506 | # two and three. 507 | @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil? 508 | @next_to_check += 4 509 | @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil? 510 | @next_to_check += 2 511 | end 512 | @primes[index] 513 | end 514 | end 515 | 516 | # Internal use. An implementation of Eratosthenes' sieve 517 | class EratosthenesSieve 518 | include Singleton 519 | 520 | def initialize 521 | @primes = [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] 522 | # @max_checked must be an even number 523 | @max_checked = @primes.last + 1 524 | end 525 | 526 | def get_nth_prime(n) 527 | compute_primes while @primes.size <= n 528 | @primes[n] 529 | end 530 | 531 | private 532 | def compute_primes 533 | # max_segment_size must be an even number 534 | max_segment_size = 1e6.to_i 535 | max_cached_prime = @primes.last 536 | # do not double count primes if #compute_primes is interrupted 537 | # by Timeout.timeout 538 | @max_checked = max_cached_prime + 1 if max_cached_prime > @max_checked 539 | 540 | segment_min = @max_checked 541 | segment_max = [segment_min + max_segment_size, max_cached_prime * 2].min 542 | root = Integer.sqrt(segment_max) 543 | 544 | segment = ((segment_min + 1) .. segment_max).step(2).to_a 545 | 546 | (1..Float::INFINITY).each do |sieving| 547 | prime = @primes[sieving] 548 | break if prime > root 549 | composite_index = (-(segment_min + 1 + prime) / 2) % prime 550 | while composite_index < segment.size do 551 | segment[composite_index] = nil 552 | composite_index += prime 553 | end 554 | end 555 | 556 | @primes.concat(segment.compact!) 557 | 558 | @max_checked = segment_max 559 | end 560 | end 561 | end 562 | -------------------------------------------------------------------------------- /prime.gemspec: -------------------------------------------------------------------------------- 1 | begin 2 | require_relative "lib/prime" 3 | rescue LoadError 4 | # for Ruby core repository 5 | require_relative "prime" 6 | end 7 | 8 | Gem::Specification.new do |spec| 9 | spec.name = "prime" 10 | spec.version = Prime::VERSION 11 | spec.authors = ["Marc-Andre Lafortune"] 12 | spec.email = ["ruby-core@marc-andre.ca"] 13 | 14 | spec.summary = %q{Prime numbers and factorization library.} 15 | spec.description = %q{Prime numbers and factorization library.} 16 | spec.homepage = "https://github.com/ruby/prime" 17 | spec.licenses = ["Ruby", "BSD-2-Clause"] 18 | 19 | spec.files = ["BSDL", "COPYING", "README.md", "Rakefile", "lib/prime.rb", "prime.gemspec", "sig/integer-extension.rbs", "sig/manifest.yaml", "sig/prime.rbs"] 20 | spec.bindir = "exe" 21 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 22 | spec.require_paths = ["lib"] 23 | 24 | spec.required_ruby_version = ">= 2.5.0" 25 | 26 | spec.add_dependency "singleton" 27 | spec.add_dependency "forwardable" 28 | end 29 | -------------------------------------------------------------------------------- /sig/integer-extension.rbs: -------------------------------------------------------------------------------- 1 | %a{annotate:rdoc:skip} 2 | class Integer 3 | # 7 | # Iterates the given block over all prime numbers. 8 | # 9 | # See `Prime`#each for more details. 10 | # 11 | def self.each_prime: (Integer) { (Integer) -> void } -> void 12 | 13 | # 17 | # Re-composes a prime factorization and returns the product. 18 | # 19 | # See Prime#int_from_prime_division for more details. 20 | # 21 | def self.from_prime_division: (Array[[ String ]]) -> Integer 22 | 23 | # 27 | # Returns the factorization of `self`. 28 | # 29 | # See Prime#prime_division for more details. 30 | # 31 | def prime_division: (?Prime::PseudoPrimeGenerator) -> Array[[ Integer, Integer ]] 32 | 33 | # 37 | # Returns true if `self` is a prime number, else returns false. Not recommended 38 | # for very big integers (> 10**23). 39 | # 40 | def prime?: () -> bool 41 | end 42 | -------------------------------------------------------------------------------- /sig/manifest.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: singleton 3 | -------------------------------------------------------------------------------- /sig/prime.rbs: -------------------------------------------------------------------------------- 1 | # 2 | # The set of all prime numbers. 3 | # 4 | # ## Example 5 | # 6 | # Prime.each(100) do |prime| 7 | # p prime #=> 2, 3, 5, 7, 11, ...., 97 8 | # end 9 | # 10 | # Prime is Enumerable: 11 | # 12 | # Prime.first 5 # => [2, 3, 5, 7, 11] 13 | # 14 | # ## Retrieving the instance 15 | # 16 | # For convenience, each instance method of `Prime`.instance can be accessed as a 17 | # class method of `Prime`. 18 | # 19 | # e.g. 20 | # Prime.instance.prime?(2) #=> true 21 | # Prime.prime?(2) #=> true 22 | # 23 | # ## Generators 24 | # 25 | # A "generator" provides an implementation of enumerating pseudo-prime numbers 26 | # and it remembers the position of enumeration and upper bound. Furthermore, it 27 | # is an external iterator of prime enumeration which is compatible with an 28 | # Enumerator. 29 | # 30 | # `Prime`::`PseudoPrimeGenerator` is the base class for generators. There are 31 | # few implementations of generator. 32 | # 33 | # `Prime`::`EratosthenesGenerator` 34 | # : Uses Eratosthenes' sieve. 35 | # `Prime`::`TrialDivisionGenerator` 36 | # : Uses the trial division method. 37 | # `Prime`::`Generator23` 38 | # : Generates all positive integers which are not divisible by either 2 or 3. 39 | # This sequence is very bad as a pseudo-prime sequence. But this is faster 40 | # and uses much less memory than the other generators. So, it is suitable 41 | # for factorizing an integer which is not large but has many prime factors. 42 | # e.g. for Prime#prime? . 43 | # 44 | class Prime 45 | include Singleton 46 | 47 | include Enumerable[Integer] 48 | 49 | extend Enumerable[Integer] 50 | 51 | # 55 | # Iterates the given block over all prime numbers. 56 | # 57 | # ## Parameters 58 | # 59 | # `ubound` 60 | # : Optional. An arbitrary positive number. The upper bound of enumeration. 61 | # The method enumerates prime numbers infinitely if `ubound` is nil. 62 | # `generator` 63 | # : Optional. An implementation of pseudo-prime generator. 64 | # 65 | # 66 | # ## Return value 67 | # 68 | # An evaluated value of the given block at the last time. Or an enumerator which 69 | # is compatible to an `Enumerator` if no block given. 70 | # 71 | # ## Description 72 | # 73 | # Calls `block` once for each prime number, passing the prime as a parameter. 74 | # 75 | # `ubound` 76 | # : Upper bound of prime numbers. The iterator stops after it yields all prime 77 | # numbers p <= `ubound`. 78 | # 79 | def self.each: (?Integer? ubound, ?PseudoPrimeGenerator generator) { (Integer) -> void } -> void 80 | | (?Integer? ubound, ?PseudoPrimeGenerator generator) -> PseudoPrimeGenerator 81 | 82 | # 86 | # Iterates the given block over all prime numbers. 87 | # 88 | # ## Parameters 89 | # 90 | # `ubound` 91 | # : Optional. An arbitrary positive number. The upper bound of enumeration. 92 | # The method enumerates prime numbers infinitely if `ubound` is nil. 93 | # `generator` 94 | # : Optional. An implementation of pseudo-prime generator. 95 | # 96 | # 97 | # ## Return value 98 | # 99 | # An evaluated value of the given block at the last time. Or an enumerator which 100 | # is compatible to an `Enumerator` if no block given. 101 | # 102 | # ## Description 103 | # 104 | # Calls `block` once for each prime number, passing the prime as a parameter. 105 | # 106 | # `ubound` 107 | # : Upper bound of prime numbers. The iterator stops after it yields all prime 108 | # numbers p <= `ubound`. 109 | # 110 | def each: (?Integer? ubound, ?PseudoPrimeGenerator generator) { (Integer) -> void } -> void 111 | | (?Integer? ubound, ?PseudoPrimeGenerator generator) -> PseudoPrimeGenerator 112 | 113 | # 117 | # Re-composes a prime factorization and returns the product. 118 | # 119 | # For the decomposition: 120 | # 121 | # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]], 122 | # 123 | # it returns: 124 | # 125 | # p_1**e_1 * p_2**e_2 * ... * p_n**e_n. 126 | # 127 | # ## Parameters 128 | # `pd` 129 | # : Array of pairs of integers. Each pair consists of a prime number -- a 130 | # prime factor -- and a natural number -- its exponent (multiplicity). 131 | # 132 | # 133 | # ## Example 134 | # Prime.int_from_prime_division([[3, 2], [5, 1]]) #=> 45 135 | # 3**2 * 5 #=> 45 136 | # 137 | def self.int_from_prime_division: (Array[[ Integer, Integer ]]) -> (Integer | Rational) 138 | 139 | # 143 | # Re-composes a prime factorization and returns the product. 144 | # 145 | # For the decomposition: 146 | # 147 | # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]], 148 | # 149 | # it returns: 150 | # 151 | # p_1**e_1 * p_2**e_2 * ... * p_n**e_n. 152 | # 153 | # ## Parameters 154 | # `pd` 155 | # : Array of pairs of integers. Each pair consists of a prime number -- a 156 | # prime factor -- and a natural number -- its exponent (multiplicity). 157 | # 158 | # 159 | # ## Example 160 | # Prime.int_from_prime_division([[3, 2], [5, 1]]) #=> 45 161 | # 3**2 * 5 #=> 45 162 | # 163 | def int_from_prime_division: (Array[[ Integer, Integer ]]) -> (Integer | Rational) 164 | 165 | # 169 | # Returns true if `value` is a prime number, else returns false. Integer#prime? 170 | # is much more performant. 171 | # 172 | # ## Parameters 173 | # 174 | # `value` 175 | # : an arbitrary integer to be checked. 176 | # `generator` 177 | # : optional. A pseudo-prime generator. 178 | # 179 | def self.prime?: (Integer value, ?PseudoPrimeGenerator generator) -> bool 180 | 181 | # 185 | # Returns true if `value` is a prime number, else returns false. Integer#prime? 186 | # is much more performant. 187 | # 188 | # ## Parameters 189 | # 190 | # `value` 191 | # : an arbitrary integer to be checked. 192 | # `generator` 193 | # : optional. A pseudo-prime generator. 194 | # 195 | def prime?: (Integer value, ?PseudoPrimeGenerator generator) -> bool 196 | 197 | # 201 | # Returns the factorization of `value`. 202 | # 203 | # For an arbitrary integer: 204 | # 205 | # p_1**e_1 * p_2**e_2 * ... * p_n**e_n, 206 | # 207 | # prime_division returns an array of pairs of integers: 208 | # 209 | # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]]. 210 | # 211 | # Each pair consists of a prime number -- a prime factor -- and a natural number 212 | # -- its exponent (multiplicity). 213 | # 214 | # ## Parameters 215 | # `value` 216 | # : An arbitrary integer. 217 | # `generator` 218 | # : Optional. A pseudo-prime generator. `generator`.succ must return the next 219 | # pseudo-prime number in ascending order. It must generate all prime 220 | # numbers, but may also generate non-prime numbers, too. 221 | # 222 | # 223 | # ### Exceptions 224 | # `ZeroDivisionError` 225 | # : when `value` is zero. 226 | # 227 | # 228 | # ## Example 229 | # 230 | # Prime.prime_division(45) #=> [[3, 2], [5, 1]] 231 | # 3**2 * 5 #=> 45 232 | # 233 | def self.prime_division: (Integer, ?PseudoPrimeGenerator generator) -> Array[[ Integer, Integer ]] 234 | 235 | # 239 | # Returns the factorization of `value`. 240 | # 241 | # For an arbitrary integer: 242 | # 243 | # p_1**e_1 * p_2**e_2 * ... * p_n**e_n, 244 | # 245 | # prime_division returns an array of pairs of integers: 246 | # 247 | # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]]. 248 | # 249 | # Each pair consists of a prime number -- a prime factor -- and a natural number 250 | # -- its exponent (multiplicity). 251 | # 252 | # ## Parameters 253 | # `value` 254 | # : An arbitrary integer. 255 | # `generator` 256 | # : Optional. A pseudo-prime generator. `generator`.succ must return the next 257 | # pseudo-prime number in ascending order. It must generate all prime 258 | # numbers, but may also generate non-prime numbers, too. 259 | # 260 | # 261 | # ### Exceptions 262 | # `ZeroDivisionError` 263 | # : when `value` is zero. 264 | # 265 | # 266 | # ## Example 267 | # 268 | # Prime.prime_division(45) #=> [[3, 2], [5, 1]] 269 | # 3**2 * 5 #=> 45 270 | # 271 | def prime_division: (Integer, ?PseudoPrimeGenerator generator) -> Array[[ Integer, Integer ]] 272 | 273 | # Returns the singleton instance. 274 | # 275 | def self.instance: () -> Prime 276 | 277 | # 278 | # An abstract class for enumerating pseudo-prime numbers. 279 | # 280 | # Concrete subclasses should override succ, next, rewind. 281 | # 282 | class PseudoPrimeGenerator 283 | # 287 | # 288 | def initialize: (?Integer?) -> void 289 | 290 | include Enumerable[Integer] 291 | 292 | # 296 | # ---- 297 | # 301 | # 302 | attr_accessor upper_bound(): Integer? 303 | 304 | # 308 | # Iterates the given block for each prime number. 309 | # 310 | def each: () { (Integer) -> void } -> void 311 | 312 | # 316 | # alias of `succ`. 317 | # 318 | def next: () -> Integer 319 | 320 | # 324 | # Rewinds the internal position for enumeration. 325 | # 326 | # See `Enumerator`#rewind. 327 | # 328 | def rewind: () -> void 329 | 330 | # 334 | # 335 | def size: () -> Float 336 | 337 | # 341 | # returns the next pseudo-prime number, and move the internal position forward. 342 | # 343 | # `PseudoPrimeGenerator`#succ raises `NotImplementedError`. 344 | # 345 | def succ: () -> Integer 346 | end 347 | 348 | # 349 | # An implementation of `PseudoPrimeGenerator`. 350 | # 351 | # Uses `EratosthenesSieve`. 352 | # 353 | class EratosthenesGenerator < PseudoPrimeGenerator 354 | end 355 | 356 | # 357 | # An implementation of `PseudoPrimeGenerator` which uses a prime table generated 358 | # by trial division. 359 | # 360 | class TrialDivisionGenerator < PseudoPrimeGenerator 361 | end 362 | 363 | # 364 | # Generates all integers which are greater than 2 and are not divisible by 365 | # either 2 or 3. 366 | # 367 | # This is a pseudo-prime generator, suitable on checking primality of an integer 368 | # by brute force method. 369 | # 370 | class Generator23 < PseudoPrimeGenerator 371 | end 372 | end 373 | -------------------------------------------------------------------------------- /test/lib/helper.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | require "core_assertions" 3 | 4 | Test::Unit::TestCase.include Test::Unit::CoreAssertions 5 | 6 | module Test 7 | module Unit 8 | class TestCase 9 | def windows? platform = RUBY_PLATFORM 10 | /mswin|mingw/ =~ platform 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/test_prime.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | require 'test/unit' 3 | require 'prime' 4 | require 'timeout' 5 | 6 | class 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_include? 31 | assert_equal(false, Prime.include?(nil)) 32 | assert_equal(true, Prime.include?(3)) 33 | assert_equal(false, Prime.include?(4)) 34 | assert_equal(true, Prime.include?(Enumerable)) 35 | assert_equal(false, Prime.include?(Comparable)) 36 | end 37 | 38 | def test_integer_each_prime 39 | primes = [] 40 | Integer.each_prime(1000) do |p| 41 | break if p > 541 42 | primes << p 43 | end 44 | assert_equal PRIMES, primes 45 | end 46 | 47 | def test_each_by_prime_number_theorem 48 | 3.upto(15) do |i| 49 | max = 2**i 50 | primes = [] 51 | Prime.each do |p| 52 | break if p >= max 53 | primes << p 54 | end 55 | 56 | # Prime number theorem 57 | assert_operator primes.length, :>=, max/Math.log(max) 58 | delta = 0.05 59 | li = (2..max).step(delta).inject(0){|sum,x| sum + delta/Math.log(x)} 60 | assert_operator primes.length, :<=, li 61 | end 62 | end 63 | 64 | def test_each_without_block 65 | enum = Prime.each 66 | assert_respond_to(enum, :each) 67 | assert_kind_of(Enumerable, enum) 68 | assert_respond_to(enum, :with_index) 69 | assert_respond_to(enum, :next) 70 | assert_respond_to(enum, :succ) 71 | assert_respond_to(enum, :rewind) 72 | end 73 | 74 | def test_instance_without_block 75 | enum = Prime.instance.each 76 | assert_respond_to(enum, :each) 77 | assert_kind_of(Enumerable, enum) 78 | assert_respond_to(enum, :with_index) 79 | assert_respond_to(enum, :next) 80 | assert_respond_to(enum, :succ) 81 | assert_respond_to(enum, :rewind) 82 | end 83 | 84 | def test_new 85 | assert_raise(NoMethodError) { Prime.new } 86 | end 87 | 88 | def test_enumerator_succ 89 | enum = Prime.each 90 | assert_equal PRIMES[0, 50], 50.times.map{ enum.succ } 91 | assert_equal PRIMES[50, 50], 50.times.map{ enum.succ } 92 | enum.rewind 93 | assert_equal PRIMES[0, 100], 100.times.map{ enum.succ } 94 | end 95 | 96 | def test_enumerator_with_index 97 | enum = Prime.each 98 | last = -1 99 | enum.with_index do |p,i| 100 | break if i >= 100 101 | assert_equal last+1, i 102 | assert_equal PRIMES[i], p 103 | last = i 104 | end 105 | end 106 | 107 | def test_enumerator_with_index_with_offset 108 | enum = Prime.each 109 | last = 5-1 110 | enum.with_index(5).each do |p,i| 111 | break if i >= 100+5 112 | assert_equal last+1, i 113 | assert_equal PRIMES[i-5], p 114 | last = i 115 | end 116 | end 117 | 118 | def test_enumerator_with_object 119 | object = Object.new 120 | enum = Prime.each 121 | enum.with_object(object).each do |p, o| 122 | assert_equal object, o 123 | break 124 | end 125 | end 126 | 127 | def test_enumerator_size 128 | enum = Prime.each 129 | assert_equal Float::INFINITY, enum.size 130 | assert_equal Float::INFINITY, enum.with_object(nil).size 131 | assert_equal Float::INFINITY, enum.with_index(42).size 132 | end 133 | 134 | def test_default_instance_does_not_have_compatibility_methods 135 | assert_not_respond_to(Prime.instance, :succ) 136 | assert_not_respond_to(Prime.instance, :next) 137 | end 138 | 139 | def test_prime_each_basic_argument_checking 140 | assert_raise(ArgumentError) { Prime.prime?(1,2) } 141 | assert_raise(ArgumentError) { Prime.prime?(1.2) } 142 | end 143 | 144 | def test_prime? 145 | assert_equal Prime.prime?(1), false 146 | assert_equal Prime.prime?(2), true 147 | assert_equal Prime.prime?(4), false 148 | end 149 | 150 | class TestPseudoPrimeGenerator < Test::Unit::TestCase 151 | def test_upper_bound 152 | pseudo_prime_generator = Prime::PseudoPrimeGenerator.new(42) 153 | assert_equal pseudo_prime_generator.upper_bound, 42 154 | end 155 | 156 | def test_succ 157 | pseudo_prime_generator = Prime::PseudoPrimeGenerator.new(42) 158 | assert_raise(NotImplementedError) { pseudo_prime_generator.succ } 159 | end 160 | 161 | def test_next 162 | pseudo_prime_generator = Prime::PseudoPrimeGenerator.new(42) 163 | assert_raise(NotImplementedError) { pseudo_prime_generator.next } 164 | end 165 | 166 | def test_rewind 167 | pseudo_prime_generator = Prime::PseudoPrimeGenerator.new(42) 168 | assert_raise(NotImplementedError) { pseudo_prime_generator.rewind } 169 | end 170 | end 171 | 172 | class TestTrialDivisionGenerator < Test::Unit::TestCase 173 | # The first 100 prime numbers 174 | PRIMES = [ 175 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 176 | 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 177 | 89, 97, 101, 103, 107, 109, 113, 127, 131, 178 | 137, 139, 149, 151, 157, 163, 167, 173, 179, 179 | 181, 191, 193, 197, 199, 211, 223, 227, 229, 180 | 233, 239, 241, 251, 257, 263, 269, 271, 277, 181 | 281, 283, 293, 307, 311, 313, 317, 331, 337, 182 | 347, 349, 353, 359, 367, 373, 379, 383, 389, 183 | 397, 401, 409, 419, 421, 431, 433, 439, 443, 184 | 449, 457, 461, 463, 467, 479, 487, 491, 499, 185 | 503, 509, 521, 523, 541, 186 | ] 187 | 188 | def test_each 189 | primes = [] 190 | Prime.each(nil, Prime::TrialDivisionGenerator.new) do |p| 191 | break if p > 541 192 | primes << p 193 | end 194 | assert_equal PRIMES, primes 195 | end 196 | 197 | def test_rewind 198 | generator = Prime::TrialDivisionGenerator.new 199 | assert_equal generator.next, 2 200 | assert_equal generator.next, 3 201 | generator.rewind 202 | assert_equal generator.next, 2 203 | end 204 | end 205 | 206 | class TestGenerator23 < Test::Unit::TestCase 207 | def test_rewind 208 | generator = Prime::Generator23.new 209 | assert_equal generator.next, 2 210 | assert_equal generator.next, 3 211 | generator.rewind 212 | assert_equal generator.next, 2 213 | end 214 | end 215 | 216 | class TestInteger < Test::Unit::TestCase 217 | def test_prime_division 218 | pd = PRIMES.inject(&:*).prime_division 219 | assert_equal PRIMES.map{|p| [p, 1]}, pd 220 | 221 | pd = (-PRIMES.inject(&:*)).prime_division 222 | assert_equal [-1, *PRIMES].map{|p| [p, 1]}, pd 223 | end 224 | 225 | def test_from_prime_division 226 | assert_equal PRIMES.inject(&:*), Integer.from_prime_division(PRIMES.map{|p| [p,1]}) 227 | 228 | assert_equal(-PRIMES.inject(&:*), Integer.from_prime_division([[-1, 1]] + PRIMES.map{|p| [p,1]})) 229 | end 230 | 231 | def test_prime? 232 | PRIMES.each do |p| 233 | assert_predicate(p, :prime?) 234 | end 235 | 236 | composites = (0..PRIMES.last).to_a - PRIMES 237 | composites.each do |c| 238 | assert_not_predicate(c, :prime?) 239 | end 240 | 241 | # mersenne numbers 242 | assert_predicate((2**31-1), :prime?) 243 | assert_not_predicate((2**32-1), :prime?) 244 | 245 | # fermat numbers 246 | assert_predicate((2**(2**4)+1), :prime?) 247 | assert_not_predicate((2**(2**5)+1), :prime?) # Euler! 248 | 249 | # large composite 250 | assert_not_predicate(((2**13-1) * (2**17-1)), :prime?) 251 | 252 | # factorial 253 | assert_not_predicate((2...100).inject(&:*), :prime?) 254 | 255 | # negative 256 | assert_not_predicate(-1, :prime?) 257 | assert_not_predicate(-2, :prime?) 258 | assert_not_predicate(-3, :prime?) 259 | assert_not_predicate(-4, :prime?) 260 | 261 | assert_equal 1229, (1..10_000).count(&:prime?) 262 | assert_equal 861, (100_000..110_000).count(&:prime?) 263 | end 264 | 265 | def test_prime_in_ractor 266 | assert_ractor(<<~RUBY, require: 'prime') 267 | class Ractor 268 | alias value take 269 | end unless Ractor.method_defined? :value # compat with Ruby 3.4 and olders 270 | 271 | # Test usage of private constant... 272 | assert_equal false, Ractor.new { ((2**13-1) * (2**17-1)).prime? }.value 273 | RUBY 274 | end if defined?(Ractor) 275 | end 276 | 277 | def test_eratosthenes_works_fine_after_timeout 278 | sieve = Prime::EratosthenesSieve.instance 279 | sieve.send(:initialize) 280 | # simulates that Timeout.timeout interrupts Prime::EratosthenesSieve#compute_primes 281 | class << Integer 282 | alias_method :org_sqrt, :sqrt 283 | end 284 | begin 285 | def Integer.sqrt(n) 286 | sleep 10 if /compute_primes/ =~ caller.first 287 | org_sqrt(n) 288 | end 289 | assert_raise(Timeout::Error) do 290 | Timeout.timeout(0.5) { Prime.each(7*37){} } 291 | end 292 | ensure 293 | class << Integer 294 | remove_method :sqrt 295 | alias_method :sqrt, :org_sqrt 296 | remove_method :org_sqrt 297 | end 298 | end 299 | 300 | assert_not_include Prime.each(7*37).to_a, 7*37, "[ruby-dev:39465]" 301 | end 302 | end 303 | -------------------------------------------------------------------------------- /test/test_rbs.rb: -------------------------------------------------------------------------------- 1 | return unless RUBY_VERSION >= "3.0.0" 2 | 3 | require "test/unit" 4 | 5 | require "rbs" 6 | require "rbs/unit_test" 7 | 8 | require "prime" 9 | 10 | module RBSTypeTest 11 | class PrimeSingletonTest < Test::Unit::TestCase 12 | include RBS::UnitTest::TypeAssertions 13 | 14 | library "singleton", "prime" 15 | testing "singleton(::Prime)" 16 | 17 | def test_each 18 | assert_send_type( 19 | "() { (::Integer) -> void } -> void", 20 | Prime, :each, &proc { break_from_block } 21 | ) 22 | 23 | assert_send_type( 24 | "(::Integer? ubound) { (::Integer) -> void } -> void", 25 | Prime, :each, 10, &proc { break_from_block } 26 | ) 27 | 28 | assert_send_type( 29 | "(::Integer? ubound, ::Prime::PseudoPrimeGenerator generator) { (::Integer) -> void } -> void", 30 | Prime, :each, 10, Prime::TrialDivisionGenerator.new, &proc { break_from_block } 31 | ) 32 | 33 | assert_send_type( 34 | "() -> ::Prime::PseudoPrimeGenerator", 35 | Prime, :each 36 | ) 37 | end 38 | 39 | def test_int_from_prime_division 40 | assert_send_type "(::Array[[ ::Integer, ::Integer ]]) -> ::Integer", 41 | Prime, :int_from_prime_division, [[3, 1], [19, 1]] 42 | assert_send_type "(::Array[[ ::Integer, ::Integer ]]) -> ::Rational", 43 | Prime, :int_from_prime_division, [[-4, -2]] 44 | end 45 | 46 | def test_prime? 47 | assert_send_type "(::Integer) -> bool", 48 | Prime, :prime?, 57 49 | end 50 | 51 | def test_prime_division 52 | assert_send_type "(::Integer) -> ::Array[[ ::Integer, ::Integer ]]", 53 | Prime, :prime_division, 57 54 | end 55 | 56 | def test_instance 57 | assert_send_type "() -> ::Prime", 58 | Prime, :instance 59 | end 60 | end 61 | 62 | class PrimeInstanceTest < Test::Unit::TestCase 63 | include RBS::UnitTest::TypeAssertions 64 | 65 | library "singleton", "prime" 66 | testing "::Prime" 67 | 68 | def test_each 69 | assert_send_type( 70 | "() { (::Integer) -> void } -> void", 71 | Prime.instance, :each, &proc { break_from_block } 72 | ) 73 | assert_send_type( 74 | "(::Integer? ubound) { (::Integer) -> void } -> void", 75 | Prime.instance, :each, 10, &proc { break_from_block } 76 | ) 77 | assert_send_type( 78 | "(::Integer? ubound, ::Prime::PseudoPrimeGenerator generator) { (::Integer) -> void } -> void", 79 | Prime.instance, :each, 10, Prime::TrialDivisionGenerator.new, &proc { break_from_block } 80 | ) 81 | assert_send_type( 82 | "() -> ::Prime::PseudoPrimeGenerator", 83 | Prime.instance, :each 84 | ) 85 | end 86 | 87 | def test_int_from_prime_division 88 | assert_send_type "(::Array[[ ::Integer, ::Integer ]]) -> ::Integer", 89 | Prime.instance, :int_from_prime_division, [[3, 1], [19, 1]] 90 | assert_send_type "(::Array[[ ::Integer, ::Integer ]]) -> ::Rational", 91 | Prime.instance, :int_from_prime_division, [[-4, -2]] 92 | end 93 | 94 | def test_prime? 95 | assert_send_type "(::Integer value) -> bool", 96 | Prime.instance, :prime?, 57 97 | end 98 | 99 | def test_prime_division 100 | assert_send_type "(::Integer) -> ::Array[[ ::Integer, ::Integer ]]", 101 | Prime.instance, :prime_division, 57 102 | end 103 | end 104 | end 105 | --------------------------------------------------------------------------------