1use crate::Base;
7use crate::{Arbi, Digit};
8use core::fmt;
9use core::str::FromStr;
10
11#[derive(Debug, PartialEq, Eq)]
35pub enum ParseError {
36 InvalidDigit,
38 Empty,
40}
41
42impl fmt::Display for ParseError {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::InvalidDigit => {
46 write!(f, "Invalid digit found for the provided base")
47 }
48 Self::Empty => {
49 write!(f, "The provided string is empty")
50 }
51 }
52 }
53}
54
55#[cfg(feature = "std")]
56impl std::error::Error for ParseError {}
57
58#[cfg(feature = "std")]
59#[cfg(test)]
60mod std_tests {
61 use super::ParseError;
62 use std::error::Error;
63
64 #[test]
65 fn test_error_trait() {
66 let err: ParseError = ParseError::InvalidDigit;
67 assert!(err.source().is_none());
68 }
69}
70
71const fn pow(base: Digit, exp: usize) -> Digit {
73 let mut result = 1;
75 let mut i = 0;
76 while i < exp {
77 result *= base;
78 i += 1;
79 }
80 result
81}
82
83#[derive(Copy, Clone)]
84pub(crate) struct BaseMbs {
85 pub(crate) mbs: usize,
87 pub(crate) base_pow_mbs: Digit,
89}
90
91pub(crate) mod configs {
93 use super::{pow, BaseMbs, Digit};
94 use crate::DDigit;
95
96 pub(crate) const BASE_MBS: [BaseMbs; 37] = compute_base_mbs();
97
98 const fn compute_base_mbs() -> [BaseMbs; 37] {
101 let mut base_mbs = [BaseMbs {
102 mbs: 0,
103 base_pow_mbs: 0,
104 }; 37];
105 let mut base = 2;
106 while base <= 36 {
107 let mut max_batch_size = 0;
109 let mut b = base as DDigit;
110 while b < Digit::MAX as DDigit {
111 b *= base as DDigit;
112 max_batch_size += 1;
113 }
114
115 base_mbs[base] = BaseMbs {
116 mbs: max_batch_size,
117 base_pow_mbs: pow(base as Digit, max_batch_size),
118 };
119 base += 1;
120 }
121 base_mbs
122 }
123}
124
125impl Arbi {
126 pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseError> {
135 let base: Base = match radix.try_into() {
136 Err(_) => panic!("`radix` is not an integer in [2, 36]"),
137 Ok(b) => b,
138 };
139
140 Arbi::from_str_base(src, base)
141 }
142
143 pub fn from_str_base(s: &str, base: Base) -> Result<Self, ParseError> {
152 let mut res = Self::new();
153 res.assign_str_base(s, base)?;
154 Ok(res)
155 }
156}
157
158#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::BaseError;
164 use alloc::string::{String, ToString};
165
166 #[test]
167 fn test_from_str_base_invalid_base() {
168 for (_, base) in &[("0", 0), ("0", 1), ("0", 37)] {
169 assert!(matches!(
170 Base::try_from(*base),
171 Err(BaseError::InvalidBase)
172 ));
173 }
174 }
175
176 #[test]
177 fn test_parse_error() {
178 assert!(matches!(
179 Arbi::from_str_base("", 10.try_into().unwrap()),
180 Err(ParseError::Empty)
181 ));
182 assert!(matches!(
183 Arbi::from_str_base(" ", 10.try_into().unwrap()),
184 Err(ParseError::InvalidDigit)
185 ));
186 for s in &[" -", " -"] {
187 assert!(matches!(
188 Arbi::from_str_base(s, 10.try_into().unwrap()),
189 Err(ParseError::InvalidDigit)
190 ));
191 }
192 }
193
194 #[test]
195 fn test_parse_error_invalid_digit() {
196 assert!(matches!(
197 Arbi::from_str_radix("-00A1", 2),
198 Err(ParseError::InvalidDigit)
199 ));
200 }
201
202 #[test]
203 fn test_from_str_base_small_strings() {
204 for (s, expected) in &[
205 ("0", 0),
206 ("-0", 0),
207 ("+0", 0),
208 ("987", 987),
209 ("-987", -987),
210 ("+987", 987),
211 ("+00100", 100),
212 ("+000000", 0),
213 ("18446744073709551615", 18446744073709551615_i128),
214 ] {
215 let arbi = Arbi::from_str_base(s, 10.try_into().unwrap()).unwrap();
216 assert_eq!(arbi, *expected);
217 }
218 }
219
220 fn test_construct_from_string(base: Base) {
221 use crate::to_string::tests::ToStringBase;
222 use crate::util::test::{
223 get_seedable_rng, get_uniform_die, Distribution,
224 };
225 use crate::{DDigit, QDigit, SDDigit, SDigit, SQDigit};
226
227 for x in [
228 0 as SQDigit,
229 Digit::MAX as SQDigit,
230 DDigit::MAX as SQDigit,
231 SDigit::MIN as SQDigit,
232 SDDigit::MIN as SQDigit,
233 SDigit::MAX as SQDigit,
234 SDDigit::MAX as SQDigit,
235 SQDigit::MIN,
236 SQDigit::MAX,
237 ] {
238 assert_eq!(
239 Arbi::from_str_base(&x.to_string_base(base), base).unwrap(),
240 x,
241 );
242 }
243
244 assert_eq!(
245 Arbi::from_str_base(&QDigit::MAX.to_string_base(base), base)
246 .unwrap(),
247 QDigit::MAX
248 );
249
250 let (mut rng, _) = get_seedable_rng();
251
252 let die = get_uniform_die(SQDigit::MIN, SQDigit::MAX);
253 let die_16 = get_uniform_die(i16::MIN, i16::MAX);
254 for _ in 0..i16::MAX {
255 let rv = die.sample(&mut rng);
256 let s = rv.to_string_base(base);
257 assert_eq!(Arbi::from_str_base(&s, base).unwrap(), rv);
258
259 let rv_16 = die_16.sample(&mut rng);
260 let s = rv_16.to_string_base(base);
261 assert_eq!(Arbi::from_str_base(&s, base).unwrap(), rv_16);
262 }
263 }
264
265 #[test]
266 fn test_construct_from_string_() {
267 for i in 2..=36 {
268 test_construct_from_string(i.try_into().unwrap());
269 }
270 }
271
272 #[test]
273 fn test_from_str_base_large_string() {
274 use crate::base::DEC;
275
276 let mut s: String = "\
277 9999090900932323293029323092309309232309920940294242043232099290329\
278 3029320312904092384092384903840928490320948239048903284920139402030\
279 2309402934092304904902394029340932049302942313094874981748920028034\
280 3298980495840298502480239483390099999029100293809809809809099923420\
281 "
282 .into();
283
284 assert_eq!(Arbi::from_str_base(&s, DEC).unwrap().to_string(), s);
285
286 s.insert(0, '-');
287 let a = Arbi::from_str_base(&s, DEC).unwrap();
288 assert!(a.is_negative());
289 assert_eq!(a.to_string(), s);
290 }
291
292 #[test]
293 fn test_from_str_radix() {
294 let a = Arbi::from_str_radix("-987654321", 10).unwrap();
295 assert_eq!(a, -987654321);
296
297 let b =
298 Arbi::from_str_radix("10001101001111000000111000010101011011", 2)
299 .unwrap();
300 assert_eq!(b, 151649486171_i64);
301 }
302
303 #[test]
304 fn test_from_str() {
305 let a = Arbi::from_str("-987654321").unwrap();
306 assert_eq!(a, -987654321);
307 }
308
309 #[test]
310 fn test_str_parse() {
311 let a: Arbi = "-987654321".parse().unwrap();
312 assert_eq!(a, -987654321);
313
314 let a = "123456789".parse::<Arbi>().unwrap();
315 assert_eq!(a, 123456789);
316
317 assert!(matches!(
318 "12A456789".parse::<Arbi>(),
319 Err(ParseError::InvalidDigit)
320 ));
321 assert!(matches!(
322 " -".parse::<Arbi>(),
323 Err(ParseError::InvalidDigit)
324 ));
325
326 let a = "\
327 123456789012345678901234567890123456789043909809801329009092930\
328 ";
329 assert_eq!(a.parse::<Arbi>().unwrap().to_string(), a);
330
331 let a = "\
332 -12345678901234567890123456789012345678909098909809802340982349\
333 ";
334 assert_eq!(a.parse::<Arbi>().unwrap().to_string(), a);
335
336 assert_eq!("0".parse::<Arbi>().unwrap(), 0);
337 }
338}
339
340impl FromStr for Arbi {
341 type Err = ParseError;
342
343 fn from_str(src: &str) -> Result<Self, Self::Err> {
355 Arbi::from_str_radix(src, 10)
356 }
357}