malachite_q/conversion/continued_fraction/to_continued_fraction.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::conversion::traits::ContinuedFraction;
11use core::mem::swap;
12use malachite_base::num::arithmetic::traits::{DivMod, Floor};
13use malachite_nz::integer::Integer;
14use malachite_nz::natural::Natural;
15
16/// An iterator that produces the continued fraction of a [`Rational`].
17///
18/// See [`continued_fraction`](Rational::continued_fraction) for more information.
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct RationalContinuedFraction {
21 numerator: Natural,
22 denominator: Natural,
23}
24
25impl RationalContinuedFraction {
26 pub_crate_test! {is_done(&self) -> bool {
27 self.denominator == 0u32 || self.numerator == 0u32
28 }}
29}
30
31impl Iterator for RationalContinuedFraction {
32 type Item = Natural;
33
34 fn next(&mut self) -> Option<Natural> {
35 if self.denominator == 0u32 || self.numerator == 0u32 {
36 None
37 } else {
38 let n;
39 (n, self.numerator) = (&self.numerator).div_mod(&self.denominator);
40 swap(&mut self.numerator, &mut self.denominator);
41 Some(n)
42 }
43 }
44}
45
46impl ContinuedFraction for Rational {
47 type CF = RationalContinuedFraction;
48
49 /// Returns the continued fraction of a [`Rational`], taking the [`Rational`] by value.
50 ///
51 /// The output has two components. The first is the first value of the continued fraction, which
52 /// may be any [`Integer`] and is equal to the floor of the [`Rational`]. The second is an
53 /// iterator that produces the remaining values, which are all positive. Using the standard
54 /// notation for continued fractions, the first value is the number before the semicolon, and
55 /// the second value produces the remaining numbers.
56 ///
57 /// Each rational number has two continued fraction representations. The shorter of the two
58 /// representations (the one that does not end in 1) is returned.
59 ///
60 /// $f(x) = (a_0, (a_1, a_2, \ldots, a_3)),$ where $x = [a_0; a_1, a_2, \ldots, a_3]$ and $a_3
61 /// \neq 1$.
62 ///
63 /// The output length is $O(n)$, where $n$ is `self.significant_bits()`.
64 ///
65 /// # Worst-case complexity per iteration
66 /// $T(n) = O(n \log n \log\log n)$
67 ///
68 /// $M(n) = O(n \log n)$
69 ///
70 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
71 ///
72 /// # Examples
73 /// ```
74 /// use itertools::Itertools;
75 /// use malachite_base::strings::ToDebugString;
76 /// use malachite_q::conversion::traits::ContinuedFraction;
77 /// use malachite_q::Rational;
78 ///
79 /// let (head, tail) = Rational::from_signeds(2, 3).continued_fraction();
80 /// let tail = tail.collect_vec();
81 /// assert_eq!(head, 0);
82 /// assert_eq!(tail.to_debug_string(), "[1, 2]");
83 ///
84 /// let (head, tail) = Rational::from_signeds(355, 113).continued_fraction();
85 /// let tail = tail.collect_vec();
86 /// assert_eq!(head, 3);
87 /// assert_eq!(tail.to_debug_string(), "[7, 16]");
88 /// ```
89 fn continued_fraction(mut self) -> (Integer, RationalContinuedFraction) {
90 let f = (&self).floor();
91 self -= Rational::from(&f);
92 let (d, n) = self.into_numerator_and_denominator();
93 (
94 f,
95 RationalContinuedFraction {
96 numerator: n,
97 denominator: d,
98 },
99 )
100 }
101}
102
103impl ContinuedFraction for &Rational {
104 type CF = RationalContinuedFraction;
105
106 /// Returns the continued fraction of a [`Rational`], taking the [`Rational`] by reference.
107 ///
108 /// The output has two components. The first is the first value of the continued fraction, which
109 /// may be any [`Integer`] and is equal to the floor of the [`Rational`]. The second is an
110 /// iterator that produces the remaining values, which are all positive. Using the standard
111 /// notation for continued fractions, the first value is the number before the semicolon, and
112 /// the second value produces the remaining numbers.
113 ///
114 /// Each rational number has two continued fraction representations. The shorter of the two
115 /// representations (the one that does not end in 1) is returned.
116 ///
117 /// $f(x) = (a_0, (a_1, a_2, \ldots, a_3)),$ where $x = [a_0; a_1, a_2, \ldots, a_3]$ and $a_3
118 /// \neq 1$.
119 ///
120 /// The output length is $O(n)$, where $n$ is `self.significant_bits()`.
121 ///
122 /// # Worst-case complexity per iteration
123 /// $T(n) = O(n \log n \log\log n)$
124 ///
125 /// $M(n) = O(n \log n)$
126 ///
127 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
128 ///
129 /// # Examples
130 /// ```
131 /// use itertools::Itertools;
132 /// use malachite_base::strings::ToDebugString;
133 /// use malachite_q::conversion::traits::ContinuedFraction;
134 /// use malachite_q::Rational;
135 ///
136 /// let (head, tail) = (&Rational::from_signeds(2, 3)).continued_fraction();
137 /// let tail = tail.collect_vec();
138 /// assert_eq!(head, 0);
139 /// assert_eq!(tail.to_debug_string(), "[1, 2]");
140 ///
141 /// let (head, tail) = (&Rational::from_signeds(355, 113)).continued_fraction();
142 /// let tail = tail.collect_vec();
143 /// assert_eq!(head, 3);
144 /// assert_eq!(tail.to_debug_string(), "[7, 16]");
145 /// ```
146 fn continued_fraction(self) -> (Integer, RationalContinuedFraction) {
147 let f = self.floor();
148 let (d, n) = (self - Rational::from(&f)).into_numerator_and_denominator();
149 (
150 f,
151 RationalContinuedFraction {
152 numerator: n,
153 denominator: d,
154 },
155 )
156 }
157}