1 /*
2     Ratio.cpp  -  source code of class Ratio
3     SPDX-FileCopyrightText: 2001-2004 Sebastian Stein <seb.kde@hpfsc.de>
4     SPDX-FileCopyrightText: 2008 Tadeu Araujo <tadeu.araujo@ltia.fc.unesp.br>
5     SPDX-FileCopyrightText: 2008 Danilo Balzaque <danilo.balzaque@ltia.fc.unesp.br>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include "Ratio.h"
11 
12 #ifdef DEBUG
13 #include <QDebug>
14 #endif
15 
16 #include "PrimeNumber.h"
17 
18 
19 /* ----- public member functions ----- */
20 
21 /* constructor */
Ratio(int pnumerator,int pdenominator)22 Ratio::Ratio(int pnumerator, int pdenominator) : m_numerator(pnumerator), m_denominator(pdenominator)
23 {
24 #ifdef DEBUG
25     qDebug() << "constructor ratio";
26 #endif
27 
28     // denominator is never allowed to be 0
29     if (!m_denominator)
30         m_denominator = 1;
31 
32     // reduce the new ratio
33     reduce();
34 }
35 
Ratio(int pnumerator,int pdenominator,bool reduce_fraction)36 Ratio::Ratio(int pnumerator, int pdenominator, bool reduce_fraction) : m_numerator(pnumerator), m_denominator(pdenominator)
37 {
38 #ifdef DEBUG
39     qDebug() << "constructor ratio";
40 #endif
41 
42     // denominator is never allowed to be 0
43     if (!m_denominator)
44         m_denominator = 1;
45 
46     // reduce the new ratio
47     if (reduce_fraction)
48         reduce();
49 }
50 
51 /* copy constructor */
Ratio(const Ratio & copy_ratio)52 Ratio::Ratio(const Ratio & copy_ratio)
53 {
54 #ifdef DEBUG
55     qDebug() << "copy constructor ratio";
56 #endif
57     setNumerator(copy_ratio.numerator(), false);
58     setDenominator(copy_ratio.denominator(), false);
59 }
60 
61 /* destructor */
~Ratio()62 Ratio::~Ratio()
63 {
64 #ifdef DEBUG
65     qDebug() << "destructor ratio";
66 #endif
67 }
68 
69 /* displays the ratio on stdout; just for debugging */
display(QTextStream & str) const70 QTextStream & Ratio::display(QTextStream & str) const
71 {
72     int tmp_width = str.fieldWidth();
73     str << qSetFieldWidth(5) << " ";
74     str << qSetFieldWidth(5) << m_numerator << QLatin1Char('\n');
75     str << qSetFieldWidth(tmp_width) << " ";
76     str << " ----- " << QLatin1Char('\n');
77     str << qSetFieldWidth(tmp_width) << " ";
78     str << qSetFieldWidth(5) << m_denominator;
79     str.flush();
80     return str;
81 }
82 
83 /* return the numerator */
numerator() const84 int Ratio::numerator() const
85 {
86     return m_numerator;
87 }
88 
89 /* return the denominator */
denominator() const90 int Ratio::denominator() const
91 {
92     return m_denominator;
93 }
94 
95 /* set the numerator */
setNumerator(int pnumerator,bool reduce_it)96 void Ratio::setNumerator(int pnumerator, bool reduce_it)
97 {
98     m_numerator = pnumerator;
99 
100     // check, if we have to reduce the ratio
101     if (reduce_it == true)
102         reduce();
103 
104     return;
105 }
106 
107 /* set the denominator */
setDenominator(int pdenominator,bool reduce_it)108 void Ratio::setDenominator(int pdenominator, bool reduce_it)
109 {
110     /* denominator is not allowed to be 0 */
111     if (!pdenominator)
112         pdenominator = 1;
113 
114     m_denominator = pdenominator;
115 
116     // check, if we have to reduce the ratio
117     if (reduce_it == true)
118         reduce();
119 
120     return;
121 }
122 
123 /* set completely new ratio */
setRatio(int pnumerator,int pdenominator,bool reduce_it)124 void Ratio::setRatio(int pnumerator, int pdenominator, bool reduce_it)
125 {
126     setNumerator(pnumerator, false);
127     setDenominator(pdenominator, false);
128 
129     // check, if we have to reduce the ratio
130     if (reduce_it == true) {
131         reduce();
132     }
133 
134     return;
135 }
136 
137 /* set completely new ratio using mixed numbers */
setRatio(int pinteger,int pnumerator,int pdenominator,bool reduce_it)138 void Ratio::setRatio(int pinteger, int pnumerator, int pdenominator, bool reduce_it)
139 {
140     // calculate new Numerator, but ignore negative values
141     int newNumerator = qAbs(pinteger * pdenominator) + qAbs(pnumerator);
142 
143     // restore negative values
144     if ((pinteger < 0 || pnumerator < 0) && !(pinteger < 0 && pnumerator < 0))
145         newNumerator *= -1;
146 
147     setRatio(newNumerator, pdenominator, reduce_it);
148 
149     return;
150 }
151 
152 
153 /* add a ratio to a ratio like c = a + b */
operator +(const Ratio & addend)154 Ratio Ratio::operator+ (const Ratio &addend)
155 {
156     // this object will be returned as the sum
157     Ratio sum(0, 1);
158 
159     // calculate and set the numerator without reducing
160     sum.setNumerator(m_numerator * addend.denominator()
161                      + addend.numerator() * m_denominator, false);
162 
163     // calculate and set the denominator without reducing
164     sum.setDenominator(m_denominator * addend.denominator(), false);
165 
166     // reduce the sum
167     sum.reduce();
168 
169     return sum;
170 }
171 
172 /* sub a ratio from a ratio like c = a - b */
operator -(Ratio subtrahend)173 Ratio Ratio::operator- (Ratio subtrahend)
174 {
175     /* this object will be returned as the difference */
176     Ratio diff(0, 1);
177 
178     /* change the sign of the subtrahend, so we can handle it as an addition */
179     subtrahend.change_sign();
180     diff = operator+ (subtrahend);
181 
182     /* we have to change the sign back, so everything will be as before */
183     subtrahend.change_sign();
184 
185     /* return the difference */
186     return diff;
187 }
188 
189 /* mul a ratio with a ratio like c = a * b */
operator *(const Ratio & factor)190 Ratio Ratio::operator*(const Ratio &factor)
191 {
192     // this object will be returned as the product
193     Ratio product(0, 1);
194 
195     // calculate and set numerator and denominator without reducing
196     product.setNumerator(m_numerator * factor.numerator(), false);
197     product.setDenominator(m_denominator * factor.denominator(), false);
198 
199     // reduce the product
200     product.reduce();
201 
202     return product;
203 }
204 
205 /* div a ratio with a ratio like c = a / b */
operator /(Ratio divisor)206 Ratio Ratio::operator/ (Ratio divisor)
207 {
208     /* this object will be returned as the quotient */
209     Ratio quotient(0, 1);
210 
211     /* exchange numerator and denominator so we can handle as multiplication */
212     divisor.reziproc();
213     quotient = operator* (divisor);
214 
215     /* go back to the original state */
216     divisor.reziproc();
217 
218     return quotient;
219 }
220 
221 /* we need this for initialization during a function prototyp;
222  * ratio fraction = 0 */
operator =(int dummy)223 Ratio Ratio::operator= (int dummy)
224 {
225     m_numerator = dummy;
226     m_denominator = 1;
227 
228     return *this;
229 }
230 
231 /* check, if the ratios are equivalent; -1/2 == 1/-2 -> true */
operator ==(const Ratio & right)232 bool Ratio::operator== (const Ratio &right)
233 {
234     signed short orig_sign = 1, right_sign = 1;
235 
236     /* we do not check the presign at this point */
237     if (qAbs(m_numerator) != qAbs(right.numerator()))
238         return false;
239     if (qAbs(m_denominator) != qAbs(right.denominator()))
240         return false;
241 
242     /* check if the signs of the ratios are equivalent */
243     if (m_numerator < 0)
244         orig_sign = -1;
245     if (m_denominator < 0)
246         orig_sign *= -1;
247     if (right.numerator() < 0)
248         right_sign = -1;
249     if (right.denominator() < 0)
250         right_sign *= -1;
251 
252     if (orig_sign != right_sign)
253         return false;
254 
255     return true;
256 }
257 
operator <(const Ratio & right)258 bool Ratio::operator< (const Ratio &right)
259 {
260     signed short sign = 1;
261     Ratio tmp_ratio = Ratio(m_numerator, m_denominator) - right;
262 
263     // check for this == right
264     if (tmp_ratio == Ratio(0, 1))
265         return false;
266 
267     // get the presign of the diff
268     if (tmp_ratio.numerator() < 0)
269         sign = -1;
270     if (tmp_ratio.denominator() < 0)
271         sign *= -1;
272 
273     // if the diff is negative, this is smaller than right
274     if (sign > 0) {
275         return false;
276     } else {
277         return true;
278     }
279 }
280 
operator >(const Ratio & right)281 bool Ratio::operator> (const Ratio &right)
282 {
283     signed short sign = 1;
284     Ratio tmp_ratio = Ratio(m_numerator, m_denominator) - right;
285 
286     // check for this == right
287     if (tmp_ratio == Ratio(0, 1))
288         return false;
289 
290     // get the presign of the diff
291     if (tmp_ratio.numerator() < 0)
292         sign = -1;
293     if (tmp_ratio.denominator() < 0)
294         sign *= -1;
295 
296     // if the diff is positive, this is smaller than right
297     if (sign < 0) {
298         return false;
299     } else {
300         return true;
301     }
302 }
303 
304 /* ----- private member functions ----- */
305 
306 /* reduce the ratio */
reduce()307 void Ratio::reduce()
308 {
309     /* we try prime numbers as divisors; I think it is the fastest way to do */
310     PrimeNumber number;
311     short sign_numerator = 0, sign_denominator = 0;
312 
313     /* make the whole ratio positive; save the signs; it is easier to reduce
314      * the ratio, if it is positive */
315     if (m_numerator < 0) { // save numerator sign
316         sign_numerator = 1;
317         m_numerator *= -1;
318     }
319     if (m_denominator < 0) { // save denominator sign
320         sign_denominator = 1;
321         m_denominator *= -1;
322     }
323 
324     for (int divisor = number.get_first();
325             divisor <= m_numerator && divisor <= m_denominator; divisor = number.get_next()) {
326         if (divisor == 0) {
327 #ifdef DEBUG
328             qDebug() << "ratio::reduce() -> divisor == 0 !!!";
329             qDebug() << "m_numerator: " << m_numerator;
330             qDebug() << "m_denominator: " << m_denominator;
331             // cin.get();
332 #endif
333             /* so that the application does not crash with a floating
334              * point exception; the error should not appear, but in some
335              * cases it does and I do not know why */
336             continue;
337         }
338 
339         /* is the prime number a divisor of numerator and denominator? */
340         if ((m_numerator % divisor == 0) && (m_denominator % divisor == 0)) {
341             /* reduce the ratio by the divisor */
342             m_numerator /= divisor;
343             m_denominator /= divisor;
344 
345             /* we have to go recursive, if the 2 is a divisor, because there
346              * is no way to step one number before 2 -> there is no prime
347              * number smaller than 2 */
348             if (divisor == 2)
349                 reduce();
350             else
351                 number.move_back(); // the prime number could be a divisor again
352         } // if ((zaehler % divisor == 0) && (nenner % divisor == 0))
353     } // for (unsigned int divisor = number.get_first(); ...
354 
355     /* restore the correct signs */
356     if (sign_numerator)
357         m_numerator *= -1;
358     if (sign_denominator)
359         m_denominator *= -1;
360     if (m_numerator == 0)
361         m_denominator = 1;
362 
363     return;
364 }
365 
366 /* exchange numerator and denominator */
reziproc()367 void Ratio::reziproc()
368 {
369     int temp = m_numerator;
370     m_numerator = m_denominator;
371     m_denominator = temp;
372 
373     return;
374 }
375 
376 /* ------ private member functions ------ */
377 
378 /* change the sign of the ratio; ratio = ratio * -1 */
change_sign()379 void Ratio::change_sign()
380 {
381     /* this would be enough to change the sign of the ratio */
382     m_numerator *= -1;
383 
384     /* if numerator and denominator both are negative, make them positive;
385      * if denominator is negative and numerator positive, exchange the sign */
386     if ((m_numerator < 0 && m_denominator < 0) || (m_numerator > 0 && m_denominator < 0)) {
387         m_numerator *= -1;
388         m_denominator *= -1;
389     }
390 
391     return;
392 }
393 
394 
395 /* ------ some prototyps of non class functions ------ */
396 
397 // it is possible to stram ratio_object
operator <<(QTextStream & str,const Ratio & pratio)398 QTextStream & operator<< (QTextStream & str, const Ratio & pratio)
399 {
400     return pratio.display(str);
401 }
402