dashu_ratio/
parse.rs

1use crate::{
2    rbig::{RBig, Relaxed},
3    repr::Repr,
4};
5use core::str::FromStr;
6use dashu_base::ParseError;
7use dashu_int::{IBig, UBig};
8
9impl Repr {
10    fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseError> {
11        if let Some(slash) = src.find('/') {
12            let num = IBig::from_str_radix(&src[..slash], radix)?;
13            let den = IBig::from_str_radix(&src[slash + 1..], radix)?;
14            let (sign, den) = den.into_parts();
15            Ok(Repr {
16                numerator: num * sign,
17                denominator: den,
18            })
19        } else {
20            let n = IBig::from_str_radix(src, radix)?;
21            Ok(Repr {
22                numerator: n,
23                denominator: UBig::ONE,
24            })
25        }
26    }
27
28    pub fn from_str_with_radix_prefix(src: &str) -> Result<(Self, u32), ParseError> {
29        if let Some(slash) = src.find('/') {
30            // first parse the numerator part
31            let (num, num_radix) = IBig::from_str_with_radix_prefix(&src[..slash])?;
32            let (den, den_radix) = IBig::from_str_with_radix_default(&src[slash + 1..], num_radix)?;
33            let (den_sign, den) = den.into_parts();
34
35            if num_radix != den_radix {
36                return Err(ParseError::InconsistentRadix);
37            }
38            Ok((
39                Repr {
40                    numerator: num * den_sign,
41                    denominator: den,
42                },
43                num_radix,
44            ))
45        } else {
46            let (n, radix) = IBig::from_str_with_radix_prefix(src)?;
47            Ok((
48                Repr {
49                    numerator: n,
50                    denominator: UBig::ONE,
51                },
52                radix,
53            ))
54        }
55    }
56}
57
58impl RBig {
59    /// Convert a string in a given base to [RBig].
60    ///
61    /// The numerator and the denominator are separated by `/`.
62    /// `src` may contain an optional `+` prefix.
63    /// Digits 10-35 are represented by `a-z` or `A-Z`.
64    ///
65    /// # Examples
66    /// ```
67    /// # use dashu_base::ParseError;
68    /// # use dashu_ratio::RBig;
69    /// assert_eq!(
70    ///     RBig::from_str_radix("+7ab/-sse", 32)?,
71    ///     RBig::from_parts((-7499).into(), 29582u16.into())
72    /// );
73    /// # Ok::<(), ParseError>(())
74    /// ```
75    #[inline]
76    pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseError> {
77        Repr::from_str_radix(src, radix).map(|repr| RBig(repr.reduce()))
78    }
79
80    /// Convert a string with optional radix prefixes to [RBig], return the
81    /// parsed integer and radix. If no prefix is present, then the default radix 10
82    /// will be used for parsing.
83    ///
84    /// `src` may contain an '+' or `-` prefix before the radix prefix of both the
85    /// numerator and denominator.
86    ///
87    /// Allowed prefixes: `0b` for binary, `0o` for octal, `0x` for hexadecimal.
88    ///
89    /// If the radix prefixes for the numerator and the denominator are not the same,
90    /// then a ParseError will be returned. The radix prefix for the denominator can be
91    /// omitted, and the radix for the numerator will used for parsing.
92    ///
93    /// # Examples
94    /// ```
95    /// # use dashu_base::ParseError;
96    /// # use dashu_ratio::RBig;
97    /// assert_eq!(RBig::from_str_with_radix_prefix("+0o17/25")?,
98    ///     (RBig::from_parts(0o17.into(), 0o25u8.into()), 8));
99    /// assert_eq!(RBig::from_str_with_radix_prefix("-0x1f/-0x1e")?,
100    ///     (RBig::from_parts(0x1f.into(), 0x1eu8.into()), 16));
101    /// # Ok::<(), ParseError>(())
102    /// ```
103    #[inline]
104    pub fn from_str_with_radix_prefix(src: &str) -> Result<(Self, u32), ParseError> {
105        Repr::from_str_with_radix_prefix(src).map(|(repr, radix)| (Self(repr.reduce()), radix))
106    }
107}
108
109impl FromStr for RBig {
110    type Err = ParseError;
111
112    #[inline]
113    fn from_str(s: &str) -> Result<Self, ParseError> {
114        Self::from_str_radix(s, 10)
115    }
116}
117
118impl Relaxed {
119    /// Convert a string in a given base to [Relaxed].
120    ///
121    /// See [RBig::from_str_radix] for details.
122    #[inline]
123    pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseError> {
124        Repr::from_str_radix(src, radix).map(|repr| Relaxed(repr.reduce2()))
125    }
126
127    /// Convert a string with optional radix prefixes to [RBig], return the
128    /// parsed integer and radix.
129    ///
130    /// See [RBig::from_str_with_radix_prefix] for details.
131    #[inline]
132    pub fn from_str_with_radix_prefix(src: &str) -> Result<(Self, u32), ParseError> {
133        Repr::from_str_with_radix_prefix(src).map(|(repr, radix)| (Self(repr.reduce2()), radix))
134    }
135}
136
137impl FromStr for Relaxed {
138    type Err = ParseError;
139
140    #[inline]
141    fn from_str(s: &str) -> Result<Self, ParseError> {
142        Self::from_str_radix(s, 10)
143    }
144}