1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2010-11-03
7 * Description : Generating random numbers
8 *
9 * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10 *
11 * This program is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation;
14 * either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 #include "randomnumbergenerator.h"
25
26 // Boost includes
27
28 // Pragma directives to reduce warnings from Boost header files.
29 #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU)
30 # pragma GCC diagnostic push
31 # pragma GCC diagnostic ignored "-Wundef"
32 #endif
33
34 #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG)
35 # pragma clang diagnostic push
36 # pragma clang diagnostic ignored "-Wundef"
37 # pragma clang diagnostic ignored "-Wunnamed-type-template-args"
38 #endif
39
40 #include <boost/random/bernoulli_distribution.hpp>
41 #include <boost/random/mersenne_twister.hpp>
42 #include <boost/random/uniform_smallint.hpp>
43 #include <boost/random/uniform_real.hpp>
44 #include <boost/random/variate_generator.hpp>
45
46 // Restore warnings
47 #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU)
48 # pragma GCC diagnostic pop
49 #endif
50
51 #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG)
52 # pragma clang diagnostic pop
53 #endif
54
55 // Qt includes
56
57 #include <QDateTime>
58 #include <QFile>
59 #include <QUuid>
60
61 // Local includes
62
63 #include "digikam_export.h"
64
65 namespace Digikam
66 {
67
NonDeterministicRandomData(int s)68 NonDeterministicRandomData::NonDeterministicRandomData(int s)
69 {
70 #ifndef Q_OS_WIN
71 {
72 // Try urandom for UNIX platforms.
73 QFile urandom(QLatin1String("/dev/urandom"));
74
75 if (urandom.exists() && urandom.open(QIODevice::ReadOnly))
76 {
77 resize(s);
78
79 if (urandom.read(data(), s) == s)
80 {
81 urandom.close();
82 return;
83 }
84
85 urandom.close();
86 }
87 }
88 #endif
89
90 /*
91 * Fallback, mostly for Windows, where UUID generation
92 * is supposed to be very good.
93 */
94 if (isEmpty())
95 {
96 reserve(s);
97
98 while (size() < s)
99 {
100 append(QByteArray::fromHex(QUuid::createUuid()
101 .toString()
102 .remove(QLatin1Char('{'))
103 .remove(QLatin1Char('}'))
104 .remove(QLatin1Char('-'))
105 .toLatin1())
106 );
107 }
108
109 resize(s);
110 }
111
112 #if 0
113 /**
114 * Implementation with boost::random_device.
115 * Works on Windows only starting with boost 1.43,
116 * before, only urandom is supported, but for that,
117 * we have a easy code snippet already.
118 */
119 const int stepSize = sizeof(boost::random_device::result_type);
120 int steps = s / stepSize;
121
122 if (s % stepSize)
123 {
124 ++steps;
125 }
126
127 resize(steps * stepSize);
128
129 boost::random_device device;
130 boost::random_device::result_type* ptr = reinterpret_cast<boost::random_device::result_type*>(data());
131
132 for (int i = 0 ; i < stepSize ; ++i)
133 {
134 *ptr++ = device();
135 }
136
137 resize(s);
138 #endif
139 }
140
141 // ---------------------------------------------------------------------------------
142
143 class Q_DECL_HIDDEN RandomNumberGenerator::Private
144 {
145 public:
146
147 enum
148 {
149 // guaranteed constant initial seed, do not change
150 InitialSeed = 5489
151 };
152
153 public:
154
Private()155 explicit Private()
156 : seed(InitialSeed),
157 engine(InitialSeed)
158 {
159 }
160
161 quint32 seed;
162 boost::mt19937 engine;
163 };
164
RandomNumberGenerator()165 RandomNumberGenerator::RandomNumberGenerator()
166 : d(new Private)
167 {
168 }
169
~RandomNumberGenerator()170 RandomNumberGenerator::~RandomNumberGenerator()
171 {
172 delete d;
173 }
174
nonDeterministicSeed()175 quint32 RandomNumberGenerator::nonDeterministicSeed()
176 {
177 NonDeterministicRandomData seed(sizeof(quint32));
178
179 return *reinterpret_cast<quint32*>(seed.data());
180 }
181
timeSeed()182 quint32 RandomNumberGenerator::timeSeed()
183 {
184 uint seed;
185 seed = quintptr(&seed) + QDateTime::currentDateTime().toSecsSinceEpoch();
186
187 return seed;
188 }
189
seedNonDeterministic()190 quint32 RandomNumberGenerator::seedNonDeterministic()
191 {
192 d->seed = nonDeterministicSeed();
193 d->engine.seed(d->seed);
194
195 return d->seed;
196 }
197
seedByTime()198 quint32 RandomNumberGenerator::seedByTime()
199 {
200 d->seed = timeSeed();
201 d->engine.seed(d->seed);
202
203 return d->seed;
204 }
205
seed(quint32 seed)206 void RandomNumberGenerator::seed(quint32 seed)
207 {
208 d->seed = seed;
209 d->engine.seed(seed);
210 }
211
reseed()212 void RandomNumberGenerator::reseed()
213 {
214 seed(d->seed);
215 }
216
currentSeed() const217 quint32 RandomNumberGenerator::currentSeed() const
218 {
219 return d->seed;
220 }
221
number(int min,int max)222 int RandomNumberGenerator::number(int min, int max)
223 {
224 boost::uniform_smallint<> distribution(min, max);
225 boost::variate_generator<boost::mt19937&, boost::uniform_smallint<> > generator(d->engine, distribution);
226
227 return generator();
228 }
229
number(double min,double max)230 double RandomNumberGenerator::number(double min, double max)
231 {
232 boost::uniform_real<> distribution(min, max);
233 boost::variate_generator<boost::mt19937&, boost::uniform_real<> > generator(d->engine, distribution);
234
235 return generator();
236 }
237
yesOrNo(double p)238 bool RandomNumberGenerator::yesOrNo(double p)
239 {
240 boost::bernoulli_distribution<> distribution(p);
241 boost::variate_generator<boost::mt19937&, boost::bernoulli_distribution<> > generator(d->engine, distribution);
242
243 return generator();
244 }
245
246 } // namespace Digikam
247