-
Notifications
You must be signed in to change notification settings - Fork 606
/
random.rb
188 lines (157 loc) · 4.32 KB
/
random.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
class Rubinius::Randomizer
def self.allocate
Rubinius.primitive :randomizer_allocate
raise PrimitiveFailure, "Randomizer.allocate primitive failed"
end
def initialize
self.seed = generate_seed
end
private :initialize
attr_reader :seed
def seed=(new_seed)
set_seed new_seed
@seed = new_seed
end
def set_seed(new_seed)
Rubinius.primitive :randomizer_seed
raise PrimitiveFailure, "Randomizer#seed primitive failed"
end
def swap_seed(new_seed)
old_seed = self.seed
self.seed = new_seed
old_seed
end
def random(limit)
if undefined.equal?(limit)
random_float
else
if limit.kind_of?(Range)
if time_value?(limit.min)
random_time_range(limit)
else
random_range(limit)
end
elsif limit.kind_of?(Float)
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
random_float * limit
elsif limit.is_a?(Integer)
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
random_integer(limit - 1)
elsif limit.respond_to?(:to_f)
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
random_float * limit
else
limit_int = Rubinius::Type.coerce_to limit, Integer, :to_int
raise ArgumentError, "invalid argument - #{limit}" if limit_int <= 0
random_integer(limit_int - 1)
end
end
end
# Generate a random Float, in the range 0...1.0
def random_float
Rubinius.primitive :randomizer_rand_float
raise PrimitiveFailure, "Randomizer#rand_float primitive failed"
end
# Generate a random Integer, in the range 0...limit
def random_integer(limit)
Rubinius.primitive :randomizer_rand_int
raise PrimitiveFailure, "Randomizer#rand_int primitive failed"
end
def random_range(limit)
return nil if limit.first.nil? || limit.last.nil?
min, max = limit.last.coerce(limit.first)
diff = max - min
return if diff < 0
return min if diff == 0
diff += 1 if max.kind_of?(Integer) && !limit.exclude_end?
random(diff) + min
end
def generate_seed
Rubinius.primitive :randomizer_gen_seed
raise PrimitiveFailure, "Randomizer#gen_seed primitive failed"
end
##
# Returns a random value from a range made out of Time, Date or DateTime
# instances.
#
# @param [Range] range
# @return [Time|Date|DateTime]
#
def random_time_range(range)
min = time_to_float(range.min)
max = time_to_float(range.max)
time = Time.at(random(min..max))
if Object.const_defined?(:DateTime) && range.min.is_a?(DateTime)
time = time.to_datetime
elsif Object.const_defined?(:DateTime) && range.min.is_a?(Date)
time = time.to_date
end
return time
end
##
# Casts a Time/Date/DateTime instance to a Float.
#
# @param [Time|Date|DateTime] input
# @return [Float]
#
def time_to_float(input)
return input.respond_to?(:to_time) ? input.to_time.to_f : input.to_f
end
##
# Checks if a given value is a Time, Date or DateTime object.
#
# @param [Mixed] input
# @return [TrueClass|FalseClass]
#
def time_value?(input)
return input.is_a?(Time) || (Object.const_defined?(:Date) && input.is_a?(Date))
end
end
class Random
def self.new_seed
Thread.current.randomizer.generate_seed
end
def self.srand(seed=undefined)
if undefined.equal? seed
seed = Thread.current.randomizer.generate_seed
end
seed = Rubinius::Type.coerce_to seed, Integer, :to_int
Thread.current.randomizer.swap_seed seed
end
def self.rand(limit=undefined)
Thread.current.randomizer.random(limit)
end
def initialize(seed=undefined)
@randomizer = Rubinius::Randomizer.new
if !undefined.equal?(seed)
@randomizer.swap_seed seed.to_int
end
end
private :initialize
def rand(limit=undefined)
@randomizer.random(limit)
end
def seed
@randomizer.seed
end
def state
@randomizer.seed
end
private :state
def ==(other)
return false unless other.kind_of?(Random)
seed == other.seed
end
# Returns a random binary string.
# The argument size specified the length of the result string.
def bytes(length)
length = Rubinius::Type.coerce_to length, Integer, :to_int
s = ''
i = 0
while i < length
s << @randomizer.random_integer(255).chr
i += 1
end
s
end
end