1use bstr::ByteSlice;
2use malachite_base::{num::conversion::traits::RoundingInto, rounding_modes::RoundingMode};
3use malachite_bigint::{BigInt, BigUint, Sign};
4use malachite_q::Rational;
5use num_traits::{One, ToPrimitive, Zero};
6
7pub fn true_div(numerator: &BigInt, denominator: &BigInt) -> f64 {
8 let rational = Rational::from_integers_ref(numerator.into(), denominator.into());
9 match rational.rounding_into(RoundingMode::Nearest) {
10 (val, std::cmp::Ordering::Less) if val == f64::MAX => f64::INFINITY,
12 (val, std::cmp::Ordering::Greater) if val == f64::MIN => f64::NEG_INFINITY,
14 (val, _) => val,
15 }
16}
17
18pub fn float_to_ratio(value: f64) -> Option<(BigInt, BigInt)> {
19 let sign = match std::cmp::PartialOrd::partial_cmp(&value, &0.0)? {
20 std::cmp::Ordering::Less => Sign::Minus,
21 std::cmp::Ordering::Equal => return Some((BigInt::zero(), BigInt::one())),
22 std::cmp::Ordering::Greater => Sign::Plus,
23 };
24 Rational::try_from(value).ok().map(|x| {
25 let (numer, denom) = x.into_numerator_and_denominator();
26 (
27 BigInt::from_biguint(sign, numer.into()),
28 BigUint::from(denom).into(),
29 )
30 })
31}
32
33pub fn bytes_to_int(lit: &[u8], mut base: u32) -> Option<BigInt> {
34 let mut lit = lit.trim();
36 let sign = match lit.first()? {
37 b'+' => Some(Sign::Plus),
38 b'-' => Some(Sign::Minus),
39 _ => None,
40 };
41 if sign.is_some() {
42 lit = &lit[1..];
43 }
44
45 let first = *lit.first()?;
47 let has_radix = if first == b'0' {
48 match base {
49 0 => {
50 if let Some(parsed) = lit.get(1).and_then(detect_base) {
51 base = parsed;
52 true
53 } else {
54 if let [_first, ref others @ .., last] = lit {
55 let is_zero =
56 others.iter().all(|&c| c == b'0' || c == b'_') && *last == b'0';
57 if !is_zero {
58 return None;
59 }
60 }
61 return Some(BigInt::zero());
62 }
63 }
64 16 => lit.get(1).map_or(false, |&b| matches!(b, b'x' | b'X')),
65 2 => lit.get(1).map_or(false, |&b| matches!(b, b'b' | b'B')),
66 8 => lit.get(1).map_or(false, |&b| matches!(b, b'o' | b'O')),
67 _ => false,
68 }
69 } else {
70 if base == 0 {
71 base = 10;
72 }
73 false
74 };
75 if has_radix {
76 lit = &lit[2..];
77 if lit.first()? == &b'_' {
78 lit = &lit[1..];
79 }
80 }
81
82 let mut last = *lit.first()?;
84 if last == b'0' {
85 let mut count = 0;
86 for &cur in &lit[1..] {
87 if cur == b'_' {
88 if last == b'_' {
89 return None;
90 }
91 } else if cur != b'0' {
92 break;
93 };
94 count += 1;
95 last = cur;
96 }
97 let prefix_last = lit[count];
98 lit = &lit[count + 1..];
99 if lit.is_empty() && prefix_last == b'_' {
100 return None;
101 }
102 }
103
104 for c in lit {
106 let c = *c;
107 if !(c.is_ascii_alphanumeric() || c == b'_') {
108 return None;
109 }
110
111 if c == b'_' && last == b'_' {
112 return None;
113 }
114
115 last = c;
116 }
117 if last == b'_' {
118 return None;
119 }
120
121 let number = if lit.is_empty() {
123 BigInt::zero()
124 } else {
125 let uint = BigUint::parse_bytes(lit, base)?;
126 BigInt::from_biguint(sign.unwrap_or(Sign::Plus), uint)
127 };
128 Some(number)
129}
130
131#[inline]
132pub fn detect_base(c: &u8) -> Option<u32> {
133 let base = match c {
134 b'x' | b'X' => 16,
135 b'b' | b'B' => 2,
136 b'o' | b'O' => 8,
137 _ => return None,
138 };
139 Some(base)
140}
141
142#[inline(always)]
144pub fn bigint_to_finite_float(int: &BigInt) -> Option<f64> {
145 int.to_f64().filter(|f| f.is_finite())
146}
147
148#[test]
149fn test_bytes_to_int() {
150 assert_eq!(bytes_to_int(&b"0b101"[..], 2).unwrap(), BigInt::from(5));
151 assert_eq!(bytes_to_int(&b"0x_10"[..], 16).unwrap(), BigInt::from(16));
152 assert_eq!(bytes_to_int(&b"0b"[..], 16).unwrap(), BigInt::from(11));
153 assert_eq!(bytes_to_int(&b"+0b101"[..], 2).unwrap(), BigInt::from(5));
154 assert_eq!(bytes_to_int(&b"0_0_0"[..], 10).unwrap(), BigInt::from(0));
155 assert_eq!(bytes_to_int(&b"09_99"[..], 0), None);
156 assert_eq!(bytes_to_int(&b"000"[..], 0).unwrap(), BigInt::from(0));
157 assert_eq!(bytes_to_int(&b"0_"[..], 0), None);
158 assert_eq!(bytes_to_int(&b"0_100"[..], 10).unwrap(), BigInt::from(100));
159}