1 #ifndef _DERANDOM_HPP
2 #define _DERANDOM_HPP
3 /*-------------------------------------------------------------------------
4 * drawElements C++ Base Library
5 * -----------------------------
6 *
7 * Copyright 2014 The Android Open Source Project
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Random number generator utilities.
24 *//*--------------------------------------------------------------------*/
25
26 #include "deDefs.hpp"
27 #include "deRandom.h"
28
29 #include <iterator> // std::distance()
30 #include <algorithm> // std::swap()
31
32 namespace de
33 {
34
35 //! Random self-test - compare returned values against hard-coded values.
36 void Random_selfTest(void);
37
38 class Random
39 {
40 public:
Random(uint32_t seed)41 Random(uint32_t seed)
42 {
43 deRandom_init(&m_rnd, seed);
44 }
~Random(void)45 ~Random(void)
46 {
47 }
48
getFloat(void)49 float getFloat(void)
50 {
51 return deRandom_getFloat(&m_rnd);
52 }
getDouble(void)53 double getDouble(void)
54 {
55 return deRandom_getDouble(&m_rnd);
56 }
getBool(void)57 bool getBool(void)
58 {
59 return deRandom_getBool(&m_rnd) == true;
60 }
61
62 float getFloat(float min, float max);
63 double getDouble(double min, double max);
64 int getInt(int min, int max);
65
getInt64(void)66 int64_t getInt64(void)
67 {
68 uint32_t upper = getUint32();
69 return static_cast<int64_t>((uint64_t)upper << 32ull | (uint64_t)getUint32());
70 }
getUint64(void)71 uint64_t getUint64(void)
72 {
73 uint32_t upper = getUint32();
74 return (uint64_t)upper << 32ull | (uint64_t)getUint32();
75 }
getInt32(void)76 int32_t getInt32(void)
77 {
78 return static_cast<int32_t>(getUint32());
79 }
getUint32(void)80 uint32_t getUint32(void)
81 {
82 return deRandom_getUint32(&m_rnd);
83 }
getUint16(void)84 uint16_t getUint16(void)
85 {
86 return (uint16_t)deRandom_getUint32(&m_rnd);
87 }
getUint8(void)88 uint8_t getUint8(void)
89 {
90 return (uint8_t)deRandom_getUint32(&m_rnd);
91 }
92
93 template <class InputIter, class OutputIter>
94 void choose(InputIter first, InputIter last, OutputIter result, int numItems);
95
96 template <typename T, class InputIter>
97 T choose(InputIter first, InputIter last);
98
99 // \note Weights must be floats
100 template <typename T, class InputIter, class WeightIter>
101 T chooseWeighted(InputIter first, InputIter last, WeightIter weight);
102
103 template <class Iterator>
104 void shuffle(Iterator first, Iterator last);
105
106 bool operator==(const Random &other) const;
107 bool operator!=(const Random &other) const;
108
109 private:
110 deRandom m_rnd;
111 } DE_WARN_UNUSED_TYPE;
112
113 // Inline implementations
114
getFloat(float min,float max)115 inline float Random::getFloat(float min, float max)
116 {
117 DE_ASSERT(min <= max);
118 return min + (max - min) * getFloat();
119 }
120
getDouble(double min,double max)121 inline double Random::getDouble(double min, double max)
122 {
123 DE_ASSERT(min <= max);
124 return min + (max - min) * getDouble();
125 }
126
getInt(int min,int max)127 inline int Random::getInt(int min, int max)
128 {
129 DE_ASSERT(min <= max);
130 if (min == (int)0x80000000 && max == (int)0x7fffffff)
131 return (int)getUint32();
132 else
133 return min + (int)(getUint32() % ((uint32_t)max - (uint32_t)min + 1u));
134 }
135
136 // Template implementations
137
138 template <class InputIter, class OutputIter>
choose(InputIter first,InputIter last,OutputIter result,int numItems)139 void Random::choose(InputIter first, InputIter last, OutputIter result, int numItems)
140 {
141 // Algorithm: Reservoir sampling
142 // http://en.wikipedia.org/wiki/Reservoir_sampling
143 // \note Will not work for suffling an array. Use suffle() instead.
144
145 int ndx;
146 for (ndx = 0; first != last; ++first, ++ndx)
147 {
148 if (ndx < numItems)
149 *(result + ndx) = *first;
150 else
151 {
152 int r = getInt(0, ndx);
153 if (r < numItems)
154 *(result + r) = *first;
155 }
156 }
157
158 DE_ASSERT(ndx >= numItems);
159 }
160
161 template <typename T, class InputIter>
choose(InputIter first,InputIter last)162 T Random::choose(InputIter first, InputIter last)
163 {
164 T val = T();
165 DE_ASSERT(first != last);
166 choose(first, last, &val, 1);
167 return val;
168 }
169
170 template <typename T, class InputIter, class WeightIter>
chooseWeighted(InputIter first,InputIter last,WeightIter weight)171 T Random::chooseWeighted(InputIter first, InputIter last, WeightIter weight)
172 {
173 // Compute weight sum
174 float weightSum = 0.0f;
175 int ndx;
176 for (ndx = 0; (first + ndx) != last; ndx++)
177 weightSum += *(weight + ndx);
178
179 // Random point in 0..weightSum
180 float p = getFloat(0.0f, weightSum);
181
182 // Find item in range
183 InputIter lastNonZero = last;
184 float curWeight = 0.0f;
185 for (ndx = 0; (first + ndx) != last; ndx++)
186 {
187 float w = *(weight + ndx);
188
189 curWeight += w;
190
191 if (p < curWeight)
192 return *(first + ndx);
193 else if (w > 0.0f)
194 lastNonZero = first + ndx;
195 }
196
197 DE_ASSERT(lastNonZero != last);
198 return *lastNonZero;
199 }
200
201 template <class Iterator>
shuffle(Iterator first,Iterator last)202 void Random::shuffle(Iterator first, Iterator last)
203 {
204 using std::swap;
205
206 // Fisher-Yates suffle
207 int numItems = (int)std::distance(first, last);
208
209 for (int i = numItems - 1; i >= 1; i--)
210 {
211 int j = getInt(0, i);
212 swap(*(first + i), *(first + j));
213 }
214 }
215
216 template <typename T>
217 T randomScalar(de::Random &rnd, T minValue, T maxValue);
218 template <>
randomScalar(de::Random & rnd,float minValue,float maxValue)219 inline float randomScalar(de::Random &rnd, float minValue, float maxValue)
220 {
221 return rnd.getFloat(minValue, maxValue);
222 }
223 template <>
randomScalar(de::Random & rnd,int32_t minValue,int32_t maxValue)224 inline int32_t randomScalar(de::Random &rnd, int32_t minValue, int32_t maxValue)
225 {
226 return rnd.getInt(minValue, maxValue);
227 }
228 template <>
randomScalar(de::Random & rnd,uint32_t minValue,uint32_t maxValue)229 inline uint32_t randomScalar(de::Random &rnd, uint32_t minValue, uint32_t maxValue)
230 {
231 if (minValue == 0 && maxValue == 0xffffffff)
232 return rnd.getUint32();
233 return minValue + rnd.getUint32() % (maxValue - minValue + 1);
234 }
235 template <>
randomScalar(de::Random & rnd,int16_t minValue,int16_t maxValue)236 inline int16_t randomScalar(de::Random &rnd, int16_t minValue, int16_t maxValue)
237 {
238 return (int16_t)rnd.getInt(minValue, maxValue);
239 }
240 template <>
randomScalar(de::Random & rnd,uint16_t minValue,uint16_t maxValue)241 inline uint16_t randomScalar(de::Random &rnd, uint16_t minValue, uint16_t maxValue)
242 {
243 return (uint16_t)(minValue + rnd.getUint16() % (maxValue - minValue + 1));
244 }
245 template <>
randomScalar(de::Random & rnd,int8_t minValue,int8_t maxValue)246 inline int8_t randomScalar(de::Random &rnd, int8_t minValue, int8_t maxValue)
247 {
248 return (int8_t)rnd.getInt(minValue, maxValue);
249 }
250 template <>
randomScalar(de::Random & rnd,uint8_t minValue,uint8_t maxValue)251 inline uint8_t randomScalar(de::Random &rnd, uint8_t minValue, uint8_t maxValue)
252 {
253 return (uint8_t)(minValue + rnd.getUint8() % (maxValue - minValue + 1));
254 }
255
256 } // namespace de
257
258 #endif // _DERANDOM_HPP
259