1 #pragma once
2 // rational_impl.hpp: definition of adaptive precision rational arithmetic type
3 //
4 // Copyright (C) 2017-2021 Stillwater Supercomputing, Inc.
5 //
6 // This file is part of the universal numbers project, which is released under an MIT Open Source license.
7 #include <cstdint>
8 #include <sstream>
9 #include <cassert>
10 #include <iostream>
11 #include <iomanip>
12 #include <vector>
13 #include <limits>
14 #include <regex>
15 #include <algorithm>
16 
17 #include <universal/native/ieee754.hpp>
18 #include <universal/string/strmanip.hpp>
19 #include <universal/number/rational/exceptions.hpp>
20 #include <universal/number/decimal/decimal.hpp>
21 
22 namespace sw::universal {
23 
24 /////////////////////////////////////////////
25 // Forward references
26 class rational;
27 rational quotient(const rational&, const rational&);
28 
29 /// <summary>
30 /// Adaptive precision rational number system type
31 /// </summary>
32 /// The rational is comprised of two adaptive rationals representing the numerator and denominator.
33 /// The digits of both are managed as a vector with the digit for 10^0 stored at index 0, 10^1 stored at index 1, etc.
34 class rational {
35 public:
rational()36 	rational() { setzero(); }
37 
38 	rational(const rational&) = default;
39 	rational(rational&&) = default;
40 
41 	rational& operator=(const rational&) = default;
42 	rational& operator=(rational&&) = default;
43 
44 	// initializers for native types
rational(char initial_value)45 	rational(char initial_value)               { *this = initial_value; }
rational(short initial_value)46 	rational(short initial_value)              { *this = initial_value; }
rational(int initial_value)47 	rational(int initial_value)                { *this = initial_value; }
rational(long initial_value)48 	rational(long initial_value)               { *this = initial_value; }
rational(long long initial_value)49 	rational(long long initial_value)          { *this = initial_value; }
rational(unsigned char initial_value)50 	rational(unsigned char initial_value)      { *this = initial_value; }
rational(unsigned short initial_value)51 	rational(unsigned short initial_value)     { *this = initial_value; }
rational(unsigned int initial_value)52 	rational(unsigned int initial_value)       { *this = initial_value; }
rational(unsigned long initial_value)53 	rational(unsigned long initial_value)      { *this = initial_value; }
rational(unsigned long long initial_value)54 	rational(unsigned long long initial_value) { *this = initial_value; }
rational(float initial_value)55 	rational(float initial_value)              { *this = initial_value; }
rational(double initial_value)56 	rational(double initial_value)             { *this = initial_value; }
57 
58 
59 	// assignment operators for native types
operator =(const std::string & digits)60 	rational& operator=(const std::string& digits) {
61 		parse(digits);
62 		return *this;
63 	}
operator =(signed char rhs)64 	rational& operator=(signed char rhs)        { return from_signed<signed char>(rhs); }
operator =(short rhs)65 	rational& operator=(short rhs)              { return from_signed<short>(rhs); }
operator =(int rhs)66 	rational& operator=(int rhs)                { return from_signed<int>(rhs); }
operator =(long rhs)67 	rational& operator=(long rhs)               { return from_signed<long>(rhs); }
operator =(long long rhs)68 	rational& operator=(long long rhs)          { return from_signed<long long>(rhs); }
operator =(unsigned char rhs)69 	rational& operator=(unsigned char rhs)      { return from_unsigned<unsigned char>(rhs); }
operator =(unsigned short rhs)70 	rational& operator=(unsigned short rhs)     { return from_unsigned<unsigned short>(rhs); }
operator =(unsigned int rhs)71 	rational& operator=(unsigned int rhs)       { return from_unsigned<unsigned int>(rhs); }
operator =(unsigned long rhs)72 	rational& operator=(unsigned long rhs)      { return from_unsigned<unsigned long>(rhs); }
operator =(unsigned long long rhs)73 	rational& operator=(unsigned long long rhs) { return from_unsigned<unsigned long long>(rhs); }
operator =(float rhs)74 	rational& operator=(float rhs)              { return from_ieee754<float>(rhs); }
operator =(double rhs)75 	rational& operator=(double rhs)             { return from_ieee754<double>(rhs); }
76 
77 #if LONG_DOUBLE_SUPPORT
rational(long double initial_value)78 	rational(long double initial_value)         { *this = initial_value; }
operator =(long double rhs)79 	rational& operator=(long double rhs)        { return from_ieee754<long double>(rhs); }
80 #endif
81 
82 	// unitary operators
operator -() const83 	rational operator-() const {
84 		rational tmp(*this);
85 		tmp.setsign(!tmp.sign());
86 		return tmp;
87 	}
operator ++(int)88 	rational operator++(int) { // postfix
89 		rational tmp(*this);
90 		++numerator;
91 		return tmp;
92 	}
operator ++()93 	rational& operator++() { // prefix
94 		++numerator;
95 		return *this;
96 	}
operator --(int)97 	rational operator--(int) { // postfix
98 		rational tmp(*this);
99 		--numerator;
100 		return tmp;
101 	}
operator --()102 	rational& operator--() { // prefix
103 		--numerator;
104 		return *this;
105 	}
106 
107 	// arithmetic operators
operator +=(const rational & rhs)108 	rational& operator+=(const rational& rhs) {
109 		decimal a = (negative ? -numerator : numerator);
110 		decimal b = denominator;
111 		decimal c = (rhs.negative ? -rhs.numerator : rhs.numerator);
112 		decimal d = rhs.denominator;
113 		if (denominator == rhs.denominator) {
114 			decimal num = a + c;
115 			negative = num.isneg();
116 			numerator = (negative ? -num : num);
117 		}
118 		else {
119 			decimal e = a * d + b * c;
120 			decimal f = b * d;
121 			negative = e.isneg();
122 			numerator = (negative ? -e : e);
123 			denominator = f;
124 		}
125 		normalize();
126 		return *this;
127 	}
operator -=(const rational & rhs)128 	rational& operator-=(const rational& rhs) {
129 		decimal a = (negative ? -numerator : numerator);
130 		decimal b = denominator;
131 		decimal c = (rhs.negative ? -rhs.numerator : rhs.numerator);
132 		decimal d = rhs.denominator;
133 		if (denominator == rhs.denominator) {
134 			decimal num = a - c;
135 			negative = num.isneg();
136 			numerator = (negative ? -num : num);
137 		}
138 		else {
139 			decimal e = a * d - b * c;
140 			decimal f = b * d;
141 			negative = e.isneg();
142 			numerator = (negative ? -e : e);
143 			denominator = f;
144 		}
145 		normalize();
146 		return *this;
147 	}
operator *=(const rational & rhs)148 	rational& operator*=(const rational& rhs) {
149 		numerator *= rhs.numerator;
150 		denominator *= rhs.denominator;
151 		negative = !((negative && rhs.negative) || (!negative && !rhs.negative));
152 		normalize();
153 		return *this;
154 	}
operator /=(const rational & rhs)155 	rational& operator/=(const rational& rhs) {
156 #if RATIONAL_THROW_ARITHMETIC_EXCEPTION
157 		if (rhs.iszero()) {
158 			throw rational_divide_by_zero();
159 		}
160 #else
161 		std::cerr << "rational_divide_by_zero\n";
162 #endif
163 		negative = !((negative && rhs.negative) || (!negative && !rhs.negative));
164 		numerator *= rhs.denominator;
165 		denominator *= rhs.numerator;
166 		normalize();
167 		return *this;
168 	}
169 
170 	// conversion operators
operator unsigned short() const171 	explicit operator unsigned short() const     { return to_unsigned<unsigned short>(); }
operator unsigned int() const172 	explicit operator unsigned int() const       { return to_unsigned<unsigned int>(); }
operator unsigned long() const173 	explicit operator unsigned long() const      { return to_unsigned<unsigned long>(); }
operator unsigned long long() const174 	explicit operator unsigned long long() const { return to_unsigned<unsigned long long>(); }
operator short() const175 	explicit operator short() const              { return to_signed<short>(); }
operator int() const176 	explicit operator int() const                { return to_signed<int>(); }
operator long() const177 	explicit operator long() const               { return to_signed<long>(); }
operator long long() const178 	explicit operator long long() const          { return to_signed<long long>(); }
operator float() const179 	explicit operator float() const              { return to_ieee754<float>(); }
operator double() const180 	explicit operator double() const             { return to_ieee754<double>(); }
operator long double() const181 	explicit operator long double() const        { return to_ieee754<long double>(); }
182 
183 	// selectors
iszero() const184 	inline bool iszero() const {
185 		return numerator.iszero();
186 	}
sign() const187 	inline bool sign() const { return negative; }
isneg() const188 	inline bool isneg() const { return negative; }   // <  0
ispos() const189 	inline bool ispos() const { return !negative; }  // >= 0
top() const190 	inline decimal top() const { return numerator; }
bottom() const191 	inline decimal bottom() const { return denominator; }
192 	// modifiers
setzero()193 	inline void setzero() {
194 		negative    = false;
195 		numerator   = 0;
196 		denominator = 1;
197 	}
setsign(bool sign)198 	inline void setsign(bool sign) { negative = sign; }
setneg()199 	inline void setneg() { negative = true; }
setpos()200 	inline void setpos() { negative = false; }
setnumerator(const decimal & num)201 	inline void setnumerator(const decimal& num) { numerator = num; }
setdenominator(const decimal & denom)202 	inline void setdenominator(const decimal& denom) { denominator = denom; }
setbits(uint64_t v)203 	inline void setbits(uint64_t v) { *this = v; } // API to be consistent with the other number systems
204 
205 	// read a rational ASCII format and make a rational type out of it
parse(const std::string & _digits)206 	bool parse(const std::string& _digits) {
207 		bool bSuccess = false;
208 		std::string digits(_digits);
209 		trim(digits);
210 		// check if the txt is an rational form:[+-]*[0123456789]+
211 		std::regex rational_regex("[+-]*[0123456789]+");
212 		if (std::regex_match(digits, rational_regex)) {
213 			// found a rational representation
214 			numerator.clear();
215 			auto it = digits.begin();
216 			if (*it == '-') {
217 				setneg();
218 				++it;
219 			}
220 			else if (*it == '+') {
221 				++it;
222 			}
223 			for (; it != digits.end(); ++it) {
224 				uint8_t v;
225 				switch (*it) {
226 				case '0':
227 					v = 0;
228 					break;
229 				case '1':
230 					v = 1;
231 					break;
232 				case '2':
233 					v = 2;
234 					break;
235 				case '3':
236 					v = 3;
237 					break;
238 				case '4':
239 					v = 4;
240 					break;
241 				case '5':
242 					v = 5;
243 					break;
244 				case '6':
245 					v = 6;
246 					break;
247 				case '7':
248 					v = 7;
249 					break;
250 				case '8':
251 					v = 8;
252 					break;
253 				case '9':
254 					v = 9;
255 					break;
256 				default:
257 					v = 0;
258 				}
259 				numerator.push_back(v);
260 			}
261 			std::reverse(numerator.begin(), numerator.end());
262 			bSuccess = true;
263 		}
264 		return bSuccess;
265 	}
266 
267 protected:
268 	// HELPER methods
269 
270 	// remove greatest common divisor out of the numerator/denominator pair
normalize()271 	inline void normalize() {
272 		decimal a, b, r;
273 		a = numerator; b = denominator;  // precondition is numerator and denominator are positive
274 #if RATIONAL_THROW_ARITHMETIC_EXCEPTION
275 		if (b.iszero()) {
276 			throw rational_divide_by_zero();
277 		}
278 #else
279 		std::cerr << "rational_divide_by_zero\n";
280 #endif
281 		while (a % b > 0) {
282 			r = a % b;
283 			a = b;
284 			b = r;
285 		}
286 		numerator /= b;
287 		denominator /= b;
288 	}
289 	// conversion functions
290 	// convert to signed int: TODO, SFINEA
291 	template<typename SignedInt>
to_signed() const292 	inline SignedInt to_signed() const { return static_cast<SignedInt>(numerator / denominator); }
293 	// convert to unsigned int: TODO, SFINEA
294 	template<typename UnsignedInt>
to_unsigned() const295 	inline UnsignedInt to_unsigned() const { return static_cast<UnsignedInt>(numerator / denominator); }
296 	// convert to ieee-754: TODO, SFINEA
297 	template<typename Ty>
to_ieee754() const298 	inline Ty to_ieee754() const { return Ty(numerator) / Ty(denominator); }
299 
300 	// from signed int: TODO, SFINEA
301 	template<typename UnsignedInt>
from_signed(UnsignedInt & rhs)302 	rational& from_signed(UnsignedInt& rhs) {
303 		if (rhs < 0) {
304 			negative = true;
305 			numerator = -rhs;
306 		}
307 		else {
308 			negative = false;
309 			numerator = rhs;
310 		}
311 		denominator = 1;
312 		return *this;
313 	}
314 	// from signed int: TODO, SFINEA
315 	template<typename UnsignedInt>
from_unsigned(UnsignedInt & rhs)316 	rational& from_unsigned(UnsignedInt& rhs) {
317 		negative  = false;
318 		numerator = rhs;
319 		denominator = 1;
320 		return *this;
321 	}
322 	// from ieee754: TODO, SFINEA
323 	template<typename Real>
from_ieee754(Real & rhs)324 	rational& from_ieee754(Real& rhs) {
325 		std::cerr << "TBD" << rhs << std::endl;
326 		return *this;
327 	}
328 
329 private:
330 	// sign-magnitude number: indicate if number is positive or negative
331 	bool negative;
332 	decimal numerator; // will be managed as a positive number
333 	decimal denominator; // will be managed as a positive number
334 
335 	friend std::ostream& operator<<(std::ostream& ostr, const rational& d);
336 	friend std::istream& operator>>(std::istream& istr, rational& d);
337 
338 	// rational - rational logic operators
339 	friend bool operator==(const rational& lhs, const rational& rhs);
340 	friend bool operator!=(const rational& lhs, const rational& rhs);
341 	friend bool operator<(const rational& lhs, const rational& rhs);
342 	friend bool operator>(const rational& lhs, const rational& rhs);
343 	friend bool operator<=(const rational& lhs, const rational& rhs);
344 	friend bool operator>=(const rational& lhs, const rational& rhs);
345 };
346 
347 ////////////////// helper functions
348 
349 
350 ////////////////// rational operators
351 
352 /// stream operators
353 
354 // generate an ASCII rational string
to_string(const rational & d)355 inline std::string to_string(const rational& d) {
356 	std::stringstream str;
357 	if (d.isneg()) str << '-';
358 	str << "TBD";
359 	return str.str();
360 }
361 
362 // generate an ASCII rational format and send to ostream
operator <<(std::ostream & ostr,const rational & d)363 inline std::ostream& operator<<(std::ostream& ostr, const rational& d) {
364 	// make certain that setw and left/right operators work properly
365 	std::stringstream str;
366 	if (d.isneg()) str << '-';
367 	str << d.numerator << '/' << d.denominator;
368 	return ostr << str.str();
369 }
370 
371 // read an ASCII rational format from an istream
operator >>(std::istream & istr,rational & p)372 inline std::istream& operator>>(std::istream& istr, rational& p) {
373 	std::string txt;
374 	istr >> txt;
375 	if (!p.parse(txt)) {
376 		std::cerr << "unable to parse -" << txt << "- into a rational value\n";
377 	}
378 	return istr;
379 }
380 
381 /// rational binary arithmetic operators
382 
383 // binary addition of rational numbers
operator +(const rational & lhs,const rational & rhs)384 inline rational operator+(const rational& lhs, const rational& rhs) {
385 	rational sum = lhs;
386 	sum += rhs;
387 	return sum;
388 }
389 // binary subtraction of rational numbers
operator -(const rational & lhs,const rational & rhs)390 inline rational operator-(const rational& lhs, const rational& rhs) {
391 	rational diff = lhs;
392 	diff -= rhs;
393 	return diff;
394 }
395 // binary mulitplication of rational numbers
operator *(const rational & lhs,const rational & rhs)396 inline rational operator*(const rational& lhs, const rational& rhs) {
397 	rational mul = lhs;
398 	mul *= rhs;
399 	return mul;
400 }
401 // binary division of rational numbers
operator /(const rational & lhs,const rational & rhs)402 inline rational operator/(const rational& lhs, const rational& rhs) {
403 	rational ratio = lhs;
404 	ratio /= rhs;
405 	return ratio;
406 }
407 
408 //////////////////////////////////////////////////////////////////////////////////
409 /// logic operators
410 
411 /// rational - rational logic operators
412 
413 // equality test
operator ==(const rational & lhs,const rational & rhs)414 bool operator==(const rational& lhs, const rational& rhs) {
415 	return lhs.numerator == rhs.numerator && lhs.denominator == rhs.denominator;
416 }
417 // inequality test
operator !=(const rational & lhs,const rational & rhs)418 bool operator!=(const rational& lhs, const rational& rhs) {
419 	return !operator==(lhs, rhs);
420 }
421 // less-than test
operator <(const rational & lhs,const rational & rhs)422 bool operator<(const rational& lhs, const rational& rhs) {
423 	// a/b < c/d  => ad / bd < cb / bd => ad < cb
424 	decimal ad = lhs.numerator * rhs.denominator;
425 	decimal bc = lhs.denominator * rhs.numerator;
426 	return ad < bc;
427 }
428 // greater-than test
operator >(const rational & lhs,const rational & rhs)429 bool operator>(const rational& lhs, const rational& rhs) {
430 	return operator<(rhs, lhs);
431 }
432 // less-or-equal test
operator <=(const rational & lhs,const rational & rhs)433 bool operator<=(const rational& lhs, const rational& rhs) {
434 	return operator<(lhs, rhs) || operator==(lhs, rhs);
435 }
436 // greater-or-equal test
operator >=(const rational & lhs,const rational & rhs)437 bool operator>=(const rational& lhs, const rational& rhs) {
438 	return !operator<(lhs, rhs);
439 }
440 
441 // rational - long logic operators
operator ==(const rational & lhs,long rhs)442 inline bool operator==(const rational& lhs, long rhs) {
443 	return lhs == rational(rhs);
444 }
operator !=(const rational & lhs,long rhs)445 inline bool operator!=(const rational& lhs, long rhs) {
446 	return !operator==(lhs, rational(rhs));
447 }
operator <(const rational & lhs,long rhs)448 inline bool operator< (const rational& lhs, long rhs) {
449 	return operator<(lhs, rational(rhs));
450 }
operator >(const rational & lhs,long rhs)451 inline bool operator> (const rational& lhs, long rhs) {
452 	return operator< (rational(rhs), lhs);
453 }
operator <=(const rational & lhs,long rhs)454 inline bool operator<=(const rational& lhs, long rhs) {
455 	return operator< (lhs, rational(rhs)) || operator==(lhs, rational(rhs));
456 }
operator >=(const rational & lhs,long rhs)457 inline bool operator>=(const rational& lhs, long rhs) {
458 	return !operator<(lhs, rational(rhs));
459 }
460 
461 // long - rational logic operators
operator ==(long lhs,const rational & rhs)462 inline bool operator==(long lhs, const rational& rhs) {
463 	return rational(lhs) == rhs;
464 }
operator !=(long lhs,const rational & rhs)465 inline bool operator!=(long lhs, const rational& rhs) {
466 	return !operator==(rational(lhs), rhs);
467 }
operator <(long lhs,const rational & rhs)468 inline bool operator< (long lhs, const rational& rhs) {
469 	return operator<(rational(lhs), rhs);
470 }
operator >(long lhs,const rational & rhs)471 inline bool operator> (long lhs, const rational& rhs) {
472 	return operator< (rational(lhs), rhs);
473 }
operator <=(long lhs,const rational & rhs)474 inline bool operator<=(long lhs, const rational& rhs) {
475 	return operator< (rational(lhs), rhs) || operator==(rational(lhs), rhs);
476 }
operator >=(long lhs,const rational & rhs)477 inline bool operator>=(long lhs, const rational& rhs) {
478 	return !operator<(rational(lhs), rhs);
479 }
480 
481 ///////////////////////////////////////////////////////////////////////
482 
483 
484 // find largest multiplier of rhs being less or equal to lhs by subtraction; assumes 0*rhs <= lhs <= 9*rhs
findLargestMultiple_(const rational & lhs,const rational & rhs)485 rational findLargestMultiple_(const rational& lhs, const rational& rhs) {
486 	rational multiplier;
487 
488 	return multiplier;
489 }
490 
491 
492 ///////////////////////
493 // decintdiv_t for rational to capture quotient and remainder during long division
494 struct rationalintdiv {
495 	rational quot; // quotient
496 	rational rem;  // remainder
497 };
498 
499 // divide integer rational a and b and return result argument
rational_divide(const rational & lhs,const rational & rhs)500 rationalintdiv rational_divide(const rational& lhs, const rational& rhs) {
501 	if (rhs.iszero()) {
502 #if RATIONAL_THROW_ARITHMETIC_EXCEPTION
503 		throw rational_divide_by_zero{};
504 #else
505 		std::cerr << "rational_divide_by_zero\n";
506 #endif // RATIONAL_THROW_ARITHMETIC_EXCEPTION
507 	}
508 
509 	// a/b / c/d => ad / bc
510 	rationalintdiv divresult;
511 	divresult.quot.setnumerator(lhs.top() * rhs.bottom());
512 	divresult.quot.setdenominator(lhs.bottom() * rhs.top());
513 	return divresult;
514 }
515 
516 // return quotient of a rational integer division
quotient(const rational & _a,const rational & _b)517 rational quotient(const rational& _a, const rational& _b) {
518 	return rational_divide(_a, _b).quot;
519 }
520 // return remainder of a rational integer division
remainder(const rational & _a,const rational & _b)521 rational remainder(const rational& _a, const rational& _b) {
522 	return rational_divide(_a, _b).rem;
523 }
524 
525 } // namespace sw::universal
526 
527