]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/multiprecision/rational_adaptor.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / multiprecision / rational_adaptor.hpp
CommitLineData
7c673cae 1///////////////////////////////////////////////////////////////
1e59de90 2// Copyright 2020 John Maddock. Distributed under the Boost
7c673cae 3// Software License, Version 1.0. (See accompanying file
92f5a8d4 4// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
7c673cae 5
1e59de90
TL
6#ifndef BOOST_MULTIPRECISION_RATIONAL_ADAPTOR_HPP
7#define BOOST_MULTIPRECISION_RATIONAL_ADAPTOR_HPP
7c673cae 8
7c673cae 9#include <boost/multiprecision/number.hpp>
1e59de90
TL
10#include <boost/multiprecision/detail/hash.hpp>
11#include <boost/multiprecision/detail/float128_functions.hpp>
12#include <boost/multiprecision/detail/no_exceptions_support.hpp>
7c673cae 13
92f5a8d4
TL
14namespace boost {
15namespace multiprecision {
16namespace backends {
7c673cae 17
1e59de90 18template <class Backend>
7c673cae
FG
19struct rational_adaptor
20{
1e59de90
TL
21 //
22 // Each backend need to declare 3 type lists which declare the types
23 // with which this can interoperate. These lists must at least contain
24 // the widest type in each category - so "long long" must be the final
25 // type in the signed_types list for example. Any narrower types if not
26 // present in the list will get promoted to the next wider type that is
27 // in the list whenever mixed arithmetic involving that type is encountered.
28 //
29 typedef typename Backend::signed_types signed_types;
30 typedef typename Backend::unsigned_types unsigned_types;
31 typedef typename Backend::float_types float_types;
7c673cae 32
1e59de90 33 typedef typename std::tuple_element<0, unsigned_types>::type ui_type;
7c673cae 34
1e59de90
TL
35 static Backend get_one()
36 {
37 Backend t;
38 t = static_cast<ui_type>(1);
39 return t;
40 }
41 static Backend get_zero()
7c673cae 42 {
1e59de90
TL
43 Backend t;
44 t = static_cast<ui_type>(0);
45 return t;
7c673cae 46 }
7c673cae 47
1e59de90 48 static const Backend& one()
7c673cae 49 {
1e59de90
TL
50 static const Backend result(get_one());
51 return result;
52 }
53 static const Backend& zero()
54 {
55 static const Backend result(get_zero());
56 return result;
7c673cae
FG
57 }
58
1e59de90 59 void normalize()
7c673cae 60 {
1e59de90
TL
61 using default_ops::eval_gcd;
62 using default_ops::eval_eq;
63 using default_ops::eval_divide;
64
65 Backend g, t;
66 eval_gcd(g, m_num, m_denom);
67 if (!eval_eq(g, one()))
68 {
69 eval_divide(t, m_num, g);
70 m_num.swap(t);
71 eval_divide(t, m_denom, g);
72 m_denom = std::move(t);
73 }
7c673cae 74 }
1e59de90
TL
75
76 // We must have a default constructor:
77 rational_adaptor()
78 : m_num(zero()), m_denom(one()) {}
79
80 rational_adaptor(const rational_adaptor& o) : m_num(o.m_num), m_denom(o.m_denom) {}
81 rational_adaptor(rational_adaptor&& o) = default;
82
83 // Optional constructors, we can make this type slightly more efficient
84 // by providing constructors from any type we can handle natively.
85 // These will also cause number<> to be implicitly constructible
86 // from these types unless we make such constructors explicit.
87 //
88 template <class Arithmetic>
89 rational_adaptor(const Arithmetic& val, typename std::enable_if<std::is_constructible<Backend, Arithmetic>::value && !std::is_floating_point<Arithmetic>::value>::type const* = nullptr)
90 : m_num(val), m_denom(one()) {}
91
92 //
93 // Pass-through 2-arg construction of components:
94 //
95 template <class T, class U>
96 rational_adaptor(const T& a, const U& b, typename std::enable_if<std::is_constructible<Backend, T const&>::value && std::is_constructible<Backend, U const&>::value>::type const* = nullptr)
97 : m_num(a), m_denom(b)
7c673cae 98 {
1e59de90 99 normalize();
7c673cae 100 }
1e59de90
TL
101 template <class T, class U>
102 rational_adaptor(T&& a, const U& b, typename std::enable_if<std::is_constructible<Backend, T>::value && std::is_constructible<Backend, U>::value>::type const* = nullptr)
103 : m_num(static_cast<T&&>(a)), m_denom(b)
7c673cae 104 {
1e59de90 105 normalize();
7c673cae 106 }
1e59de90
TL
107 template <class T, class U>
108 rational_adaptor(T&& a, U&& b, typename std::enable_if<std::is_constructible<Backend, T>::value && std::is_constructible<Backend, U>::value>::type const* = nullptr)
109 : m_num(static_cast<T&&>(a)), m_denom(static_cast<U&&>(b))
7c673cae 110 {
1e59de90 111 normalize();
7c673cae 112 }
1e59de90
TL
113 template <class T, class U>
114 rational_adaptor(const T& a, U&& b, typename std::enable_if<std::is_constructible<Backend, T>::value && std::is_constructible<Backend, U>::value>::type const* = nullptr)
115 : m_num(a), m_denom(static_cast<U&&>(b))
7c673cae 116 {
1e59de90
TL
117 normalize();
118 }
119 //
120 // In the absense of converting constructors, operator= takes the strain.
121 // In addition to the usual suspects, there must be one operator= for each type
122 // listed in signed_types, unsigned_types, and float_types plus a string constructor.
123 //
124 rational_adaptor& operator=(const rational_adaptor& o) = default;
125 rational_adaptor& operator=(rational_adaptor&& o) = default;
126 template <class Arithmetic>
127 inline typename std::enable_if<!std::is_floating_point<Arithmetic>::value, rational_adaptor&>::type operator=(const Arithmetic& i)
128 {
129 m_num = i;
130 m_denom = one();
7c673cae
FG
131 return *this;
132 }
92f5a8d4 133 rational_adaptor& operator=(const char* s)
7c673cae 134 {
1e59de90
TL
135 using default_ops::eval_eq;
136
92f5a8d4 137 std::string s1;
1e59de90 138 multiprecision::number<Backend> v1, v2;
92f5a8d4
TL
139 char c;
140 bool have_hex = false;
1e59de90 141 const char* p = s; // saved for later
7c673cae 142
92f5a8d4 143 while ((0 != (c = *s)) && (c == 'x' || c == 'X' || c == '-' || c == '+' || (c >= '0' && c <= '9') || (have_hex && (c >= 'a' && c <= 'f')) || (have_hex && (c >= 'A' && c <= 'F'))))
7c673cae 144 {
92f5a8d4 145 if (c == 'x' || c == 'X')
7c673cae
FG
146 have_hex = true;
147 s1.append(1, c);
148 ++s;
149 }
150 v1.assign(s1);
151 s1.erase();
92f5a8d4 152 if (c == '/')
7c673cae
FG
153 {
154 ++s;
92f5a8d4 155 while ((0 != (c = *s)) && (c == 'x' || c == 'X' || c == '-' || c == '+' || (c >= '0' && c <= '9') || (have_hex && (c >= 'a' && c <= 'f')) || (have_hex && (c >= 'A' && c <= 'F'))))
7c673cae 156 {
92f5a8d4 157 if (c == 'x' || c == 'X')
7c673cae
FG
158 have_hex = true;
159 s1.append(1, c);
160 ++s;
161 }
162 v2.assign(s1);
163 }
164 else
165 v2 = 1;
92f5a8d4 166 if (*s)
7c673cae 167 {
1e59de90
TL
168 BOOST_MP_THROW_EXCEPTION(std::runtime_error(std::string("Could not parse the string \"") + p + std::string("\" as a valid rational number.")));
169 }
170 multiprecision::number<Backend> gcd;
171 eval_gcd(gcd.backend(), v1.backend(), v2.backend());
172 if (!eval_eq(gcd.backend(), one()))
173 {
174 v1 /= gcd;
175 v2 /= gcd;
7c673cae 176 }
1e59de90
TL
177 num() = std::move(std::move(v1).backend());
178 denom() = std::move(std::move(v2).backend());
7c673cae
FG
179 return *this;
180 }
1e59de90
TL
181 template <class Float>
182 typename std::enable_if<std::is_floating_point<Float>::value, rational_adaptor&>::type operator=(Float i)
183 {
184 using default_ops::eval_eq;
185 BOOST_MP_FLOAT128_USING using std::floor; using std::frexp; using std::ldexp;
186
187 int e;
188 Float f = frexp(i, &e);
189#ifdef BOOST_HAS_FLOAT128
190 f = ldexp(f, std::is_same<float128_type, Float>::value ? 113 : std::numeric_limits<Float>::digits);
191 e -= std::is_same<float128_type, Float>::value ? 113 : std::numeric_limits<Float>::digits;
192#else
193 f = ldexp(f, std::numeric_limits<Float>::digits);
194 e -= std::numeric_limits<Float>::digits;
195#endif
196 number<Backend> num(f);
197 number<Backend> denom(1u);
198 if (e > 0)
199 {
200 num <<= e;
201 }
202 else if (e < 0)
203 {
204 denom <<= -e;
205 }
206 number<Backend> gcd;
207 eval_gcd(gcd.backend(), num.backend(), denom.backend());
208 if (!eval_eq(gcd.backend(), one()))
209 {
210 num /= gcd;
211 denom /= gcd;
212 }
213 this->num() = std::move(std::move(num).backend());
214 this->denom() = std::move(std::move(denom).backend());
215 return *this;
216 }
217
7c673cae
FG
218 void swap(rational_adaptor& o)
219 {
1e59de90
TL
220 m_num.swap(o.m_num);
221 m_denom.swap(o.m_denom);
7c673cae 222 }
92f5a8d4 223 std::string str(std::streamsize digits, std::ios_base::fmtflags f) const
7c673cae 224 {
1e59de90 225 using default_ops::eval_eq;
7c673cae
FG
226 //
227 // We format the string ourselves so we can match what GMP's mpq type does:
228 //
1e59de90
TL
229 std::string result = num().str(digits, f);
230 if (!eval_eq(denom(), one()))
7c673cae
FG
231 {
232 result.append(1, '/');
1e59de90 233 result.append(denom().str(digits, f));
7c673cae
FG
234 }
235 return result;
236 }
237 void negate()
238 {
1e59de90 239 m_num.negate();
7c673cae 240 }
92f5a8d4 241 int compare(const rational_adaptor& o) const
7c673cae 242 {
1e59de90
TL
243 std::ptrdiff_t s1 = eval_get_sign(*this);
244 std::ptrdiff_t s2 = eval_get_sign(o);
245 if (s1 != s2)
246 {
247 return s1 < s2 ? -1 : 1;
248 }
249 else if (s1 == 0)
250 return 0; // both zero.
251
252 bool neg = false;
253 if (s1 >= 0)
254 {
255 s1 = eval_msb(num()) + eval_msb(o.denom());
256 s2 = eval_msb(o.num()) + eval_msb(denom());
257 }
258 else
259 {
260 Backend t(num());
261 t.negate();
262 s1 = eval_msb(t) + eval_msb(o.denom());
263 t = o.num();
264 t.negate();
265 s2 = eval_msb(t) + eval_msb(denom());
266 neg = true;
267 }
268 s1 -= s2;
269 if (s1 < -1)
270 return neg ? 1 : -1;
271 else if (s1 > 1)
272 return neg ? -1 : 1;
273
274 Backend t1, t2;
275 eval_multiply(t1, num(), o.denom());
276 eval_multiply(t2, o.num(), denom());
277 return t1.compare(t2);
7c673cae 278 }
1e59de90
TL
279 //
280 // Comparison with arithmetic types, default just constructs a temporary:
281 //
282 template <class A>
283 typename std::enable_if<boost::multiprecision::detail::is_arithmetic<A>::value, int>::type compare(A i) const
7c673cae 284 {
1e59de90
TL
285 rational_adaptor t;
286 t = i; // Note: construct directly from i if supported.
287 return compare(t);
7c673cae 288 }
7c673cae 289
1e59de90
TL
290 Backend& num() { return m_num; }
291 const Backend& num()const { return m_num; }
292 Backend& denom() { return m_denom; }
293 const Backend& denom()const { return m_denom; }
294
295 #ifndef BOOST_MP_STANDALONE
7c673cae 296 template <class Archive>
1e59de90 297 void serialize(Archive& ar, const std::integral_constant<bool, true>&)
7c673cae
FG
298 {
299 // Saving
1e59de90
TL
300 number<Backend> n(num()), d(denom());
301 ar& boost::make_nvp("numerator", n);
302 ar& boost::make_nvp("denominator", d);
7c673cae
FG
303 }
304 template <class Archive>
1e59de90 305 void serialize(Archive& ar, const std::integral_constant<bool, false>&)
7c673cae
FG
306 {
307 // Loading
1e59de90
TL
308 number<Backend> n, d;
309 ar& boost::make_nvp("numerator", n);
310 ar& boost::make_nvp("denominator", d);
311 num() = n.backend();
312 denom() = d.backend();
7c673cae
FG
313 }
314 template <class Archive>
315 void serialize(Archive& ar, const unsigned int /*version*/)
316 {
1e59de90
TL
317 using tag = typename Archive::is_saving;
318 using saving_tag = std::integral_constant<bool, tag::value>;
319 serialize(ar, saving_tag());
7c673cae 320 }
1e59de90
TL
321 #endif // BOOST_MP_STANDALONE
322
92f5a8d4 323 private:
1e59de90 324 Backend m_num, m_denom;
7c673cae
FG
325};
326
1e59de90
TL
327//
328// Helpers:
329//
330template <class T>
331inline constexpr typename std::enable_if<std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_signed, bool>::type
332is_minus_one(const T&)
7c673cae 333{
1e59de90 334 return false;
7c673cae 335}
1e59de90
TL
336template <class T>
337inline constexpr typename std::enable_if<!std::numeric_limits<T>::is_specialized || std::numeric_limits<T>::is_signed, bool>::type
338is_minus_one(const T& val)
7c673cae 339{
1e59de90 340 return val == -1;
7c673cae 341}
1e59de90
TL
342
343//
344// Required non-members:
345//
346template <class Backend>
347inline void eval_add(rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
7c673cae 348{
1e59de90 349 eval_add_subtract_imp(a, a, b, true);
7c673cae 350}
1e59de90
TL
351template <class Backend>
352inline void eval_subtract(rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
7c673cae 353{
1e59de90
TL
354 eval_add_subtract_imp(a, a, b, false);
355}
356
357template <class Backend>
358inline void eval_multiply(rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
359{
360 eval_multiply_imp(a, a, b.num(), b.denom());
7c673cae
FG
361}
362
1e59de90
TL
363template <class Backend>
364void eval_divide(rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
365{
366 using default_ops::eval_divide;
367 rational_adaptor<Backend> t;
368 eval_divide(t, a, b);
369 a = std::move(t);
370}
371//
372// Conversions:
373//
7c673cae 374template <class R, class IntBackend>
1e59de90 375inline typename std::enable_if<number_category<R>::value == number_kind_floating_point>::type eval_convert_to(R* result, const rational_adaptor<IntBackend>& backend)
7c673cae
FG
376{
377 //
378 // The generic conversion is as good as anything we can write here:
379 //
380 ::boost::multiprecision::detail::generic_convert_rational_to_float(*result, backend);
381}
382
383template <class R, class IntBackend>
1e59de90 384inline typename std::enable_if<(number_category<R>::value != number_kind_integer) && (number_category<R>::value != number_kind_floating_point) && !std::is_enum<R>::value>::type eval_convert_to(R* result, const rational_adaptor<IntBackend>& backend)
7c673cae 385{
1e59de90
TL
386 using default_ops::eval_convert_to;
387 R d;
388 eval_convert_to(result, backend.num());
389 eval_convert_to(&d, backend.denom());
390 *result /= d;
7c673cae
FG
391}
392
1e59de90
TL
393template <class R, class Backend>
394inline typename std::enable_if<number_category<R>::value == number_kind_integer>::type eval_convert_to(R* result, const rational_adaptor<Backend>& backend)
7c673cae 395{
1e59de90
TL
396 using default_ops::eval_divide;
397 using default_ops::eval_convert_to;
398 Backend t;
399 eval_divide(t, backend.num(), backend.denom());
400 eval_convert_to(result, t);
7c673cae
FG
401}
402
1e59de90
TL
403//
404// Hashing support, not strictly required, but it is used in our tests:
405//
406template <class Backend>
407inline std::size_t hash_value(const rational_adaptor<Backend>& arg)
408{
409 std::size_t result = hash_value(arg.num());
410 std::size_t result2 = hash_value(arg.denom());
411 boost::multiprecision::detail::hash_combine(result, result2);
412 return result;
413}
414//
415// assign_components:
416//
417template <class Backend>
418void assign_components(rational_adaptor<Backend>& result, Backend const& a, Backend const& b)
419{
420 using default_ops::eval_gcd;
421 using default_ops::eval_divide;
422 using default_ops::eval_eq;
423
424 Backend g;
425 eval_gcd(g, a, b);
426 if (eval_eq(g, rational_adaptor<Backend>::one()))
427 {
428 result.num() = a;
429 result.denom() = b;
430 }
431 else
432 {
433 eval_divide(result.num(), a, g);
434 eval_divide(result.denom(), b, g);
435 }
436}
437//
438// Again for arithmetic types, overload for whatever arithmetic types are directly supported:
439//
440template <class Backend, class Arithmetic1, class Arithmetic2>
441inline void assign_components(rational_adaptor<Backend>& result, const Arithmetic1& a, const Arithmetic2& b)
442{
443 using default_ops::eval_gcd;
444 using default_ops::eval_divide;
445 using default_ops::eval_eq;
446
447 Backend g;
448 result.num() = a;
449 eval_gcd(g, result.num(), b);
450 if (eval_eq(g, rational_adaptor<Backend>::one()))
451 {
452 result.denom() = b;
453 }
454 else
455 {
456 eval_divide(result.num(), g);
457 eval_divide(result.denom(), b, g);
458 }
459}
460//
461// Optional comparison operators:
462//
463template <class Backend>
464inline bool eval_is_zero(const rational_adaptor<Backend>& arg)
7c673cae 465{
92f5a8d4 466 using default_ops::eval_is_zero;
1e59de90 467 return eval_is_zero(arg.num());
7c673cae 468}
1e59de90
TL
469
470template <class Backend>
471inline int eval_get_sign(const rational_adaptor<Backend>& arg)
7c673cae 472{
92f5a8d4 473 using default_ops::eval_get_sign;
1e59de90 474 return eval_get_sign(arg.num());
7c673cae
FG
475}
476
1e59de90
TL
477template <class Backend>
478inline bool eval_eq(const rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
7c673cae 479{
1e59de90
TL
480 using default_ops::eval_eq;
481 return eval_eq(a.num(), b.num()) && eval_eq(a.denom(), b.denom());
7c673cae
FG
482}
483
1e59de90
TL
484template <class Backend, class Arithmetic>
485inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value&& std::is_integral<Arithmetic>::value, bool>::type
486 eval_eq(const rational_adaptor<Backend>& a, Arithmetic b)
7c673cae 487{
1e59de90
TL
488 using default_ops::eval_eq;
489 return eval_eq(a.denom(), rational_adaptor<Backend>::one()) && eval_eq(a.num(), b);
490}
491
492template <class Backend, class Arithmetic>
493inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value&& std::is_integral<Arithmetic>::value, bool>::type
494 eval_eq(Arithmetic b, const rational_adaptor<Backend>& a)
495{
496 using default_ops::eval_eq;
497 return eval_eq(a.denom(), rational_adaptor<Backend>::one()) && eval_eq(a.num(), b);
498}
499
500//
501// Arithmetic operations, starting with addition:
502//
503template <class Backend, class Arithmetic>
504void eval_add_subtract_imp(rational_adaptor<Backend>& result, const Arithmetic& arg, bool isaddition)
505{
506 using default_ops::eval_multiply;
507 using default_ops::eval_divide;
508 using default_ops::eval_add;
509 using default_ops::eval_gcd;
510 Backend t;
511 eval_multiply(t, result.denom(), arg);
512 if (isaddition)
513 eval_add(result.num(), t);
514 else
515 eval_subtract(result.num(), t);
516 //
517 // There is no need to re-normalize here, we have
518 // (a + bm) / b
519 // and gcd(a + bm, b) = gcd(a, b) = 1
520 //
521 /*
522 eval_gcd(t, result.num(), result.denom());
523 if (!eval_eq(t, rational_adaptor<Backend>::one()) != 0)
524 {
525 Backend t2;
526 eval_divide(t2, result.num(), t);
527 t2.swap(result.num());
528 eval_divide(t2, result.denom(), t);
529 t2.swap(result.denom());
530 }
531 */
532}
533
534template <class Backend, class Arithmetic>
535inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && (std::is_integral<Arithmetic>::value || std::is_same<Arithmetic, Backend>::value)>::type
536 eval_add(rational_adaptor<Backend>& result, const Arithmetic& arg)
537{
538 eval_add_subtract_imp(result, arg, true);
539}
540
541template <class Backend, class Arithmetic>
542inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && (std::is_integral<Arithmetic>::value || std::is_same<Arithmetic, Backend>::value)>::type
543 eval_subtract(rational_adaptor<Backend>& result, const Arithmetic& arg)
544{
545 eval_add_subtract_imp(result, arg, false);
546}
547
548template <class Backend>
549void eval_add_subtract_imp(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b, bool isaddition)
550{
551 using default_ops::eval_eq;
552 using default_ops::eval_multiply;
553 using default_ops::eval_divide;
554 using default_ops::eval_add;
555 using default_ops::eval_subtract;
556 //
557 // Let a = an/ad
558 // b = bn/bd
559 // g = gcd(ad, bd)
560 // result = rn/rd
561 //
562 // Then:
563 // rn = an * (bd/g) + bn * (ad/g)
564 // rd = ad * (bd/g)
565 // = (ad/g) * (bd/g) * g
566 //
567 // And the whole thing can then be rescaled by
568 // gcd(rn, g)
569 //
570 Backend gcd, t1, t2, t3, t4;
571 //
572 // Begin by getting the gcd of the 2 denominators:
573 //
574 eval_gcd(gcd, a.denom(), b.denom());
575 //
576 // Do we have gcd > 1:
577 //
578 if (!eval_eq(gcd, rational_adaptor<Backend>::one()))
579 {
580 //
581 // Scale the denominators by gcd, and put the results in t1 and t2:
582 //
583 eval_divide(t1, b.denom(), gcd);
584 eval_divide(t2, a.denom(), gcd);
585 //
586 // multiply the numerators by the scale denominators and put the results in t3, t4:
587 //
588 eval_multiply(t3, a.num(), t1);
589 eval_multiply(t4, b.num(), t2);
590 //
591 // Add them up:
592 //
593 if (isaddition)
594 eval_add(t3, t4);
595 else
596 eval_subtract(t3, t4);
597 //
598 // Get the gcd of gcd and our numerator (t3):
599 //
600 eval_gcd(t4, t3, gcd);
601 if (eval_eq(t4, rational_adaptor<Backend>::one()))
602 {
603 result.num() = t3;
604 eval_multiply(result.denom(), t1, a.denom());
605 }
606 else
607 {
608 //
609 // Uncommon case where gcd is not 1, divide the numerator
610 // and the denominator terms by the new gcd. Note we perform division
611 // on the existing gcd value as this is the smallest of the 3 denominator
612 // terms we'll be multiplying together, so there's a good chance it's a
613 // single limb value already:
614 //
615 eval_divide(result.num(), t3, t4);
616 eval_divide(t3, gcd, t4);
617 eval_multiply(t4, t1, t2);
618 eval_multiply(result.denom(), t4, t3);
619 }
620 }
621 else
622 {
623 //
624 // Most common case (approx 60%) where gcd is one:
625 //
626 eval_multiply(t1, a.num(), b.denom());
627 eval_multiply(t2, a.denom(), b.num());
628 if (isaddition)
629 eval_add(result.num(), t1, t2);
630 else
631 eval_subtract(result.num(), t1, t2);
632 eval_multiply(result.denom(), a.denom(), b.denom());
633 }
634}
635
636
637template <class Backend>
638inline void eval_add(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
639{
640 eval_add_subtract_imp(result, a, b, true);
641}
642template <class Backend>
643inline void eval_subtract(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
644{
645 eval_add_subtract_imp(result, a, b, false);
646}
647
648template <class Backend, class Arithmetic>
649void eval_add_subtract_imp(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const Arithmetic& b, bool isaddition)
650{
651 using default_ops::eval_add;
652 using default_ops::eval_subtract;
653 using default_ops::eval_multiply;
654
655 if (&result == &a)
656 return eval_add_subtract_imp(result, b, isaddition);
657
658 eval_multiply(result.num(), a.denom(), b);
659 if (isaddition)
660 eval_add(result.num(), a.num());
661 else
662 BOOST_IF_CONSTEXPR(std::numeric_limits<Backend>::is_signed == false)
663 {
664 Backend t;
665 eval_subtract(t, a.num(), result.num());
666 result.num() = std::move(t);
667 }
668 else
669 {
670 eval_subtract(result.num(), a.num());
671 result.negate();
672 }
673 result.denom() = a.denom();
674 //
675 // There is no need to re-normalize here, we have
676 // (a + bm) / b
677 // and gcd(a + bm, b) = gcd(a, b) = 1
678 //
679}
680template <class Backend, class Arithmetic>
681inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && (std::is_integral<Arithmetic>::value || std::is_same<Arithmetic, Backend>::value)>::type
682 eval_add(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const Arithmetic& b)
683{
684 eval_add_subtract_imp(result, a, b, true);
685}
686template <class Backend, class Arithmetic>
687inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && (std::is_integral<Arithmetic>::value || std::is_same<Arithmetic, Backend>::value)>::type
688 eval_subtract(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const Arithmetic& b)
689{
690 eval_add_subtract_imp(result, a, b, false);
691}
692
693//
694// Multiplication:
695//
696template <class Backend>
697void eval_multiply_imp(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const Backend& b_num, const Backend& b_denom)
698{
699 using default_ops::eval_multiply;
700 using default_ops::eval_divide;
701 using default_ops::eval_gcd;
702 using default_ops::eval_get_sign;
703 using default_ops::eval_eq;
704
705 Backend gcd_left, gcd_right, t1, t2;
706 eval_gcd(gcd_left, a.num(), b_denom);
707 eval_gcd(gcd_right, b_num, a.denom());
708 //
709 // Unit gcd's are the most likely case:
710 //
711 bool b_left = eval_eq(gcd_left, rational_adaptor<Backend>::one());
712 bool b_right = eval_eq(gcd_right, rational_adaptor<Backend>::one());
713
714 if (b_left && b_right)
715 {
716 eval_multiply(result.num(), a.num(), b_num);
717 eval_multiply(result.denom(), a.denom(), b_denom);
718 }
719 else if (b_left)
720 {
721 eval_divide(t2, b_num, gcd_right);
722 eval_multiply(result.num(), a.num(), t2);
723 eval_divide(t1, a.denom(), gcd_right);
724 eval_multiply(result.denom(), t1, b_denom);
725 }
726 else if (b_right)
727 {
728 eval_divide(t1, a.num(), gcd_left);
729 eval_multiply(result.num(), t1, b_num);
730 eval_divide(t2, b_denom, gcd_left);
731 eval_multiply(result.denom(), a.denom(), t2);
732 }
733 else
734 {
735 eval_divide(t1, a.num(), gcd_left);
736 eval_divide(t2, b_num, gcd_right);
737 eval_multiply(result.num(), t1, t2);
738 eval_divide(t1, a.denom(), gcd_right);
739 eval_divide(t2, b_denom, gcd_left);
740 eval_multiply(result.denom(), t1, t2);
741 }
742 //
743 // We may have b_denom negative if this is actually division, if so just correct things now:
744 //
745 if (eval_get_sign(b_denom) < 0)
746 {
747 result.num().negate();
748 result.denom().negate();
749 }
750}
751
752template <class Backend>
753void eval_multiply(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
754{
755 using default_ops::eval_multiply;
756
757 if (&a == &b)
758 {
759 // squaring, gcd's are 1:
760 eval_multiply(result.num(), a.num(), b.num());
761 eval_multiply(result.denom(), a.denom(), b.denom());
762 return;
763 }
764 eval_multiply_imp(result, a, b.num(), b.denom());
765}
766
767template <class Backend, class Arithmetic>
768void eval_multiply_imp(Backend& result_num, Backend& result_denom, Arithmetic arg)
769{
770 if (arg == 0)
771 {
772 result_num = rational_adaptor<Backend>::zero();
773 result_denom = rational_adaptor<Backend>::one();
774 return;
775 }
776 else if (arg == 1)
777 return;
778
779 using default_ops::eval_multiply;
780 using default_ops::eval_divide;
781 using default_ops::eval_gcd;
782 using default_ops::eval_convert_to;
783
784 Backend gcd, t;
785 Arithmetic integer_gcd;
786 eval_gcd(gcd, result_denom, arg);
787 eval_convert_to(&integer_gcd, gcd);
788 arg /= integer_gcd;
789 if (boost::multiprecision::detail::unsigned_abs(arg) > 1)
790 {
791 eval_multiply(t, result_num, arg);
792 result_num = std::move(t);
793 }
794 else if (is_minus_one(arg))
795 result_num.negate();
796 if (integer_gcd > 1)
797 {
798 eval_divide(t, result_denom, integer_gcd);
799 result_denom = std::move(t);
800 }
801}
802template <class Backend>
803void eval_multiply_imp(Backend& result_num, Backend& result_denom, Backend arg)
804{
805 using default_ops::eval_multiply;
806 using default_ops::eval_divide;
807 using default_ops::eval_gcd;
808 using default_ops::eval_convert_to;
809 using default_ops::eval_is_zero;
810 using default_ops::eval_eq;
811 using default_ops::eval_get_sign;
812
813 if (eval_is_zero(arg))
814 {
815 result_num = rational_adaptor<Backend>::zero();
816 result_denom = rational_adaptor<Backend>::one();
817 return;
818 }
819 else if (eval_eq(arg, rational_adaptor<Backend>::one()))
820 return;
821
822 Backend gcd, t;
823 eval_gcd(gcd, result_denom, arg);
824 if (!eval_eq(gcd, rational_adaptor<Backend>::one()))
825 {
826 eval_divide(t, arg, gcd);
827 arg = t;
828 }
829 else
830 t = arg;
831 if (eval_get_sign(arg) < 0)
832 t.negate();
833
834 if (!eval_eq(t, rational_adaptor<Backend>::one()))
835 {
836 eval_multiply(t, result_num, arg);
837 result_num = std::move(t);
838 }
839 else if (eval_get_sign(arg) < 0)
840 result_num.negate();
841 if (!eval_eq(gcd, rational_adaptor<Backend>::one()))
842 {
843 eval_divide(t, result_denom, gcd);
844 result_denom = std::move(t);
845 }
846}
847
848template <class Backend, class Arithmetic>
849inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && (std::is_integral<Arithmetic>::value || std::is_same<Arithmetic, Backend>::value)>::type
850 eval_multiply(rational_adaptor<Backend>& result, const Arithmetic& arg)
851{
852 eval_multiply_imp(result.num(), result.denom(), arg);
853}
854
855template <class Backend, class Arithmetic>
856typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && std::is_integral<Arithmetic>::value>::type
857 eval_multiply_imp(rational_adaptor<Backend>& result, const Backend& a_num, const Backend& a_denom, Arithmetic b)
858{
859 if (b == 0)
860 {
861 result.num() = rational_adaptor<Backend>::zero();
862 result.denom() = rational_adaptor<Backend>::one();
863 return;
864 }
865 else if (b == 1)
866 {
867 result.num() = a_num;
868 result.denom() = a_denom;
869 return;
870 }
871
872 using default_ops::eval_multiply;
873 using default_ops::eval_divide;
874 using default_ops::eval_gcd;
875 using default_ops::eval_convert_to;
876
877 Backend gcd;
878 Arithmetic integer_gcd;
879 eval_gcd(gcd, a_denom, b);
880 eval_convert_to(&integer_gcd, gcd);
881 b /= integer_gcd;
882 if (boost::multiprecision::detail::unsigned_abs(b) > 1)
883 eval_multiply(result.num(), a_num, b);
884 else if (is_minus_one(b))
885 {
886 result.num() = a_num;
887 result.num().negate();
888 }
889 else
890 result.num() = a_num;
891 if (integer_gcd > 1)
892 eval_divide(result.denom(), a_denom, integer_gcd);
893 else
894 result.denom() = a_denom;
895}
896template <class Backend>
897inline void eval_multiply_imp(rational_adaptor<Backend>& result, const Backend& a_num, const Backend& a_denom, const Backend& b)
898{
899 result.num() = a_num;
900 result.denom() = a_denom;
901 eval_multiply_imp(result.num(), result.denom(), b);
902}
903
904template <class Backend, class Arithmetic>
905inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && (std::is_integral<Arithmetic>::value || std::is_same<Arithmetic, Backend>::value)>::type
906 eval_multiply(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const Arithmetic& b)
907{
908 if (&result == &a)
909 return eval_multiply(result, b);
910
911 eval_multiply_imp(result, a.num(), a.denom(), b);
912}
913
914template <class Backend, class Arithmetic>
915inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && (std::is_integral<Arithmetic>::value || std::is_same<Arithmetic, Backend>::value)>::type
916 eval_multiply(rational_adaptor<Backend>& result, const Arithmetic& b, const rational_adaptor<Backend>& a)
917{
918 return eval_multiply(result, a, b);
919}
920
921//
922// Division:
923//
924template <class Backend>
925inline void eval_divide(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, const rational_adaptor<Backend>& b)
926{
927 using default_ops::eval_multiply;
928 using default_ops::eval_get_sign;
929
930 if (eval_get_sign(b.num()) == 0)
931 {
932 BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
933 return;
934 }
935 if (&a == &b)
936 {
937 // Huh? Really?
938 result.num() = result.denom() = rational_adaptor<Backend>::one();
939 return;
940 }
941 if (&result == &b)
942 {
943 rational_adaptor<Backend> t(b);
944 return eval_divide(result, a, t);
945 }
946 eval_multiply_imp(result, a, b.denom(), b.num());
947}
948
949template <class Backend, class Arithmetic>
950inline typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && (std::is_integral<Arithmetic>::value || std::is_same<Arithmetic, Backend>::value)>::type
951 eval_divide(rational_adaptor<Backend>& result, const Arithmetic& b, const rational_adaptor<Backend>& a)
952{
953 using default_ops::eval_get_sign;
954
955 if (eval_get_sign(a.num()) == 0)
956 {
957 BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
958 return;
959 }
960 if (&a == &result)
961 {
962 eval_multiply_imp(result.denom(), result.num(), b);
963 result.num().swap(result.denom());
964 }
965 else
966 eval_multiply_imp(result, a.denom(), a.num(), b);
967
968 if (eval_get_sign(result.denom()) < 0)
969 {
970 result.num().negate();
971 result.denom().negate();
972 }
973}
974
975template <class Backend, class Arithmetic>
976typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && std::is_integral<Arithmetic>::value>::type
977eval_divide(rational_adaptor<Backend>& result, Arithmetic arg)
978{
979 if (arg == 0)
980 {
981 BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
982 return;
983 }
984 else if (arg == 1)
985 return;
986 else if (is_minus_one(arg))
987 {
988 result.negate();
989 return;
990 }
991 if (eval_get_sign(result) == 0)
992 {
993 return;
994 }
995
996
997 using default_ops::eval_multiply;
998 using default_ops::eval_gcd;
999 using default_ops::eval_convert_to;
1000 using default_ops::eval_divide;
1001
1002 Backend gcd, t;
1003 Arithmetic integer_gcd;
1004 eval_gcd(gcd, result.num(), arg);
1005 eval_convert_to(&integer_gcd, gcd);
1006 arg /= integer_gcd;
1007
1008 eval_multiply(t, result.denom(), boost::multiprecision::detail::unsigned_abs(arg));
1009 result.denom() = std::move(t);
1010 if (arg < 0)
1011 {
1012 result.num().negate();
1013 }
1014 if (integer_gcd > 1)
1015 {
1016 eval_divide(t, result.num(), integer_gcd);
1017 result.num() = std::move(t);
1018 }
1019}
1020template <class Backend>
1021void eval_divide(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, Backend arg)
1022{
1023 using default_ops::eval_multiply;
1024 using default_ops::eval_gcd;
1025 using default_ops::eval_convert_to;
1026 using default_ops::eval_divide;
1027 using default_ops::eval_is_zero;
1028 using default_ops::eval_eq;
1029 using default_ops::eval_get_sign;
1030
1031 if (eval_is_zero(arg))
1032 {
1033 BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
1034 return;
1035 }
1036 else if (eval_eq(a, rational_adaptor<Backend>::one()) || (eval_get_sign(a) == 0))
1037 {
1038 if (&result != &a)
1039 result = a;
1040 return;
1041 }
1042
1043 Backend gcd, u_arg, t;
1044 eval_gcd(gcd, a.num(), arg);
1045 bool has_unit_gcd = eval_eq(gcd, rational_adaptor<Backend>::one());
1046 if (!has_unit_gcd)
1047 {
1048 eval_divide(u_arg, arg, gcd);
1049 arg = u_arg;
1050 }
1051 else
1052 u_arg = arg;
1053 if (eval_get_sign(u_arg) < 0)
1054 u_arg.negate();
1055
1056 eval_multiply(t, a.denom(), u_arg);
1057 result.denom() = std::move(t);
1058
1059 if (!has_unit_gcd)
1060 {
1061 eval_divide(t, a.num(), gcd);
1062 result.num() = std::move(t);
1063 }
1064 else if (&result != &a)
1065 result.num() = a.num();
1066
1067 if (eval_get_sign(arg) < 0)
1068 {
1069 result.num().negate();
1070 }
1071}
1072template <class Backend>
1073void eval_divide(rational_adaptor<Backend>& result, Backend arg)
1074{
1075 eval_divide(result, result, arg);
1076}
1077
1078template <class Backend, class Arithmetic>
1079typename std::enable_if<std::is_convertible<Arithmetic, Backend>::value && std::is_integral<Arithmetic>::value>::type
1080 eval_divide(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& a, Arithmetic arg)
1081{
1082 if (&result == &a)
1083 return eval_divide(result, arg);
1084 if (arg == 0)
1085 {
1086 BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
1087 return;
1088 }
1089 else if (arg == 1)
1090 {
1091 result = a;
1092 return;
1093 }
1094 else if (is_minus_one(arg))
1095 {
1096 result = a;
1097 result.num().negate();
1098 return;
1099 }
1100
1101 if (eval_get_sign(a) == 0)
1102 {
1103 result = a;
1104 return;
1105 }
1106
1107 using default_ops::eval_multiply;
1108 using default_ops::eval_divide;
1109 using default_ops::eval_gcd;
1110 using default_ops::eval_convert_to;
1111
1112 Backend gcd;
1113 Arithmetic integer_gcd;
1114 eval_gcd(gcd, a.num(), arg);
1115 eval_convert_to(&integer_gcd, gcd);
1116 arg /= integer_gcd;
1117 eval_multiply(result.denom(), a.denom(), boost::multiprecision::detail::unsigned_abs(arg));
1118
1119 if (integer_gcd > 1)
1120 {
1121 eval_divide(result.num(), a.num(), integer_gcd);
1122 }
1123 else
1124 result.num() = a.num();
1125 if (arg < 0)
1126 {
1127 result.num().negate();
1128 }
1129}
1130
1131//
1132// Increment and decrement:
1133//
1134template <class Backend>
1135inline void eval_increment(rational_adaptor<Backend>& arg)
1136{
1137 using default_ops::eval_add;
1138 eval_add(arg.num(), arg.denom());
1139}
1140template <class Backend>
1141inline void eval_decrement(rational_adaptor<Backend>& arg)
1142{
1143 using default_ops::eval_subtract;
1144 eval_subtract(arg.num(), arg.denom());
1145}
1146
1147//
1148// abs:
1149//
1150template <class Backend>
1151inline void eval_abs(rational_adaptor<Backend>& result, const rational_adaptor<Backend>& arg)
1152{
1153 using default_ops::eval_abs;
1154 eval_abs(result.num(), arg.num());
1155 result.denom() = arg.denom();
7c673cae
FG
1156}
1157
7c673cae
FG
1158} // namespace backends
1159
1e59de90
TL
1160//
1161// Import the backend into this namespace:
1162//
1163using boost::multiprecision::backends::rational_adaptor;
1164//
1165// Define a category for this number type, one of:
1166//
1167// number_kind_integer
1168// number_kind_floating_point
1169// number_kind_rational
1170// number_kind_fixed_point
1171// number_kind_complex
1172//
1173template<class Backend>
1174struct number_category<rational_adaptor<Backend> > : public std::integral_constant<int, number_kind_rational>
92f5a8d4
TL
1175{};
1176
1177template <class IntBackend>
1e59de90 1178struct expression_template_default<backends::rational_adaptor<IntBackend> > : public expression_template_default<IntBackend>
92f5a8d4 1179{};
7c673cae 1180
92f5a8d4 1181template <class Backend, expression_template_option ExpressionTemplates>
1e59de90 1182struct component_type<number<rational_adaptor<Backend>, ExpressionTemplates> >
7c673cae 1183{
92f5a8d4 1184 typedef number<Backend, ExpressionTemplates> type;
7c673cae
FG
1185};
1186
1187template <class IntBackend, expression_template_option ET>
1188inline number<IntBackend, ET> numerator(const number<rational_adaptor<IntBackend>, ET>& val)
1189{
1e59de90 1190 return val.backend().num();
7c673cae
FG
1191}
1192template <class IntBackend, expression_template_option ET>
1193inline number<IntBackend, ET> denominator(const number<rational_adaptor<IntBackend>, ET>& val)
1194{
1e59de90 1195 return val.backend().denom();
7c673cae
FG
1196}
1197
1e59de90
TL
1198template <class Backend>
1199struct is_unsigned_number<rational_adaptor<Backend> > : public is_unsigned_number<Backend>
92f5a8d4 1200{};
7c673cae 1201
7c673cae 1202
92f5a8d4 1203}} // namespace boost::multiprecision
7c673cae 1204
92f5a8d4 1205namespace std {
7c673cae 1206
1e59de90
TL
1207 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
1208 class numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> > : public std::numeric_limits<boost::multiprecision::number<IntBackend, ExpressionTemplates> >
1209 {
1210 using base_type = std::numeric_limits<boost::multiprecision::number<IntBackend> >;
1211 using number_type = boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend> >;
7c673cae 1212
1e59de90
TL
1213 public:
1214 static constexpr bool is_integer = false;
1215 static constexpr bool is_exact = true;
1216 static constexpr number_type(min)() { return (base_type::min)(); }
1217 static constexpr number_type(max)() { return (base_type::max)(); }
1218 static constexpr number_type lowest() { return -(max)(); }
1219 static constexpr number_type epsilon() { return base_type::epsilon(); }
1220 static constexpr number_type round_error() { return epsilon() / 2; }
1221 static constexpr number_type infinity() { return base_type::infinity(); }
1222 static constexpr number_type quiet_NaN() { return base_type::quiet_NaN(); }
1223 static constexpr number_type signaling_NaN() { return base_type::signaling_NaN(); }
1224 static constexpr number_type denorm_min() { return base_type::denorm_min(); }
1225 };
7c673cae 1226
1e59de90
TL
1227 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
1228 constexpr bool numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> >::is_integer;
1229 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
1230 constexpr bool numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> >::is_exact;
7c673cae 1231
92f5a8d4 1232} // namespace std
7c673cae
FG
1233
1234#endif