Wildmeshing Toolkit
Rational.cpp
Go to the documentation of this file.
1 #include "Rational.hpp"
2 
3 #include <cassert>
4 #include <cmath>
5 #include <limits>
6 #include <sstream>
7 
8 namespace wmtk {
10 {
11  if (m_is_rounded) return;
12  mpq_canonicalize(value);
13 }
14 
15 int Rational::get_sign() const
16 {
17  if (m_is_rounded) return d_value == 0 ? 0 : (d_value < 0 ? -1 : 1);
18 
19  return mpq_sgn(value);
20 }
21 
22 Rational::Rational(bool rounded)
23  : Rational(0.0, rounded)
24 {}
25 
26 Rational::Rational(int v, bool rounded)
27  : Rational((double)v, rounded)
28 {}
29 
30 Rational::Rational(double d, bool rounded)
31  : m_is_rounded(rounded)
32 {
33  if (m_is_rounded) {
34  d_value = d;
35  } else {
36  mpq_init(value);
37  mpq_set_d(value, d);
38 
39  d_value = std::numeric_limits<double>::lowest();
40  // canonicalize();
41  }
42 }
43 
44 Rational::Rational(const mpq_t& v_)
45 {
46  mpq_init(value);
47  mpq_set(value, v_);
48  // canonicalize();
49  m_is_rounded = false;
50 
51  d_value = std::numeric_limits<double>::lowest();
52 }
53 
55  : d_value(other.d_value)
56  , m_is_rounded(other.m_is_rounded)
57 {
58  if (!m_is_rounded) {
59  mpq_init(value);
60  mpq_set(value, other.value);
61  }
62 }
63 Rational::Rational(const Rational& other, bool rounded)
64  : d_value(other.d_value)
65  , m_is_rounded(rounded)
66 {
67  if (!m_is_rounded) {
68  mpq_init(value);
69 
70  if (other.m_is_rounded)
71  mpq_set_d(value, other.d_value);
72  else
73  mpq_set(value, other.value);
74 
75  d_value = std::numeric_limits<double>::lowest();
76  } else {
77  d_value = other.to_double();
78  }
79 }
80 
81 
82 Rational::Rational(const std::string& data, bool rounded)
83  : m_is_rounded(rounded)
84 {
85  if (m_is_rounded) {
86  mpq_t tmp_r;
87  mpq_init(tmp_r);
88  mpq_set_str(tmp_r, data.c_str(), 10);
89 
90  d_value = mpq_get_d(tmp_r);
91  mpq_clear(tmp_r);
92  } else {
93  mpq_init(value);
94  mpq_set_str(value, data.c_str(), 10);
95  d_value = std::numeric_limits<double>::lowest();
96  }
97 }
98 
100 {
101  if (!m_is_rounded) mpq_clear(value);
102 }
103 
104 Rational operator+(const Rational& x, const Rational& y)
105 {
106  if (x.m_is_rounded && y.m_is_rounded) {
107  return Rational(x.d_value + y.d_value, true);
108  }
109 
110  Rational r_out;
111 
112  if (x.m_is_rounded && !y.m_is_rounded) {
113  mpq_t tmp;
114  mpq_init(tmp);
115  mpq_set_d(tmp, x.d_value);
116  mpq_add(r_out.value, tmp, y.value);
117  mpq_clear(tmp);
118  } else if (!x.m_is_rounded && y.m_is_rounded) {
119  mpq_t tmp;
120  mpq_init(tmp);
121  mpq_set_d(tmp, y.d_value);
122  mpq_add(r_out.value, x.value, tmp);
123  mpq_clear(tmp);
124  } else
125  mpq_add(r_out.value, x.value, y.value);
126 
127  return r_out;
128 }
129 
130 Rational operator-(const Rational& x, const Rational& y)
131 {
132  if (x.m_is_rounded && y.m_is_rounded) {
133  return Rational(x.d_value - y.d_value, true);
134  }
135 
136  Rational r_out;
137 
138  if (x.m_is_rounded && !y.m_is_rounded) {
139  mpq_t tmp;
140  mpq_init(tmp);
141  mpq_set_d(tmp, x.d_value);
142  mpq_sub(r_out.value, tmp, y.value);
143  mpq_clear(tmp);
144  } else if (!x.m_is_rounded && y.m_is_rounded) {
145  mpq_t tmp;
146  mpq_init(tmp);
147  mpq_set_d(tmp, y.d_value);
148  mpq_sub(r_out.value, x.value, tmp);
149  mpq_clear(tmp);
150  } else
151  mpq_sub(r_out.value, x.value, y.value);
152 
153  return r_out;
154 }
155 
156 
158 {
159  if (x.m_is_rounded) return Rational(-x.d_value, true);
160 
161  Rational r_out;
162  mpq_neg(r_out.value, x.value);
163  return r_out;
164 }
165 
166 Rational pow(const Rational& x, int p)
167 {
168  if (x.m_is_rounded) return Rational(std::pow(x.d_value, p), true);
169 
170  Rational r_out = x;
171  for (int i = 1; i < std::abs(p); i++) {
172  r_out = r_out * x;
173  }
174  if (p < 0) return Rational(1.0, false) / r_out;
175  return r_out;
176 }
177 
178 Rational operator*(const Rational& x, const Rational& y)
179 {
180  if (x.m_is_rounded && y.m_is_rounded) {
181  return Rational(x.d_value * y.d_value, true);
182  }
183 
184  Rational r_out;
185 
186  if (x.m_is_rounded && !y.m_is_rounded) {
187  mpq_t tmp;
188  mpq_init(tmp);
189  mpq_set_d(tmp, x.d_value);
190  mpq_mul(r_out.value, tmp, y.value);
191  mpq_clear(tmp);
192  } else if (!x.m_is_rounded && y.m_is_rounded) {
193  mpq_t tmp;
194  mpq_init(tmp);
195  mpq_set_d(tmp, y.d_value);
196  mpq_mul(r_out.value, x.value, tmp);
197  mpq_clear(tmp);
198  } else
199  mpq_mul(r_out.value, x.value, y.value);
200 
201  return r_out;
202 }
203 
204 Rational operator/(const Rational& x, const Rational& y)
205 {
206  if (x.m_is_rounded && y.m_is_rounded) {
207  return Rational(x.d_value / y.d_value, true);
208  }
209 
210  Rational r_out;
211 
212  if (x.m_is_rounded && !y.m_is_rounded) {
213  mpq_t tmp;
214  mpq_init(tmp);
215  mpq_set_d(tmp, x.d_value);
216  mpq_div(r_out.value, tmp, y.value);
217  mpq_clear(tmp);
218  } else if (!x.m_is_rounded && y.m_is_rounded) {
219  mpq_t tmp;
220  mpq_init(tmp);
221  mpq_set_d(tmp, y.d_value);
222  mpq_div(r_out.value, x.value, tmp);
223  mpq_clear(tmp);
224  } else
225  mpq_div(r_out.value, x.value, y.value);
226 
227  return r_out;
228 }
229 
231 {
232  if (this == &x) return *this;
233 
234  if (!m_is_rounded) mpq_clear(value);
235 
237  d_value = x.d_value;
238 
239  if (!x.m_is_rounded) { //&& !m_is_rounded
240  mpq_init(value);
241  mpq_set(value, x.value);
242  }
243 
244  return *this;
245 }
246 
248 {
249  if (m_is_rounded)
250  d_value = x;
251  else {
252  mpq_set_d(value, x);
253  d_value = std::numeric_limits<double>::lowest();
254  }
255  // canonicalize();
256  return *this;
257 }
258 
259 int cmp(const Rational& r, const Rational& r1)
260 {
261  if (r.m_is_rounded && r1.m_is_rounded)
262  return r.d_value == r1.d_value ? 0 : (r.d_value > r1.d_value ? 1 : -1);
263 
264  if (r.m_is_rounded && !r1.m_is_rounded) {
265  mpq_t tmp;
266  mpq_init(tmp);
267  mpq_set_d(tmp, r.d_value);
268  int res = mpq_cmp(tmp, r1.value);
269  mpq_clear(tmp);
270  return res;
271  }
272 
273  if (!r.m_is_rounded && r1.m_is_rounded) {
274  mpq_t tmp;
275  mpq_init(tmp);
276  mpq_set_d(tmp, r1.d_value);
277  int res = mpq_cmp(r.value, tmp);
278  mpq_clear(tmp);
279  return res;
280  }
281 
282  return mpq_cmp(r.value, r1.value);
283 }
284 
285 
286 bool operator==(const Rational& r, const Rational& r1)
287 {
288  if (r.m_is_rounded && r1.m_is_rounded) return r.d_value == r1.d_value;
289 
290  if (r.m_is_rounded && !r1.m_is_rounded) {
291  mpq_t tmp;
292  mpq_init(tmp);
293  mpq_set_d(tmp, r.d_value);
294  bool res = mpq_equal(tmp, r1.value);
295  mpq_clear(tmp);
296  return res;
297  }
298 
299  if (!r.m_is_rounded && r1.m_is_rounded) {
300  mpq_t tmp;
301  mpq_init(tmp);
302  mpq_set_d(tmp, r1.d_value);
303  bool res = mpq_equal(r.value, tmp);
304  mpq_clear(tmp);
305  return res;
306  }
307 
308  return mpq_equal(r.value, r1.value);
309 }
310 
311 bool operator!=(const Rational& r, const Rational& r1)
312 {
313  return !(r == r1);
314 }
315 
316 // to double
317 double Rational::to_double() const
318 {
319  if (m_is_rounded) return d_value;
320  return mpq_get_d(value);
321 }
322 
323 Rational::operator double() const
324 {
325  return to_double();
326 }
327 
329 {
330  if (r0.m_is_rounded) {
331  return Rational(std::abs(r0.d_value), true);
332  } else {
333  Rational r;
334  mpq_abs(r.value, r0.value);
335  return r;
336  }
337 }
338 
339 //<<
340 std::ostream& operator<<(std::ostream& os, const Rational& r)
341 {
342  os << r.to_double();
343  return os;
344 }
345 
346 void Rational::init_from_binary(const std::string& v)
347 {
348  mpq_set_str(value, v.c_str(), 2);
349 }
350 std::string Rational::to_binary() const
351 {
352  if (m_is_rounded) {
353  mpq_t tmp;
354  mpq_init(tmp);
355  mpq_set_d(tmp, d_value);
356 
357  std::string v(mpq_get_str(NULL, 2, tmp));
358  mpq_clear(tmp);
359  return v;
360  }
361 
362  std::string v(mpq_get_str(NULL, 2, value));
363  return v;
364 }
365 
366 std::string Rational::serialize() const
367 {
368  return numerator() + "/" + denominator() + "/" + (m_is_rounded ? "1" : "0");
369 }
370 
371 Rational::Rational(const Eigen::VectorX<char>& data)
372 {
373  std::stringstream numss;
374  std::stringstream denomss;
375  int counter = 0;
376  for (int64_t i = 0; i < data.size(); ++i) {
377  if (data[i] == '/') {
378  ++counter;
379  continue;
380  }
381 
382  if (counter == 0)
383  numss << data[i];
384  else if (counter == 1)
385  denomss << data[i];
386  else {
387  assert(data[i] == '0' || data[i] == '1');
388  m_is_rounded = data[i] == '1';
389  break;
390  }
391  }
392 
393  const auto num = numss.str();
394  const auto denom = denomss.str();
395 
396  // const auto num = tokens[0];
397  // const auto denom = tokens[1];
398 
399  // std::regex regex{R"([/]+)"}; // split on /
400  // std::sregex_token_iterator it{data.begin(), data.end(), regex, -1};
401  // std::vector<std::string> tokens{it, {}};
402  // assert(tokens.size() >= 3);
403 
404  // const auto num = tokens[0];
405  // const auto denom = tokens[1];
406  // assert(tokens[2][0] == '0' || tokens[2][0] == '1');
407  // m_is_rounded = tokens[2][0] == '1';
408 
409  if (m_is_rounded) {
410  mpq_t tmp_r;
411  mpq_init(tmp_r);
412  std::string tmp = num + "/" + denom;
413  mpq_set_str(tmp_r, tmp.c_str(), 10);
414 
415  d_value = mpq_get_d(tmp_r);
416  mpq_clear(tmp_r);
417 
418  } else {
419  mpq_init(value);
420  std::string tmp = num + "/" + denom;
421  mpq_set_str(value, tmp.c_str(), 10);
422  d_value = std::numeric_limits<double>::lowest();
423  }
424 }
425 
426 
427 std::string Rational::numerator() const
428 {
429  mpq_t tmp;
430  mpq_init(tmp);
431  if (m_is_rounded)
432  mpq_set_d(tmp, d_value);
433  else
434  mpq_set(tmp, value);
435 
436 
437  mpz_t num;
438  mpz_init(num);
439 
440  mpq_get_num(num, tmp);
441  std::string v(mpz_get_str(NULL, 10, num));
442 
443  mpz_clear(num);
444  mpq_clear(tmp);
445 
446  return v;
447 }
448 
449 std::string Rational::denominator() const
450 {
451  mpq_t tmp;
452  mpq_init(tmp);
453  if (m_is_rounded)
454  mpq_set_d(tmp, d_value);
455  else
456  mpq_set(tmp, value);
457 
458  mpz_t denom;
459  mpz_init(denom);
460  mpq_get_den(denom, tmp);
461 
462  std::string v(mpz_get_str(NULL, 10, denom));
463 
464  mpz_clear(denom);
465  mpq_clear(tmp);
466 
467  return v;
468 }
469 
470 } // namespace wmtk
Rational(bool rounded=false)
Definition: Rational.cpp:22
double to_double() const
Definition: Rational.cpp:317
std::string numerator() const
Definition: Rational.cpp:427
Rational & operator=(const Rational &x)
Definition: Rational.cpp:230
std::string serialize() const
Definition: Rational.cpp:366
void init_from_binary(const std::string &v)
Definition: Rational.cpp:346
std::string denominator() const
Definition: Rational.cpp:449
void canonicalize()
Definition: Rational.cpp:9
double d_value
Definition: Rational.hpp:91
std::string to_binary() const
Definition: Rational.cpp:350
int get_sign() const
Definition: Rational.cpp:15
bool m_is_rounded
Definition: Rational.hpp:92
Definition: Accessor.hpp:6
int cmp(const Rational &r, const Rational &r1)
Definition: Rational.cpp:259
constexpr bool operator!=(PrimitiveType a, PrimitiveType b)
constexpr bool operator==(PrimitiveType a, PrimitiveType b)
Rational abs(const Rational &r0)
Definition: Rational.cpp:328
constexpr PrimitiveType operator-(PrimitiveType pt, int8_t n)
Rational operator/(const Rational &x, const Rational &y)
Definition: Rational.cpp:204
Rational operator*(const Rational &x, const Rational &y)
Definition: Rational.cpp:178
Rational pow(const Rational &x, int p)
Definition: Rational.cpp:166
constexpr PrimitiveType operator+(PrimitiveType pt, int8_t n)
std::ostream & operator<<(std::ostream &os, const Rational &r)
Definition: Rational.cpp:340