#ifndef _DERANDOM_HPP #define _DERANDOM_HPP /*------------------------------------------------------------------------- * drawElements C++ Base Library * ----------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Random number generator utilities. *//*--------------------------------------------------------------------*/ #include "deDefs.hpp" #include "deRandom.h" #include // std::distance() #include // std::swap() namespace de { //! Random self-test - compare returned values against hard-coded values. void Random_selfTest(void); class Random { public: Random(uint32_t seed) { deRandom_init(&m_rnd, seed); } ~Random(void) { } float getFloat(void) { return deRandom_getFloat(&m_rnd); } double getDouble(void) { return deRandom_getDouble(&m_rnd); } bool getBool(void) { return deRandom_getBool(&m_rnd) == true; } float getFloat(float min, float max); double getDouble(double min, double max); int getInt(int min, int max); int64_t getInt64(void) { uint32_t upper = getUint32(); return static_cast((uint64_t)upper << 32ull | (uint64_t)getUint32()); } uint64_t getUint64(void) { uint32_t upper = getUint32(); return (uint64_t)upper << 32ull | (uint64_t)getUint32(); } int32_t getInt32(void) { return static_cast(getUint32()); } uint32_t getUint32(void) { return deRandom_getUint32(&m_rnd); } uint16_t getUint16(void) { return (uint16_t)deRandom_getUint32(&m_rnd); } uint8_t getUint8(void) { return (uint8_t)deRandom_getUint32(&m_rnd); } template void choose(InputIter first, InputIter last, OutputIter result, int numItems); template T choose(InputIter first, InputIter last); // \note Weights must be floats template T chooseWeighted(InputIter first, InputIter last, WeightIter weight); template void shuffle(Iterator first, Iterator last); bool operator==(const Random &other) const; bool operator!=(const Random &other) const; private: deRandom m_rnd; } DE_WARN_UNUSED_TYPE; // Inline implementations inline float Random::getFloat(float min, float max) { DE_ASSERT(min <= max); return min + (max - min) * getFloat(); } inline double Random::getDouble(double min, double max) { DE_ASSERT(min <= max); return min + (max - min) * getDouble(); } inline int Random::getInt(int min, int max) { DE_ASSERT(min <= max); if (min == (int)0x80000000 && max == (int)0x7fffffff) return (int)getUint32(); else return min + (int)(getUint32() % ((uint32_t)max - (uint32_t)min + 1u)); } // Template implementations template void Random::choose(InputIter first, InputIter last, OutputIter result, int numItems) { // Algorithm: Reservoir sampling // http://en.wikipedia.org/wiki/Reservoir_sampling // \note Will not work for suffling an array. Use suffle() instead. int ndx; for (ndx = 0; first != last; ++first, ++ndx) { if (ndx < numItems) *(result + ndx) = *first; else { int r = getInt(0, ndx); if (r < numItems) *(result + r) = *first; } } DE_ASSERT(ndx >= numItems); } template T Random::choose(InputIter first, InputIter last) { T val = T(); DE_ASSERT(first != last); choose(first, last, &val, 1); return val; } template T Random::chooseWeighted(InputIter first, InputIter last, WeightIter weight) { // Compute weight sum float weightSum = 0.0f; int ndx; for (ndx = 0; (first + ndx) != last; ndx++) weightSum += *(weight + ndx); // Random point in 0..weightSum float p = getFloat(0.0f, weightSum); // Find item in range InputIter lastNonZero = last; float curWeight = 0.0f; for (ndx = 0; (first + ndx) != last; ndx++) { float w = *(weight + ndx); curWeight += w; if (p < curWeight) return *(first + ndx); else if (w > 0.0f) lastNonZero = first + ndx; } DE_ASSERT(lastNonZero != last); return *lastNonZero; } template void Random::shuffle(Iterator first, Iterator last) { using std::swap; // Fisher-Yates suffle int numItems = (int)std::distance(first, last); for (int i = numItems - 1; i >= 1; i--) { int j = getInt(0, i); swap(*(first + i), *(first + j)); } } template T randomScalar(de::Random &rnd, T minValue, T maxValue); template <> inline float randomScalar(de::Random &rnd, float minValue, float maxValue) { return rnd.getFloat(minValue, maxValue); } template <> inline int32_t randomScalar(de::Random &rnd, int32_t minValue, int32_t maxValue) { return rnd.getInt(minValue, maxValue); } template <> inline uint32_t randomScalar(de::Random &rnd, uint32_t minValue, uint32_t maxValue) { if (minValue == 0 && maxValue == 0xffffffff) return rnd.getUint32(); return minValue + rnd.getUint32() % (maxValue - minValue + 1); } template <> inline int16_t randomScalar(de::Random &rnd, int16_t minValue, int16_t maxValue) { return (int16_t)rnd.getInt(minValue, maxValue); } template <> inline uint16_t randomScalar(de::Random &rnd, uint16_t minValue, uint16_t maxValue) { return (uint16_t)(minValue + rnd.getUint16() % (maxValue - minValue + 1)); } template <> inline int8_t randomScalar(de::Random &rnd, int8_t minValue, int8_t maxValue) { return (int8_t)rnd.getInt(minValue, maxValue); } template <> inline uint8_t randomScalar(de::Random &rnd, uint8_t minValue, uint8_t maxValue) { return (uint8_t)(minValue + rnd.getUint8() % (maxValue - minValue + 1)); } } // namespace de #endif // _DERANDOM_HPP