-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathrational.hpp
324 lines (268 loc) · 9 KB
/
rational.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
// Boost rational.hpp header file ------------------------------------------//
// (C) Copyright Paul Moore 1999. Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or
// implied warranty, and with no claim as to its suitability for any purpose.
// See http://www.boost.org for most recent version including documentation.
// Credits:
// Thanks to the boost mailing list in general for useful comments.
// Particular contributions included:
// Andrew D Jewell, for reminding me to take care to avoid overflow
// Ed Brey, for many comments, including picking up on some dreadful typos
// Revision History
// 19 Nov 00 Throw on divide by zero in operator /= (John (EBo) David)
// 23 Jun 00 Incorporate changes from Mark Rodgers for Borland C++
// 22 Jun 00 Change _MSC_VER to BOOST_MSVC so other compilers are not
// affected (Beman Dawes)
// 6 Mar 00 Fix operator-= normalization, #include <string> (Jens Maurer)
// 14 Dec 99 Modifications based on comments from the boost list
// 09 Dec 99 Initial Version (Paul Moore)
#ifndef BOOST_RATIONAL_HPP
#define BOOST_RATIONAL_HPP
#include <iostream> // for std::istream and std::ostream
#include <stdexcept> // for std::domain_error
#include <string> // for std::string implicit constructor
#include <boost/operators.hpp> // for boost::addable etc
#include <cstdlib> // for std::abs
#include <boost/config.hpp> // for BOOST_NO_STDC_NAMESPACE, BOOST_MSVC
namespace boost {
template <typename IntType>
IntType gcd(IntType n, IntType m)
{
while (m) {
IntType r = n % m;
if (r < 0)
r += m;
n = m;
m = r;
}
return n;
}
template <typename IntType>
IntType lcm(IntType n, IntType m)
{
n /= gcd(n, m);
n *= m;
return n;
}
class bad_rational : public std::domain_error
{
public:
explicit bad_rational() : std::domain_error("bad rational: zero denominator") {}
};
// The following is an awful lot of code to implement a 1-line abs() function.
#ifdef BOOST_MSVC
// This is a gross hack. MS Visual C++ has utterly broken namespace reslution
// rules. Basically, the call to abs(int) in abs(rational) below will not find
// ::abs(). So the only way we have of fixing this is to reimplement std::abs
// in the boost:: namespace! In practice, this is a relatively minor bit of
// pollution, so we should be OK.
inline int abs(int n) { return ::abs(n); }
inline long abs(long n) { return ::labs(n); }
#endif
template <typename IntType>
class rational;
template <typename IntType>
rational<IntType> abs(const rational<IntType>& r);
template <typename IntType>
class rational :
less_than_comparable < rational<IntType> >,
equality_comparable < rational<IntType> >,
addable < rational<IntType> >,
subtractable < rational<IntType> >,
multipliable < rational<IntType> >,
dividable < rational<IntType> >,
incrementable < rational<IntType> >,
decrementable < rational<IntType> >
{
typedef IntType int_type;
public:
rational(IntType n = 0) : num(n), den(1) {}
rational(IntType n, IntType d) : num(n), den(d) { normalize(); }
// Default copy constructor and assignment are fine
// Assign in place
rational& assign(IntType n, IntType d);
// Access to representation
IntType numerator() const { return num; }
IntType denominator() const { return den; }
// Arithmetic assignment operators
rational& operator+= (const rational& r);
rational& operator-= (const rational& r);
rational& operator*= (const rational& r);
rational& operator/= (const rational& r);
// Increment and decrement
const rational& operator++();
const rational& operator--();
// Comparison operators
bool operator< (const rational& r) const;
bool operator== (const rational& r) const;
private:
// Implementation - numerator and denominator (normalized).
// Other possibilities - separate whole-part, or sign, fields?
IntType num;
IntType den;
// Representation note: Fractions are kept in normalized form at all
// times. normalized form is defined as gcd(num,den) == 1 and den > 0.
// In particular, note that the implementation of abs() below relies
// on den always being positive.
void normalize();
};
// Assign in place
template <typename IntType>
inline rational<IntType>& rational<IntType>::assign(IntType n, IntType d)
{
num = n;
den = d;
normalize();
return *this;
}
// Unary plus and minus
template <typename IntType>
inline rational<IntType> operator+ (const rational<IntType>& r)
{
return r;
}
template <typename IntType>
inline rational<IntType> operator- (const rational<IntType>& r)
{
return rational<IntType>(-r.numerator(), r.denominator());
}
// Arithmetic assignment operators
template <typename IntType>
rational<IntType>& rational<IntType>::operator+= (const rational<IntType>& r)
{
// This calculation avoids overflow
IntType new_den = lcm(den, r.den);
num = num * (new_den / den) + r.num * (new_den / r.den);
den = new_den;
normalize(); // Example where this is needed: 1/2 + 1/2
return *this;
}
template <typename IntType>
rational<IntType>& rational<IntType>::operator-= (const rational<IntType>& r)
{
// This calculation avoids overflow
IntType new_den = lcm(den, r.den);
num = num * (new_den / den) - r.num * (new_den / r.den);
den = new_den;
normalize(); // Example where this is needed: 1/2 + 1/2
return *this;
}
template <typename IntType>
rational<IntType>& rational<IntType>::operator*= (const rational<IntType>& r)
{
// Avoid overflow and preserve normalization
IntType gcd1 = gcd<IntType>(num, r.den);
IntType gcd2 = gcd<IntType>(r.num, den);
num = (num/gcd1) * (r.num/gcd2);
den = (den/gcd2) * (r.den/gcd1);
return *this;
}
template <typename IntType>
rational<IntType>& rational<IntType>::operator/= (const rational<IntType>& r)
{
// Trap division by zero
if (r.num == 0) throw bad_rational();
// Avoid overflow and preserve normalization
IntType gcd1 = gcd<IntType>(num, r.num);
IntType gcd2 = gcd<IntType>(r.den, den);
num = (num/gcd1) * (r.den/gcd2);
den = (den/gcd2) * (r.num/gcd1);
return *this;
}
// Increment and decrement
template <typename IntType>
inline const rational<IntType>& rational<IntType>::operator++()
{
// This can never denormalise the fraction
num += den;
return *this;
}
template <typename IntType>
inline const rational<IntType>& rational<IntType>::operator--()
{
// This can never denormalise the fraction
num -= den;
return *this;
}
// Comparison operators
template <typename IntType>
bool rational<IntType>::operator< (const rational<IntType>& r) const
{
// Avoid overflow
IntType gcd1 = gcd<IntType>(num, r.num);
IntType gcd2 = gcd<IntType>(r.den, den);
return (num/gcd1) * (r.den/gcd2) < (den/gcd2) * (r.num/gcd1);
}
template <typename IntType>
inline bool rational<IntType>::operator== (const rational<IntType>& r) const
{
return ((num == r.num) && (den == r.den));
}
// Normalisation
template <typename IntType>
void rational<IntType>::normalize()
{
if (den == 0)
throw bad_rational();
IntType g = gcd<IntType>(num, den);
num /= g;
den /= g;
if (den < 0) {
num = -num;
den = -den;
}
}
// Input and output
template <typename IntType>
std::istream& operator>> (std::istream& is, rational<IntType>& r)
{
IntType n = 0, d = 1;
char c = 0;
is >> n;
c = is.get();
if (c == '/')
is >> d;
else
is.putback(c);
if (is)
r.assign(n, d);
return is;
}
// Add manipulators for output format?
template <typename IntType>
std::ostream& operator<< (std::ostream& os, const rational<IntType>& r)
{
os << r.numerator() << '/' << r.denominator();
return os;
}
// Type conversion
template <typename T, typename IntType>
inline T rational_cast(const rational<IntType>& src)
{
return static_cast<T>(src.numerator())/src.denominator();
}
#ifdef __GNUC__
// Workaround for a bug in gcc 2.95.2 - using statements at function scope are
// silently ignored - to get around this, we need to include std::abs at
// namespace scope.
using std::abs;
#endif
template <typename IntType>
inline rational<IntType> abs(const rational<IntType>& r)
{
#if !defined(BOOST_NO_STDC_NAMESPACE) && !defined(BOOST_MSVC)
// We want to use abs() unadorned below, so that if IntType is a
// user-defined type, the name lookup rules will work to find an
// appropriate abs() defined for IntType.
//
// We protect this with BOOST_NO_STDC_NAMESPACE, as some compilers
// don't put abs into std:: (notably MS Visual C++) and so we can't
// "use" it from there.
using std::abs;
#endif
// Note that with normalized fractions, the denominator is always positive.
return rational<IntType>(abs(r.numerator()), r.denominator());
}
} // namespace boost
#endif // BOOST_RATIONAL_HPP