From 1ba67c259266481ecea44e10021102864699faa5 Mon Sep 17 00:00:00 2001 From: James 'zofrex' Sanderson Date: Mon, 9 Nov 2020 11:15:23 +0000 Subject: [PATCH 1/5] Sort Random --- core/random.rbs | 75 ++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/core/random.rbs b/core/random.rbs index 69549c59a..dbe2a8493 100644 --- a/core/random.rbs +++ b/core/random.rbs @@ -21,6 +21,43 @@ class Random < Object include Random::Formatter + # Creates a new PRNG using `seed` to set the initial state. If `seed` is + # omitted, the generator is initialized with Random.new_seed. + # + # See Random.srand for more information on the use of seed values. + # + def initialize: (?Integer seed) -> void + + # Returns an arbitrary seed value. This is used by Random.new when no seed value + # is specified as an argument. + # + # Random.new_seed #=> 115032730400174366788466674494640623225 + # + def self.new_seed: () -> Integer + + # Alias of Random::DEFAULT.rand. + # + def self.rand: (?Integer max) -> Numeric + + # Seeds the system pseudo-random number generator, Random::DEFAULT, with + # `number`. The previous seed value is returned. + # + # If `number` is omitted, seeds the generator using a source of entropy provided + # by the operating system, if available (/dev/urandom on Unix systems or the RSA + # cryptographic provider on Windows), which is then combined with the time, the + # process id, and a sequence number. + # + # srand may be used to ensure repeatable sequences of pseudo-random numbers + # between different runs of the program. By setting the seed to a known value, + # programs can be made deterministic during testing. + # + # srand 1234 # => 268519324636777531569100071560086917274 + # [ rand, rand ] # => [0.1915194503788923, 0.6221087710398319] + # [ rand(10), rand(1000) ] # => [4, 664] + # srand 1234 # => 1234 + # [ rand, rand ] # => [0.1915194503788923, 0.6221087710398319] + # + def self.srand: (?Integer number) -> Numeric # Returns true if the two generators have the same internal state, otherwise # false. Equivalent generators will return the same sequence of pseudo-random # numbers. Two generators will generally have the same state only if they were @@ -50,13 +87,6 @@ class Random < Object # def bytes: (Integer size) -> String - # Creates a new PRNG using `seed` to set the initial state. If `seed` is - # omitted, the generator is initialized with Random.new_seed. - # - # See Random.srand for more information on the use of seed values. - # - def initialize: (?Integer seed) -> void - # When `max` is an Integer, `rand` returns a random integer greater than or # equal to zero and less than `max`. Unlike Kernel.rand, when `max` is a # negative integer or zero, `rand` raises an ArgumentError. @@ -96,37 +126,6 @@ class Random < Object # prng2.rand(100) #=> 47 # def seed: () -> Integer - - # Returns an arbitrary seed value. This is used by Random.new when no seed value - # is specified as an argument. - # - # Random.new_seed #=> 115032730400174366788466674494640623225 - # - def self.new_seed: () -> Integer - - # Alias of Random::DEFAULT.rand. - # - def self.rand: (?Integer max) -> Numeric - - # Seeds the system pseudo-random number generator, Random::DEFAULT, with - # `number`. The previous seed value is returned. - # - # If `number` is omitted, seeds the generator using a source of entropy provided - # by the operating system, if available (/dev/urandom on Unix systems or the RSA - # cryptographic provider on Windows), which is then combined with the time, the - # process id, and a sequence number. - # - # srand may be used to ensure repeatable sequences of pseudo-random numbers - # between different runs of the program. By setting the seed to a known value, - # programs can be made deterministic during testing. - # - # srand 1234 # => 268519324636777531569100071560086917274 - # [ rand, rand ] # => [0.1915194503788923, 0.6221087710398319] - # [ rand(10), rand(1000) ] # => [4, 664] - # srand 1234 # => 1234 - # [ rand, rand ] # => [0.1915194503788923, 0.6221087710398319] - # - def self.srand: (?Integer number) -> Numeric end # The default Pseudorandom number generator. Used by class methods of Random. From a8238c085f811e2c578a7f34e25b0b5b300404e7 Mon Sep 17 00:00:00 2001 From: James 'zofrex' Sanderson Date: Mon, 9 Nov 2020 11:18:40 +0000 Subject: [PATCH 2/5] Add more types for Random --- core/random.rbs | 48 ++++++++++++++++--- test/stdlib/Random_test.rb | 97 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 test/stdlib/Random_test.rb diff --git a/core/random.rbs b/core/random.rbs index dbe2a8493..815981a81 100644 --- a/core/random.rbs +++ b/core/random.rbs @@ -19,8 +19,6 @@ # of 2**19937-1. # class Random < Object - include Random::Formatter - # Creates a new PRNG using `seed` to set the initial state. If `seed` is # omitted, the generator is initialized with Random.new_seed. # @@ -28,6 +26,13 @@ class Random < Object # def initialize: (?Integer seed) -> void + include Random::Formatter + + # Returns a random binary string. The argument `size` specifies the length of + # the returned string. + # + def self.bytes: (Integer size) -> String + # Returns an arbitrary seed value. This is used by Random.new when no seed value # is specified as an argument. # @@ -37,8 +42,10 @@ class Random < Object # Alias of Random::DEFAULT.rand. # - def self.rand: (?Integer max) -> Numeric - + def self.rand: () -> Float + | (Integer | ::Range[Integer] | Numeric max) -> Integer + | (Float | ::Range[Float] max) -> Float + | (::Range[Numeric]) -> Numeric # Seeds the system pseudo-random number generator, Random::DEFAULT, with # `number`. The previous seed value is returned. # @@ -57,7 +64,23 @@ class Random < Object # srand 1234 # => 1234 # [ rand, rand ] # => [0.1915194503788923, 0.6221087710398319] # - def self.srand: (?Integer number) -> Numeric + def self.srand: (?Integer number) -> Integer + + # Returns a string, using platform providing features. Returned value is + # expected to be a cryptographically secure pseudo-random number in binary form. + # This method raises a RuntimeError if the feature provided by platform failed + # to prepare the result. + # + # In 2017, Linux manpage random(7) writes that "no cryptographic primitive + # available today can hope to promise more than 256 bits of security". So it + # might be questionable to pass size > 32 to this method. + # + # Random.urandom(8) #=> "\x78\x41\xBA\xAF\x7D\xEA\xD8\xEA" + # + def self.urandom: (Integer) -> String + + public + # Returns true if the two generators have the same internal state, otherwise # false. Equivalent generators will return the same sequence of pseudo-random # numbers. Two generators will generally have the same state only if they were @@ -111,8 +134,9 @@ class Random < Object # (`-`) and add (`+`)methods, or rand will raise an ArgumentError. # def rand: () -> Float - | (Integer | ::Range[Integer] max) -> Integer + | (Integer | ::Range[Integer] | Numeric max) -> Integer | (Float | ::Range[Float] max) -> Float + | (::Range[Numeric]) -> Numeric # Returns the seed value used to initialize the generator. This may be used to # initialize another generator with the same state at a later time, causing it @@ -126,6 +150,18 @@ class Random < Object # prng2.rand(100) #=> 47 # def seed: () -> Integer + + private + + def initialize_copy: (self object) -> self + + def left: () -> untyped + + def marshal_dump: () -> untyped + + def marshal_load: (untyped) -> untyped + + def state: () -> untyped end # The default Pseudorandom number generator. Used by class methods of Random. diff --git a/test/stdlib/Random_test.rb b/test/stdlib/Random_test.rb new file mode 100644 index 000000000..b923c881d --- /dev/null +++ b/test/stdlib/Random_test.rb @@ -0,0 +1,97 @@ +require_relative "test_helper" + +class RandomSingletonTest < Minitest::Test + include TypeAssertions + + # library "pathname", "set", "securerandom" # Declare library signatures to load + testing "singleton(::Random)" + + + def test_srand + assert_send_type "(?::Integer number) -> ::Numeric", + Random, :srand + assert_send_type "(?::Integer number) -> ::Numeric", + Random, :srand, 0 + end + + def test_rand + assert_send_type "() -> ::Float", + Random, :rand + assert_send_type "(::Integer) -> ::Integer", + Random, :rand, 1 + assert_send_type "(::Range[Integer]) -> ::Integer", + Random, :rand, 1..10 + assert_send_type "(::Numeric) -> ::Integer", + Random, :rand, Rational(7,6) + assert_send_type "(::Float) -> ::Float", + Random, :rand, 1.5 + assert_send_type "(::Range[Float]) -> ::Float", + Random, :rand, 1.5..5.5 + assert_send_type "(::Range[Numeric]) -> ::Numeric", + Random, :rand, Rational(1/6)..Rational(13/6) + end + + def test_new + assert_send_type "(?::Integer seed) -> ::Random", + Random, :new + end + + def test_bytes + assert_send_type "(::Integer size) -> ::String", + Random, :bytes, 0 + end + + def test_new_seed + assert_send_type "() -> ::Integer", + Random, :new_seed + end + + def test_urandom + assert_send_type "(::Integer) -> ::String", + Random, :urandom, 0 + end +end + +class RandomTest < Minitest::Test + include TypeAssertions + + # library "pathname", "set", "securerandom" # Declare library signatures to load + testing "::Random" + + def test_double_equal + assert_send_type "(untyped arg0) -> bool", + Random.new, :==, Random.new + end + + def test_initialize + assert_send_type "(?::Integer seed) -> void", + Random.new, :initialize + end + + def test_rand + assert_send_type "() -> ::Float", + Random.new, :rand + assert_send_type "(::Integer | ::Range[::Integer] max) -> ::Integer", + Random.new, :rand, 10 + assert_send_type "(::Integer | ::Range[::Integer] max) -> ::Integer", + Random.new, :rand, 0..10 + assert_send_type "(::Numeric) -> ::Integer", + Random.new, :rand, Rational(7,6) + assert_send_type "(::Float | ::Range[::Float] max) -> ::Float", + Random.new, :rand, 0.9 + assert_send_type "(::Float | ::Range[::Float] max) -> ::Float", + Random.new, :rand, 0.1..0.9 + assert_send_type "(::Range[Numeric]) -> ::Numeric", + Random.new, :rand, Rational(1/6)..Rational(13/6) + end + + def test_bytes + assert_send_type "(::Integer size) -> ::String", + Random.new, :bytes, 1 + end + + def test_seed + assert_send_type "() -> ::Integer", + Random.new, :seed + end +end From 823f08762f0dcddd3a709761b3d22b0980906f55 Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Sun, 13 Nov 2022 23:59:04 +0900 Subject: [PATCH 3/5] Tweak Random.rand typedef --- core/random.rbs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/random.rbs b/core/random.rbs index 815981a81..bdc42907a 100644 --- a/core/random.rbs +++ b/core/random.rbs @@ -134,9 +134,9 @@ class Random < Object # (`-`) and add (`+`)methods, or rand will raise an ArgumentError. # def rand: () -> Float - | (Integer | ::Range[Integer] | Numeric max) -> Integer | (Float | ::Range[Float] max) -> Float - | (::Range[Numeric]) -> Numeric + | (Integer | ::Range[Integer] | Numeric max) -> Integer + | (::Range[T]) -> T # Returns the seed value used to initialize the generator. This may be used to # initialize another generator with the same state at a later time, causing it From 4281c6452715889eef56d53213a145376ddc65ab Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Mon, 14 Nov 2022 00:35:35 +0900 Subject: [PATCH 4/5] rake annotate --- core/random.rbs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/random.rbs b/core/random.rbs index 80bc0c97b..4a95c9831 100644 --- a/core/random.rbs +++ b/core/random.rbs @@ -21,6 +21,10 @@ # use SecureRandom for security purpose, instead of this PRNG. # class Random < RBS::Unnamed::Random_Base + # # Creates a new PRNG using `seed` to set the initial state. If `seed` is # omitted, the generator is initialized with Random.new_seed. # @@ -28,6 +32,10 @@ class Random < RBS::Unnamed::Random_Base # def initialize: (?Integer seed) -> void + # # Returns a random binary string. The argument `size` specifies the length of # the returned string. # @@ -109,6 +117,10 @@ class Random < RBS::Unnamed::Random_Base # def self.srand: (?Integer number) -> Integer + # # Returns a string, using platform providing features. Returned value is # expected to be a cryptographically secure pseudo-random number in binary form. # This method raises a RuntimeError if the feature provided by platform failed @@ -122,6 +134,10 @@ class Random < RBS::Unnamed::Random_Base # def self.urandom: (Integer) -> String + # # Returns the seed value used to initialize the generator. This may be used to # initialize another generator with the same state at a later time, causing it # to produce the same sequence of numbers. From 224a180287a86f0984d36f0deb2e48c7762375bd Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Mon, 21 Nov 2022 17:07:15 +0900 Subject: [PATCH 5/5] Fix incorrect type annotation of Random.rand --- core/random.rbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/random.rbs b/core/random.rbs index 4a95c9831..b55942aaf 100644 --- a/core/random.rbs +++ b/core/random.rbs @@ -91,7 +91,7 @@ class Random < RBS::Unnamed::Random_Base def self.rand: () -> Float | (Integer | ::Range[Integer] max) -> Integer | (Float | ::Range[Float] max) -> Float - | (::Range[T]) -> T + | [T < Numeric] (::Range[T]) -> T #