aGrUM  0.20.3
a C++ library for (probabilistic) graphical models
rational_tpl.h
Go to the documentation of this file.
1 /**
2  *
3  * Copyright (c) 2005-2021 by 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 = (uint64_t)den_max; /// signed and unsigned comparison resolution ...
198 
199  /// reciprocal over iterations
201 
202  /// convergents
203  std::vector< uint64_t > p({0, 1});
204  std::vector< uint64_t > q({1, 0});
205 
206  /// quotients
207  std::vector< uint64_t > a;
208 
210 
211  uint64_t n;
212  double delta, delta_tmp;
213 
214  /// we find all convergents until we met den_max
215  while (true) {
217 
218  p_tmp = a.back() * p.back() + p[p.size() - 2];
219  q_tmp = a.back() * q.back() + q[q.size() - 2];
220 
221  if (q_tmp > denMax || p_tmp > denMax) break;
222 
223  p.push_back(p_tmp);
224  q.push_back(q_tmp);
225 
226  if (std::fabs(rnumber - a.back()) < 1e-6) break;
227 
228  rnumber = GUM_SCALAR(1.) / (rnumber - a.back());
229  } /// end of while
230 
231  if (a.size() < 2 || q.back() == denMax || p.back() == denMax) {
232  numerator = p.back();
233  if (number < 0) numerator = -numerator;
234  denominator = q.back();
235  return;
236  }
237 
238  /// we can start looking at the semi-convergents made of the last two
239  /// convergents
240  /// before the one within precision zero of number found previously
241  Idx i = Idx(p.size() - 1);
242 
243  /// the last convergent has already been computed previously : end of for is
244  /// p.size() - 2
245  /// for ( ; i < p.size() - 1; ++i ) {
246  for (n = a[i - 1] - 1; n >= (a[i - 1] + 2) / 2; --n) {
247  p_tmp = n * p[i] + p[i - 1];
248  q_tmp = n * q[i] + q[i - 1];
249 
250  if (q_tmp > denMax || p_tmp > denMax) continue;
251 
253  if (number < 0) numerator = -numerator;
254  denominator = q_tmp;
255  return;
256  } // end of for
257 
258  // Test n = a[i-1]/2
259  n = a[i - 1] / 2;
260  p_tmp = n * p[i] + p[i - 1];
261  q_tmp = n * q[i] + q[i - 1];
262 
263  delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
264  delta = std::fabs(pnumber - ((double)p[i]) / q[i]);
265 
266  if (delta_tmp < delta && q_tmp <= denMax && p_tmp <= denMax) {
268  if (number < 0) numerator = -numerator;
269  denominator = q_tmp;
270  } else {
271  numerator = (int64_t)p[i];
272  if (number < 0) numerator = -numerator;
273 
274  denominator = q[i];
275  }
276 
277  ///}
278  }
279 
280 } // namespace gum
INLINE void emplace(Args &&... args)
Definition: set_tpl.h:643