File indexing completed on 2024-04-21 03:41:49

0001 /*
0002     Ratio.cpp  -  source code of class Ratio
0003     SPDX-FileCopyrightText: 2001-2004 Sebastian Stein <seb.kde@hpfsc.de>
0004     SPDX-FileCopyrightText: 2008 Tadeu Araujo <tadeu.araujo@ltia.fc.unesp.br>
0005     SPDX-FileCopyrightText: 2008 Danilo Balzaque <danilo.balzaque@ltia.fc.unesp.br>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "Ratio.h"
0011 
0012 #ifdef DEBUG
0013 #include <QDebug>
0014 #endif
0015 
0016 #include "PrimeNumber.h"
0017 
0018 
0019 /* ----- public member functions ----- */
0020 
0021 /* constructor */
0022 Ratio::Ratio(int pnumerator, int pdenominator) : m_numerator(pnumerator), m_denominator(pdenominator)
0023 {
0024 #ifdef DEBUG
0025     qDebug() << "constructor ratio";
0026 #endif
0027 
0028     // denominator is never allowed to be 0
0029     if (!m_denominator)
0030         m_denominator = 1;
0031 
0032     // reduce the new ratio
0033     reduce();
0034 }
0035 
0036 Ratio::Ratio(int pnumerator, int pdenominator, bool reduce_fraction) : m_numerator(pnumerator), m_denominator(pdenominator)
0037 {
0038 #ifdef DEBUG
0039     qDebug() << "constructor ratio";
0040 #endif
0041 
0042     // denominator is never allowed to be 0
0043     if (!m_denominator)
0044         m_denominator = 1;
0045 
0046     // reduce the new ratio
0047     if (reduce_fraction)
0048         reduce();
0049 }
0050 
0051 /* destructor */
0052 Ratio::~Ratio()
0053 {
0054 #ifdef DEBUG
0055     qDebug() << "destructor ratio";
0056 #endif
0057 }
0058 
0059 /* displays the ratio on stdout; just for debugging */
0060 QTextStream & Ratio::display(QTextStream & str) const
0061 {
0062     int tmp_width = str.fieldWidth();
0063     str << qSetFieldWidth(5) << " ";
0064     str << qSetFieldWidth(5) << m_numerator << QLatin1Char('\n');
0065     str << qSetFieldWidth(tmp_width) << " ";
0066     str << " ----- " << QLatin1Char('\n');
0067     str << qSetFieldWidth(tmp_width) << " ";
0068     str << qSetFieldWidth(5) << m_denominator;
0069     str.flush();
0070     return str;
0071 }
0072 
0073 /* return the numerator */
0074 int Ratio::numerator() const
0075 {
0076     return m_numerator;
0077 }
0078 
0079 /* return the denominator */
0080 int Ratio::denominator() const
0081 {
0082     return m_denominator;
0083 }
0084 
0085 /* set the numerator */
0086 void Ratio::setNumerator(int pnumerator, bool reduce_it)
0087 {
0088     m_numerator = pnumerator;
0089 
0090     // check, if we have to reduce the ratio
0091     if (reduce_it == true)
0092         reduce();
0093 
0094     return;
0095 }
0096 
0097 /* set the denominator */
0098 void Ratio::setDenominator(int pdenominator, bool reduce_it)
0099 {
0100     /* denominator is not allowed to be 0 */
0101     if (!pdenominator)
0102         pdenominator = 1;
0103 
0104     m_denominator = pdenominator;
0105 
0106     // check, if we have to reduce the ratio
0107     if (reduce_it == true)
0108         reduce();
0109 
0110     return;
0111 }
0112 
0113 /* set completely new ratio */
0114 void Ratio::setRatio(int pnumerator, int pdenominator, bool reduce_it)
0115 {
0116     setNumerator(pnumerator, false);
0117     setDenominator(pdenominator, false);
0118 
0119     // check, if we have to reduce the ratio
0120     if (reduce_it == true) {
0121         reduce();
0122     }
0123 
0124     return;
0125 }
0126 
0127 /* set completely new ratio using mixed numbers */
0128 void Ratio::setRatio(int pinteger, int pnumerator, int pdenominator, bool reduce_it)
0129 {
0130     // calculate new Numerator, but ignore negative values
0131     int newNumerator = qAbs(pinteger * pdenominator) + qAbs(pnumerator);
0132 
0133     // restore negative values
0134     if ((pinteger < 0 || pnumerator < 0) && !(pinteger < 0 && pnumerator < 0))
0135         newNumerator *= -1;
0136 
0137     setRatio(newNumerator, pdenominator, reduce_it);
0138 
0139     return;
0140 }
0141 
0142 
0143 /* add a ratio to a ratio like c = a + b */
0144 Ratio Ratio::operator+ (const Ratio &addend)
0145 {
0146     // this object will be returned as the sum
0147     Ratio sum(0, 1);
0148 
0149     // calculate and set the numerator without reducing
0150     sum.setNumerator(m_numerator * addend.denominator()
0151                      + addend.numerator() * m_denominator, false);
0152 
0153     // calculate and set the denominator without reducing
0154     sum.setDenominator(m_denominator * addend.denominator(), false);
0155 
0156     // reduce the sum
0157     sum.reduce();
0158 
0159     return sum;
0160 }
0161 
0162 /* sub a ratio from a ratio like c = a - b */
0163 Ratio Ratio::operator- (Ratio subtrahend)
0164 {
0165     /* this object will be returned as the difference */
0166     Ratio diff(0, 1);
0167 
0168     /* change the sign of the subtrahend, so we can handle it as an addition */
0169     subtrahend.change_sign();
0170     diff = operator+ (subtrahend);
0171 
0172     /* we have to change the sign back, so everything will be as before */
0173     subtrahend.change_sign();
0174 
0175     /* return the difference */
0176     return diff;
0177 }
0178 
0179 /* mul a ratio with a ratio like c = a * b */
0180 Ratio Ratio::operator*(const Ratio &factor)
0181 {
0182     // this object will be returned as the product
0183     Ratio product(0, 1);
0184 
0185     // calculate and set numerator and denominator without reducing
0186     product.setNumerator(m_numerator * factor.numerator(), false);
0187     product.setDenominator(m_denominator * factor.denominator(), false);
0188 
0189     // reduce the product
0190     product.reduce();
0191 
0192     return product;
0193 }
0194 
0195 /* div a ratio with a ratio like c = a / b */
0196 Ratio Ratio::operator/ (Ratio divisor)
0197 {
0198     /* this object will be returned as the quotient */
0199     Ratio quotient(0, 1);
0200 
0201     /* exchange numerator and denominator so we can handle as multiplication */
0202     divisor.reziproc();
0203     quotient = operator* (divisor);
0204 
0205     /* go back to the original state */
0206     divisor.reziproc();
0207 
0208     return quotient;
0209 }
0210 
0211 /* we need this for initialization during a function prototyp;
0212  * ratio fraction = 0 */
0213 Ratio Ratio::operator= (int dummy)
0214 {
0215     m_numerator = dummy;
0216     m_denominator = 1;
0217 
0218     return *this;
0219 }
0220 
0221 /* check, if the ratios are equivalent; -1/2 == 1/-2 -> true */
0222 bool Ratio::operator== (const Ratio &right)
0223 {
0224     signed short orig_sign = 1, right_sign = 1;
0225 
0226     /* we do not check the presign at this point */
0227     if (qAbs(m_numerator) != qAbs(right.numerator()))
0228         return false;
0229     if (qAbs(m_denominator) != qAbs(right.denominator()))
0230         return false;
0231 
0232     /* check if the signs of the ratios are equivalent */
0233     if (m_numerator < 0)
0234         orig_sign = -1;
0235     if (m_denominator < 0)
0236         orig_sign *= -1;
0237     if (right.numerator() < 0)
0238         right_sign = -1;
0239     if (right.denominator() < 0)
0240         right_sign *= -1;
0241 
0242     if (orig_sign != right_sign)
0243         return false;
0244 
0245     return true;
0246 }
0247 
0248 bool Ratio::operator< (const Ratio &right)
0249 {
0250     signed short sign = 1;
0251     Ratio tmp_ratio = Ratio(m_numerator, m_denominator) - right;
0252 
0253     // check for this == right
0254     if (tmp_ratio == Ratio(0, 1))
0255         return false;
0256 
0257     // get the presign of the diff
0258     if (tmp_ratio.numerator() < 0)
0259         sign = -1;
0260     if (tmp_ratio.denominator() < 0)
0261         sign *= -1;
0262 
0263     // if the diff is negative, this is smaller than right
0264     if (sign > 0) {
0265         return false;
0266     } else {
0267         return true;
0268     }
0269 }
0270 
0271 bool Ratio::operator> (const Ratio &right)
0272 {
0273     signed short sign = 1;
0274     Ratio tmp_ratio = Ratio(m_numerator, m_denominator) - right;
0275 
0276     // check for this == right
0277     if (tmp_ratio == Ratio(0, 1))
0278         return false;
0279 
0280     // get the presign of the diff
0281     if (tmp_ratio.numerator() < 0)
0282         sign = -1;
0283     if (tmp_ratio.denominator() < 0)
0284         sign *= -1;
0285 
0286     // if the diff is positive, this is smaller than right
0287     if (sign < 0) {
0288         return false;
0289     } else {
0290         return true;
0291     }
0292 }
0293 
0294 /* ----- private member functions ----- */
0295 
0296 /* reduce the ratio */
0297 void Ratio::reduce()
0298 {
0299     /* we try prime numbers as divisors; I think it is the fastest way to do */
0300     PrimeNumber number;
0301     short sign_numerator = 0, sign_denominator = 0;
0302 
0303     /* make the whole ratio positive; save the signs; it is easier to reduce
0304      * the ratio, if it is positive */
0305     if (m_numerator < 0) { // save numerator sign
0306         sign_numerator = 1;
0307         m_numerator *= -1;
0308     }
0309     if (m_denominator < 0) { // save denominator sign
0310         sign_denominator = 1;
0311         m_denominator *= -1;
0312     }
0313 
0314     for (int divisor = number.get_first();
0315             divisor <= m_numerator && divisor <= m_denominator; divisor = number.get_next()) {
0316         if (divisor == 0) {
0317 #ifdef DEBUG
0318             qDebug() << "ratio::reduce() -> divisor == 0 !!!";
0319             qDebug() << "m_numerator: " << m_numerator;
0320             qDebug() << "m_denominator: " << m_denominator;
0321             // cin.get();
0322 #endif
0323             /* so that the application does not crash with a floating
0324              * point exception; the error should not appear, but in some
0325              * cases it does and I do not know why */
0326             continue;
0327         }
0328 
0329         /* is the prime number a divisor of numerator and denominator? */
0330         if ((m_numerator % divisor == 0) && (m_denominator % divisor == 0)) {
0331             /* reduce the ratio by the divisor */
0332             m_numerator /= divisor;
0333             m_denominator /= divisor;
0334 
0335             /* we have to go recursive, if the 2 is a divisor, because there
0336              * is no way to step one number before 2 -> there is no prime
0337              * number smaller than 2 */
0338             if (divisor == 2)
0339                 reduce();
0340             else
0341                 number.move_back(); // the prime number could be a divisor again
0342         } // if ((zaehler % divisor == 0) && (nenner % divisor == 0))
0343     } // for (unsigned int divisor = number.get_first(); ...
0344 
0345     /* restore the correct signs */
0346     if (sign_numerator)
0347         m_numerator *= -1;
0348     if (sign_denominator)
0349         m_denominator *= -1;
0350     if (m_numerator == 0)
0351         m_denominator = 1;
0352 
0353     return;
0354 }
0355 
0356 /* exchange numerator and denominator */
0357 void Ratio::reziproc()
0358 {
0359     int temp = m_numerator;
0360     m_numerator = m_denominator;
0361     m_denominator = temp;
0362 
0363     return;
0364 }
0365 
0366 /* ------ private member functions ------ */
0367 
0368 /* change the sign of the ratio; ratio = ratio * -1 */
0369 void Ratio::change_sign()
0370 {
0371     /* this would be enough to change the sign of the ratio */
0372     m_numerator *= -1;
0373 
0374     /* if numerator and denominator both are negative, make them positive;
0375      * if denominator is negative and numerator positive, exchange the sign */
0376     if ((m_numerator < 0 && m_denominator < 0) || (m_numerator > 0 && m_denominator < 0)) {
0377         m_numerator *= -1;
0378         m_denominator *= -1;
0379     }
0380 
0381     return;
0382 }
0383 
0384 
0385 /* ------ some prototyps of non class functions ------ */
0386 
0387 // it is possible to stram ratio_object
0388 QTextStream & operator<< (QTextStream & str, const Ratio & pratio)
0389 {
0390     return pratio.display(str);
0391 }