1 // This file is part of the SpeedCrunch project
2 // Copyright (C) 2015 Pol Welter <polwelter@gmail.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program 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 this program; see the file COPYING.  If not, write to
16 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 // Boston, MA 02110-1301, USA.
18 
19 #include "rational.h"
20 #include "math/hmath.h"
21 #include "numberformatter.h"
22 
23 #include <cmath>
24 #include <QString>
25 #include <QStringList>
26 #include <limits.h>
27 
28 
normalize()29 void Rational::normalize()
30 {
31     if(m_denom==0) {
32         m_valid = false;
33         return;
34     }
35     if(m_num==0) {
36         m_denom=1;
37         return;
38     }
39     int g = gcd(abs(m_num), abs(m_denom));
40     m_num /= g;
41     m_denom /= g;
42     if(m_denom<0) {
43         m_num = -m_num;
44         m_denom = -m_denom;
45     }
46 }
47 
compare(const Rational & other) const48 int Rational::compare(const Rational &other) const
49 {
50     return m_num*other.m_denom - m_denom*other.m_num;
51 }
52 
53 
54 /*
55  *  Find a rational approximation to num using a continued fraction scheme.
56  *  Code adapted from the 'Fraction' module for the PYTHON programming language,
57  *  authored by Sjoerd Mullender and Jeffrey Yasskin.
58  */
Rational(const HNumber & num)59 Rational::Rational(const HNumber &num) :
60     m_num(1), m_denom(1), m_valid(1)
61 {
62     if(num.isZero()) {
63         m_num = 0;
64         return;
65     }
66     if(HMath::abs(num)>HNumber(INT_MAX) || HMath::abs(num)<HNumber(1)/HNumber(INT_MAX)) {
67         m_valid = false;
68         return;
69     }
70     if(num.isInteger()) {
71         m_num = num.toInt();
72         return;
73     }
74     const unsigned long long MAXD = INT_MAX/2; // maximal denominator
75     unsigned long long p0=0, q0=1, p1=1, q1=0;
76     HNumber val(HMath::abs(num));
77     while(true) {
78         long a = HMath::floor(val).toInt();
79         unsigned long long q2 = q0 + a*q1;
80         if(q2>MAXD)
81             break;
82         unsigned long long temp1=p0, temp2=p1, temp3=q1;
83         p0 = temp2;
84         q0 = temp3;
85         p1 = temp1 + a*temp2;
86         q1 = q2;
87         if(HMath::frac(val).isZero()) break;
88         val = HNumber(1)/HMath::frac(val);
89         if(val>HNumber(MAXD)) break;
90     }
91 
92     Rational bound(p1, q1);
93     if(num<0) {
94         bound.m_num *= -1;
95     }
96     *this = bound;
97 
98 }
99 
Rational(const double & num)100 Rational::Rational(const double &num):
101     m_num(1), m_denom(1), m_valid(1)
102 {
103     if (num==0) {
104        m_num = 0;
105        return;
106     }
107     if (std::abs(num)>INT_MAX || std::abs(1./num)>INT_MAX) {
108         m_valid = false;
109         return;
110     }
111     const long long MAXD = INT_MAX/2; // maximal denominator
112     long long p0=0, q0=1, p1=1, q1=0;
113 
114     double val = fabs(num);
115     while(true) {
116         unsigned long long  a = static_cast<unsigned long long>(floor(val));
117         unsigned long long q2 = q0 + a*q1;
118         if(q2>MAXD)
119             break;
120         unsigned long long temp1=p0, temp2=p1, temp3=q1;
121         p0 = temp2;
122         q0 = temp3;
123         p1 = temp1 + a*temp2;
124         q1 = q2;
125         if(val==a) break;
126         val = 1/(val-a);
127     }
128 
129     Rational bound(p1, q1);
130     if(num<0)
131         bound.m_num *= -1;
132 
133     *this = bound;
134 }
135 
Rational(const QString & str)136 Rational::Rational(const QString &str) : m_num(0), m_denom(1), m_valid(true)
137 {
138     if(str=="") {
139         m_valid=false;
140         return;
141     }
142     QStringList l = str.split("/");
143     if(l.size()==1) {
144         bool ok;
145         m_num = l.at(0).toInt(&ok);
146         if(!ok) {
147             m_valid=false;
148             return;
149         }
150     } else if(l.size()==2) {
151         bool ok;
152         m_num = l.at(0).toInt(&ok);
153         if(!ok) {
154             m_valid=false;
155             return;
156         }
157         m_denom = l.at(1).toInt(&ok);
158         if(!ok) {
159             m_valid=false;
160             return;
161         }
162     }
163 }
164 
operator *(const Rational & other) const165 Rational Rational::operator*(const Rational &other) const
166 {
167     return Rational(this->m_num * other.m_num, this->m_denom * other.m_denom);
168 }
169 
operator /(const Rational & other) const170 Rational Rational::operator/(const Rational &other) const
171 {
172     if(other.isZero()) return Rational(1,0); // Rational(1,0) will set m_valid=false
173     return Rational(this->m_num / other.m_num, this->m_denom / other.m_denom);
174 }
175 
operator +(const Rational & other) const176 Rational Rational::operator+(const Rational &other) const
177 {
178     return Rational(this->m_num*other.m_denom + this->m_denom*other.m_num, this->m_denom*other.m_denom);
179 }
180 
operator -(const Rational & other) const181 Rational Rational::operator-(const Rational &other) const
182 {
183     return Rational(this->m_num*other.m_denom - this->m_denom*other.m_num, this->m_denom*other.m_denom);
184 }
185 
operator =(const Rational & other)186 Rational &Rational::operator=(const Rational &other)
187 {
188     m_num = other.m_num;
189     m_denom = other.m_denom;
190     m_valid = other.m_valid;
191     return *this;
192 }
193 
operator +=(const Rational & other)194 Rational &Rational::operator+=(const Rational &other)
195 {
196     return operator=(*this + other);
197 }
198 
operator -=(const Rational & other)199 Rational &Rational::operator-=(const Rational &other)
200 {
201     return operator=(*this - other);
202 }
203 
operator *=(const Rational & other)204 Rational &Rational::operator*=(const Rational &other)
205 {
206     return operator=(*this * other);
207 }
208 
operator /=(const Rational & other)209 Rational &Rational::operator/=(const Rational &other)
210 {
211     return operator=(*this / other);
212 }
213 
operator <(const Rational & other) const214 bool Rational::operator<(const Rational &other) const
215 {
216     return compare(other)<0;
217 }
218 
operator ==(const Rational & other) const219 bool Rational::operator==(const Rational &other) const
220 {
221     return compare(other) == 0;
222 }
223 
operator >(const Rational & other) const224 bool Rational::operator>(const Rational &other) const
225 {
226     return compare(other)>0;
227 }
228 
isZero() const229 bool Rational::isZero() const
230 {
231     return m_num==0;
232 }
233 
isValid() const234 bool Rational::isValid() const
235 {
236     return m_valid;
237 }
238 
toString() const239 QString Rational::toString() const
240 {
241     if(m_denom==1)
242         return QString::fromLatin1("%1").arg(m_num);
243     return QString::fromLatin1("%1/%2").arg(m_num).arg(m_denom);
244 }
245 
toHNumber() const246 HNumber Rational::toHNumber() const
247 {
248     HNumber num(m_num);
249     HNumber denom(m_denom);
250     return num/denom;
251 
252 }
253 
toDouble() const254 double Rational::toDouble() const
255 {
256     return double(m_num)/m_denom;
257 }
258