blob: 4f6d15ca14a33c847296f40761d18d8b2f12fd4b [file] [log] [blame]
#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 <iterator> // std::distance()
#include <algorithm> // std::swap()
namespace de
{
//! Random self-test - compare returned values against hard-coded values.
void Random_selfTest (void);
class Random
{
public:
Random (deUint32 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) == DE_TRUE; }
float getFloat (float min, float max);
double getDouble (double min, double max);
int getInt (int min, int max);
deUint64 getUint64 (void) { deUint32 upper = getUint32(); return (deUint64)upper << 32ull | (deUint64)getUint32(); }
deUint32 getUint32 (void) { return deRandom_getUint32(&m_rnd); }
deUint16 getUint16 (void) { return (deUint16)deRandom_getUint32(&m_rnd); }
deUint8 getUint8 (void) { return (deUint8)deRandom_getUint32(&m_rnd); }
template <class InputIter, class OutputIter>
void choose (InputIter first, InputIter last, OutputIter result, int numItems);
template <typename T, class InputIter>
T choose (InputIter first, InputIter last);
// \note Weights must be floats
template <typename T, class InputIter, class WeightIter>
T chooseWeighted (InputIter first, InputIter last, WeightIter weight);
template <class Iterator>
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() % (deUint32)(max-min+1));
}
// Template implementations
template <class InputIter, class OutputIter>
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 <typename T, class InputIter>
T Random::choose (InputIter first, InputIter last)
{
T val = T();
DE_ASSERT(first != last);
choose(first, last, &val, 1);
return val;
}
template <typename T, class InputIter, class WeightIter>
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 <class Iterator>
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<typename T> 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 deInt32 randomScalar (de::Random& rnd, deInt32 minValue, deInt32 maxValue) { return rnd.getInt(minValue, maxValue); }
template<> inline deUint32 randomScalar (de::Random& rnd, deUint32 minValue, deUint32 maxValue) { return minValue + rnd.getUint32() % (maxValue - minValue + 1); }
} // de
#endif // _DERANDOM_HPP