From e3c1586bceb2c0a10dfcc10ecd763feea6e7cff9 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Fri, 24 Nov 2023 11:55:49 +0900 Subject: [PATCH 1/5] bundle rbs --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 5e6ae50..edad0c4 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,6 @@ group :development do gem "rake" gem "test-unit" gem "test-unit-ruby-core" + + gem "rbs", require: false end From fc1c58dee654440dfa56036c54b029a0ca0d1b5d Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Fri, 24 Nov 2023 11:56:08 +0900 Subject: [PATCH 2/5] Copy RBS files from gem_rbs_collection --- sig/integer-extension.rbs | 41 +++++ sig/manifest.yaml | 2 + sig/prime.rbs | 372 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 415 insertions(+) create mode 100644 sig/integer-extension.rbs create mode 100644 sig/manifest.yaml create mode 100644 sig/prime.rbs diff --git a/sig/integer-extension.rbs b/sig/integer-extension.rbs new file mode 100644 index 0000000..19077c8 --- /dev/null +++ b/sig/integer-extension.rbs @@ -0,0 +1,41 @@ +%a{annotate:rdoc:skip} +class Integer + # + # Iterates the given block over all prime numbers. + # + # See `Prime`#each for more details. + # + def self.each_prime: (Integer) { (Integer) -> void } -> void + + # + # Re-composes a prime factorization and returns the product. + # + # See Prime#int_from_prime_division for more details. + # + def self.from_prime_division: (Array[[ String ]]) -> Integer + + # + # Returns the factorization of `self`. + # + # See Prime#prime_division for more details. + # + def prime_division: (?Prime::PseudoPrimeGenerator) -> Array[[ Integer, Integer ]] + + # + # Returns true if `self` is a prime number, else returns false. Not recommended + # for very big integers (> 10**23). + # + def prime?: () -> bool +end diff --git a/sig/manifest.yaml b/sig/manifest.yaml new file mode 100644 index 0000000..1f02379 --- /dev/null +++ b/sig/manifest.yaml @@ -0,0 +1,2 @@ +dependencies: + - name: singleton diff --git a/sig/prime.rbs b/sig/prime.rbs new file mode 100644 index 0000000..a4038ad --- /dev/null +++ b/sig/prime.rbs @@ -0,0 +1,372 @@ +# +# The set of all prime numbers. +# +# ## Example +# +# Prime.each(100) do |prime| +# p prime #=> 2, 3, 5, 7, 11, ...., 97 +# end +# +# Prime is Enumerable: +# +# Prime.first 5 # => [2, 3, 5, 7, 11] +# +# ## Retrieving the instance +# +# For convenience, each instance method of `Prime`.instance can be accessed as a +# class method of `Prime`. +# +# e.g. +# Prime.instance.prime?(2) #=> true +# Prime.prime?(2) #=> true +# +# ## Generators +# +# A "generator" provides an implementation of enumerating pseudo-prime numbers +# and it remembers the position of enumeration and upper bound. Furthermore, it +# is an external iterator of prime enumeration which is compatible with an +# Enumerator. +# +# `Prime`::`PseudoPrimeGenerator` is the base class for generators. There are +# few implementations of generator. +# +# `Prime`::`EratosthenesGenerator` +# : Uses Eratosthenes' sieve. +# `Prime`::`TrialDivisionGenerator` +# : Uses the trial division method. +# `Prime`::`Generator23` +# : Generates all positive integers which are not divisible by either 2 or 3. +# This sequence is very bad as a pseudo-prime sequence. But this is faster +# and uses much less memory than the other generators. So, it is suitable +# for factorizing an integer which is not large but has many prime factors. +# e.g. for Prime#prime? . +# +class Prime + include Singleton + + include Enumerable[Integer] + + extend Enumerable[Integer] + + # + # Iterates the given block over all prime numbers. + # + # ## Parameters + # + # `ubound` + # : Optional. An arbitrary positive number. The upper bound of enumeration. + # The method enumerates prime numbers infinitely if `ubound` is nil. + # `generator` + # : Optional. An implementation of pseudo-prime generator. + # + # + # ## Return value + # + # An evaluated value of the given block at the last time. Or an enumerator which + # is compatible to an `Enumerator` if no block given. + # + # ## Description + # + # Calls `block` once for each prime number, passing the prime as a parameter. + # + # `ubound` + # : Upper bound of prime numbers. The iterator stops after it yields all prime + # numbers p <= `ubound`. + # + def self.each: (?Integer? ubound, ?PseudoPrimeGenerator generator) { (Integer) -> void } -> void + | (?Integer? ubound, ?PseudoPrimeGenerator generator) -> PseudoPrimeGenerator + + # + # Iterates the given block over all prime numbers. + # + # ## Parameters + # + # `ubound` + # : Optional. An arbitrary positive number. The upper bound of enumeration. + # The method enumerates prime numbers infinitely if `ubound` is nil. + # `generator` + # : Optional. An implementation of pseudo-prime generator. + # + # + # ## Return value + # + # An evaluated value of the given block at the last time. Or an enumerator which + # is compatible to an `Enumerator` if no block given. + # + # ## Description + # + # Calls `block` once for each prime number, passing the prime as a parameter. + # + # `ubound` + # : Upper bound of prime numbers. The iterator stops after it yields all prime + # numbers p <= `ubound`. + # + def each: (?Integer? ubound, ?PseudoPrimeGenerator generator) { (Integer) -> void } -> void + | (?Integer? ubound, ?PseudoPrimeGenerator generator) -> PseudoPrimeGenerator + + # + # Re-composes a prime factorization and returns the product. + # + # For the decomposition: + # + # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]], + # + # it returns: + # + # p_1**e_1 * p_2**e_2 * ... * p_n**e_n. + # + # ## Parameters + # `pd` + # : Array of pairs of integers. Each pair consists of a prime number -- a + # prime factor -- and a natural number -- its exponent (multiplicity). + # + # + # ## Example + # Prime.int_from_prime_division([[3, 2], [5, 1]]) #=> 45 + # 3**2 * 5 #=> 45 + # + def self.int_from_prime_division: (Array[[ Integer, Integer ]]) -> Integer + + # + # Re-composes a prime factorization and returns the product. + # + # For the decomposition: + # + # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]], + # + # it returns: + # + # p_1**e_1 * p_2**e_2 * ... * p_n**e_n. + # + # ## Parameters + # `pd` + # : Array of pairs of integers. Each pair consists of a prime number -- a + # prime factor -- and a natural number -- its exponent (multiplicity). + # + # + # ## Example + # Prime.int_from_prime_division([[3, 2], [5, 1]]) #=> 45 + # 3**2 * 5 #=> 45 + # + def int_from_prime_division: (Array[[ Integer, Integer ]]) -> Integer + + # + # Returns true if `value` is a prime number, else returns false. Integer#prime? + # is much more performant. + # + # ## Parameters + # + # `value` + # : an arbitrary integer to be checked. + # `generator` + # : optional. A pseudo-prime generator. + # + def self.prime?: (Integer value, ?PseudoPrimeGenerator generator) -> bool + + # + # Returns true if `value` is a prime number, else returns false. Integer#prime? + # is much more performant. + # + # ## Parameters + # + # `value` + # : an arbitrary integer to be checked. + # `generator` + # : optional. A pseudo-prime generator. + # + def prime?: (Integer value, ?PseudoPrimeGenerator generator) -> bool + + # + # Returns the factorization of `value`. + # + # For an arbitrary integer: + # + # p_1**e_1 * p_2**e_2 * ... * p_n**e_n, + # + # prime_division returns an array of pairs of integers: + # + # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]]. + # + # Each pair consists of a prime number -- a prime factor -- and a natural number + # -- its exponent (multiplicity). + # + # ## Parameters + # `value` + # : An arbitrary integer. + # `generator` + # : Optional. A pseudo-prime generator. `generator`.succ must return the next + # pseudo-prime number in ascending order. It must generate all prime + # numbers, but may also generate non-prime numbers, too. + # + # + # ### Exceptions + # `ZeroDivisionError` + # : when `value` is zero. + # + # + # ## Example + # + # Prime.prime_division(45) #=> [[3, 2], [5, 1]] + # 3**2 * 5 #=> 45 + # + def self.prime_division: (Integer, ?PseudoPrimeGenerator generator) -> Array[[ Integer, Integer ]] + + # + # Returns the factorization of `value`. + # + # For an arbitrary integer: + # + # p_1**e_1 * p_2**e_2 * ... * p_n**e_n, + # + # prime_division returns an array of pairs of integers: + # + # [[p_1, e_1], [p_2, e_2], ..., [p_n, e_n]]. + # + # Each pair consists of a prime number -- a prime factor -- and a natural number + # -- its exponent (multiplicity). + # + # ## Parameters + # `value` + # : An arbitrary integer. + # `generator` + # : Optional. A pseudo-prime generator. `generator`.succ must return the next + # pseudo-prime number in ascending order. It must generate all prime + # numbers, but may also generate non-prime numbers, too. + # + # + # ### Exceptions + # `ZeroDivisionError` + # : when `value` is zero. + # + # + # ## Example + # + # Prime.prime_division(45) #=> [[3, 2], [5, 1]] + # 3**2 * 5 #=> 45 + # + def prime_division: (Integer, ?PseudoPrimeGenerator generator) -> Array[[ Integer, Integer ]] + + # Returns the singleton instance. + # + def self.instance: () -> Prime + + # + # An abstract class for enumerating pseudo-prime numbers. + # + # Concrete subclasses should override succ, next, rewind. + # + class PseudoPrimeGenerator + # + # + def initialize: (?Integer?) -> void + + include Enumerable[Integer] + + # + # ---- + # + # + attr_accessor upper_bound(): Integer? + + # + # Iterates the given block for each prime number. + # + def each: () { (Integer) -> void } -> void + + # + # alias of `succ`. + # + def next: () -> Integer + + # + # Rewinds the internal position for enumeration. + # + # See `Enumerator`#rewind. + # + def rewind: () -> void + + # + # + def size: () -> Float + + # + # returns the next pseudo-prime number, and move the internal position forward. + # + # `PseudoPrimeGenerator`#succ raises `NotImplementedError`. + # + def succ: () -> Integer + end + + # + # An implementation of `PseudoPrimeGenerator`. + # + # Uses `EratosthenesSieve`. + # + class EratosthenesGenerator < PseudoPrimeGenerator + end + + # + # An implementation of `PseudoPrimeGenerator` which uses a prime table generated + # by trial division. + # + class TrialDivisionGenerator < PseudoPrimeGenerator + end + + # + # Generates all integers which are greater than 2 and are not divisible by + # either 2 or 3. + # + # This is a pseudo-prime generator, suitable on checking primality of an integer + # by brute force method. + # + class Generator23 < PseudoPrimeGenerator + end +end From 0319c6d949d8e72e7fa3ae2c0080a99d0eef9996 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 6 Dec 2023 17:27:54 +0900 Subject: [PATCH 3/5] bundle dev version of RBS --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index edad0c4..1ff3dc8 100644 --- a/Gemfile +++ b/Gemfile @@ -7,5 +7,5 @@ group :development do gem "test-unit" gem "test-unit-ruby-core" - gem "rbs", require: false + gem "rbs", "~> 3.4.0.dev", require: false, git: "https://github.com/ruby/rbs.git", branch: "master" end From 57cded21ef5f1ff96b590297b06af3f2d3acf458 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 6 Dec 2023 17:27:58 +0900 Subject: [PATCH 4/5] Add test --- test/test_rbs.rb | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 test/test_rbs.rb diff --git a/test/test_rbs.rb b/test/test_rbs.rb new file mode 100644 index 0000000..cfdbfbb --- /dev/null +++ b/test/test_rbs.rb @@ -0,0 +1,98 @@ +require "test/unit" + +require "rbs" +require "rbs/unit_test" + +require "prime" + +module RBSTypeTest + class PrimeSingletonTest < Test::Unit::TestCase + include RBS::UnitTest::TypeAssertions + + library "singleton", "prime" + testing "singleton(::Prime)" + + def test_each + assert_send_type( + "() { (::Integer) -> void } -> void", + Prime, :each, &proc { break_from_block } + ) + + assert_send_type( + "(::Integer? ubound) { (::Integer) -> void } -> void", + Prime, :each, 10, &proc { break_from_block } + ) + + assert_send_type( + "(::Integer? ubound, ::Prime::PseudoPrimeGenerator generator) { (::Integer) -> void } -> void", + Prime, :each, 10, Prime::TrialDivisionGenerator.new, &proc { break_from_block } + ) + + assert_send_type( + "() -> ::Prime::PseudoPrimeGenerator", + Prime, :each + ) + end + + def test_int_from_prime_division + assert_send_type "(::Array[[ ::Integer, ::Integer ]]) -> ::Integer", + Prime, :int_from_prime_division, [[3, 1], [19, 1]] + end + + def test_prime? + assert_send_type "(::Integer) -> bool", + Prime, :prime?, 57 + end + + def test_prime_division + assert_send_type "(::Integer) -> ::Array[[ ::Integer, ::Integer ]]", + Prime, :prime_division, 57 + end + + def test_instance + assert_send_type "() -> ::Prime", + Prime, :instance + end + end + + class PrimeInstanceTest < Test::Unit::TestCase + include RBS::UnitTest::TypeAssertions + + library "singleton", "prime" + testing "::Prime" + + def test_each + assert_send_type( + "() { (::Integer) -> void } -> void", + Prime.instance, :each, &proc { break_from_block } + ) + assert_send_type( + "(::Integer? ubound) { (::Integer) -> void } -> void", + Prime.instance, :each, 10, &proc { break_from_block } + ) + assert_send_type( + "(::Integer? ubound, ::Prime::PseudoPrimeGenerator generator) { (::Integer) -> void } -> void", + Prime.instance, :each, 10, Prime::TrialDivisionGenerator.new, &proc { break_from_block } + ) + assert_send_type( + "() -> ::Prime::PseudoPrimeGenerator", + Prime.instance, :each + ) + end + + def test_int_from_prime_division + assert_send_type "(::Array[[ ::Integer, ::Integer ]]) -> ::Integer", + Prime.instance, :int_from_prime_division, [[3, 1], [19, 1]] + end + + def test_prime? + assert_send_type "(::Integer value) -> bool", + Prime.instance, :prime?, 57 + end + + def test_prime_division + assert_send_type "(::Integer) -> ::Array[[ ::Integer, ::Integer ]]", + Prime.instance, :prime_division, 57 + end + end +end From 7e232ad0fe46380989eacbd5c2bb852defe66d3a Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Thu, 7 Dec 2023 12:34:37 +0900 Subject: [PATCH 5/5] Run RBS test with Ruby >= `3.0` --- Gemfile | 5 ++++- test/test_rbs.rb | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 1ff3dc8..cdd4905 100644 --- a/Gemfile +++ b/Gemfile @@ -7,5 +7,8 @@ group :development do gem "test-unit" gem "test-unit-ruby-core" - gem "rbs", "~> 3.4.0.dev", require: false, git: "https://github.com/ruby/rbs.git", branch: "master" + # RBS requires Ruby >= 3.0 + if RUBY_VERSION >= "3.0.0" + gem "rbs", "~> 3.4.0.dev", require: false, git: "https://github.com/ruby/rbs.git", branch: "master" + end end diff --git a/test/test_rbs.rb b/test/test_rbs.rb index cfdbfbb..8fd7c23 100644 --- a/test/test_rbs.rb +++ b/test/test_rbs.rb @@ -1,3 +1,5 @@ +return unless RUBY_VERSION >= "3.0.0" + require "test/unit" require "rbs"