Skip to main content

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().div_ceil(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().div_ceil(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.midpoint(r);
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 Scan for Decimal {
144    type Output = Self;
145    fn scan<I: ScanSource>(iter: &mut I) -> Option<Self::Output> {
146        iter.next_token()?.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);