1use super::{Decimal, Digit, Digits, Sign, UnsignedDecimal};
2use num::{BigInt, BigUint, Zero as _};
3
4pub type Parent = String;
5
6pub fn from_arg(mut arg: Parent) -> Option<Decimal> {
7 use UnsignedDecimal::*;
8
9 let mut exponent = take_exponent(&mut arg)?;
10 let mut arg = arg.into_bytes();
11 let sign = take_sign(&mut arg);
12 let shift = take_radix(&mut arg);
13 if arg.is_empty() {
14 return None;
15 }
16 let trailing = remove_trailing_zeros(&mut arg);
17 let magnitude = if arg.is_empty() {
18 Zero
19 } else {
20 remove_leading_zeros(&mut arg);
21 let adjust = trailing as isize - shift as isize;
22 exponent += adjust;
23 let digits = into_digits_inner(arg.into_boxed_slice())?;
24 NonZero {
25 base: Digits(digits),
26 exponent,
27 }
28 };
29 Some(Decimal { sign, magnitude })
30}
31
32fn take_exponent(arg: &mut String) -> Option<BigInt> {
33 let (left, right) = match arg.split_once(['E', 'e']) {
34 None => return Some(BigInt::zero()),
35 Some(parts) => parts,
36 };
37 let exponent = parse_exponent(right)?;
38 arg.truncate(left.len());
39 Some(exponent)
40}
41
42fn parse_exponent(exponent: &str) -> Option<BigInt> {
43 use num::bigint::Sign::*;
44
45 let sign = if exponent.starts_with('-') {
46 Minus
47 } else {
48 Plus
49 };
50 let magnitude = exponent.strip_prefix(['+', '-']).unwrap_or(exponent);
51 if magnitude.is_empty() {
52 return None;
53 }
54 let mut sum = BigUint::zero();
55 for byte in magnitude.bytes() {
56 let digit = match byte {
57 b'0'..=b'9' => byte - b'0',
58 _ => return None,
59 };
60 sum = 10u32 * sum + digit;
61 }
62 let magnitude = sum;
63 let exponent = BigInt::from_biguint(sign, magnitude);
64 Some(exponent)
65}
66
67fn take_sign(arg: &mut Vec<u8>) -> Sign {
68 use Sign::*;
69
70 let sign = match **arg {
71 [b'+', ..] => Some(Positive),
72 [b'-', ..] => Some(Negative),
73 _ => None,
74 };
75 if sign.is_some() {
76 arg.remove(0);
77 }
78 sign.unwrap_or(Positive)
79}
80
81fn take_radix(arg: &mut Vec<u8>) -> usize {
82 let radix = match arg.iter().position(|byte| *byte == b'.') {
83 None => return 0,
84 Some(index) => index,
85 };
86 arg.remove(radix);
87 arg.len() - radix
88}
89
90fn remove_trailing_zeros(arg: &mut Vec<u8>) -> usize {
91 let length = arg.len();
92 let last = arg
93 .iter()
94 .rposition(|byte| *byte != b'0')
95 .map(|index| index + 1)
96 .unwrap_or(0);
97 arg.truncate(last);
98 length - last
99}
100
101fn remove_leading_zeros(arg: &mut Vec<u8>) {
102 let first = arg.iter().position(|byte| *byte != b'0').unwrap_or(0);
103 let truncate = arg[first..].len();
104 arg.copy_within(first.., 0);
105 arg.truncate(truncate);
106}
107
108fn into_digits_inner(mut arg: Box<[u8]>) -> Option<Box<[Digit]>> {
109 for byte in arg.iter_mut() {
110 match byte {
111 b'0'..=b'9' => *byte -= b'0',
112 _ => return None,
113 }
114 }
115 Some(unsafe { Box::from_raw(Box::into_raw(arg) as *mut [Digit]) })
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use Digit::*;
122
123 pub fn parse<A: ToString, const LEN: usize>(args: [A; LEN]) -> crate::ParseResult<Decimal> {
124 crate::internal::parse(args)
125 }
126
127 #[test]
128 fn empty() {
129 parse([""]).unwrap_err();
130 }
131
132 #[test]
133 fn plus() {
134 parse(["+"]).unwrap_err();
135 }
136
137 #[test]
138 fn minus() {
139 parse(["-"]).unwrap_err();
140 }
141
142 #[test]
143 fn period() {
144 parse(["."]).unwrap_err();
145 }
146
147 #[test]
148 fn plus_period() {
149 parse(["+."]).unwrap_err();
150 }
151
152 #[test]
153 fn minus_period() {
154 parse(["-."]).unwrap_err();
155 }
156
157 #[test]
158 fn e() {
159 parse(["e"]).unwrap_err();
160 }
161
162 #[test]
163 fn zero() {
164 let Decimal { sign, magnitude } = parse(["0"]).unwrap();
165 assert!(sign.is_positive());
166 match magnitude {
167 UnsignedDecimal::Zero => (),
168 UnsignedDecimal::NonZero { .. } => panic!("`magnitude` should be zero"),
169 }
170 }
171
172 #[test]
173 fn plus_one() {
174 let Decimal { sign, magnitude } = parse(["+1"]).unwrap();
175 assert!(sign.is_positive());
176 let (base, exponent) = match magnitude {
177 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
178 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
179 };
180 assert_eq!(*base.into_inner(), [D1]);
181 assert_eq!(exponent, BigInt::from(0));
182 }
183
184 #[test]
185 fn minus_one() {
186 let Decimal { sign, magnitude } = parse(["-1"]).unwrap();
187 assert!(sign.is_negative());
188 let (base, exponent) = match magnitude {
189 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
190 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
191 };
192 assert_eq!(*base.into_inner(), [D1]);
193 assert_eq!(exponent, BigInt::from(0));
194 }
195
196 #[test]
197 fn radix_one() {
198 let Decimal { sign, magnitude } = parse([".1"]).unwrap();
199 assert!(sign.is_positive());
200 let (base, exponent) = match magnitude {
201 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
202 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
203 };
204 assert_eq!(*base.into_inner(), [D1]);
205 assert_eq!(exponent, BigInt::from(-1));
206 }
207
208 #[test]
209 fn radix_plus_one() {
210 parse([".+1"]).unwrap_err();
211 }
212
213 #[test]
214 fn radix_minus_one() {
215 parse([".-1"]).unwrap_err();
216 }
217
218 #[test]
219 fn one_e_one() {
220 let Decimal { sign, magnitude } = parse(["1e1"]).unwrap();
221 assert!(sign.is_positive());
222 let (base, exponent) = match magnitude {
223 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
224 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
225 };
226 assert_eq!(*base.into_inner(), [D1]);
227 assert_eq!(exponent, BigInt::from(1));
228 }
229
230 #[test]
231 fn one_e_plus_one() {
232 let Decimal { sign, magnitude } = parse(["1e+1"]).unwrap();
233 assert!(sign.is_positive());
234 let (base, exponent) = match magnitude {
235 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
236 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
237 };
238 assert_eq!(*base.into_inner(), [D1]);
239 assert_eq!(exponent, BigInt::from(1));
240 }
241
242 #[test]
243 fn one_e_minus_one() {
244 let Decimal { sign, magnitude } = parse(["1e-1"]).unwrap();
245 assert!(sign.is_positive());
246 let (base, exponent) = match magnitude {
247 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
248 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
249 };
250 assert_eq!(*base.into_inner(), [D1]);
251 assert_eq!(exponent, BigInt::from(-1));
252 }
253
254 #[test]
255 fn leading_zeros() {
256 let Decimal { sign, magnitude } = parse(["+0001"]).unwrap();
257 assert!(sign.is_positive());
258 let (base, exponent) = match magnitude {
259 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
260 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
261 };
262 assert_eq!(*base.into_inner(), [D1]);
263 assert_eq!(exponent, BigInt::from(0));
264 }
265
266 #[test]
267 fn trailing_zeros() {
268 let Decimal { sign, magnitude } = parse(["1000.000e000"]).unwrap();
269 assert!(sign.is_positive());
270 let (base, exponent) = match magnitude {
271 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
272 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
273 };
274 assert_eq!(*base.into_inner(), [D1]);
275 assert_eq!(exponent, BigInt::from(3));
276 }
277
278 #[test]
279 fn typical() {
280 let Decimal { sign, magnitude } = parse(["123.456e789"]).unwrap();
281 assert!(sign.is_positive());
282 let (base, exponent) = match magnitude {
283 UnsignedDecimal::Zero => panic!("`magnitude` should be non-zero"),
284 UnsignedDecimal::NonZero { base, exponent } => (base, exponent),
285 };
286 assert_eq!(*base.into_inner(), [D1, D2, D3, D4, D5, D6]);
287 assert_eq!(exponent, BigInt::from(786));
288 }
289}