summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2017-10-23 19:16:34 -0700
committerThiago Macieira <thiago.macieira@intel.com>2017-11-11 08:11:07 +0000
commit08bf28de03f16e5b014b23e228dfc3cfc2ac7feb (patch)
tree0919b98368e7f6ed8faf7bb2fa71b1d711d09a27
parentcfad4e298f4d65fe26c3a4109d19839bdfcd30c2 (diff)
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>
-rw-r--r--src/corelib/global/qrandom.cpp90
-rw-r--r--src/corelib/global/qrandom.h24
-rw-r--r--tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp41
3 files changed, 142 insertions, 13 deletions
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
index 2241dc7095..a9596df432 100644
--- a/src/corelib/global/qrandom.cpp
+++ b/src/corelib/global/qrandom.cpp
@@ -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()
*/
/*!
@@ -659,6 +667,24 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
*/
/*!
+ \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
A typedef to the type that operator()() returns. That is, quint32.
@@ -692,6 +718,31 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
*/
/*!
+ \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)
Generates 32-bit quantities and stores them in the range between \a begin
@@ -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
diff --git a/src/corelib/global/qrandom.h b/src/corelib/global/qrandom.h
index c25a0ad643..c31c9afddb 100644
--- a/src/corelib/global/qrandom.h
+++ b/src/corelib/global/qrandom.h
@@ -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();
diff --git a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
index 9c1828d1dd..220ec9a2f8 100644
--- a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
+++ b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
@@ -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()