QRandomGenerator: add more of the std Random Engine API
This brings us to almost parity with the C++11 Random Engine API requirements (see chapter 26.5.1.4 [rand.req.eng]). We don't implement the templated Sseq requirements because it would require moving the implementation details to the public API. And we don't implement the <iostreams> code because we don't want to. Change-Id: Icaa86fc7b54d4b368c0efffd14f05ff813ebd759 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
cfad4e298f
commit
08bf28de03
@ -558,7 +558,15 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
|
||||
|
||||
QRandomGenerator is modeled after the requirements for random number
|
||||
engines in the C++ Standard Library and may be used in almost all contexts
|
||||
that the Standard Library engines can.
|
||||
that the Standard Library engines can. Exceptions to the requirements are
|
||||
the following:
|
||||
|
||||
\list
|
||||
\li QRandomGenerator does not support seeding from another seed
|
||||
sequence-like class besides std::seed_seq itself;
|
||||
\li QRandomGenerator is not comparable (but is copyable) or
|
||||
streamable to \c{std::ostream} or from \c{std::istream}.
|
||||
\endlist
|
||||
|
||||
QRandomGenerator is also compatible with the uniform distribution classes
|
||||
\c{std::uniform_int_distribution} and \c{std:uniform_real_distribution}, as
|
||||
@ -575,13 +583,13 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator::QRandomGenerator(quint32 seed)
|
||||
\fn QRandomGenerator::QRandomGenerator(quint32 seedValue)
|
||||
|
||||
Initializes this QRandomGenerator object with the value \a seed as
|
||||
the seed. Two objects constructed with the same seed value will
|
||||
Initializes this QRandomGenerator object with the value \a seedValue as
|
||||
the seed. Two objects constructed or reseeded with the same seed value will
|
||||
produce the same number sequence.
|
||||
|
||||
\sa securelySeeded()
|
||||
\sa seed(), securelySeeded()
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -592,7 +600,7 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
|
||||
array \a seedBuffer as the seed. Two objects constructed or reseeded with
|
||||
the same seed value will produce the same number sequence.
|
||||
|
||||
\sa securelySeeded()
|
||||
\sa seed(), securelySeeded()
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -609,7 +617,7 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
|
||||
QRandomGenerator generator(sseq);
|
||||
\endcode
|
||||
|
||||
\sa securelySeeded()
|
||||
\sa seed(), securelySeeded()
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -626,7 +634,7 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
|
||||
QRandomGenerator generator(sseq);
|
||||
\endcode
|
||||
|
||||
\sa securelySeeded()
|
||||
\sa seed(), securelySeeded()
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -637,7 +645,7 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
|
||||
sseq as the seed. Two objects constructed or reseeded with the same seed
|
||||
value will produce the same number sequence.
|
||||
|
||||
\sa securelySeeded()
|
||||
\sa seed(), securelySeeded()
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -658,6 +666,24 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
|
||||
that shares no relationship with the QRandomGenerator::global().
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
|
||||
\relates QRandomGenerator
|
||||
|
||||
Returns true if the two the two engines \a rng1 and \a rng2 are at the same
|
||||
state or if they are both reading from the operating system facilities,
|
||||
false otherwise.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool operator!=(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
|
||||
\relates QRandomGenerator
|
||||
|
||||
Returns true if the two the two engines \a rng1 and \a rng2 are at
|
||||
different states or if one of them is reading from the operating system
|
||||
facilities and the other is not, false otherwise.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QRandomGenerator::result_type
|
||||
|
||||
@ -691,6 +717,31 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
|
||||
\sa min(), QRandomGenerator64::max()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QRandomGenerator::seed(quint32 seed)
|
||||
|
||||
Reseeds this object using the value \a seed as the seed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QRandomGenerator::seed(std::seed_seq &seed)
|
||||
\overload
|
||||
|
||||
Reseeds this object using the seed sequence \a sseq as the seed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QRandomGenerator::discard(unsigned long long z)
|
||||
|
||||
Discards the next \a z entries from the sequence. This method is equivalent
|
||||
to calling generate() \a z times and discarding the result, as in:
|
||||
|
||||
\code
|
||||
while (z--)
|
||||
generator.generate();
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QRandomGenerator::generate(ForwardIterator begin, ForwardIterator end)
|
||||
|
||||
@ -1086,6 +1137,27 @@ QRandomGenerator::QRandomGenerator(const quint32 *begin, const quint32 *end)
|
||||
new (&storage.engine()) RandomEngine(s);
|
||||
}
|
||||
|
||||
void QRandomGenerator::discard(unsigned long long z)
|
||||
{
|
||||
if (Q_UNLIKELY(type == SystemRNG))
|
||||
return;
|
||||
|
||||
PRNGLocker lock(this);
|
||||
storage.engine().discard(z);
|
||||
}
|
||||
|
||||
bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
|
||||
{
|
||||
if (rng1.type != rng2.type)
|
||||
return false;
|
||||
if (rng1.type == SystemRNG)
|
||||
return true;
|
||||
|
||||
// Lock global() if either is it (otherwise this locking is a no-op)
|
||||
PRNGLocker locker(&rng1 == QRandomGenerator::global() ? &rng1 : &rng2);
|
||||
return rng1.storage.engine() == rng2.storage.engine();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
|
@ -52,8 +52,8 @@ class QRandomGenerator
|
||||
template <typename UInt> using IfValidUInt =
|
||||
typename std::enable_if<std::is_unsigned<UInt>::value && sizeof(UInt) >= sizeof(uint), bool>::type;
|
||||
public:
|
||||
QRandomGenerator(quint32 seed = 1)
|
||||
: QRandomGenerator(&seed, 1)
|
||||
QRandomGenerator(quint32 seedValue = 1)
|
||||
: QRandomGenerator(&seedValue, 1)
|
||||
{}
|
||||
template <qssize_t N> QRandomGenerator(const quint32 (&seedBuffer)[N])
|
||||
: QRandomGenerator(seedBuffer, seedBuffer + N)
|
||||
@ -68,6 +68,12 @@ public:
|
||||
Q_CORE_EXPORT QRandomGenerator(const QRandomGenerator &other);
|
||||
Q_CORE_EXPORT QRandomGenerator &operator=(const QRandomGenerator &other);
|
||||
|
||||
friend Q_CORE_EXPORT bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2);
|
||||
friend bool operator!=(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
|
||||
{
|
||||
return !(rng1 == rng2);
|
||||
}
|
||||
|
||||
quint32 generate()
|
||||
{
|
||||
quint32 ret;
|
||||
@ -151,6 +157,9 @@ public:
|
||||
// API like std:: random engines
|
||||
typedef quint32 result_type;
|
||||
result_type operator()() { return generate(); }
|
||||
void seed(quint32 s = 1) { *this = { s }; }
|
||||
void seed(std::seed_seq &sseq) Q_DECL_NOTHROW { *this = { sseq }; }
|
||||
Q_CORE_EXPORT void discard(unsigned long long z);
|
||||
static Q_DECL_CONSTEXPR result_type min() { return (std::numeric_limits<result_type>::min)(); }
|
||||
static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits<result_type>::max)(); }
|
||||
|
||||
@ -202,8 +211,8 @@ public:
|
||||
result_type operator()() { return generate64(); }
|
||||
|
||||
#ifndef Q_QDOC
|
||||
QRandomGenerator64(quint32 seed = 1)
|
||||
: QRandomGenerator(seed)
|
||||
QRandomGenerator64(quint32 seedValue = 1)
|
||||
: QRandomGenerator(seedValue)
|
||||
{}
|
||||
template <qssize_t N> QRandomGenerator64(const quint32 (&seedBuffer)[N])
|
||||
: QRandomGenerator(seedBuffer)
|
||||
@ -219,6 +228,13 @@ public:
|
||||
{}
|
||||
QRandomGenerator64(const QRandomGenerator &other) : QRandomGenerator(other) {}
|
||||
|
||||
void discard(unsigned long long z)
|
||||
{
|
||||
Q_ASSERT_X(z * 2 > z, "QRandomGenerator64::discard",
|
||||
"Overflow. Are you sure you want to skip over 9 quintillion samples?");
|
||||
QRandomGenerator::discard(z * 2);
|
||||
}
|
||||
|
||||
static Q_DECL_CONSTEXPR result_type min() { return (std::numeric_limits<result_type>::min)(); }
|
||||
static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits<result_type>::max)(); }
|
||||
static Q_CORE_EXPORT QRandomGenerator64 *system();
|
||||
|
@ -73,6 +73,7 @@ public slots:
|
||||
private slots:
|
||||
void basics();
|
||||
void knownSequence();
|
||||
void discard();
|
||||
void copying();
|
||||
void copyingGlobal();
|
||||
void copyingSystem();
|
||||
@ -191,32 +192,63 @@ void tst_QRandomGenerator::knownSequence()
|
||||
QRandomGenerator rng;
|
||||
for (quint32 x : defaultRngResults)
|
||||
QCOMPARE(rng(), x);
|
||||
|
||||
// should work again if we reseed it
|
||||
rng.seed();
|
||||
for (quint32 x : defaultRngResults)
|
||||
QCOMPARE(rng(), x);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::discard()
|
||||
{
|
||||
QRandomGenerator rng;
|
||||
rng.discard(1);
|
||||
QCOMPARE(rng(), defaultRngResults[1]);
|
||||
|
||||
rng.discard(9);
|
||||
QCOMPARE(rng(), defaultRngResults[11]);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::copying()
|
||||
{
|
||||
QRandomGenerator rng1;
|
||||
QRandomGenerator rng2 = rng1;
|
||||
QCOMPARE(rng1, rng2);
|
||||
|
||||
quint32 samples[20];
|
||||
rng1.fillRange(samples);
|
||||
|
||||
// not equal anymore
|
||||
QVERIFY(rng1 != rng2);
|
||||
|
||||
// should produce the same sequence, whichever it was
|
||||
for (quint32 x : samples)
|
||||
QCOMPARE(rng2(), x);
|
||||
|
||||
// now they should compare equal again
|
||||
QCOMPARE(rng1, rng2);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::copyingGlobal()
|
||||
{
|
||||
QRandomGenerator &global = *QRandomGenerator::global();
|
||||
QRandomGenerator copy = global;
|
||||
QCOMPARE(copy, global);
|
||||
QCOMPARE(global, copy);
|
||||
|
||||
quint32 samples[20];
|
||||
global.fillRange(samples);
|
||||
|
||||
// not equal anymore
|
||||
QVERIFY(copy != global);
|
||||
|
||||
// should produce the same sequence, whichever it was
|
||||
for (quint32 x : samples)
|
||||
QCOMPARE(copy(), x);
|
||||
|
||||
// equal again
|
||||
QCOMPARE(copy, global);
|
||||
QCOMPARE(global, copy);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::copyingSystem()
|
||||
@ -225,15 +257,24 @@ void tst_QRandomGenerator::copyingSystem()
|
||||
QRandomGenerator copy = system;
|
||||
QRandomGenerator copy2 = copy;
|
||||
copy2 = copy;
|
||||
QCOMPARE(system, copy);
|
||||
QCOMPARE(copy, copy2);
|
||||
|
||||
quint32 samples[20];
|
||||
copy2.fillRange(samples);
|
||||
|
||||
// they still compre equally
|
||||
QCOMPARE(system, copy);
|
||||
QCOMPARE(copy, copy2);
|
||||
|
||||
// should NOT produce the same sequence, whichever it was
|
||||
int sameCount = 0;
|
||||
for (quint32 x : samples)
|
||||
sameCount += (copy() == x);
|
||||
QVERIFY(sameCount < 20);
|
||||
|
||||
QCOMPARE(system, copy);
|
||||
QCOMPARE(copy, copy2);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::systemRng()
|
||||
|
Loading…
Reference in New Issue
Block a user