malachite_q/arithmetic/
simplest_rational_in_interval.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::Rational;
10use crate::arithmetic::traits::SimplestRationalInInterval;
11use crate::conversion::continued_fraction::to_continued_fraction::RationalContinuedFraction;
12use crate::conversion::traits::ContinuedFraction;
13use core::cmp::{
14    Ordering::{self, *},
15    max, min,
16};
17use core::mem::swap;
18use malachite_base::num::arithmetic::traits::{AddMul, Ceiling, Floor, UnsignedAbs};
19use malachite_base::num::basic::traits::{One, Two, Zero};
20use malachite_base::num::conversion::traits::IsInteger;
21use malachite_nz::natural::Natural;
22
23fn min_helper_oo<'a>(ox: &'a Option<Natural>, oy: &'a Option<Natural>) -> &'a Natural {
24    if let Some(x) = ox.as_ref() {
25        if let Some(y) = oy.as_ref() {
26            min(x, y)
27        } else {
28            x
29        }
30    } else {
31        oy.as_ref().unwrap()
32    }
33}
34
35fn min_helper_xo<'a>(x: &'a Natural, oy: &'a Option<Natural>) -> &'a Natural {
36    if let Some(y) = oy.as_ref() {
37        min(x, y)
38    } else {
39        x
40    }
41}
42
43fn simplest_rational_one_alt_helper(
44    x: &Natural,
45    oy_n: &Option<Natural>,
46    mut cf_y: RationalContinuedFraction,
47    numerator: &Natural,
48    denominator: &Natural,
49    previous_numerator: &Natural,
50    previous_denominator: &Natural,
51) -> Rational {
52    // use [a_0; a_1, ... a_k - 1, 1] and [b_0; b_1, ... b_k]
53    let (n, d) = if oy_n.is_some() && x - Natural::ONE == *oy_n.as_ref().unwrap() {
54        let next_numerator = previous_numerator.add_mul(numerator, oy_n.as_ref().unwrap());
55        let next_denominator = previous_denominator.add_mul(denominator, oy_n.as_ref().unwrap());
56        let next_oy_n = cf_y.next();
57        if next_oy_n == Some(Natural::ONE) {
58            let next_next_numerator = numerator + &next_numerator;
59            let next_next_denominator = denominator + &next_denominator;
60            // since y_n = 1, cf_y is not exhausted yet
61            let y_n = cf_y.next().unwrap() + Natural::ONE;
62            (
63                next_numerator.add_mul(next_next_numerator, &y_n),
64                next_denominator.add_mul(next_next_denominator, y_n),
65            )
66        } else {
67            (
68                numerator + (next_numerator << 1),
69                denominator + (next_denominator << 1),
70            )
71        }
72    } else {
73        let ox_n_m_1 = x - Natural::ONE;
74        let m = min_helper_xo(&ox_n_m_1, oy_n);
75        let next_numerator = previous_numerator.add_mul(numerator, m);
76        let next_denominator = previous_denominator.add_mul(denominator, m);
77        (
78            numerator + (next_numerator << 1),
79            denominator + (next_denominator << 1),
80        )
81    };
82    Rational {
83        sign: true,
84        numerator: n,
85        denominator: d,
86    }
87}
88
89fn update_best(best: &mut Option<Rational>, x: &Rational, y: &Rational, candidate: Rational) {
90    if best.is_none() && candidate > *x && candidate < *y {
91        *best = Some(candidate);
92    }
93}
94
95impl Rational {
96    /// Compares two [`Rational`]s according to their complexity.
97    ///
98    /// Complexity is defined as follows: If two [`Rational`]s have different denominators, then the
99    /// one with the larger denominator is more complex. If they have the same denominator, then the
100    /// one whose numerator is further from zero is more complex. Finally, if $q > 0$, then $q$ is
101    /// simpler than $-q$.
102    ///
103    /// The [`Rational`]s ordered by complexity look like this:
104    /// $$
105    /// 0, 1, -1, 2, -2, \ldots, 1/2, -1/2, 3/2, -3/2, \ldots, 1/3, -1/3, 2/3, -2/3, \ldots, \ldots.
106    /// $$
107    /// This order is a well-order, and the order type of the [`Rational`]s under this order is
108    /// $\omega^2$.
109    ///
110    /// # Worst-case complexity
111    /// $T(n) = O(n)$
112    ///
113    /// $M(n) = O(1)$
114    ///
115    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
116    ///
117    /// # Examples
118    /// ```
119    /// use malachite_q::Rational;
120    /// use std::cmp::Ordering::*;
121    ///
122    /// assert_eq!(
123    ///     Rational::from_signeds(1, 2).cmp_complexity(&Rational::from_signeds(1, 3)),
124    ///     Less
125    /// );
126    /// assert_eq!(
127    ///     Rational::from_signeds(1, 2).cmp_complexity(&Rational::from_signeds(3, 2)),
128    ///     Less
129    /// );
130    /// assert_eq!(
131    ///     Rational::from_signeds(1, 2).cmp_complexity(&Rational::from_signeds(-1, 2)),
132    ///     Less
133    /// );
134    /// ```
135    pub fn cmp_complexity(&self, other: &Self) -> Ordering {
136        self.denominator_ref()
137            .cmp(other.denominator_ref())
138            .then_with(|| self.numerator_ref().cmp(other.numerator_ref()))
139            .then_with(|| (*self < 0u32).cmp(&(*other < 0u32)))
140    }
141}
142
143impl SimplestRationalInInterval for Rational {
144    /// Finds the simplest [`Rational`] contained in an open interval.
145    ///
146    /// Let $f(x, y) = p/q$, with $p$ and $q$ relatively prime. Then the following properties hold:
147    /// - $x < p/q < y$
148    /// - If $x < m/n < y$, then $n \geq q$
149    /// - If $x < m/q < y$, then $|p| \leq |m|$
150    ///
151    /// # Worst-case complexity
152    /// $T(n) = O(n^2 \log n \log\log n)$
153    ///
154    /// $M(n) = O(n \log n)$
155    ///
156    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(x.significant_bits(),
157    /// y.significant_bits())`.
158    ///
159    /// # Panics
160    /// Panics if $x \geq y$.
161    ///
162    /// # Examples
163    /// ```
164    /// use malachite_base::num::basic::traits::Zero;
165    /// use malachite_q::arithmetic::traits::SimplestRationalInInterval;
166    /// use malachite_q::Rational;
167    ///
168    /// assert_eq!(
169    ///     Rational::simplest_rational_in_open_interval(
170    ///         &Rational::from_signeds(1, 3),
171    ///         &Rational::from_signeds(1, 2)
172    ///     ),
173    ///     Rational::from_signeds(2, 5)
174    /// );
175    /// assert_eq!(
176    ///     Rational::simplest_rational_in_open_interval(
177    ///         &Rational::from_signeds(-1, 3),
178    ///         &Rational::from_signeds(1, 3)
179    ///     ),
180    ///     Rational::ZERO
181    /// );
182    /// assert_eq!(
183    ///     Rational::simplest_rational_in_open_interval(
184    ///         &Rational::from_signeds(314, 100),
185    ///         &Rational::from_signeds(315, 100)
186    ///     ),
187    ///     Rational::from_signeds(22, 7)
188    /// );
189    /// ```
190    fn simplest_rational_in_open_interval(x: &Self, y: &Self) -> Rational {
191        assert!(x < y);
192        if *x < 0u32 && *y > 0u32 {
193            return Self::ZERO;
194        }
195        let neg_x;
196        let neg_y;
197        let (neg, x, y) = if *x < 0u32 {
198            neg_x = -x;
199            neg_y = -y;
200            (true, &neg_y, &neg_x)
201        } else {
202            (false, x, y)
203        };
204        let (floor_x, mut cf_x) = x.continued_fraction();
205        let floor_x = floor_x.unsigned_abs();
206        let (floor_y, mut cf_y) = y.continued_fraction();
207        let floor_y = floor_y.unsigned_abs();
208        let mut best = None;
209        if floor_x == floor_y {
210            let floor = floor_x;
211            let mut previous_numerator = Natural::ONE;
212            let mut previous_denominator = Natural::ZERO;
213            let mut numerator = floor;
214            let mut denominator = Natural::ONE;
215            let mut ox_n = cf_x.next();
216            let mut oy_n = cf_y.next();
217            while ox_n == oy_n {
218                // They are both Some
219                swap(&mut numerator, &mut previous_numerator);
220                swap(&mut denominator, &mut previous_denominator);
221                numerator = (&numerator).add_mul(&previous_numerator, &ox_n.unwrap());
222                denominator = (&denominator).add_mul(&previous_denominator, &oy_n.unwrap());
223                ox_n = cf_x.next();
224                oy_n = cf_y.next();
225            }
226            // use [x_0; x_1, ... x_k] and [y_0; y_1, ... y_k]
227            let m = min_helper_oo(&ox_n, &oy_n) + Natural::ONE;
228            let n = (&previous_numerator).add_mul(&numerator, &m);
229            let d = (&previous_denominator).add_mul(&denominator, &m);
230            let candidate = Self {
231                sign: true,
232                numerator: n,
233                denominator: d,
234            };
235            update_best(&mut best, x, y, candidate);
236            if let Some(x_n) = ox_n.as_ref()
237                && cf_x.is_done()
238            {
239                update_best(
240                    &mut best,
241                    x,
242                    y,
243                    simplest_rational_one_alt_helper(
244                        x_n,
245                        &oy_n,
246                        cf_y.clone(),
247                        &numerator,
248                        &denominator,
249                        &previous_numerator,
250                        &previous_denominator,
251                    ),
252                );
253            }
254            if let Some(y_n) = oy_n.as_ref()
255                && cf_y.is_done()
256            {
257                update_best(
258                    &mut best,
259                    x,
260                    y,
261                    simplest_rational_one_alt_helper(
262                        y_n,
263                        &ox_n,
264                        cf_x.clone(),
265                        &numerator,
266                        &denominator,
267                        &previous_numerator,
268                        &previous_denominator,
269                    ),
270                );
271            }
272            if ox_n.is_some() && oy_n.is_some() && cf_x.is_done() != cf_y.is_done() {
273                if cf_y.is_done() {
274                    swap(&mut ox_n, &mut oy_n);
275                    swap(&mut cf_y, &mut cf_x);
276                }
277                let x_n = ox_n.unwrap();
278                let y_n = oy_n.unwrap();
279                if y_n == x_n - Natural::ONE {
280                    let next_y_n = cf_y.next().unwrap();
281                    let next_numerator = (&previous_numerator).add_mul(&numerator, &y_n);
282                    let next_denominator = (&previous_denominator).add_mul(&denominator, &y_n);
283                    let (n, d) = if cf_y.is_done() && next_y_n == 2u32 {
284                        (
285                            next_numerator * Natural::from(3u32) + (numerator << 1),
286                            next_denominator * Natural::from(3u32) + (denominator << 1),
287                        )
288                    } else {
289                        (
290                            previous_numerator + (numerator << 1),
291                            previous_denominator + (denominator << 1),
292                        )
293                    };
294                    let candidate = Self {
295                        sign: true,
296                        numerator: n,
297                        denominator: d,
298                    };
299                    update_best(&mut best, x, y, candidate);
300                }
301            }
302        } else {
303            let candidate = if floor_y - Natural::ONE != floor_x || !cf_y.is_done() {
304                Self::from(floor_x + Natural::ONE)
305            } else {
306                let floor = floor_x;
307                // [f; x_1, x_2, x_3...] and [f + 1]. But to get any good candidates, we need [f;
308                // x_1, x_2, x_3...] and [f; 1]. If x_1 does not exist, the result is [f; 2].
309                let (n, d) = if cf_x.is_done() {
310                    ((floor << 1) | Natural::ONE, Natural::TWO)
311                } else {
312                    let x_1 = cf_x.next().unwrap();
313                    if x_1 > Natural::ONE {
314                        if x_1 == 2u32 && cf_x.is_done() {
315                            // [f; 1, 1] and [f; 1], so [f; 1, 2] is a candidate.
316                            (
317                                floor * Natural::from(3u32) + Natural::TWO,
318                                Natural::from(3u32),
319                            )
320                        } else {
321                            // If x_1 > 1, we have [f; 2] as a candidate.
322                            ((floor << 1) | Natural::ONE, Natural::TWO)
323                        }
324                    } else {
325                        // x_2 exists since x_1 was 1
326                        let x_2 = cf_x.next().unwrap();
327                        // [f; 1, x_2] and [f; 1], so [f; 1, x_2 + 1] is a candidate. [f; 1, x_2 -
328                        // 1, 1] and [f; 1], but [f; 1, x_2] is not in the interval
329                        let k = &x_2 + Natural::ONE;
330                        (&floor * &k + floor + k, x_2 + Natural::TWO)
331                    }
332                };
333                Self {
334                    sign: true,
335                    numerator: n,
336                    denominator: d,
337                }
338            };
339            update_best(&mut best, x, y, candidate);
340        }
341        let best = best.unwrap();
342        if neg { -best } else { best }
343    }
344
345    /// Finds the simplest [`Rational`] contained in a closed interval.
346    ///
347    /// Let $f(x, y) = p/q$, with $p$ and $q$ relatively prime. Then the following properties hold:
348    /// - $x \leq p/q \leq y$
349    /// - If $x \leq m/n \leq y$, then $n \geq q$
350    /// - If $x \leq m/q \leq y$, then $|p| \leq |m|$
351    ///
352    /// # Worst-case complexity
353    /// $T(n) = O(n^2 \log n \log\log n)$
354    ///
355    /// $M(n) = O(n \log n)$
356    ///
357    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(x.significant_bits(),
358    /// y.significant_bits())`.
359    ///
360    /// # Panics
361    /// Panics if $x > y$.
362    ///
363    /// # Examples
364    /// ```
365    /// use malachite_base::num::basic::traits::Zero;
366    /// use malachite_q::arithmetic::traits::SimplestRationalInInterval;
367    /// use malachite_q::Rational;
368    ///
369    /// assert_eq!(
370    ///     Rational::simplest_rational_in_closed_interval(
371    ///         &Rational::from_signeds(1, 3),
372    ///         &Rational::from_signeds(1, 2)
373    ///     ),
374    ///     Rational::from_signeds(1, 2)
375    /// );
376    /// assert_eq!(
377    ///     Rational::simplest_rational_in_closed_interval(
378    ///         &Rational::from_signeds(-1, 3),
379    ///         &Rational::from_signeds(1, 3)
380    ///     ),
381    ///     Rational::ZERO
382    /// );
383    /// assert_eq!(
384    ///     Rational::simplest_rational_in_closed_interval(
385    ///         &Rational::from_signeds(314, 100),
386    ///         &Rational::from_signeds(315, 100)
387    ///     ),
388    ///     Rational::from_signeds(22, 7)
389    /// );
390    /// ```
391    fn simplest_rational_in_closed_interval(x: &Self, y: &Self) -> Rational {
392        assert!(x <= y);
393        if x == y {
394            return x.clone();
395        } else if *x <= 0u32 && *y >= 0u32 {
396            return Self::ZERO;
397        } else if x.is_integer() {
398            return if y.is_integer() {
399                if *x >= 0u32 {
400                    min(x, y).clone()
401                } else {
402                    max(x, y).clone()
403                }
404            } else if *x >= 0u32 {
405                x.clone()
406            } else {
407                Self::from(y.floor())
408            };
409        } else if y.is_integer() {
410            return if *x >= 0u32 {
411                Self::from(x.ceiling())
412            } else {
413                y.clone()
414            };
415        }
416        let mut best = Self::simplest_rational_in_open_interval(x, y);
417        for q in [x, y] {
418            if q.cmp_complexity(&best) == Less {
419                best = q.clone();
420            }
421        }
422        best
423    }
424}