aGrUM  0.20.2
a C++ library for (probabilistic) graphical models
rational_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright 2005-2020 Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4  * info_at_agrum_dot_org
5  *
6  * This library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 
22 /**
23  * @file
24  * @brief Class template used to approximate decimal numbers by rationals.
25  * @author Matthieu HOURBRACQ and Pierre-Henri WUILLEMIN(@LIP6)
26  */
27 
28 // To help IDE Parsers
29 #include <agrum/agrum.h>
30 #include <agrum/tools/core/math/rational.h>
31 
32 namespace gum {
33 
34  template < typename GUM_SCALAR >
35  void Rational< GUM_SCALAR >::farey(int64_t& numerator,
37  const GUM_SCALAR& number,
38  const int64_t& den_max,
39  const GUM_SCALAR& zero) {
40  bool isNegative = (number < 0) ? true : false;
42 
43  if (std::abs(pnumber - GUM_SCALAR(1.)) < zero) {
44  numerator = (isNegative) ? -1 : 1;
45  denominator = 1;
46  return;
47  } else if (pnumber < zero) {
48  numerator = 0;
49  denominator = 1;
50  return;
51  }
52 
53  int64_t a(0), b(1), c(1), d(1);
54  double mediant(0.0F);
55 
56  while (b <= den_max && d <= den_max) {
57  mediant = (GUM_SCALAR)(a + c) / (GUM_SCALAR)(b + d);
58 
59  if (std::fabs(pnumber - mediant) < zero) {
60  if (b + d <= den_max) {
61  numerator = (isNegative) ? -(a + c) : (a + c);
62  denominator = b + d;
63  return;
64  } else if (d > b) {
65  numerator = (isNegative) ? -c : c;
66  denominator = d;
67  return;
68  } else {
69  numerator = (isNegative) ? -a : a;
70  denominator = b;
71  return;
72  }
73  } else if (pnumber > mediant) {
74  a = a + c;
75  b = b + d;
76  } else {
77  c = a + c;
78  d = b + d;
79  }
80  }
81 
82  if (b > den_max) {
83  numerator = (isNegative) ? -c : c;
84  denominator = d;
85  return;
86  } else {
87  numerator = (isNegative) ? -a : a;
88  denominator = b;
89  return;
90  }
91  } /// end of farey func
92 
93  template < typename GUM_SCALAR >
96  const GUM_SCALAR& number,
97  const double& zero) {
98  const GUM_SCALAR pnumber = (number > 0) ? number : -number;
99 
100  /// reciprocal over iterations
102 
103  /// convergents
104  std::vector< uint64_t > p({0, 1});
105  std::vector< uint64_t > q({1, 0});
106 
107  /// quotients
108  std::vector< uint64_t > a;
109 
111 
112  uint64_t n;
113  double delta, delta_tmp;
114 
115  /// we find all convergents until we found a best one
116  /// since we look for a delta < zero, we can start looking for
117  /// semi-convergents
118  /// when we found a convergent with delta < zero, and look for the
119  /// semi-convergents before
120  while (true) {
122  p.push_back(a.back() * p.back() + p[p.size() - 2]);
123  q.push_back(a.back() * q.back() + q[q.size() - 2]);
124 
125  delta = std::fabs(pnumber - (GUM_SCALAR)p.back() / q.back());
126 
127  if (delta < zero) {
128  numerator = (int64_t)p.back();
129  if (number < 0) numerator = -numerator;
130  denominator = q.back();
131  break;
132  }
133 
134  if (std::abs(rnumber - a.back()) < 1e-6) break;
135 
136  rnumber = GUM_SCALAR(1.) / (rnumber - a.back());
137  } /// end of while
138 
139  if (a.size() < 2) return;
140 
141  /// we can start looking at the semi-convergents made of the last two
142  /// convergents
143  /// before the one within precision zero of number found previously
144  Idx i = Idx(p.size() - 2);
145  /// the last convergent has already been computed previously : end of for is
146  /// p.size() - 2
147  /// for ( ; i < p.size() - 1; ++i ) {
148  // Test n = a[i-1]/2 ( when a[i-1] is even )
149  n = a[i - 1] / 2;
150  p_tmp = n * p[i] + p[i - 1];
151  q_tmp = n * q[i] + q[i - 1];
152 
153  delta = std::fabs(pnumber - ((double)p[i]) / q[i]);
154  delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
155 
156  if (delta < zero) {
157  numerator = (int64_t)p[i];
158  if (number < 0) numerator = -numerator;
159  denominator = q[i];
160  return;
161  }
162 
163  if (delta_tmp < zero) {
165  if (number < 0) numerator = -numerator;
166  denominator = q_tmp;
167  return;
168  }
169 
170  // next semi-convergents until next convergent from smaller denominator to
171  // bigger
172  // denominator
173  for (n = (a[i - 1] + 2) / 2; n < a[i - 1]; ++n) {
174  p_tmp = n * p[i] + p[i - 1];
175  q_tmp = n * q[i] + q[i - 1];
176 
177  delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
178 
179  if (delta_tmp < zero) {
181  if (number < 0) numerator = -numerator;
182  denominator = q_tmp;
183  return;
184  }
185  } /// end of for
186 
187  ///} // end of for
188  }
189 
190  template < typename GUM_SCALAR >
193  const GUM_SCALAR& number,
194  const int64_t& den_max) {
195  const GUM_SCALAR pnumber = (number > 0) ? number : -number;
196 
197  const uint64_t denMax
198  = (uint64_t)den_max; /// signed and unsigned comparison resolution ...
199 
200  /// reciprocal over iterations
202 
203  /// convergents
204  std::vector< uint64_t > p({0, 1});
205  std::vector< uint64_t > q({1, 0});
206 
207  /// quotients
208  std::vector< uint64_t > a;
209 
211 
212  uint64_t n;
213  double delta, delta_tmp;
214 
215  /// we find all convergents until we met den_max
216  while (true) {
218 
219  p_tmp = a.back() * p.back() + p[p.size() - 2];
220  q_tmp = a.back() * q.back() + q[q.size() - 2];
221 
222  if (q_tmp > denMax || p_tmp > denMax) break;
223 
224  p.push_back(p_tmp);
225  q.push_back(q_tmp);
226 
227  if (std::fabs(rnumber - a.back()) < 1e-6) break;
228 
229  rnumber = GUM_SCALAR(1.) / (rnumber - a.back());
230  } /// end of while
231 
232  if (a.size() < 2 || q.back() == denMax || p.back() == denMax) {
233  numerator = p.back();
234  if (number < 0) numerator = -numerator;
235  denominator = q.back();
236  return;
237  }
238 
239  /// we can start looking at the semi-convergents made of the last two
240  /// convergents
241  /// before the one within precision zero of number found previously
242  Idx i = Idx(p.size() - 1);
243 
244  /// the last convergent has already been computed previously : end of for is
245  /// p.size() - 2
246  /// for ( ; i < p.size() - 1; ++i ) {
247  for (n = a[i - 1] - 1; n >= (a[i - 1] + 2) / 2; --n) {
248  p_tmp = n * p[i] + p[i - 1];
249  q_tmp = n * q[i] + q[i - 1];
250 
251  if (q_tmp > denMax || p_tmp > denMax) continue;
252 
254  if (number < 0) numerator = -numerator;
255  denominator = q_tmp;
256  return;
257  } // end of for
258 
259  // Test n = a[i-1]/2
260  n = a[i - 1] / 2;
261  p_tmp = n * p[i] + p[i - 1];
262  q_tmp = n * q[i] + q[i - 1];
263 
264  delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
265  delta = std::fabs(pnumber - ((double)p[i]) / q[i]);
266 
267  if (delta_tmp < delta && q_tmp <= denMax && p_tmp <= denMax) {
269  if (number < 0) numerator = -numerator;
270  denominator = q_tmp;
271  } else {
272  numerator = (int64_t)p[i];
273  if (number < 0) numerator = -numerator;
274 
275  denominator = q[i];
276  }
277 
278  ///}
279  }
280 
281 } // namespace gum
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:669