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:
Thiago Macieira 2017-10-23 19:16:34 -07:00
parent cfad4e298f
commit 08bf28de03
3 changed files with 142 additions and 13 deletions

View File

@ -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

View File

@ -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();

View File

@ -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()