competitive/num/decimal/
convert.rs

1use super::*;
2use std::{
3    fmt::{self, Display},
4    str::FromStr,
5};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct ParseDecimalError {
9    kind: DecimalErrorKind,
10}
11
12impl ParseDecimalError {
13    fn empty() -> Self {
14        Self {
15            kind: DecimalErrorKind::Empty,
16        }
17    }
18    fn invalid_digit() -> Self {
19        Self {
20            kind: DecimalErrorKind::InvalidDigit,
21        }
22    }
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26enum DecimalErrorKind {
27    Empty,
28    InvalidDigit,
29}
30
31impl Display for ParseDecimalError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self.kind {
34            DecimalErrorKind::Empty => write!(f, "empty string"),
35            DecimalErrorKind::InvalidDigit => write!(f, "invalid digit"),
36        }
37    }
38}
39
40impl FromStr for Decimal {
41    type Err = ParseDecimalError;
42
43    fn from_str(s: &str) -> Result<Self, Self::Err> {
44        if s.is_empty() {
45            return Err(ParseDecimalError::empty());
46        }
47
48        let (s, sign) = if let Some(s) = s.strip_prefix('+') {
49            (s, Sign::Plus)
50        } else if let Some(s) = s.strip_prefix('-') {
51            (s, Sign::Minus)
52        } else {
53            (s, Sign::Plus)
54        };
55
56        let (integer_str, decimal_str) = if let Some((integer_str, decimal_str)) = s.split_once('.')
57        {
58            (integer_str, decimal_str)
59        } else {
60            (s, "")
61        };
62
63        if !integer_str.is_ascii() || !decimal_str.is_ascii() {
64            return Err(ParseDecimalError::invalid_digit());
65        }
66
67        let integer_bytes = integer_str.trim_start_matches('0').as_bytes();
68        let decimal_bytes = decimal_str.trim_end_matches('0').as_bytes();
69
70        let mut integer = Vec::with_capacity((integer_bytes.len() + (RADIX_LEN - 1)) / RADIX_LEN);
71        for chunk in integer_bytes.rchunks(18) {
72            let chunk = unsafe { std::str::from_utf8_unchecked(chunk) };
73            match chunk.parse::<u64>() {
74                Ok(val) => integer.push(val),
75                Err(_) => return Err(ParseDecimalError::invalid_digit()),
76            }
77        }
78
79        let mut decimal = Vec::with_capacity((decimal_bytes.len() + (RADIX_LEN - 1)) / RADIX_LEN);
80        for chunk in decimal_bytes.chunks(18) {
81            let chunk = unsafe { std::str::from_utf8_unchecked(chunk) };
82            match chunk.parse::<u64>() {
83                Ok(val) => decimal.push(val * POW10[RADIX_LEN - chunk.len()]),
84                Err(_) => return Err(ParseDecimalError::invalid_digit()),
85            }
86        }
87
88        let sign = if integer.is_empty() && decimal.is_empty() {
89            Sign::Zero
90        } else {
91            sign
92        };
93
94        Ok(Decimal {
95            sign,
96            integer,
97            decimal,
98        })
99    }
100}
101
102impl Display for Decimal {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        match self.sign {
105            Sign::Minus => write!(f, "-")?,
106            Sign::Zero => return write!(f, "0"),
107            Sign::Plus => {}
108        }
109
110        if let Some(last) = self.integer.last() {
111            write!(f, "{}", last)?;
112            for &val in self.integer.iter().rev().skip(1) {
113                write!(f, "{:018}", val)?;
114            }
115        } else {
116            write!(f, "0")?;
117        }
118
119        if let Some(last) = self.decimal.last() {
120            write!(f, ".")?;
121            for &val in self.decimal.iter().take(self.decimal.len() - 1) {
122                write!(f, "{:018}", val)?;
123            }
124            let mut l = 0;
125            let mut r = RADIX_LEN;
126            while r - l > 1 {
127                let m = (l + r) / 2;
128                if last % POW10[m] == 0 {
129                    l = m;
130                } else {
131                    r = m;
132                }
133            }
134            debug_assert!(last % POW10[l] == 0);
135            debug_assert!(r == RADIX_LEN || last % POW10[r] != 0);
136            write!(f, "{:0width$}", last / POW10[l], width = RADIX_LEN - l)?;
137        }
138
139        Ok(())
140    }
141}
142
143impl IterScan for Decimal {
144    type Output = Self;
145    fn scan<'a, I: Iterator<Item = &'a str>>(iter: &mut I) -> Option<Self::Output> {
146        iter.next()?.parse().ok()
147    }
148}
149
150macro_rules! impl_from_unsigned {
151    ($base:ty; $($t:ty)*) => {
152        $(
153            impl From<$t> for Decimal {
154                fn from(val: $t) -> Self {
155                    if val == 0 {
156                        return Decimal::zero();
157                    }
158                    let mut val = val as $base;
159                    let mut integer = Vec::new();
160                    while val > 0 {
161                        integer.push((val % RADIX as $base) as u64);
162                        val /= RADIX as $base;
163                    }
164                    Decimal {
165                        sign: Sign::Plus,
166                        integer,
167                        decimal: Vec::new(),
168                    }
169                }
170            }
171        )*
172    };
173}
174impl_from_unsigned!(u64; u8 u16 u32 u64 usize);
175impl_from_unsigned!(u128; u128);
176
177macro_rules! impl_from_signed {
178    ($base:ty; $($t:ty)*) => {
179        $(
180            impl From<$t> for Decimal {
181                fn from(val: $t) -> Self {
182                    let d = Decimal::from(val.unsigned_abs() as $base);
183                    if val.is_negative() {
184                        -d
185                    } else {
186                        d
187                    }
188                }
189            }
190        )*
191    };
192}
193impl_from_signed!(u64; i8 i16 i32 i64 isize);
194impl_from_signed!(u128; i128);
195
196macro_rules! impl_from_through_string {
197    ($($t:ty)*) => {
198        $(
199            impl From<$t> for Decimal {
200                fn from(val: $t) -> Self {
201                    val.to_string().parse().unwrap()
202                }
203            }
204        )*
205    };
206}
207impl_from_through_string!(f32 f64);
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212    use test_case::test_case;
213
214    #[test_case(
215        "0",
216        Ok(Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] });
217        "zero"
218    )]
219    #[test_case(
220        "1",
221        Ok(Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![] });
222        "plus integer"
223    )]
224    #[test_case(
225        "+1",
226        Ok(Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![] });
227        "plus integer with plus"
228    )]
229    #[test_case(
230        "-1",
231        Ok(Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![] });
232        "minus integer"
233    )]
234    #[test_case(
235        "1.2",
236        Ok(Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![200000000000000000] });
237        "plus decimal"
238    )]
239    #[test_case(
240        "-1.2",
241        Ok(Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![200000000000000000] });
242        "minus decimal"
243    )]
244    #[test_case(
245        "000000000000000000001.00000000000000000000",
246        Ok(Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![] });
247        "zero padding"
248    )]
249    #[test_case(
250        ".1",
251        Ok(Decimal { sign: Sign::Plus, integer: vec![], decimal: vec![100000000000000000] });
252        "without integer"
253    )]
254    #[test_case(
255        "12345678901234567890.12345678901234567890",
256        Ok(Decimal { sign: Sign::Plus, integer: vec![345678901234567890, 12], decimal: vec![123456789012345678, 900000000000000000] });
257        "long"
258    )]
259    #[test_case(
260        "",
261        Err(ParseDecimalError { kind: DecimalErrorKind::Empty });
262        "empty"
263    )]
264    #[test_case(
265        "a.012",
266        Err(ParseDecimalError { kind: DecimalErrorKind::InvalidDigit });
267        "invalid digit in integer"
268    )]
269    #[test_case(
270        "012.a",
271        Err(ParseDecimalError { kind: DecimalErrorKind::InvalidDigit });
272        "invalid digit in decimal"
273    )]
274    fn test_from_str(s: &str, expected: Result<Decimal, ParseDecimalError>) {
275        assert_eq!(expected, s.parse());
276    }
277
278    #[test_case(
279        Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] },
280        "0";
281        "zero"
282    )]
283    #[test_case(
284        Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![] },
285        "1";
286        "plus integer"
287    )]
288    #[test_case(
289        Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![] },
290        "-1";
291        "minus integer"
292    )]
293    #[test_case(
294        Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![200000000000000000] },
295        "1.2";
296        "plus decimal"
297    )]
298    #[test_case(
299        Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![200000000000000000] },
300        "-1.2";
301        "minus decimal"
302    )]
303    #[test_case(
304        Decimal { sign: Sign::Plus, integer: vec![], decimal: vec![100000000000000000] },
305        "0.1";
306        "without integer"
307    )]
308    #[test_case(
309        Decimal { sign: Sign::Plus, integer: vec![345678901234567890, 12], decimal: vec![123456789012345678, 900000000000000000] },
310        "12345678901234567890.1234567890123456789";
311        "long"
312    )]
313    #[test_case(
314        Decimal { sign: Sign::Plus, integer: vec![0], decimal: vec![1] },
315        "0.000000000000000001";
316        "small decimal"
317    )]
318    fn test_display(decimal: Decimal, expected: &str) {
319        assert_eq!(expected, decimal.to_string());
320    }
321
322    #[test_case(u8::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u8 zero")]
323    #[test_case(u8::MAX, Decimal { sign: Sign::Plus, integer: vec![255], decimal: vec![] }; "u8 max")]
324    #[test_case(u16::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u16 zero")]
325    #[test_case(u16::MAX, Decimal { sign: Sign::Plus, integer: vec![65535], decimal: vec![] }; "u16 max")]
326    #[test_case(u32::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u32 zero")]
327    #[test_case(u32::MAX, Decimal { sign: Sign::Plus, integer: vec![4294967295], decimal: vec![] }; "u32 max")]
328    #[test_case(u64::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u64 zero")]
329    #[test_case(u64::MAX, Decimal { sign: Sign::Plus, integer: vec![446744073709551615, 18], decimal: vec![] }; "u64 max")]
330    #[test_case(u128::MIN, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "u128 zero")]
331    #[test_case(u128::MAX, Decimal { sign: Sign::Plus, integer: vec![374607431768211455, 282366920938463463, 340], decimal: vec![] }; "u128 max")]
332    #[test_case(i8::MIN, Decimal { sign: Sign::Minus, integer: vec![128], decimal: vec![] }; "i8 min")]
333    #[test_case(0i8, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i8 zero")]
334    #[test_case(i8::MAX, Decimal { sign: Sign::Plus, integer: vec![127], decimal: vec![] }; "i8 max")]
335    #[test_case(i16::MIN, Decimal { sign: Sign::Minus, integer: vec![32768], decimal: vec![] }; "i16 min")]
336    #[test_case(0i16, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i16 zero")]
337    #[test_case(i16::MAX, Decimal { sign: Sign::Plus, integer: vec![32767], decimal: vec![] }; "i16 max")]
338    #[test_case(i32::MIN, Decimal { sign: Sign::Minus, integer: vec![2147483648], decimal: vec![] }; "i32 min")]
339    #[test_case(0i32, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i32 zero")]
340    #[test_case(i32::MAX, Decimal { sign: Sign::Plus, integer: vec![2147483647], decimal: vec![] }; "i32 max")]
341    #[test_case(i64::MIN, Decimal { sign: Sign::Minus, integer: vec![223372036854775808, 9], decimal: vec![] }; "i64 min")]
342    #[test_case(0i64, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i64 zero")]
343    #[test_case(i64::MAX, Decimal { sign: Sign::Plus, integer: vec![223372036854775807, 9], decimal: vec![] }; "i64 max")]
344    #[test_case(i128::MIN, Decimal { sign: Sign::Minus, integer: vec![687303715884105728, 141183460469231731, 170], decimal: vec![] }; "i128 min")]
345    #[test_case(0i128, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "i128 zero")]
346    #[test_case(i128::MAX, Decimal { sign: Sign::Plus, integer: vec![687303715884105727, 141183460469231731, 170], decimal: vec![] }; "i128 max")]
347    #[test_case(0f32, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "f32 zero")]
348    #[test_case(1.1f32, Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![100000000000000000] }; "f32 plus")]
349    #[test_case(-1.1f32, Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![100000000000000000] }; "f32 minus")]
350    #[test_case(0f64, Decimal { sign: Sign::Zero, integer: vec![], decimal: vec![] }; "f64 zero")]
351    #[test_case(1.1f64, Decimal { sign: Sign::Plus, integer: vec![1], decimal: vec![100000000000000000] }; "f64 plus")]
352    #[test_case(-1.1f64, Decimal { sign: Sign::Minus, integer: vec![1], decimal: vec![100000000000000000] }; "f64 minus")]
353    fn test_from(val: impl Into<Decimal>, expected: Decimal) {
354        assert_eq!(expected, val.into());
355    }
356}