# Random number generation based on code from GST-3.2.5 (2.02)
# @see qmjAJh
class PiRandom
def initialize
self.seed(Time.now.to_f)
end
def seed(value)
@state = (value / 100000.0 + value) % 1.0
self.rand
self.rand
end
def rand(*args)
case args.size
when 0
@state = ((@state + Math::PI) ** 4) % 1.0
when 1
rand_with(args.first)
else
raise ArgumentError.new("wrong number of arguments "\
"(given #{args.size}, expected 0..1)")
end
end
private def rand_with(arg)
case arg
when Float
# [0, arg) exclusive.
self.rand * [arg, 0].max
when Integer
# [0, arg] inclusive.
rand_with(arg + 1.0).to_i
when Range
min = arg.min
max = arg.max
raise ArgumentError.new("invalid argument: #{arg}")\
if min.nil? || max.nil?
rand_with(max - min) + min
else
raise TypeError.new("no support for: #{arg.class}")
end
end
end
# Test.
require "test/unit"
extend Test::Unit::Assertions
def chi_square(n, r, prng)
f = Array.new(r, 0)
n.times do
f[prng.rand(0...r)] += 1
end
t = f.sum {|x| x * x}
r * t / n - n
end
prng = PiRandom.new
# Distribution.
5.times do
p chi_square(1000, 100, prng)
end
n = 100
# Canonical.
n.times do
assert do
t = prng.rand
0.0 <= t && t < 1.0
end
end
# Max.
n.times do
assert do
t = prng.rand(5)
0 <= t && t <= 5
end
assert do
t = prng.rand(5.0)
0.0 <= t && t < 5.0
end
end
# Range.
n.times do
assert do
t = prng.rand(1..5)
1 <= t && t <= 5
end
assert do
t = prng.rand(1...5)
1 <= t && t < 5
end
assert do
t = prng.rand(1.0..5.0)
1.0 <= t && t < 5.0
end
end
IyBSYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gYmFzZWQgb24gY29kZSBmcm9tIEdTVC0zLjIuNSAoMi4wMikKIyBAc2VlIHFtakFKaAoKY2xhc3MgUGlSYW5kb20KICBkZWYgaW5pdGlhbGl6ZQogICAgc2VsZi5zZWVkKFRpbWUubm93LnRvX2YpCiAgZW5kCgogIGRlZiBzZWVkKHZhbHVlKQogICAgQHN0YXRlID0gKHZhbHVlIC8gMTAwMDAwLjAgKyB2YWx1ZSkgJSAxLjAKICAgIHNlbGYucmFuZAogICAgc2VsZi5yYW5kCiAgZW5kCgogIGRlZiByYW5kKCphcmdzKQogICAgY2FzZSBhcmdzLnNpemUKICAgIHdoZW4gMAogICAgICBAc3RhdGUgPSAoKEBzdGF0ZSArIE1hdGg6OlBJKSAqKiA0KSAlIDEuMAogICAgd2hlbiAxCiAgICAgIHJhbmRfd2l0aChhcmdzLmZpcnN0KQogICAgZWxzZQogICAgICByYWlzZSBBcmd1bWVudEVycm9yLm5ldygid3JvbmcgbnVtYmVyIG9mIGFyZ3VtZW50cyAiXAogICAgICAgICAgICAiKGdpdmVuICN7YXJncy5zaXplfSwgZXhwZWN0ZWQgMC4uMSkiKQogICAgZW5kCiAgZW5kCgogIHByaXZhdGUgZGVmIHJhbmRfd2l0aChhcmcpCiAgICBjYXNlIGFyZwogICAgd2hlbiBGbG9hdAogICAgICAjIFswLCBhcmcpIGV4Y2x1c2l2ZS4KICAgICAgc2VsZi5yYW5kICogW2FyZywgMF0ubWF4CiAgICB3aGVuIEludGVnZXIKICAgICAgIyBbMCwgYXJnXSBpbmNsdXNpdmUuCiAgICAgIHJhbmRfd2l0aChhcmcgKyAxLjApLnRvX2kKICAgIHdoZW4gUmFuZ2UKICAgICAgbWluID0gYXJnLm1pbgogICAgICBtYXggPSBhcmcubWF4CiAgICAgIHJhaXNlIEFyZ3VtZW50RXJyb3IubmV3KCJpbnZhbGlkIGFyZ3VtZW50OiAje2FyZ30iKVwKICAgICAgICAgICAgaWYgbWluLm5pbD8gfHwgbWF4Lm5pbD8KICAgICAgcmFuZF93aXRoKG1heCAtIG1pbikgKyBtaW4KICAgIGVsc2UKICAgICAgcmFpc2UgVHlwZUVycm9yLm5ldygibm8gc3VwcG9ydCBmb3I6ICN7YXJnLmNsYXNzfSIpCiAgICBlbmQKICBlbmQKZW5kCgojIFRlc3QuCgpyZXF1aXJlICJ0ZXN0L3VuaXQiCmV4dGVuZCBUZXN0OjpVbml0OjpBc3NlcnRpb25zCgpkZWYgY2hpX3NxdWFyZShuLCByLCBwcm5nKQogIGYgPSBBcnJheS5uZXcociwgMCkKICBuLnRpbWVzIGRvCiAgICBmW3BybmcucmFuZCgwLi4ucildICs9IDEKICBlbmQKICB0ID0gZi5zdW0ge3x4fCB4ICogeH0KICByICogdCAvIG4gLSBuCmVuZAoKcHJuZyA9IFBpUmFuZG9tLm5ldwoKIyBEaXN0cmlidXRpb24uCgo1LnRpbWVzIGRvCiAgcCBjaGlfc3F1YXJlKDEwMDAsIDEwMCwgcHJuZykKZW5kCgpuID0gMTAwCgojIENhbm9uaWNhbC4KCm4udGltZXMgZG8KICBhc3NlcnQgZG8KICAgIHQgPSBwcm5nLnJhbmQKICAgIDAuMCA8PSB0ICYmIHQgPCAxLjAKICBlbmQKZW5kCgojIE1heC4KCm4udGltZXMgZG8KICBhc3NlcnQgZG8KICAgIHQgPSBwcm5nLnJhbmQoNSkKICAgIDAgPD0gdCAmJiB0IDw9IDUKICBlbmQKICBhc3NlcnQgZG8KICAgIHQgPSBwcm5nLnJhbmQoNS4wKQogICAgMC4wIDw9IHQgJiYgdCA8IDUuMAogIGVuZAplbmQKCiMgUmFuZ2UuCgpuLnRpbWVzIGRvCiAgYXNzZXJ0IGRvCiAgICB0ID0gcHJuZy5yYW5kKDEuLjUpCiAgICAxIDw9IHQgJiYgdCA8PSA1CiAgZW5kCiAgYXNzZXJ0IGRvCiAgICB0ID0gcHJuZy5yYW5kKDEuLi41KQogICAgMSA8PSB0ICYmIHQgPCA1CiAgZW5kCiAgYXNzZXJ0IGRvCiAgICB0ID0gcHJuZy5yYW5kKDEuMC4uNS4wKQogICAgMS4wIDw9IHQgJiYgdCA8IDUuMAogIGVuZAplbmQ=