1 /* Webcamoid, webcam capture application.
2 * Copyright (C) 2016 Gonzalo Exequiel Pedone
3 *
4 * Webcamoid is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Webcamoid is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Web-Site: http://webcamoid.github.io/
18 */
19
20 #include <QDataStream>
21 #include <QDebug>
22 #include <QRegExp>
23 #include <QStringList>
24
25 #include "akfrac.h"
26
27 class AkFracPrivate
28 {
29 public:
30 qint64 m_num;
31 qint64 m_den;
32
33 template<typename T>
34 inline static T sign(T n);
35 inline static qint64 gcd(qint64 num, qint64 den);
36 inline static void reduce(qint64 *num, qint64 *den);
37 };
38
AkFrac(QObject * parent)39 AkFrac::AkFrac(QObject *parent):
40 QObject(parent)
41 {
42 this->d = new AkFracPrivate();
43 this->d->m_num = 0;
44 this->d->m_den = 0;
45 }
46
AkFrac(qint64 num,qint64 den)47 AkFrac::AkFrac(qint64 num, qint64 den):
48 QObject(nullptr)
49 {
50 this->d = new AkFracPrivate();
51 this->d->m_num = 0;
52 this->d->m_den = 0;
53
54 this->setNumDen(num, den);
55 }
56
AkFrac(const QString & fracString)57 AkFrac::AkFrac(const QString &fracString):
58 QObject(nullptr)
59 {
60 this->d = new AkFracPrivate();
61 this->d->m_num = 0;
62 this->d->m_den = 0;
63
64 this->setNumDen(fracString);
65 }
66
AkFrac(const AkFrac & other)67 AkFrac::AkFrac(const AkFrac &other):
68 QObject()
69 {
70 this->d = new AkFracPrivate();
71 this->d->m_num = other.d->m_num;
72 this->d->m_den = other.d->m_den;
73 }
74
~AkFrac()75 AkFrac::~AkFrac()
76 {
77 delete this->d;
78 }
79
operator =(const AkFrac & other)80 AkFrac &AkFrac::operator =(const AkFrac &other)
81 {
82 if (this != &other) {
83 this->d->m_num = other.d->m_num;
84 this->d->m_den = other.d->m_den;
85 }
86
87 return *this;
88 }
89
operator ==(const AkFrac & other) const90 bool AkFrac::operator ==(const AkFrac &other) const
91 {
92 if (this->d->m_den == 0 && other.d->m_den != 0)
93 return false;
94
95 if (this->d->m_den != 0 && other.d->m_den == 0)
96 return false;
97
98 return this->d->m_num * other.d->m_den == this->d->m_den * other.d->m_num;
99 }
100
operator !=(const AkFrac & other) const101 bool AkFrac::operator !=(const AkFrac &other) const
102 {
103 return !(*this == other);
104 }
105
operator *(const AkFrac & other) const106 AkFrac AkFrac::operator *(const AkFrac &other) const
107 {
108 return AkFrac(this->d->m_num * other.d->m_num,
109 this->d->m_den * other.d->m_den);
110 }
111
operator bool() const112 AkFrac::operator bool() const
113 {
114 return this->d->m_den != 0;
115 }
116
operator QString() const117 AkFrac::operator QString() const
118 {
119 return QString("%1/%2").arg(this->d->m_num).arg(this->d->m_den);
120 }
121
num() const122 qint64 AkFrac::num() const
123 {
124 return this->d->m_num;
125 }
126
den() const127 qint64 AkFrac::den() const
128 {
129 return this->d->m_den;
130 }
131
value() const132 qreal AkFrac::value() const
133 {
134 if (!this->d->m_den)
135 return qQNaN();
136
137 return qreal(this->d->m_num) / this->d->m_den;
138 }
139
fastValue() const140 qint64 AkFrac::fastValue() const
141 {
142 if (!this->d->m_den)
143 return 0;
144
145 return this->d->m_num / this->d->m_den;
146 }
147
isValid() const148 bool AkFrac::isValid() const
149 {
150 return *this;
151 }
152
toString() const153 QString AkFrac::toString() const
154 {
155 return *this;
156 }
157
invert() const158 AkFrac AkFrac::invert() const
159 {
160 return AkFrac(this->d->m_den, this->d->m_num);
161 }
162
setNumDen(qint64 num,qint64 den)163 void AkFrac::setNumDen(qint64 num, qint64 den)
164 {
165 bool changed = false;
166
167 if (!den) {
168 if (this->d->m_num != 0) {
169 this->d->m_num = 0;
170 changed = true;
171 emit this->numChanged(0);
172 }
173
174 if (this->d->m_den != 0) {
175 this->d->m_den = 0;
176 changed = true;
177 emit this->denChanged(0);
178 emit this->isValidChanged(false);
179 }
180
181 if (changed) {
182 emit this->valueChanged(qQNaN());
183 emit this->stringChanged("0/0");
184 }
185
186 return;
187 }
188
189 num = AkFracPrivate::sign(den) * num;
190 den = qAbs(den);
191 AkFracPrivate::reduce(&num, &den);
192
193 if (this->d->m_num != num) {
194 this->d->m_num = num;
195 changed = true;
196 emit this->numChanged(num);
197 }
198
199 if (this->d->m_den != den) {
200 if (!this->d->m_den)
201 emit this->isValidChanged(true);
202
203 this->d->m_den = den;
204 changed = true;
205 emit this->denChanged(den);
206 }
207
208 if (changed) {
209 emit this->valueChanged(this->value());
210 emit this->stringChanged(*this);
211 }
212 }
213
setNumDen(const QString & fracString)214 void AkFrac::setNumDen(const QString &fracString)
215 {
216 bool ok = false;
217 auto str = fracString.trimmed();
218 auto index = str.indexOf('/');
219
220 if (index < 1) {
221 qint64 num = str.toLongLong(&ok);
222
223 if (ok)
224 this->setNumDen(num, 1);
225 else
226 this->setNumDen(0, 0);
227 } else {
228 qint64 num = str.left(index).trimmed().toLongLong(&ok);
229
230 if (ok) {
231 auto n = str.size() - index - 1;
232
233 if (n > 0) {
234 qint64 den = str.right(n).trimmed().toLongLong(&ok);
235
236 if (ok && den > 0)
237 this->setNumDen(num, den);
238 else
239 this->setNumDen(0, 0);
240 } else {
241 this->setNumDen(0, 0);
242 }
243 } else {
244 this->setNumDen(0, 0);
245 }
246 }
247 }
248
setNum(qint64 num)249 void AkFrac::setNum(qint64 num)
250 {
251 this->setNumDen(num, this->d->m_den);
252 }
253
setDen(qint64 den)254 void AkFrac::setDen(qint64 den)
255 {
256 this->setNumDen(this->d->m_num, den);
257 }
258
resetNum()259 void AkFrac::resetNum()
260 {
261 this->setNum(0);
262 }
263
resetDen()264 void AkFrac::resetDen()
265 {
266 this->setDen(0);
267 }
268
operator <<(QDebug debug,const AkFrac & frac)269 QDebug operator <<(QDebug debug, const AkFrac &frac)
270 {
271 debug.nospace() << "AkFrac("
272 << frac.num()
273 << ","
274 << frac.den()
275 << ")";
276
277 return debug.space();
278 }
279
operator >>(QDataStream & istream,AkFrac & frac)280 QDataStream &operator >>(QDataStream &istream, AkFrac &frac)
281 {
282 qint64 num;
283 qint64 den;
284 istream >> num;
285 istream >> den;
286 frac.setNum(num);
287 frac.setDen(den);
288
289 return istream;
290 }
291
operator <<(QDataStream & ostream,const AkFrac & frac)292 QDataStream &operator <<(QDataStream &ostream, const AkFrac &frac)
293 {
294 ostream << frac.num();
295 ostream << frac.den();
296
297 return ostream;
298 }
299
operator *(int number,const AkFrac & frac)300 AkFrac operator *(int number, const AkFrac &frac)
301 {
302 return {number * frac.num(), frac.den()};
303 }
304
operator /(int number,const AkFrac & frac)305 AkFrac operator /(int number, const AkFrac &frac)
306 {
307 return number * frac.invert();
308 }
309
operator /(const AkFrac & fracNum,const AkFrac & fracDen)310 AkFrac operator /(const AkFrac &fracNum, const AkFrac &fracDen)
311 {
312 return {fracNum.num() * fracDen.den(),
313 fracNum.den() * fracDen.num()};
314 }
315
operator +(const AkFrac & frac1,const AkFrac & frac2)316 AkFrac operator +(const AkFrac &frac1, const AkFrac &frac2)
317 {
318 return {frac1.num() * frac2.den() + frac2.num() * frac1.den(),
319 frac1.den() * frac2.den()};
320 }
321
operator -(const AkFrac & frac1,const AkFrac & frac2)322 AkFrac operator -(const AkFrac &frac1, const AkFrac &frac2)
323 {
324 return {frac1.num() * frac2.den() - frac2.num() * frac1.den(),
325 frac1.den() * frac2.den()};
326 }
327
328 template<typename T>
sign(T n)329 T AkFracPrivate::sign(T n)
330 {
331 return (n < 0)? -1: 1;
332 }
333
gcd(qint64 num,qint64 den)334 qint64 AkFracPrivate::gcd(qint64 num, qint64 den)
335 {
336 num = qAbs(num);
337 den = qAbs(den);
338
339 while (num > 0) {
340 qint64 tmp = num;
341 num = den % num;
342 den = tmp;
343 }
344
345 return den;
346 }
347
reduce(qint64 * num,qint64 * den)348 void AkFracPrivate::reduce(qint64 *num, qint64 *den)
349 {
350 qint64 gcd = AkFracPrivate::gcd(*num, *den);
351
352 if (gcd) {
353 *num /= gcd;
354 *den /= gcd;
355 }
356 }
357
358 #include "moc_akfrac.cpp"
359