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