1use crate::cbig::CBig;
9use core::str::FromStr;
10use dashu_base::ParseError;
11use dashu_float::round::Round;
12use dashu_float::{FBig, Repr};
13use dashu_int::Word;
14
15impl<R: Round, const B: Word> FromStr for CBig<R, B> {
16 type Err = ParseError;
17
18 fn from_str(s: &str) -> Result<Self, ParseError> {
19 let s = s.trim();
20 if s.is_empty() {
21 return Err(ParseError::NoDigits);
22 }
23 if s.contains('(') || s.contains(')') {
25 return Err(ParseError::InvalidDigit);
26 }
27
28 let i_count = s.bytes().filter(|&c| c == b'i').count();
30 if i_count > 1 {
31 return Err(ParseError::InvalidDigit);
32 }
33
34 if i_count == 0 {
35 let re = FBig::<R, B>::from_str(s)?;
37 return Ok(CBig::from(re));
38 }
39
40 if !s.ends_with('i') {
42 return Err(ParseError::InvalidDigit);
43 }
44 let prefix = &s[..s.len() - 1];
45
46 let split = prefix.rfind(['+', '-']).filter(|&pos| pos > 0);
50 let (real_str, imag_str) = match split {
51 Some(pos) => (&prefix[..pos], &prefix[pos..]),
52 None => ("", prefix),
53 };
54
55 let im = match imag_str {
56 "" | "+" => FBig::<R, B>::ONE,
57 "-" => FBig::<R, B>::NEG_ONE,
58 other => FBig::<R, B>::from_str(other)?,
59 };
60
61 let re = if real_str.is_empty() {
62 FBig::from_repr(Repr::zero(), im.context())
64 } else {
65 FBig::<R, B>::from_str(real_str)?
66 };
67
68 Ok(CBig::from_parts(re, im))
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use alloc::format;
76 use dashu_float::round::mode;
77
78 type C = CBig<mode::HalfAway, 10>;
79
80 fn parse_ok(s: &str) -> C {
81 s.parse()
82 .unwrap_or_else(|e| panic!("failed to parse {s:?}: {e:?}"))
83 }
84
85 #[test]
86 fn roundtrip_display_fromstr() {
87 let cases = [
88 "0", "5", "-7i", "i", "-i", "1+2i", "-3-4i", "1+i", "2-i", "4i",
89 ];
90 for s in cases {
91 let z: C = parse_ok(s);
92 assert_eq!(format!("{}", z), s, "roundtrip failed for {s:?}");
93 }
94 }
95
96 #[test]
97 fn pure_real() {
98 let z: C = "5".parse().unwrap();
99 assert!(z.im().is_pos_zero());
100 assert_eq!(z.re().significand(), &5.into());
101 }
102
103 #[test]
104 fn pure_imaginary_unit() {
105 let z: C = "i".parse().unwrap();
106 assert!(z.re().is_pos_zero());
107 assert_eq!(z.im().significand(), &1.into());
108
109 let z: C = "-i".parse().unwrap();
110 assert_eq!(z.im().significand(), &(-1i32).into());
111 }
112
113 #[test]
114 fn malformed_rejected() {
115 assert!("(1 2)".parse::<C>().is_err());
116 assert!("".parse::<C>().is_err());
117 assert!("1+2".parse::<C>().is_err()); assert!("ii".parse::<C>().is_err());
119 assert!("1+2ii".parse::<C>().is_err());
120 assert!("i5".parse::<C>().is_err()); }
122}