malachite_q/conversion/digits/
from_digits.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 alloc::vec::Vec;
11use malachite_base::num::arithmetic::traits::{CheckedLogBase2, Pow};
12use malachite_base::num::basic::traits::One;
13use malachite_base::num::conversion::traits::{Digits, ExactFrom};
14use malachite_base::rational_sequences::RationalSequence;
15use malachite_nz::natural::Natural;
16
17impl Rational {
18    /// Converts base-$b$ digits to a [`Rational`]. The inputs are taken by value.
19    ///
20    /// The input consists of the digits of the integer portion of the [`Rational`] and the digits
21    /// of the fractional portion. The integer-portion digits are ordered from least- to
22    /// most-significant, and the fractional-portion digits from most- to least.
23    ///
24    /// The fractional-portion digits may end in infinitely many zeros or $(b-1)$s; these are
25    /// handled correctly.
26    ///
27    /// # Worst-case complexity
28    /// $T(n, m) = O(nm \log (nm)^2 \log\log (nm))$
29    ///
30    /// $M(n, m) = O(nm \log (nm))$
31    ///
32    /// where $T$ is time, $M$ is additional memory, $n$ is `max(before_point.len(),
33    /// after_point.component_len())`, and $m$ is `base.significant_bits()`.
34    ///
35    /// # Panics
36    /// Panics if `base` is less than 2.
37    ///
38    /// # Examples
39    /// ```
40    /// use malachite_base::rational_sequences::RationalSequence;
41    /// use malachite_base::vecs::vec_from_str;
42    /// use malachite_nz::natural::Natural;
43    /// use malachite_q::Rational;
44    ///
45    /// let before_point = vec_from_str("[3]").unwrap();
46    /// let after_point =
47    ///     RationalSequence::from_vecs(Vec::new(), vec_from_str("[1, 4, 2, 8, 5, 7]").unwrap());
48    /// assert_eq!(
49    ///     Rational::from_digits(&Natural::from(10u32), before_point, after_point).to_string(),
50    ///     "22/7"
51    /// );
52    ///
53    /// // 21.34565656...
54    /// let before_point = vec_from_str("[1, 2]").unwrap();
55    /// let after_point = RationalSequence::from_vecs(
56    ///     vec_from_str("[3, 4]").unwrap(),
57    ///     vec_from_str("[5, 6]").unwrap(),
58    /// );
59    /// assert_eq!(
60    ///     Rational::from_digits(&Natural::from(10u32), before_point, after_point).to_string(),
61    ///     "105661/4950"
62    /// );
63    /// ```
64    pub fn from_digits(
65        base: &Natural,
66        before_point: Vec<Natural>,
67        after_point: RationalSequence<Natural>,
68    ) -> Rational {
69        if let Some(log_base) = base.checked_log_base_2() {
70            return Rational::from_power_of_2_digits(log_base, before_point, after_point);
71        }
72        let (non_repeating, repeating) = after_point.into_vecs();
73        let r_len = u64::exact_from(repeating.len());
74        let nr_len = u64::exact_from(non_repeating.len());
75        let nr = Natural::from_digits_asc(base, non_repeating.into_iter().rev()).unwrap();
76        let r = Natural::from_digits_asc(base, repeating.into_iter().rev()).unwrap();
77        let floor =
78            Rational::from(Natural::from_digits_asc(base, before_point.into_iter()).unwrap());
79        floor
80            + if r == 0u32 {
81                Rational::from_naturals(nr, base.pow(nr_len))
82            } else {
83                (Rational::from_naturals(r, base.pow(r_len) - Natural::ONE) + Rational::from(nr))
84                    / Rational::from(base.pow(nr_len))
85            }
86    }
87
88    /// Converts base-$b$ digits to a [`Rational`]. The inputs are taken by reference.
89    ///
90    /// The input consists of the digits of the integer portion of the [`Rational`] and the digits
91    /// of the fractional portion. The integer-portion digits are ordered from least- to
92    /// most-significant, and the fractional-portion digits from most- to least.
93    ///
94    /// The fractional-portion digits may end in infinitely many zeros or $(b-1)$s; these are
95    /// handled correctly.
96    ///
97    /// # Worst-case complexity
98    /// $T(n, m) = O(nm \log (nm)^2 \log\log (nm))$
99    ///
100    /// $M(n, m) = O(nm \log (nm))$
101    ///
102    /// where $T$ is time, $M$ is additional memory, $n$ is `max(before_point.len(),
103    /// after_point.component_len())`, and $m$ is `base.significant_bits()`.
104    ///
105    /// # Panics
106    /// Panics if `base` is less than 2.
107    ///
108    /// # Examples
109    /// ```
110    /// use malachite_base::rational_sequences::RationalSequence;
111    /// use malachite_base::vecs::vec_from_str;
112    /// use malachite_nz::natural::Natural;
113    /// use malachite_q::Rational;
114    ///
115    /// let before_point = vec_from_str("[3]").unwrap();
116    /// let after_point =
117    ///     RationalSequence::from_vecs(Vec::new(), vec_from_str("[1, 4, 2, 8, 5, 7]").unwrap());
118    /// assert_eq!(
119    ///     Rational::from_digits_ref(&Natural::from(10u32), &before_point, &after_point)
120    ///         .to_string(),
121    ///     "22/7"
122    /// );
123    ///
124    /// // 21.34565656...
125    /// let before_point = vec_from_str("[1, 2]").unwrap();
126    /// let after_point = RationalSequence::from_vecs(
127    ///     vec_from_str("[3, 4]").unwrap(),
128    ///     vec_from_str("[5, 6]").unwrap(),
129    /// );
130    /// assert_eq!(
131    ///     Rational::from_digits_ref(&Natural::from(10u32), &before_point, &after_point)
132    ///         .to_string(),
133    ///     "105661/4950"
134    /// );
135    /// ```
136    pub fn from_digits_ref(
137        base: &Natural,
138        before_point: &[Natural],
139        after_point: &RationalSequence<Natural>,
140    ) -> Rational {
141        if let Some(log_base) = base.checked_log_base_2() {
142            return Rational::from_power_of_2_digits_ref(log_base, before_point, after_point);
143        }
144        let (non_repeating, repeating) = after_point.to_vecs();
145        let r_len = u64::exact_from(repeating.len());
146        let nr_len = u64::exact_from(non_repeating.len());
147        let nr = Natural::from_digits_asc(base, non_repeating.into_iter().rev()).unwrap();
148        let r = Natural::from_digits_asc(base, repeating.into_iter().rev()).unwrap();
149        let floor =
150            Rational::from(Natural::from_digits_asc(base, before_point.iter().cloned()).unwrap());
151        floor
152            + if r == 0u32 {
153                Rational::from_naturals(nr, base.pow(nr_len))
154            } else {
155                (Rational::from_naturals(r, base.pow(r_len) - Natural::ONE) + Rational::from(nr))
156                    / Rational::from(base.pow(nr_len))
157            }
158    }
159}