1use malachite_base::{num::conversion::traits::RoundingInto, rounding_modes::RoundingMode};
2use malachite_bigint::{BigInt, BigUint, Sign};
3use malachite_q::Rational;
4use num_traits::{One, ToPrimitive, Zero};
5
6pub fn true_div(numerator: &BigInt, denominator: &BigInt) -> f64 {
7 let rational = Rational::from_integers_ref(numerator.into(), denominator.into());
8 match rational.rounding_into(RoundingMode::Nearest) {
9 (val, core::cmp::Ordering::Less) if val == f64::MAX => f64::INFINITY,
11 (val, core::cmp::Ordering::Greater) if val == f64::MIN => f64::NEG_INFINITY,
13 (val, _) => val,
14 }
15}
16
17pub fn float_to_ratio(value: f64) -> Option<(BigInt, BigInt)> {
18 let sign = match core::cmp::PartialOrd::partial_cmp(&value, &0.0)? {
19 core::cmp::Ordering::Less => Sign::Minus,
20 core::cmp::Ordering::Equal => return Some((BigInt::zero(), BigInt::one())),
21 core::cmp::Ordering::Greater => Sign::Plus,
22 };
23 Rational::try_from(value).ok().map(|x| {
24 let (numer, denom) = x.into_numerator_and_denominator();
25 (
26 BigInt::from_biguint(sign, numer.into()),
27 BigUint::from(denom).into(),
28 )
29 })
30}
31
32#[derive(Copy, Clone, Debug, Eq, PartialEq)]
33pub enum BytesToIntError {
34 InvalidLiteral { base: u32 },
35 InvalidBase,
36 DigitLimit { got: usize, limit: usize },
37}
38
39pub fn bytes_to_int(
42 buf: &[u8],
43 mut base: u32,
44 digit_limit: usize,
45) -> Result<BigInt, BytesToIntError> {
46 if base != 0 && !(2..=36).contains(&base) {
47 return Err(BytesToIntError::InvalidBase);
48 }
49
50 let mut buf = buf.trim_ascii();
51
52 let sign = match buf.first() {
54 Some(b'+') => Some(Sign::Plus),
55 Some(b'-') => Some(Sign::Minus),
56 None => return Err(BytesToIntError::InvalidLiteral { base }),
57 _ => None,
58 };
59
60 if sign.is_some() {
61 buf = &buf[1..];
62 }
63
64 let mut error_if_nonzero = false;
65 if base == 0 {
66 match (buf.first(), buf.get(1)) {
67 (Some(v), _) if *v != b'0' => base = 10,
68 (_, Some(b'x' | b'X')) => base = 16,
69 (_, Some(b'o' | b'O')) => base = 8,
70 (_, Some(b'b' | b'B')) => base = 2,
71 (_, _) => {
72 base = 10;
74 error_if_nonzero = true;
75 }
76 }
77 }
78
79 if error_if_nonzero {
80 if let [_first, others @ .., last] = buf {
81 let is_zero = *last == b'0' && others.iter().all(|&c| c == b'0' || c == b'_');
82 if !is_zero {
83 return Err(BytesToIntError::InvalidLiteral { base });
84 }
85 }
86 return Ok(BigInt::zero());
87 }
88
89 if buf.first().is_some_and(|&v| v == b'0')
90 && buf.get(1).is_some_and(|&v| {
91 (base == 16 && (v == b'x' || v == b'X'))
92 || (base == 8 && (v == b'o' || v == b'O'))
93 || (base == 2 && (v == b'b' || v == b'B'))
94 })
95 {
96 buf = &buf[2..];
97
98 if buf.first().is_some_and(|&v| v == b'_') {
100 buf = &buf[1..];
101 }
102 }
103
104 let mut prev = *buf
106 .first()
107 .ok_or(BytesToIntError::InvalidLiteral { base })?;
108
109 if prev == b'_' || !prev.is_ascii_alphanumeric() {
111 return Err(BytesToIntError::InvalidLiteral { base });
112 }
113
114 let mut digits = 1;
116 for &cur in buf.iter().skip(1) {
117 if cur == b'_' {
118 if prev == b'_' {
120 return Err(BytesToIntError::InvalidLiteral { base });
121 }
122 } else if cur.is_ascii_alphanumeric() {
123 digits += 1;
124 } else {
125 return Err(BytesToIntError::InvalidLiteral { base });
126 }
127
128 prev = cur;
129 }
130
131 if prev == b'_' {
133 return Err(BytesToIntError::InvalidLiteral { base });
134 }
135
136 if digit_limit > 0 && !base.is_power_of_two() && digits > digit_limit {
137 return Err(BytesToIntError::DigitLimit {
138 got: digits,
139 limit: digit_limit,
140 });
141 }
142
143 let uint = BigUint::parse_bytes(buf, base).ok_or(BytesToIntError::InvalidLiteral { base })?;
144 Ok(BigInt::from_biguint(sign.unwrap_or(Sign::Plus), uint))
145}
146
147#[inline(always)]
149pub fn bigint_to_finite_float(int: &BigInt) -> Option<f64> {
150 int.to_f64().filter(|f| f.is_finite())
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 const DIGIT_LIMIT: usize = 4300; #[test]
160 fn bytes_to_int_valid() {
161 for ((buf, base), expected) in [
162 (("0b101", 2), BigInt::from(5)),
163 (("0x_10", 16), BigInt::from(16)),
164 (("0b", 16), BigInt::from(11)),
165 (("+0b101", 2), BigInt::from(5)),
166 (("0_0_0", 10), BigInt::from(0)),
167 (("000", 0), BigInt::from(0)),
168 (("0_100", 10), BigInt::from(100)),
169 ] {
170 assert_eq!(
171 bytes_to_int(buf.as_bytes(), base, DIGIT_LIMIT),
172 Ok(expected)
173 );
174 }
175 }
176
177 #[test]
178 fn bytes_to_int_invalid_literal() {
179 for ((buf, base), expected) in [
180 (("09_99", 0), BytesToIntError::InvalidLiteral { base: 10 }),
181 (("0_", 0), BytesToIntError::InvalidLiteral { base: 10 }),
182 (("0_", 2), BytesToIntError::InvalidLiteral { base: 2 }),
183 ] {
184 assert_eq!(
185 bytes_to_int(buf.as_bytes(), base, DIGIT_LIMIT),
186 Err(expected)
187 )
188 }
189 }
190
191 #[test]
192 fn bytes_to_int_invalid_base() {
193 for base in [1, 37] {
194 assert_eq!(
195 bytes_to_int("012345".as_bytes(), base, DIGIT_LIMIT),
196 Err(BytesToIntError::InvalidBase)
197 )
198 }
199 }
200
201 #[test]
202 fn bytes_to_int_digit_limit() {
203 assert_eq!(
204 bytes_to_int("012345".as_bytes(), 10, 5),
205 Err(BytesToIntError::DigitLimit { got: 6, limit: 5 })
206 );
207 }
208}