dashu_ratio/simplify.rs
1//! Implementations of methods related to simplification.
2//!
3//! Note that these methods are only implemented for [RBig] but not for [Relaxed][crate::Relaxed]
4//! because the latter one is naturally not in the simplest form.
5
6use crate::{error::panic_divide_by_0, rbig::RBig, repr::Repr};
7use core::{cmp::Ordering, mem};
8use dashu_base::{AbsOrd, Approximation, DivRem, UnsignedAbs};
9use dashu_int::{IBig, Sign, UBig};
10
11impl Repr {
12 /// Find the simplest rational number in the open interval `(lower, upper)`.
13 /// See [RBig::simplest_in()] and <https://stackoverflow.com/q/66980340/5960776>.
14 pub fn simplest_in(mut lower: Self, mut upper: Self) -> Self {
15 let sign = if lower.numerator.sign() != upper.numerator.sign() {
16 // if lower < 0 < upper, then 0 is the simplest
17 return Self::zero();
18 } else {
19 lower.numerator.sign()
20 };
21 lower = lower.abs();
22 upper = upper.abs();
23
24 match lower.cmp(&upper) {
25 // swap so that lower is less than upper
26 Ordering::Greater => mem::swap(&mut lower, &mut upper),
27 Ordering::Equal => return sign * lower,
28 Ordering::Less => {}
29 }
30
31 let Repr {
32 numerator: mut num_l,
33 denominator: den_l,
34 } = lower;
35 let Repr {
36 numerator: mut num_r,
37 denominator: den_r,
38 } = upper;
39
40 // negative values might exist during the calculation
41 let (mut den_l, mut den_r) = (IBig::from(den_l), IBig::from(den_r));
42
43 // use continued fraction expansion to find this float
44 let (mut n0, mut d0) = (IBig::ONE, IBig::ZERO);
45 let (mut n1, mut d1) = (IBig::ZERO, IBig::ONE);
46 let (num, den) = loop {
47 let (q, r1) = num_l.div_rem(&den_l);
48
49 n1 += &q * &n0;
50 mem::swap(&mut n0, &mut n1);
51 d1 += &q * &d0;
52 mem::swap(&mut d0, &mut d1);
53
54 let r2 = mem::take(&mut num_r) - q * &den_r;
55 num_l = mem::replace(&mut den_r, r1);
56 num_r = mem::replace(&mut den_l, r2);
57
58 if num_l < den_l {
59 break (n0 + n1, d0 + d1);
60 }
61 };
62
63 debug_assert!(num.sign() == den.sign());
64 Repr {
65 numerator: num.unsigned_abs() * sign,
66 denominator: den.unsigned_abs(),
67 }
68 }
69}
70
71/// Implementation of simplest_from_f32, simplest_from_f64
72macro_rules! impl_simplest_from_float {
73 ($f:ident) => {{
74 if $f.is_infinite() || $f.is_nan() {
75 return None;
76 } else if $f == 0. {
77 return Some(Self::ZERO);
78 }
79
80 // get the range (f - ulp/2, f + ulp/2)
81 // if f is negative, then range will be flipped by simplest_in()
82 let mut est = Repr::try_from($f).unwrap();
83 est.numerator <<= 1;
84 est.denominator <<= 1;
85 let left = Self(
86 Repr {
87 numerator: &est.numerator + IBig::ONE,
88 denominator: est.denominator.clone(),
89 }
90 .reduce(),
91 );
92 let right = Self(
93 Repr {
94 numerator: est.numerator - IBig::ONE,
95 denominator: est.denominator,
96 }
97 .reduce(),
98 );
99
100 // find the simplest float in the range
101 let mut simplest = Self::simplest_in(left.clone(), right.clone());
102 if $f.to_bits() & 1 == 0 {
103 // consider boundry values when last bit is 0 (because ties to even)
104 if left.is_simpler_than(&simplest) {
105 simplest = left;
106 }
107 if right.is_simpler_than(&simplest) {
108 simplest = right;
109 }
110 }
111 Some(simplest)
112 }};
113}
114
115impl RBig {
116 /// Determine if this rational number is simpler than the other number.
117 ///
118 /// This method only make sense for canonicalized ratios.
119 #[inline]
120 pub fn is_simpler_than(&self, other: &Self) -> bool {
121 (self.denominator() < other.denominator()) // first compare denominator
122 && self.numerator().abs_cmp(other.numerator()).is_le() // then compare numerator
123 && self.sign() > other.sign() // then compare sign
124 }
125
126 /// Find the simplest rational number in the rounding interval of the [f32] number.
127 ///
128 /// This method returns None when the floating point value is not representable by a rational number,
129 /// such as infinities or nans.
130 ///
131 /// See [RBig::simplest_in] for the definition of `simplicity`.
132 ///
133 /// The rounding interval of a [f32] value is an interval such that all numbers in this
134 /// range will rounded to this [f32] value. For example the rounding interval for `1f32`
135 /// is `[1. - f32::EPSILON / 2, 1. + f32::EPSILON / 2]`. That is, the error of result value will
136 /// be less than 1/2 ULP.
137 ///
138 /// This method can be used to recover the original fraction represented as a division of [f32].
139 /// See the examples below.
140 ///
141 /// # Examples
142 ///
143 /// ```
144 /// # use dashu_ratio::RBig;
145 /// assert_eq!(
146 /// RBig::simplest_from_f32(1e-1).unwrap(),
147 /// RBig::from_parts(1.into(), 10u8.into())
148 /// );
149 /// assert_eq!(
150 /// RBig::simplest_from_f32(22./7.).unwrap(),
151 /// RBig::from_parts(22.into(), 7u8.into())
152 /// );
153 /// ```
154 pub fn simplest_from_f32(f: f32) -> Option<Self> {
155 impl_simplest_from_float!(f)
156 }
157
158 /// Find the simplest rational number in the rounding interval of the [f64] number.
159 ///
160 /// This method returns None when the floating point value is not representable by a rational number,
161 /// such as infinities or nans.
162 ///
163 /// See [RBig::simplest_in] for the definition of `simplicity`.
164 ///
165 /// The rounding interval of a [f64] value is an interval such that all numbers in this
166 /// range will rounded to this [f64] value. For example the rounding interval for `1f64`
167 /// is `[1. - f64::EPSILON / 2, 1. + f64::EPSILON / 2]`. That is, the error of result value will
168 /// be less than 1/2 ULP.
169 ///
170 /// This method can be used to recover the original fraction represented as a division of [f64].
171 /// See the examples below.
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// # use dashu_ratio::RBig;
177 /// assert_eq!(
178 /// RBig::simplest_from_f64(1e-1).unwrap(),
179 /// RBig::from_parts(1.into(), 10u8.into())
180 /// );
181 /// assert_eq!(
182 /// RBig::simplest_from_f64(22./7.).unwrap(),
183 /// RBig::from_parts(22.into(), 7u8.into())
184 /// );
185 pub fn simplest_from_f64(f: f64) -> Option<Self> {
186 impl_simplest_from_float!(f)
187 }
188
189 /// Find the simplest rational number in the open interval `(lower, upper)`.
190 ///
191 /// A rational `n₁/d₁` is simpler than another rational number `n₂/d₂` if:
192 /// * `d₁ < d₂` (compare denominator)
193 /// * or `|n₁| < |n₂|` (then compare the magnitude of numerator)
194 /// * or `n₂ < 0 < n₁` (then compare the sign)
195 ///
196 /// `lower` and `upper` will be swapped if necessary. If `lower` and `upper` are
197 /// the same number, then this number will be directly returned.
198 ///
199 /// # Examples
200 ///
201 /// ```
202 /// # use dashu_ratio::RBig;
203 /// let a = RBig::from_parts(1234.into(), 5678u16.into());
204 /// let b = RBig::from_parts(1235.into(), 5679u16.into());
205 /// let s = RBig::simplest_in(a, b);
206 /// // 1234/5678 < 5/23 < 1235/5679
207 /// assert_eq!(s, RBig::from_parts(5.into(), 23u8.into()));
208 /// ```
209 ///
210 #[inline]
211 pub fn simplest_in(lower: Self, upper: Self) -> Self {
212 Self(Repr::simplest_in(lower.0, upper.0).reduce())
213 }
214
215 /// Find the previous and next value in farey sequence (from 0 to 1) with `limit` as the order.
216 ///
217 /// This function requires `-1 < x < 1` and `x.denominator` > `limit`
218 fn farey_neighbors(x: &Self, limit: &UBig) -> (Self, Self) {
219 debug_assert!(x.denominator() > limit);
220 debug_assert!(!x.numerator().is_zero());
221 debug_assert!(x.numerator().abs_cmp(x.denominator()).is_le());
222
223 let (mut left, mut right) = match x.sign() {
224 Sign::Positive => (Repr::zero(), Repr::one()),
225 Sign::Negative => (Repr::neg_one(), Repr::zero()),
226 };
227
228 // the Farey neighbors can be found directly by adding the
229 // numerator and denominator together, see <https://en.wikipedia.org/wiki/Farey_sequence#Farey_neighbours>.
230 loop {
231 let mut next = Repr {
232 numerator: &left.numerator + &right.numerator,
233 denominator: &left.denominator + &right.denominator,
234 };
235
236 // test if the denominator has exceeded the limit
237 if &next.denominator > limit {
238 next = next.reduce();
239 if &next.denominator > limit {
240 return (Self(left), Self(right));
241 }
242 }
243
244 // tighten the bounds
245 if next > x.0 {
246 right = next;
247 } else {
248 left = next;
249 }
250 }
251 }
252
253 /// Find the closest rational number to this number with a limit of the denominator.
254 ///
255 /// If the denominator of this number is larger than the limit, then it returns the closest one
256 /// between `self.next_up()` and `self.next_down()` to `self`. If the denominator of this number
257 /// is already less than or equal to the limit, then `Exact(self)` will be returned.
258 ///
259 /// The error `|self - self.nearest()|` will be less than `1/(2*limit)`, and the sign of
260 /// the error `self - self.nearest()` will be returned if the result is not [Exact][Approximation::Exact].
261 ///
262 /// # Examples
263 ///
264 /// ```
265 /// # use dashu_base::{Approximation::*, Sign};
266 /// # use dashu_ratio::RBig;
267 /// let a: RBig = 3.141592653.try_into().unwrap();
268 /// assert_eq!(a.nearest(&10u8.into()), Inexact(
269 /// RBig::from_parts(22.into(), 7u8.into()),
270 /// Sign::Positive // 22/7 > 3.141592653
271 /// ));
272 /// ```
273 pub fn nearest(&self, limit: &UBig) -> Approximation<Self, Sign> {
274 if limit.is_zero() {
275 panic_divide_by_0()
276 }
277
278 // directly return this number if it's already simple enough
279 if self.denominator() <= limit {
280 return Approximation::Exact(self.clone());
281 }
282
283 let (trunc, r) = self.clone().split_at_point();
284 let (left, right) = Self::farey_neighbors(&r, limit);
285
286 // find the closest one (compare r - left and right - r)
287 let mut mid = (&left + &right).0;
288 mid.denominator <<= 1;
289 if r.0 > mid {
290 Approximation::Inexact(trunc + right, Sign::Positive)
291 } else {
292 Approximation::Inexact(trunc + left, Sign::Negative)
293 }
294 }
295
296 /// Find the closest rational number that is greater than this number and has a denominator less than `limit`.
297 ///
298 /// It's equivalent to finding the next element in Farey sequence of order `limit`. The error
299 /// `|self - self.next_up()|` will be less than `1/limit`.
300 ///
301 /// # Examples
302 ///
303 /// ```
304 /// # use dashu_ratio::RBig;
305 /// let a: RBig = 3.141592653.try_into().unwrap();
306 /// assert_eq!(a.next_up(&10u8.into()), RBig::from_parts(22.into(), 7u8.into()));
307 /// ```
308 pub fn next_up(&self, limit: &UBig) -> Self {
309 if limit.is_zero() {
310 panic_divide_by_0()
311 }
312
313 let (trunc, fract) = self.clone().split_at_point();
314 let up = if self.denominator() <= limit {
315 // If the denominator of the number is already small enough, increase the number a little
316 // bit before finding the farey neighbors. Note that the distance between two adjacent
317 // numbers in a farey sequence is at least limit^-2, so we just increase limit^-2
318 let target = fract
319 + Self(Repr {
320 numerator: IBig::ONE,
321 denominator: limit.sqr(),
322 });
323 Self::farey_neighbors(&target, limit).1
324 } else {
325 // otherwise we can directly find the next value by finding farey bounds
326 Self::farey_neighbors(&fract, limit).1
327 };
328 trunc + up
329 }
330
331 /// Find the closest rational number that is less than this number and has a denominator less than `limit`.
332 ///
333 /// It's equivalent to finding the previous element in Farey sequence of order `limit`. The error
334 /// `|self - self.next_down()|` will be less than `1/limit`.
335 ///
336 /// # Examples
337 ///
338 /// ```
339 /// # use dashu_ratio::RBig;
340 /// let a: RBig = 3.141592653.try_into().unwrap();
341 /// assert_eq!(a.next_down(&10u8.into()), RBig::from_parts(25.into(), 8u8.into()));
342 /// ```
343 pub fn next_down(&self, limit: &UBig) -> Self {
344 if limit.is_zero() {
345 panic_divide_by_0()
346 }
347
348 // similar to next_up()
349 let (trunc, fract) = self.clone().split_at_point();
350 let down = if self.denominator() <= limit {
351 let target = fract
352 - Self(Repr {
353 numerator: IBig::ONE,
354 denominator: limit.sqr(),
355 });
356 Self::farey_neighbors(&target, limit).0
357 } else {
358 Self::farey_neighbors(&fract, limit).0
359 };
360 trunc + down
361 }
362}