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