competitive/num/decimal/
convert.rs1use 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);