/* Webcamoid, webcam capture application. * Copyright (C) 2016 Gonzalo Exequiel Pedone * * Webcamoid is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Webcamoid is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Webcamoid. If not, see . * * Web-Site: http://webcamoid.github.io/ */ #include #include #include #include #include "akfrac.h" class AkFracPrivate { public: qint64 m_num; qint64 m_den; template inline static T sign(T n); inline static qint64 gcd(qint64 num, qint64 den); inline static void reduce(qint64 *num, qint64 *den); }; AkFrac::AkFrac(QObject *parent): QObject(parent) { this->d = new AkFracPrivate(); this->d->m_num = 0; this->d->m_den = 0; } AkFrac::AkFrac(qint64 num, qint64 den): QObject(nullptr) { this->d = new AkFracPrivate(); this->d->m_num = 0; this->d->m_den = 0; this->setNumDen(num, den); } AkFrac::AkFrac(const QString &fracString): QObject(nullptr) { this->d = new AkFracPrivate(); this->d->m_num = 0; this->d->m_den = 0; this->setNumDen(fracString); } AkFrac::AkFrac(const AkFrac &other): QObject() { this->d = new AkFracPrivate(); this->d->m_num = other.d->m_num; this->d->m_den = other.d->m_den; } AkFrac::~AkFrac() { delete this->d; } AkFrac &AkFrac::operator =(const AkFrac &other) { if (this != &other) { this->d->m_num = other.d->m_num; this->d->m_den = other.d->m_den; } return *this; } bool AkFrac::operator ==(const AkFrac &other) const { if (this->d->m_den == 0 && other.d->m_den != 0) return false; if (this->d->m_den != 0 && other.d->m_den == 0) return false; return this->d->m_num * other.d->m_den == this->d->m_den * other.d->m_num; } bool AkFrac::operator !=(const AkFrac &other) const { return !(*this == other); } AkFrac AkFrac::operator *(const AkFrac &other) const { return AkFrac(this->d->m_num * other.d->m_num, this->d->m_den * other.d->m_den); } AkFrac::operator bool() const { return this->d->m_den != 0; } AkFrac::operator QString() const { return QString("%1/%2").arg(this->d->m_num).arg(this->d->m_den); } qint64 AkFrac::num() const { return this->d->m_num; } qint64 AkFrac::den() const { return this->d->m_den; } qreal AkFrac::value() const { if (!this->d->m_den) return qQNaN(); return qreal(this->d->m_num) / this->d->m_den; } qint64 AkFrac::fastValue() const { if (!this->d->m_den) return 0; return this->d->m_num / this->d->m_den; } bool AkFrac::isValid() const { return *this; } QString AkFrac::toString() const { return *this; } AkFrac AkFrac::invert() const { return AkFrac(this->d->m_den, this->d->m_num); } void AkFrac::setNumDen(qint64 num, qint64 den) { bool changed = false; if (!den) { if (this->d->m_num != 0) { this->d->m_num = 0; changed = true; emit this->numChanged(0); } if (this->d->m_den != 0) { this->d->m_den = 0; changed = true; emit this->denChanged(0); emit this->isValidChanged(false); } if (changed) { emit this->valueChanged(qQNaN()); emit this->stringChanged("0/0"); } return; } num = AkFracPrivate::sign(den) * num; den = qAbs(den); AkFracPrivate::reduce(&num, &den); if (this->d->m_num != num) { this->d->m_num = num; changed = true; emit this->numChanged(num); } if (this->d->m_den != den) { if (!this->d->m_den) emit this->isValidChanged(true); this->d->m_den = den; changed = true; emit this->denChanged(den); } if (changed) { emit this->valueChanged(this->value()); emit this->stringChanged(*this); } } void AkFrac::setNumDen(const QString &fracString) { bool ok = false; auto str = fracString.trimmed(); auto index = str.indexOf('/'); if (index < 1) { qint64 num = str.toLongLong(&ok); if (ok) this->setNumDen(num, 1); else this->setNumDen(0, 0); } else { qint64 num = str.left(index).trimmed().toLongLong(&ok); if (ok) { auto n = str.size() - index - 1; if (n > 0) { qint64 den = str.right(n).trimmed().toLongLong(&ok); if (ok && den > 0) this->setNumDen(num, den); else this->setNumDen(0, 0); } else { this->setNumDen(0, 0); } } else { this->setNumDen(0, 0); } } } void AkFrac::setNum(qint64 num) { this->setNumDen(num, this->d->m_den); } void AkFrac::setDen(qint64 den) { this->setNumDen(this->d->m_num, den); } void AkFrac::resetNum() { this->setNum(0); } void AkFrac::resetDen() { this->setDen(0); } QDebug operator <<(QDebug debug, const AkFrac &frac) { debug.nospace() << "AkFrac(" << frac.num() << "," << frac.den() << ")"; return debug.space(); } QDataStream &operator >>(QDataStream &istream, AkFrac &frac) { qint64 num; qint64 den; istream >> num; istream >> den; frac.setNum(num); frac.setDen(den); return istream; } QDataStream &operator <<(QDataStream &ostream, const AkFrac &frac) { ostream << frac.num(); ostream << frac.den(); return ostream; } AkFrac operator *(int number, const AkFrac &frac) { return {number * frac.num(), frac.den()}; } AkFrac operator /(int number, const AkFrac &frac) { return number * frac.invert(); } AkFrac operator /(const AkFrac &fracNum, const AkFrac &fracDen) { return {fracNum.num() * fracDen.den(), fracNum.den() * fracDen.num()}; } AkFrac operator +(const AkFrac &frac1, const AkFrac &frac2) { return {frac1.num() * frac2.den() + frac2.num() * frac1.den(), frac1.den() * frac2.den()}; } AkFrac operator -(const AkFrac &frac1, const AkFrac &frac2) { return {frac1.num() * frac2.den() - frac2.num() * frac1.den(), frac1.den() * frac2.den()}; } template T AkFracPrivate::sign(T n) { return (n < 0)? -1: 1; } qint64 AkFracPrivate::gcd(qint64 num, qint64 den) { num = qAbs(num); den = qAbs(den); while (num > 0) { qint64 tmp = num; num = den % num; den = tmp; } return den; } void AkFracPrivate::reduce(qint64 *num, qint64 *den) { qint64 gcd = AkFracPrivate::gcd(*num, *den); if (gcd) { *num /= gcd; *den /= gcd; } } #include "moc_akfrac.cpp"