Skip to main content

litex/rational_expression/
evaluate.rs

1use crate::common::count_range_integer::{
2    count_closed_range_integer_endpoints, count_half_open_range_integer_endpoints,
3};
4use crate::prelude::*;
5use crate::rational_expression::evaluate_div::safe_div;
6
7impl Obj {
8    pub fn evaluate_to_normalized_decimal_number(&self) -> Option<Number> {
9        let result = match self {
10            Obj::Number(number) => Some(number.clone()),
11            Obj::Add(add) => {
12                let left_number = add.left.evaluate_to_normalized_decimal_number();
13                let right_number = add.right.evaluate_to_normalized_decimal_number();
14                if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
15                    let a = &left_number.normalized_value;
16                    let b = &right_number.normalized_value;
17                    let sum = if normalized_decimal_str_is_non_negative(a)
18                        && normalized_decimal_str_is_non_negative(b)
19                    {
20                        // `add_decimal_str_and_normalize` 仅适用于两操作数非负(见函数注释)
21                        add_decimal_str_and_normalize(a, b)
22                    } else {
23                        add_signed_decimal_str(a, b)
24                    };
25                    Some(Number::new(sum))
26                } else {
27                    None
28                }
29            }
30            Obj::Sub(sub) => {
31                let left_number = sub.left.evaluate_to_normalized_decimal_number();
32                let right_number = sub.right.evaluate_to_normalized_decimal_number();
33                if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
34                    let a = &left_number.normalized_value;
35                    let b = &right_number.normalized_value;
36                    let diff = if normalized_decimal_str_is_non_negative(a)
37                        && normalized_decimal_str_is_non_negative(b)
38                    {
39                        // `sub_decimal_str_and_normalize` 的竖式比较同样按非负量设计
40                        sub_decimal_str_and_normalize(a, b)
41                    } else {
42                        sub_signed_decimal_str(a, b)
43                    };
44                    Some(Number::new(diff))
45                } else {
46                    None
47                }
48            }
49            Obj::Mul(mul) => {
50                let left_number = mul.left.evaluate_to_normalized_decimal_number();
51                let right_number = mul.right.evaluate_to_normalized_decimal_number();
52                if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
53                    Some(Number::new(mul_signed_decimal_str(
54                        &left_number.normalized_value,
55                        &right_number.normalized_value,
56                    )))
57                } else {
58                    None
59                }
60            }
61            Obj::Mod(mod_obj) => {
62                let left_number = mod_obj.left.evaluate_to_normalized_decimal_number();
63                let right_number = mod_obj.right.evaluate_to_normalized_decimal_number();
64                if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
65                    Some(Number::new(mod_decimal_str_and_normalize(
66                        &left_number.normalized_value,
67                        &right_number.normalized_value,
68                    )))
69                } else {
70                    None
71                }
72            }
73            Obj::Pow(pow_obj) => {
74                let base_number = pow_obj.base.evaluate_to_normalized_decimal_number();
75                let exponent_number = pow_obj.exponent.evaluate_to_normalized_decimal_number();
76                if let (Some(base_number), Some(exponent_number)) = (base_number, exponent_number) {
77                    pow_decimal_str_and_normalize(
78                        &base_number.normalized_value,
79                        &exponent_number.normalized_value,
80                    )
81                    .map(Number::new)
82                } else {
83                    None
84                }
85            }
86            Obj::Div(div) => {
87                let left_number = div.left.evaluate_to_normalized_decimal_number();
88                let right_number = div.right.evaluate_to_normalized_decimal_number();
89                if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
90                    let exact_quotient_string = safe_div(
91                        &left_number.normalized_value,
92                        &right_number.normalized_value,
93                    );
94
95                    if let Some(exact_quotient_string) = exact_quotient_string {
96                        Some(Number::new(exact_quotient_string))
97                    } else {
98                        None
99                    }
100                } else {
101                    return None;
102                }
103            }
104            Obj::Abs(a) => match a.arg.evaluate_to_normalized_decimal_number() {
105                Some(inner) => {
106                    let s = inner.normalized_value.trim();
107                    if let Some(rest) = s.strip_prefix('-') {
108                        Some(Number::new(rest.trim().to_string()))
109                    } else {
110                        Some(inner)
111                    }
112                }
113                None => None,
114            },
115            Obj::Max(m) => {
116                let left_number = m.left.evaluate_to_normalized_decimal_number();
117                let right_number = m.right.evaluate_to_normalized_decimal_number();
118                if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
119                    let a = left_number.normalized_value.trim();
120                    let b = right_number.normalized_value.trim();
121                    let diff = sub_signed_decimal_str(a, b);
122                    let d = diff.trim();
123                    if d.starts_with('-') {
124                        Some(right_number)
125                    } else {
126                        Some(left_number)
127                    }
128                } else {
129                    None
130                }
131            }
132            Obj::Min(m) => {
133                let left_number = m.left.evaluate_to_normalized_decimal_number();
134                let right_number = m.right.evaluate_to_normalized_decimal_number();
135                if let (Some(left_number), Some(right_number)) = (left_number, right_number) {
136                    let a = left_number.normalized_value.trim();
137                    let b = right_number.normalized_value.trim();
138                    let diff = sub_signed_decimal_str(a, b);
139                    let d = diff.trim();
140                    if d.starts_with('-') || d == "0" {
141                        Some(left_number)
142                    } else {
143                        Some(right_number)
144                    }
145                } else {
146                    None
147                }
148            }
149            Obj::CartDim(cart_dim) => match &*cart_dim.set {
150                Obj::Cart(cart) => Some(Number::new(cart.args.len().to_string())),
151                _ => None,
152            },
153            Obj::TupleDim(tuple_dim) => match &*tuple_dim.arg {
154                Obj::Tuple(tuple) => Some(Number::new(tuple.args.len().to_string())),
155                _ => None,
156            },
157            Obj::Count(count) => match &*count.set {
158                Obj::ListSet(list_set) => Some(Number::new(list_set.list.len().to_string())),
159                Obj::ClosedRange(cr) => {
160                    let a = cr.start.evaluate_to_normalized_decimal_number()?;
161                    let b = cr.end.evaluate_to_normalized_decimal_number()?;
162                    count_closed_range_integer_endpoints(&a, &b)
163                }
164                Obj::Range(r) => {
165                    let a = r.start.evaluate_to_normalized_decimal_number()?;
166                    let b = r.end.evaluate_to_normalized_decimal_number()?;
167                    count_half_open_range_integer_endpoints(&a, &b)
168                }
169                // |A_1 × ... × A_n| = |A_1| * ... * |A_n|; empty product is 1.
170                Obj::Cart(cart) => {
171                    let mut acc = "1".to_string();
172                    for arg in cart.args.iter() {
173                        let factor_count = Obj::Count(Count::new((**arg).clone()))
174                            .evaluate_to_normalized_decimal_number()?;
175                        acc = mul_signed_decimal_str(
176                            acc.trim(),
177                            factor_count.normalized_value.trim(),
178                        );
179                    }
180                    Some(Number::new(acc))
181                }
182                _ => None,
183            },
184            _ => None,
185        };
186
187        match result {
188            Some(number) => Some(number),
189            None => None,
190        }
191    }
192
193    pub fn two_objs_can_be_calculated_and_equal_by_calculation(&self, other: &Obj) -> bool {
194        match (
195            self.evaluate_to_normalized_decimal_number(),
196            other.evaluate_to_normalized_decimal_number(),
197        ) {
198            (Some(left_number), Some(right_number)) => {
199                return left_number.normalized_value == right_number.normalized_value;
200            }
201            _ => return false,
202        }
203    }
204}
205
206/// 规范化后的十进制串是否表示非负数(无 `-` 前缀;`-0` 若已规范为 `0` 亦视为非负)。
207fn normalized_decimal_str_is_non_negative(s: &str) -> bool {
208    !s.trim().starts_with('-')
209}
210
211fn split_sign_and_magnitude(number_string: &str) -> (bool, String) {
212    let trimmed_number_string = number_string.trim();
213    if let Some(stripped_number_string) = trimmed_number_string.strip_prefix('-') {
214        (true, stripped_number_string.trim().to_string())
215    } else {
216        (false, trimmed_number_string.to_string())
217    }
218}
219
220pub fn mul_signed_decimal_str(left_number_string: &str, right_number_string: &str) -> String {
221    let (left_is_negative, left_magnitude_number_string) =
222        split_sign_and_magnitude(left_number_string);
223    let (right_is_negative, right_magnitude_number_string) =
224        split_sign_and_magnitude(right_number_string);
225    let multiplied_magnitude_number_string = mul_decimal_str_and_normalize(
226        &left_magnitude_number_string,
227        &right_magnitude_number_string,
228    );
229    let multiplied_magnitude_is_zero = multiplied_magnitude_number_string == "0";
230    let multiplied_result_is_negative = left_is_negative ^ right_is_negative;
231    if multiplied_result_is_negative && !multiplied_magnitude_is_zero {
232        normalize_decimal_number_string(&format!("-{}", multiplied_magnitude_number_string))
233    } else {
234        normalize_decimal_number_string(&multiplied_magnitude_number_string)
235    }
236}
237
238/// 带符号加法 a + b(系数合并用;`add_decimal_str_and_normalize` 仅适用于非负操作数)
239pub fn add_signed_decimal_str(a: &str, b: &str) -> String {
240    let (a_neg, a_mag) = split_sign_and_magnitude(a);
241    let (b_neg, b_mag) = split_sign_and_magnitude(b);
242    match (a_neg, b_neg) {
243        (false, false) => add_decimal_str_and_normalize(&a_mag, &b_mag),
244        (true, true) => {
245            let sum_mag = add_decimal_str_and_normalize(&a_mag, &b_mag);
246            if sum_mag == "0" {
247                "0".to_string()
248            } else {
249                normalize_decimal_number_string(&format!("-{}", sum_mag))
250            }
251        }
252        (false, true) => sub_decimal_str_and_normalize(&a_mag, &b_mag),
253        (true, false) => sub_decimal_str_and_normalize(&b_mag, &a_mag),
254    }
255}
256
257/// 带符号减法 a - b
258pub fn sub_signed_decimal_str(a: &str, b: &str) -> String {
259    add_signed_decimal_str(a, &mul_signed_decimal_str(b, "-1"))
260}
261
262impl Obj {
263    pub fn replace_with_numeric_result_if_can_be_calculated(&self) -> (Obj, bool) {
264        if let Some(calculated_number) = self.evaluate_to_normalized_decimal_number() {
265            (Obj::Number(calculated_number), true)
266        } else {
267            (self.clone(), false)
268        }
269    }
270}
271
272/// 竖式加法:两个表示非负数的数字串(可含小数点),返回和的字符串
273pub fn add_decimal_str_and_normalize(a: &str, b: &str) -> String {
274    let (mut int_a, mut frac_a) = parse_decimal_parts(a);
275    let (mut int_b, mut frac_b) = parse_decimal_parts(b);
276    let frac_len = frac_a.len().max(frac_b.len());
277    frac_a.resize(frac_len, 0);
278    frac_b.resize(frac_len, 0);
279    let int_len = int_a.len().max(int_b.len());
280    int_a.reverse();
281    int_b.reverse();
282    int_a.resize(int_len, 0);
283    int_b.resize(int_len, 0);
284
285    let mut out_frac = vec![0u8; frac_len];
286    let mut carry = 0u8;
287    for i in (0..frac_len).rev() {
288        let sum = frac_a[i] + frac_b[i] + carry;
289        out_frac[i] = sum % 10;
290        carry = sum / 10;
291    }
292    let mut out_int = Vec::with_capacity(int_len + 1);
293    for i in 0..int_len {
294        let sum = int_a[i] + int_b[i] + carry;
295        out_int.push(sum % 10);
296        carry = sum / 10;
297    }
298    if carry > 0 {
299        out_int.push(carry);
300    }
301    out_int.reverse();
302
303    let int_str: String = out_int.iter().map(|&d| (b'0' + d) as char).collect();
304    let frac_str: String = out_frac.iter().map(|&d| (b'0' + d) as char).collect();
305    let result = if frac_str.is_empty() || out_frac.iter().all(|&d| d == 0) {
306        int_str
307    } else {
308        format!("{}.{}", int_str, frac_str.trim_end_matches('0'))
309    };
310    normalize_decimal_number_string(&result)
311}
312
313/// 竖式减法:a - b,若 a >= b 返回非负结果字符串,否则返回 "-" + (b - a) 的字符串
314pub fn sub_decimal_str_and_normalize(a: &str, b: &str) -> String {
315    let (int_a, frac_a) = parse_decimal_parts(a);
316    let (int_b, frac_b) = parse_decimal_parts(b);
317    let frac_len = frac_a.len().max(frac_b.len());
318    let mut fa: Vec<u8> = frac_a.iter().cloned().collect();
319    let mut fb: Vec<u8> = frac_b.iter().cloned().collect();
320    fa.resize(frac_len, 0);
321    fb.resize(frac_len, 0);
322    let int_len = int_a.len().max(int_b.len());
323    let mut ia: Vec<u8> = int_a.iter().cloned().collect();
324    let mut ib: Vec<u8> = int_b.iter().cloned().collect();
325    ia.reverse();
326    ib.reverse();
327    ia.resize(int_len, 0);
328    ib.resize(int_len, 0);
329
330    let cmp = compare_decimal_parts(&ia, &fa, &ib, &fb);
331    let (top_int, top_frac, bot_int, bot_frac) = if cmp >= 0 {
332        (ia, fa, ib, fb)
333    } else {
334        let inner = sub_decimal_str_and_normalize(b, a);
335        return normalize_decimal_number_string(&format!("-{}", inner));
336    };
337
338    let mut out_frac = vec![0u8; frac_len];
339    let mut borrow: i16 = 0;
340    for i in (0..frac_len).rev() {
341        let mut d = top_frac[i] as i16 - bot_frac[i] as i16 - borrow;
342        borrow = 0;
343        if d < 0 {
344            d += 10;
345            borrow = 1;
346        }
347        out_frac[i] = d as u8;
348    }
349    let mut out_int = Vec::with_capacity(int_len);
350    for i in 0..int_len {
351        let mut d = top_int[i] as i16 - bot_int[i] as i16 - borrow;
352        borrow = 0;
353        if d < 0 {
354            d += 10;
355            borrow = 1;
356        }
357        out_int.push(d as u8);
358    }
359    out_int.reverse();
360    let start = out_int
361        .iter()
362        .position(|&d| d != 0)
363        .unwrap_or(out_int.len().saturating_sub(1));
364    let out_int = out_int[start..].to_vec();
365
366    let int_str: String = if out_int.is_empty() {
367        "0".to_string()
368    } else {
369        out_int.iter().map(|&d| (b'0' + d) as char).collect()
370    };
371    let frac_str: String = out_frac.iter().map(|&d| (b'0' + d) as char).collect();
372    let frac_trim = frac_str.trim_end_matches('0');
373    let result = if frac_trim.is_empty() {
374        int_str
375    } else {
376        format!("{}.{}", int_str, frac_trim)
377    };
378    normalize_decimal_number_string(&result)
379}
380
381fn compare_decimal_parts(int_a: &[u8], frac_a: &[u8], int_b: &[u8], frac_b: &[u8]) -> i32 {
382    let len_a = int_a.len();
383    let len_b = int_b.len();
384    if len_a != len_b {
385        return (len_a as i32) - (len_b as i32);
386    }
387    for i in (0..len_a).rev() {
388        if int_a[i] != int_b[i] {
389            return int_a[i] as i32 - int_b[i] as i32;
390        }
391    }
392    for i in 0..frac_a.len().max(frac_b.len()) {
393        let da = match frac_a.get(i) {
394            Some(&d) => d,
395            None => 0,
396        };
397        let db = match frac_b.get(i) {
398            Some(&d) => d,
399            None => 0,
400        };
401        if da != db {
402            return da as i32 - db as i32;
403        }
404    }
405    0
406}
407
408/// 竖式乘法:两个非负数字串,返回积的字符串(product[0]=个位,即最低位)
409pub fn mul_decimal_str_and_normalize(a: &str, b: &str) -> String {
410    let (int_a, frac_a) = parse_decimal_parts(a);
411    let (int_b, frac_b) = parse_decimal_parts(b);
412    let frac_places = frac_a.len() + frac_b.len();
413    let digits_a: Vec<u8> = int_a
414        .iter()
415        .cloned()
416        .chain(frac_a.iter().cloned())
417        .collect();
418    let digits_b: Vec<u8> = int_b
419        .iter()
420        .cloned()
421        .chain(frac_b.iter().cloned())
422        .collect();
423    let len_a = digits_a.len();
424    let len_b = digits_b.len();
425    let mut product = vec![0u32; len_a + len_b];
426    for (i, &da) in digits_a.iter().enumerate() {
427        for (j, &db) in digits_b.iter().enumerate() {
428            let place = (len_a - 1 - i) + (len_b - 1 - j);
429            product[place] += da as u32 * db as u32;
430        }
431    }
432    let mut carry = 0u32;
433    for p in product.iter_mut() {
434        *p += carry;
435        carry = *p / 10;
436        *p %= 10;
437    }
438    while carry > 0 {
439        product.push(carry % 10);
440        carry /= 10;
441    }
442    let total_len = product.len();
443    let int_part: String = if frac_places >= total_len {
444        "0".to_string()
445    } else {
446        product[frac_places..]
447            .iter()
448            .rev()
449            .map(|&d| (b'0' + d as u8) as char)
450            .collect::<String>()
451            .trim_start_matches('0')
452            .to_string()
453    };
454    let frac_part: String = if frac_places == 0 {
455        String::new()
456    } else {
457        product[..frac_places.min(total_len)]
458            .iter()
459            .rev()
460            .map(|&d| (b'0' + d as u8) as char)
461            .collect::<String>()
462            .trim_end_matches('0')
463            .to_string()
464    };
465    let int_str = if int_part.is_empty() { "0" } else { &int_part };
466    let result = if frac_part.is_empty() {
467        int_str.to_string()
468    } else {
469        format!("{}.{}", int_str, frac_part)
470    };
471    normalize_decimal_number_string(&result)
472}
473
474/// 竖式取余:a mod b,返回余数字符串。约定:b 仅为非零纯整数(字符串),a 取整数部分参与运算。
475pub fn mod_decimal_str_and_normalize(a: &str, b: &str) -> String {
476    let (int_a, _) = parse_decimal_parts(a);
477    let (int_b, _) = parse_decimal_parts(b);
478    let a_digits = trim_leading_zeros(&int_a);
479    let b_digits = trim_leading_zeros(&int_b);
480    if a_digits.is_empty() {
481        return "0".to_string();
482    }
483    if b_digits.is_empty() || (b_digits.len() == 1 && b_digits[0] == 0) {
484        return "0".to_string();
485    }
486    if compare_digits(&a_digits, &b_digits) == std::cmp::Ordering::Less {
487        return digits_to_string(&a_digits);
488    }
489    let mut current: Vec<u8> = vec![];
490    for &da in &a_digits {
491        current.push(da);
492        current = trim_leading_zeros(&current);
493        let mut d = 9u8;
494        loop {
495            let product = mul_digit(&b_digits, d);
496            if compare_digits(&current, &product) != std::cmp::Ordering::Less {
497                current = sub_digits(&current, &product);
498                break;
499            }
500            if d == 0 {
501                break;
502            }
503            d -= 1;
504        }
505    }
506    normalize_decimal_number_string(&digits_to_string(&current))
507}
508
509// Non-negative integer exponent only; fractional exp => None (no exact decimal fold).
510pub fn pow_decimal_str_and_normalize(base: &str, exp: &str) -> Option<String> {
511    let (exp_int, exp_frac) = parse_decimal_parts(exp);
512    if exp_frac.iter().any(|&d| d != 0) {
513        return None;
514    }
515    let mut n = 0usize;
516    for &d in &exp_int {
517        n = n.saturating_mul(10).saturating_add(d as usize);
518    }
519    if n == 0 {
520        return Some("1".to_string());
521    }
522    let mut acc = "1".to_string();
523    let mut b = base.to_string();
524    let mut e = n;
525    while e > 0 {
526        if e % 2 == 1 {
527            acc = mul_decimal_str_and_normalize(&acc, &b);
528        }
529        b = mul_decimal_str_and_normalize(&b, &b);
530        e /= 2;
531    }
532    Some(normalize_decimal_number_string(&acc))
533}
534
535fn trim_leading_zeros(d: &[u8]) -> Vec<u8> {
536    let start = d.iter().position(|&x| x != 0).unwrap_or(d.len());
537    d[start..].to_vec()
538}
539
540/// 数字序列转字符串(高位在前)
541fn digits_to_string(d: &[u8]) -> String {
542    let t = trim_leading_zeros(d);
543    if t.is_empty() {
544        return "0".to_string();
545    }
546    t.iter().map(|&x| (b'0' + x) as char).collect()
547}
548
549/// 大数乘一位数:b * d,0 <= d <= 9,返回各位(高位在前)
550fn mul_digit(b: &[u8], d: u8) -> Vec<u8> {
551    if d == 0 {
552        return vec![0];
553    }
554    let mut b = b.to_vec();
555    b.reverse();
556    let mut carry = 0u16;
557    for x in b.iter_mut() {
558        let p = *x as u16 * d as u16 + carry;
559        *x = (p % 10) as u8;
560        carry = p / 10;
561    }
562    while carry > 0 {
563        b.push((carry % 10) as u8);
564        carry /= 10;
565    }
566    b.reverse();
567    trim_leading_zeros(&b)
568}
569
570/// 比较两个“整数”数字序列(高位在前)
571fn compare_digits(a: &[u8], b: &[u8]) -> std::cmp::Ordering {
572    let a = trim_leading_zeros(a);
573    let b = trim_leading_zeros(b);
574    if a.len() != b.len() {
575        return a.len().cmp(&b.len());
576    }
577    for (x, y) in a.iter().zip(b.iter()) {
578        if x != y {
579            return x.cmp(y);
580        }
581    }
582    std::cmp::Ordering::Equal
583}
584
585/// 大数减法:要求 a >= b,返回 a - b 的各位(高位在前)
586fn sub_digits(a: &[u8], b: &[u8]) -> Vec<u8> {
587    let mut a = a.to_vec();
588    let mut b = b.to_vec();
589    let len = a.len().max(b.len());
590    a.reverse();
591    b.reverse();
592    a.resize(len, 0);
593    b.resize(len, 0);
594    let mut borrow: i16 = 0;
595    let mut out = Vec::with_capacity(len);
596    for i in 0..len {
597        let mut d = a[i] as i16 - b[i] as i16 - borrow;
598        borrow = 0;
599        if d < 0 {
600            d += 10;
601            borrow = 1;
602        }
603        out.push(d as u8);
604    }
605    out.reverse();
606    trim_leading_zeros(&out)
607}
608
609/// 化简结果:多个负号合并(---1.1 -> -1.1)、0.0或者-0 写成 0、小数尾零去掉(1.000 -> 1)
610pub fn normalize_decimal_number_string(s: &str) -> String {
611    let s = s.trim();
612    if s.is_empty() {
613        return "0".to_string();
614    }
615    let minus_count = s.chars().take_while(|&c| c == '-').count();
616    let rest = s[minus_count..].trim();
617    let negative = (minus_count % 2) == 1;
618
619    let magnitude = if rest.contains('.') {
620        let (int_str, frac_str) = rest.split_once('.').unwrap_or((rest, ""));
621        let frac_trimmed = frac_str.trim_end_matches('0');
622        let int_trimmed = int_str.trim_start_matches('0');
623        let int_part = if int_trimmed.is_empty() || int_trimmed == "." {
624            "0"
625        } else {
626            int_trimmed
627        };
628        if frac_trimmed.is_empty() {
629            int_part.to_string()
630        } else {
631            format!("{}.{}", int_part, frac_trimmed)
632        }
633    } else {
634        let t = rest.trim_start_matches('0');
635        if t.is_empty() { "0" } else { t }.to_string()
636    };
637
638    let is_zero = magnitude == "0"
639        || (magnitude.starts_with("0.") && magnitude[2..].chars().all(|c| c == '0'));
640    if is_zero {
641        "0".to_string()
642    } else if negative {
643        format!("-{}", magnitude)
644    } else {
645        magnitude
646    }
647}
648
649/// 解析数字串为 (整数部分数字, 小数部分数字),允许 "123.45"、"123"、".5"、"0.5"
650fn parse_decimal_parts(s: &str) -> (Vec<u8>, Vec<u8>) {
651    let s = s.trim();
652    let (int_str, frac_str) = match s.find('.') {
653        Some(i) => (&s[..i], &s[i + 1..]),
654        None => (s, ""),
655    };
656    let int_digits: Vec<u8> = if int_str.is_empty() || int_str == "-" {
657        vec![0]
658    } else {
659        int_str
660            .chars()
661            .filter(|c| c.is_ascii_digit())
662            .map(|c| c as u8 - b'0')
663            .collect()
664    };
665    let frac_digits: Vec<u8> = frac_str
666        .chars()
667        .filter(|c| c.is_ascii_digit())
668        .map(|c| c as u8 - b'0')
669        .collect();
670    let int_digits = if int_digits.is_empty() {
671        vec![0]
672    } else {
673        int_digits
674    };
675    (int_digits, frac_digits)
676}