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