Skip to main content

factorion_lib/
parse.rs

1//! Parses text and extracts calculations
2
3use crate::locale::NumFormat;
4use crate::rug::{Complete, Float, Integer, integer::IntegerExt64};
5
6use crate::Consts;
7use crate::{
8    calculation_results::Number,
9    calculation_tasks::{CalculationBase, CalculationJob},
10};
11
12pub mod recommended {
13    use factorion_math::rug::Integer;
14
15    pub static INTEGER_CONSTRUCTION_LIMIT: fn() -> Integer = || 200_000_000u128.into();
16}
17
18const POI_STARTS: &[char] = &[
19    NEGATION,
20    '!', // PREFIX_OPS
21    '.', // Decimal separators
22    ESCAPE,
23    '0', // Digits
24    '1',
25    '2',
26    '3',
27    '4',
28    '5',
29    '6',
30    '7',
31    '8',
32    '9',
33    'p', // Constants
34    'e',
35    't',
36    'i',
37    'π',
38    'ɸ',
39    'τ',
40    '∞',
41    '^', // Tetration
42    URI_POI,
43    SPOILER_POI,
44    SPOILER_HTML_POI,
45    PAREN_START,
46    PAREN_END,
47];
48
49const NEGATION: char = '-';
50const PAREN_START: char = '(';
51const PAREN_END: char = ')';
52const ESCAPE: char = '\\';
53const URI_START: &str = "://";
54const URI_POI: char = ':';
55const SPOILER_START: &str = ">!";
56const SPOILER_END: &str = "!<";
57const SPOILER_POI: char = '>';
58const SPOILER_HTML_START: &str = "&gt;!";
59const SPOILER_HTML_END: &str = "!&lt;";
60const SPOILER_HTML_POI: char = '&';
61
62const CONSTANT_STARTS: &[char] = &['p', 'e', 't', 'i', 'π', 'ɸ', 'τ', '∞'];
63static E: fn(u32) -> Number = |prec| Number::Float(Float::with_val(prec, 1).exp().into());
64static PHI: fn(u32) -> Number = |prec| {
65    Number::Float(Float::into(
66        ((1.0 + Float::with_val(prec, 5).sqrt()) as Float) / 2.0,
67    ))
68};
69static PI: fn(u32) -> Number =
70    |prec| Number::Float(Float::with_val(prec, crate::rug::float::Constant::Pi).into());
71static TAU: fn(u32) -> Number = |prec| {
72    Number::Float(Float::into(
73        Float::with_val(prec, crate::rug::float::Constant::Pi) * 2.0,
74    ))
75};
76
77const SEPARATORS: [char; 4] = ['.', ',', '_', '\''];
78
79const PREFIX_OPS: [char; 1] = ['!'];
80#[allow(dead_code)]
81const POSTFIX_OPS: [char; 2] = ['!', '?'];
82
83const INTEGER_ONLY_OPS: [i32; 1] = [0];
84
85pub fn parse(
86    mut text: &str,
87    do_termial: bool,
88    consts: &Consts,
89    locale: &NumFormat,
90) -> Vec<CalculationJob> {
91    // Parsing rules:
92    // - prefix has precedence before suffix (unimplemented)
93    // - anything within a spoiler should be ignored
94    // - operations may be nested through parentheses
95    // - operations can be negated through -
96    // - parens may contain:
97    //   - numbers
98    //   - operations
99    //   - parens
100    //   - whitespace
101    // - operations are:
102    //   - subfactorials !n
103    //   - (multi-)factorials n!+
104    //   - termials n?
105    // - numbers are in order:
106    //   - a string of digits
107    //   - a decimal separator and further digits
108    //   - a base 10 exponent, which is:
109    //     - an e or E followed by
110    //     - optionally a + or -
111    //     - a string of digits
112    // - numbers need to at least have the first or second criteria
113
114    // Parsing:
115    // 1. skip to interesting
116    // 2. If spoiler, skip
117    // 3. If negation, save
118    // 4. If paren start, push (with negation)
119    // 5. If paren end, pop
120    //   1. If had prefix, use base, set base
121    //   2. If has postfix, use base, set base
122    // 6. If prefix
123    //   1. If on number, set base
124    //     1. If has postfix, use base, set base
125    //   2. If on paren, push (with negation and level)
126    // 7. If number, parse
127    //   1. If has postfix, set base
128    //   2. If in parens, set as base
129    // 8. If on toplevel, add base to jobs
130    //
131    // when setting base:
132    // 1. If base is set, add previous to jobs
133    // 2. override base
134    let mut jobs = Vec::new();
135    let mut base: Option<CalculationBase> = None;
136    let mut paren_steps: Vec<(u32, Option<i32>, bool)> = Vec::new();
137    let mut current_negative: u32 = 0;
138    let mut last_len = usize::MAX;
139    let mut had_text_before = false;
140    while !text.is_empty() {
141        if last_len == text.len() {
142            panic!("Parser caught in a loop! Text: \"{text}\"")
143        }
144        last_len = text.len();
145
146        text = text.trim_start();
147        if text.len() != last_len {
148            current_negative = 0;
149            had_text_before = false;
150        }
151        // Text (1.)
152        let Some(position_of_interest) = text.find(POI_STARTS) else {
153            break;
154        };
155        if position_of_interest != 0 {
156            // poison paren
157            if let Some(step) = paren_steps.last_mut() {
158                step.2 = true;
159            }
160            current_negative = 0;
161            had_text_before = false;
162        }
163        let had_text =
164            text[..position_of_interest].ends_with(char::is_alphabetic) || had_text_before;
165        had_text_before = false;
166        // so we can just ignore everything before
167        text = &text[position_of_interest..];
168        if text.starts_with(ESCAPE) {
169            // Escapes
170            text = &text[1..];
171            let end = if text.starts_with(SPOILER_START) {
172                1
173            } else if text.starts_with(SPOILER_HTML_START) {
174                4
175            } else if text.starts_with(URI_START) {
176                3
177            } else {
178                0
179            };
180            text = &text[end..];
181            continue;
182        } else if text.starts_with(URI_START) {
183            // URI
184            let end = text.find(char::is_whitespace).unwrap_or(text.len());
185            text = &text[end..];
186            continue;
187        } else if text.starts_with(SPOILER_START) {
188            // Spoiler (2.)
189            let mut end = 0;
190            loop {
191                // look for next end tag
192                if let Some(e) = text[end..].find(SPOILER_END) {
193                    if e == 0 {
194                        panic!("Parser loop Spoiler! Text \"{text}\"");
195                    }
196                    end += e;
197                    // is escaped -> look further
198                    if text[end.saturating_sub(1)..].starts_with(ESCAPE) {
199                        end += 1;
200                        continue;
201                    }
202                    break;
203                } else {
204                    // if we find none, we skip only the start (without the !)
205                    end = 0;
206                    break;
207                }
208            }
209            current_negative = 0;
210            text = &text[end + 1..];
211            continue;
212        } else if text.starts_with(SPOILER_HTML_START) {
213            // Spoiler (html) (2.)
214            let mut end = 0;
215            loop {
216                // look for next end tag
217                if let Some(e) = text[end..].find(SPOILER_HTML_END) {
218                    if e == 0 {
219                        panic!("Parser loop Spoiler! Text \"{text}\"");
220                    }
221                    end += e;
222                    // is escaped -> look further
223                    if text[end.saturating_sub(1)..].starts_with(ESCAPE) {
224                        end += 1;
225                        continue;
226                    }
227                    break;
228                } else {
229                    // if we find none, we skip only the start (without the !)
230                    end = 0;
231                    break;
232                }
233            }
234            current_negative = 0;
235            text = &text[end + 4..];
236            continue;
237        } else if text.starts_with(NEGATION) {
238            // Negation (3.)
239            let end = text.find(|c| c != NEGATION).unwrap_or(text.len());
240            current_negative = end as u32;
241            text = &text[end..];
242            continue;
243        } else if text.starts_with(PAREN_START) {
244            // Paren Start (without prefix op) (4.)
245            paren_steps.push((current_negative, None, false));
246            // Submit current base (we won't use it anymore)
247            if let Some(CalculationBase::Calc(job)) = base.take() {
248                jobs.push(*job);
249            }
250            current_negative = 0;
251            text = &text[1..];
252            continue;
253        } else if text.starts_with(PAREN_END) {
254            // Paren End (5.)
255            text = &text[1..];
256            current_negative = 0;
257            // Paren mismatch?
258            let Some(step) = paren_steps.pop() else {
259                continue;
260            };
261            // poisoned paren
262            if step.2 {
263                if let Some(CalculationBase::Calc(job)) = base.take() {
264                    jobs.push(*job);
265                }
266                // no number (maybe var) => poison outer paren
267                if let Some(step) = paren_steps.last_mut() {
268                    step.2 = true;
269                }
270                continue;
271            }
272            let mut had_op = false;
273            // Prefix? (5.2.)
274            if let Some(level) = step.1 {
275                // base available?
276                let Some(inner) = base.take() else {
277                    // no number (maybe var) => poison outer paren
278                    if let Some(step) = paren_steps.last_mut() {
279                        step.2 = true;
280                    }
281                    continue;
282                };
283                if let (CalculationBase::Num(Number::Float(_)), true) =
284                    (&inner, INTEGER_ONLY_OPS.contains(&level))
285                {
286                    continue;
287                }
288                base = Some(CalculationBase::Calc(Box::new(CalculationJob {
289                    base: inner,
290                    level,
291                    negative: 0,
292                })));
293                had_op = true;
294            }
295            // Postfix? (5.1.)
296            let Some(levels) = parse_ops(&mut text, false, do_termial) else {
297                base.take();
298                // no number (maybe var) => poison outer paren
299                if let Some(step) = paren_steps.last_mut() {
300                    step.2 = true;
301                }
302                continue;
303            };
304            if !levels.is_empty() {
305                // Set as base (5.1.2.)
306                for level in levels {
307                    // base available?
308                    let Some(inner) = base.take() else {
309                        continue;
310                    };
311                    base = Some(CalculationBase::Calc(Box::new(CalculationJob {
312                        base: inner,
313                        level,
314                        negative: 0,
315                    })));
316                    had_op = true;
317                }
318            }
319            if !had_op {
320                match &mut base {
321                    Some(CalculationBase::Calc(job)) => job.negative += step.0,
322                    Some(CalculationBase::Num(n)) => {
323                        if step.0 % 2 != 0 {
324                            n.negate();
325                        }
326                    }
327                    None => {}
328                }
329            } else {
330                match &mut base {
331                    Some(CalculationBase::Num(n)) => {
332                        if step.0 % 2 == 1 {
333                            n.negate();
334                        }
335                    }
336                    Some(CalculationBase::Calc(job)) => job.negative += step.0,
337                    None => {
338                        // no number (maybe var) => poison outer paren
339                        if let Some(step) = paren_steps.last_mut() {
340                            step.2 = true;
341                        }
342                    }
343                }
344                continue;
345            };
346        } else if text.starts_with(PREFIX_OPS) {
347            // Prefix OP (6.)
348            let Ok(level) = parse_op(&mut text, true, do_termial) else {
349                // also skip number to prevent stuff like "!!!1!" getting through
350                parse_num(&mut text, false, true, consts, locale);
351                continue;
352            };
353            // On number (6.1.)
354            if let Some(num) = parse_num(&mut text, false, true, consts, locale) {
355                // set base (6.1.2.)
356                if let Some(CalculationBase::Calc(job)) = base.take() {
357                    // multiple number, likely expression => poision paren
358                    if let Some(step) = paren_steps.last_mut() {
359                        step.2 = true;
360                    }
361                    jobs.push(*job);
362                }
363                if let (Number::Float(_), true) = (&num, INTEGER_ONLY_OPS.contains(&level)) {
364                    continue;
365                }
366                base = Some(CalculationBase::Calc(Box::new(CalculationJob {
367                    base: CalculationBase::Num(num),
368                    level,
369                    negative: current_negative,
370                })));
371                current_negative = 0;
372                let Some(levels) = parse_ops(&mut text, false, do_termial) else {
373                    continue;
374                };
375                for level in levels {
376                    // base available?
377                    let Some(inner) = base.take() else {
378                        continue;
379                    };
380                    base = Some(CalculationBase::Calc(Box::new(CalculationJob {
381                        base: inner,
382                        level,
383                        negative: 0,
384                    })));
385                }
386            } else {
387                // on paren? (6.2.)
388                if text.starts_with(PAREN_START) {
389                    paren_steps.push((current_negative, Some(level), false));
390                    current_negative = 0;
391                    text = &text[1..];
392                }
393                continue;
394            };
395        } else {
396            // Number (7.)
397            if text.starts_with('.') && !text[1..].starts_with(char::is_numeric) {
398                // Is a period
399                text = &text[1..];
400                continue;
401            }
402            let Some(num) = parse_num(&mut text, had_text, false, consts, locale) else {
403                had_text_before = true;
404                // advance one char to avoid loop
405                let mut end = 1;
406                while !text.is_char_boundary(end) && end < text.len() {
407                    end += 1;
408                }
409                text = &text[end.min(text.len())..];
410                continue;
411            };
412            // postfix? (7.1.)
413            let Some(levels) = parse_ops(&mut text, false, do_termial) else {
414                continue;
415            };
416            if !levels.is_empty() {
417                let levels = levels.into_iter();
418                if let Some(CalculationBase::Calc(job)) = base.take() {
419                    // multiple number, likely expression => poision paren
420                    if let Some(step) = paren_steps.last_mut() {
421                        step.2 = true;
422                    }
423                    jobs.push(*job);
424                }
425                base = Some(CalculationBase::Num(num));
426                for level in levels {
427                    let previous = base.take().unwrap();
428                    if let (CalculationBase::Num(Number::Float(_)), true) =
429                        (&previous, INTEGER_ONLY_OPS.contains(&level))
430                    {
431                        continue;
432                    }
433                    base = Some(CalculationBase::Calc(Box::new(CalculationJob {
434                        base: previous,
435                        level,
436                        negative: 0,
437                    })))
438                }
439                if let Some(CalculationBase::Calc(job)) = &mut base {
440                    job.negative = current_negative;
441                }
442            } else {
443                // in parens? (7.2.)
444                if !paren_steps.is_empty() {
445                    let mut num = num;
446                    if current_negative % 2 == 1 {
447                        num.negate();
448                    }
449
450                    if base.is_none() {
451                        base = Some(CalculationBase::Num(num))
452                    } else {
453                        // multiple number, likely expression => poision paren
454                        if let Some(step) = paren_steps.last_mut() {
455                            step.2 = true;
456                        }
457                    }
458                }
459            }
460            current_negative = 0;
461        };
462        // toplevel? (8.)
463        if paren_steps.is_empty()
464            && let Some(CalculationBase::Calc(job)) = base.take()
465        {
466            jobs.push(*job);
467        }
468    }
469    if let Some(CalculationBase::Calc(job)) = base.take() {
470        jobs.push(*job);
471    }
472    jobs.sort();
473    jobs.dedup();
474    jobs
475}
476
477enum ParseOpErr {
478    NonOp,
479    InvalidOp,
480}
481
482fn parse_op(text: &mut &str, prefix: bool, do_termial: bool) -> Result<i32, ParseOpErr> {
483    let op = text.chars().next().ok_or(ParseOpErr::NonOp)?;
484    let end = text.find(|c| c != op).unwrap_or(text.len());
485    let res = match op {
486        '!' => {
487            if prefix {
488                if end != 1 {
489                    Err(ParseOpErr::InvalidOp)
490                } else {
491                    Ok(0)
492                }
493            } else {
494                Ok(end as i32)
495            }
496        }
497        '?' => {
498            if !do_termial {
499                Err(ParseOpErr::NonOp)
500            } else if prefix {
501                Err(ParseOpErr::InvalidOp)
502            } else {
503                Ok(-(end as i32))
504            }
505        }
506        _ => return Err(ParseOpErr::NonOp),
507    };
508    *text = &text[end..];
509    res
510}
511
512fn parse_ops(text: &mut &str, prefix: bool, do_termial: bool) -> Option<Vec<i32>> {
513    let mut res = Vec::new();
514    loop {
515        match parse_op(text, prefix, do_termial) {
516            Ok(op) => res.push(op),
517            Err(ParseOpErr::NonOp) => break,
518            Err(ParseOpErr::InvalidOp) => return None,
519        }
520    }
521    Some(res)
522}
523
524fn parse_num(
525    text: &mut &str,
526    had_text: bool,
527    had_op: bool,
528    consts: &Consts,
529    locale: &NumFormat,
530) -> Option<Number> {
531    let prec = consts.float_precision;
532    if text.starts_with(CONSTANT_STARTS) {
533        let (n, x) = if text.starts_with("pi") {
534            ("pi".len(), PI(prec))
535        } else if text.starts_with("π") {
536            ("π".len(), PI(prec))
537        } else if text.starts_with("phi") {
538            ("phi".len(), PHI(prec))
539        } else if text.starts_with("ɸ") {
540            ("ɸ".len(), PHI(prec))
541        } else if text.starts_with("tau") {
542            ("tau".len(), TAU(prec))
543        } else if text.starts_with("τ") {
544            ("τ".len(), TAU(prec))
545        } else if text.starts_with("e") {
546            ("e".len(), E(prec))
547        } else if text.starts_with("infinity") {
548            ("infinity".len(), Number::ComplexInfinity)
549        } else if text.starts_with("inf") {
550            ("inf".len(), Number::ComplexInfinity)
551        } else if text.starts_with("∞\u{303}") {
552            ("∞\u{303}".len(), Number::ComplexInfinity)
553        } else if text.starts_with("∞") {
554            ("∞".len(), Number::ComplexInfinity)
555        } else {
556            return None;
557        };
558        if had_text || text[n..].starts_with(char::is_alphabetic) {
559            return None;
560        }
561        *text = &text[n..];
562        return Some(x);
563    }
564
565    if text.starts_with("^(") {
566        let orig_text = &text[..];
567        *text = &text[2..];
568        let end = text
569            .find(|c: char| (!c.is_numeric() && !SEPARATORS.contains(&c)) || c == locale.decimal)
570            .unwrap_or(text.len());
571        let part = &text[..end];
572        *text = &text[end..];
573        if text.starts_with(")10")
574            && !text[3..].starts_with(POSTFIX_OPS)
575            && !text[3..].starts_with(char::is_numeric)
576            // Intentionally not allowing decimal
577            && !(text[3..].starts_with(SEPARATORS) && text[4..].starts_with(char::is_numeric))
578        {
579            *text = &text[3..];
580            let part = part.replace(SEPARATORS, "");
581            let n = part.parse::<Integer>().ok()?;
582            match n.to_usize() {
583                Some(0) => return Some(1.into()),
584                Some(1) => return Some(10.into()),
585                Some(2) => return Some(Number::Exact(10_000_000_000u64.into())),
586                _ => {
587                    return Some(Number::ApproximateDigitsTower(
588                        false,
589                        false,
590                        n - 1,
591                        1.into(),
592                    ));
593                }
594            }
595        } else {
596            // Skip ^ only (because ret None)
597            *text = orig_text;
598            return None;
599        }
600    }
601
602    if text.starts_with("10^") || text.starts_with("10\\^") {
603        let orig_text = &text[..];
604        if had_op {
605            if &text[2..3] == "^" {
606                *text = &text[3..];
607            } else {
608                *text = &text[4..];
609            }
610            // Don't skip power if op won't be in exponent, so it will be skipped by tetration parsing
611            if text.starts_with("(") || text.starts_with("\\(") {
612                *text = &orig_text[2..];
613            }
614            return Some(Number::Exact(10.into()));
615        }
616        let mut cur_parens = 0;
617        let mut depth = 0;
618
619        let mut max_no_paren_level = 0;
620        let mut no_paren_inner = &text[0..];
621
622        while text.starts_with("10^") || text.starts_with("10\\^") {
623            let base_text;
624            if &text[2..3] == "^" {
625                base_text = &text[2..];
626                *text = &text[3..];
627            } else {
628                base_text = &text[3..];
629                *text = &text[4..];
630            }
631            if text.starts_with("\\(") {
632                *text = &text[2..];
633                cur_parens += 1;
634            } else if text.starts_with("(") {
635                *text = &text[1..];
636                cur_parens += 1;
637            }
638            // we're at base and pushed a paren
639            if depth == max_no_paren_level && cur_parens == 0 {
640                max_no_paren_level += 1;
641                no_paren_inner = base_text;
642            }
643            depth += 1;
644        }
645
646        let top = match parse_num_simple(text, had_op, consts, locale, prec) {
647            Some(Number::Exact(n)) => n,
648            Some(Number::Approximate(_, n)) => {
649                depth += 1;
650                n
651            }
652            _ => {
653                *text = &orig_text[3..];
654                return None;
655            }
656        };
657
658        for _ in 0..cur_parens {
659            if text.starts_with("\\)") {
660                *text = &text[2..];
661            } else if text.starts_with(")") {
662                *text = &text[1..];
663            } else {
664                *text = &orig_text[2..];
665                return None;
666            }
667        }
668
669        // If postfix op in exponent, ingore that it's in exponent
670        if max_no_paren_level != 0 && text.starts_with(POSTFIX_OPS) {
671            *text = no_paren_inner;
672            return None;
673        }
674
675        if depth == 1 {
676            if top < consts.integer_construction_limit {
677                return Some(Number::Exact(
678                    Integer::u64_pow_u64(10, top.to_u64().unwrap()).complete(),
679                ));
680            }
681            return Some(Number::ApproximateDigits(false, top));
682        } else {
683            return Some(Number::ApproximateDigitsTower(
684                false,
685                false,
686                (depth - 1).into(),
687                top,
688            ));
689        }
690    }
691
692    parse_num_simple(text, had_op, consts, locale, prec)
693}
694
695fn parse_num_simple(
696    text: &mut &str,
697    had_op: bool,
698    consts: &Consts<'_>,
699    locale: &NumFormat,
700    prec: u32,
701) -> Option<crate::calculation_results::CalculationResult> {
702    let integer_part = {
703        let end = text
704            .find(|c: char| (!c.is_numeric() && !SEPARATORS.contains(&c)) || c == locale.decimal)
705            .unwrap_or(text.len());
706        let part = &text[..end];
707        *text = &text[end..];
708        part
709    };
710    let decimal_part = if text.starts_with(locale.decimal) {
711        *text = &text[1..];
712        let end = text
713            .find(|c: char| (!c.is_numeric() && !SEPARATORS.contains(&c)) || c == locale.decimal)
714            .unwrap_or(text.len());
715        let part = &text[..end];
716        *text = &text[end..];
717        part
718    } else {
719        &text[..0]
720    };
721    let exponent_part = if text.starts_with(['e', 'E']) {
722        *text = &text[1..];
723        let negative = if text.starts_with('+') {
724            *text = &text[1..];
725            false
726        } else if text.starts_with('-') {
727            *text = &text[1..];
728            true
729        } else {
730            false
731        };
732        let end = text
733            .find(|c: char| (!c.is_numeric() && !SEPARATORS.contains(&c)) || c == locale.decimal)
734            .unwrap_or(text.len());
735        let part = &text[..end];
736        *text = &text[end..];
737        (part, negative)
738    } else if !had_op
739        && (text.trim_start().starts_with("\\*10^")
740            || text.trim_start().starts_with("\\* 10^")
741            || text.trim_start().starts_with("\\*10\\^")
742            || text.trim_start().starts_with("\\* 10\\^")
743            || text.trim_start().starts_with("⨉10^")
744            || text.trim_start().starts_with("⨉ 10^")
745            || text.trim_start().starts_with("⨉10\\^")
746            || text.trim_start().starts_with("⨉ 10\\^")
747            || text.trim_start().starts_with("x10^")
748            || text.trim_start().starts_with("x 10^")
749            || text.trim_start().starts_with("x10\\^")
750            || text.trim_start().starts_with("x 10\\^"))
751    {
752        let pre_orig_text = &text[..];
753        let start = text.find("^").unwrap();
754        let orig_text = &text[start..];
755        *text = &text[start + 1..];
756        let paren = if text.starts_with('(') {
757            *text = &text[1..];
758            true
759        } else {
760            false
761        };
762        let negative = if text.starts_with('+') {
763            *text = &text[1..];
764            false
765        } else if text.starts_with('-') {
766            *text = &text[1..];
767            true
768        } else {
769            false
770        };
771        let end = text.find(|c: char| !c.is_numeric()).unwrap_or(text.len());
772        let part = &text[..end];
773        *text = &text[end..];
774        if paren {
775            if text.starts_with(')') {
776                *text = &text[1..];
777            } else {
778                *text = orig_text;
779                return None;
780            }
781        }
782        if text.starts_with(POSTFIX_OPS) {
783            //  10^(50)! => (10^50)!, 10^50! => 50!
784            // if !paren text ohne 10^ else text mit 10^
785            if paren {
786                *text = pre_orig_text;
787            } else {
788                *text = orig_text;
789            }
790            return None;
791        }
792        (part, negative)
793    } else {
794        (&text[..0], false)
795    };
796    let fraction_part = if !had_op && text.starts_with(['/']) {
797        *text = &text[1..];
798        let end = text
799            .find(|c: char| (!c.is_numeric() && !SEPARATORS.contains(&c)) || c == locale.decimal)
800            .unwrap_or(text.len());
801        let part = &text[..end];
802        *text = &text[end..];
803        part
804    } else {
805        &text[..0]
806    };
807    if text.starts_with(POSTFIX_OPS) && !fraction_part.is_empty() {
808        let fraction_part = fraction_part.replace(SEPARATORS, "");
809        let n = fraction_part.parse::<Integer>().ok()?;
810        return Some(Number::Exact(n));
811    }
812    if integer_part.is_empty() && decimal_part.is_empty() {
813        return None;
814    }
815    let exponent = if !exponent_part.0.is_empty() {
816        let exponent_part = (exponent_part.0.replace(SEPARATORS, ""), exponent_part.1);
817        let mut e = exponent_part.0.parse::<Integer>().ok()?;
818        if exponent_part.1 {
819            e *= -1;
820        }
821        e
822    } else {
823        0.into()
824    };
825    let divisor = if !fraction_part.is_empty() {
826        let fraction_part = fraction_part.replace(SEPARATORS, "");
827        fraction_part.parse::<Integer>().ok()?
828    } else {
829        Integer::ONE.clone()
830    };
831    let integer_part = integer_part.replace(SEPARATORS, "");
832    let decimal_part = decimal_part.replace(SEPARATORS, "");
833    if exponent >= decimal_part.len() as i64
834        && exponent <= consts.integer_construction_limit.clone() - integer_part.len() as i64
835        && (divisor == 1 || exponent >= consts.integer_construction_limit.clone() / 10)
836    {
837        let exponent = exponent - decimal_part.len();
838        let n = format!("{integer_part}{decimal_part}")
839            .parse::<Integer>()
840            .ok()?;
841        let num = (n * Integer::u64_pow_u64(10, exponent.to_u64().unwrap()).complete()) / divisor;
842        Some(Number::Exact(num))
843    } else if exponent <= consts.integer_construction_limit.clone() - integer_part.len() as i64 {
844        let x = Float::parse(format!(
845            "{integer_part}.{decimal_part}{}{}{}",
846            if !exponent_part.0.is_empty() { "e" } else { "" },
847            if exponent_part.1 { "-" } else { "" },
848            exponent_part.0
849        ))
850        .ok()?;
851        let x = Float::with_val(prec, x) / divisor;
852        if x.is_integer() {
853            let n = x.to_integer().unwrap();
854            Some(Number::Exact(n))
855        } else if x.is_finite() {
856            Some(Number::Float(x.into()))
857        } else {
858            None
859        }
860    } else {
861        let x = Float::parse(format!("{integer_part}.{decimal_part}")).ok()?;
862        let x = Float::with_val(prec, x) / divisor;
863        if x.is_finite() {
864            let (b, e) = crate::math::adjust_approximate((x, exponent));
865            Some(Number::Approximate(b.into(), e))
866        } else {
867            None
868        }
869    }
870}
871
872#[cfg(test)]
873mod test {
874    use super::*;
875    use crate::calculation_tasks::CalculationBase::Num;
876    use arbtest::arbtest;
877
878    use crate::recommended::FLOAT_PRECISION;
879
880    #[test]
881    fn test_text_only() {
882        let consts = Consts::default();
883        let jobs = parse(
884            "just some words of encouragement!",
885            true,
886            &consts,
887            &NumFormat { decimal: '.' },
888        );
889        assert_eq!(jobs, []);
890    }
891    #[test]
892    fn test_factorial() {
893        let consts = Consts::default();
894        let jobs = parse(
895            "a factorial 15!",
896            true,
897            &consts,
898            &NumFormat { decimal: '.' },
899        );
900        assert_eq!(
901            jobs,
902            [CalculationJob {
903                base: CalculationBase::Num(15.into()),
904                level: 1,
905                negative: 0
906            }]
907        );
908    }
909    #[test]
910    fn test_multifactorial() {
911        let consts = Consts::default();
912        let jobs = parse(
913            "a factorial 15!!! actually a multi",
914            true,
915            &consts,
916            &NumFormat { decimal: '.' },
917        );
918        assert_eq!(
919            jobs,
920            [CalculationJob {
921                base: CalculationBase::Num(15.into()),
922                level: 3,
923                negative: 0
924            }]
925        );
926    }
927    #[test]
928    fn test_subfactorial() {
929        let consts = Consts::default();
930        let jobs = parse(
931            "a factorial !15 actually a sub",
932            true,
933            &consts,
934            &NumFormat { decimal: '.' },
935        );
936        assert_eq!(
937            jobs,
938            [CalculationJob {
939                base: CalculationBase::Num(15.into()),
940                level: 0,
941                negative: 0
942            }]
943        );
944    }
945    #[test]
946    fn test_submultifactorial() {
947        let consts = Consts::default();
948        let jobs = parse(
949            "not well defined !!!15",
950            true,
951            &consts,
952            &NumFormat { decimal: '.' },
953        );
954        assert_eq!(jobs, []);
955    }
956    #[test]
957    fn test_termial() {
958        let consts = Consts::default();
959        let jobs = parse("a termial 15?", true, &consts, &NumFormat { decimal: '.' });
960        assert_eq!(
961            jobs,
962            [CalculationJob {
963                base: CalculationBase::Num(15.into()),
964                level: -1,
965                negative: 0
966            }]
967        );
968    }
969    #[test]
970    fn test_no_termial() {
971        let consts = Consts::default();
972        let jobs = parse(
973            "not enabled 15?",
974            false,
975            &consts,
976            &NumFormat { decimal: '.' },
977        );
978        assert_eq!(jobs, []);
979    }
980    #[test]
981    fn test_multitermial() {
982        let consts = Consts::default();
983        let jobs = parse(
984            "a termial 15??? actually a multi",
985            true,
986            &consts,
987            &NumFormat { decimal: '.' },
988        );
989        assert_eq!(
990            jobs,
991            [CalculationJob {
992                base: CalculationBase::Num(15.into()),
993                level: -3,
994                negative: 0
995            }]
996        );
997    }
998    #[test]
999    fn test_subtermial() {
1000        let consts = Consts::default();
1001        let jobs = parse(
1002            "a termial ?15 actually a sub",
1003            true,
1004            &consts,
1005            &NumFormat { decimal: '.' },
1006        );
1007        assert_eq!(jobs, []);
1008    }
1009    #[test]
1010    fn test_chain() {
1011        let consts = Consts::default();
1012        let jobs = parse(
1013            "a factorialchain (15!)!",
1014            true,
1015            &consts,
1016            &NumFormat { decimal: '.' },
1017        );
1018        assert_eq!(
1019            jobs,
1020            [CalculationJob {
1021                base: CalculationBase::Calc(Box::new(CalculationJob {
1022                    base: CalculationBase::Num(15.into()),
1023                    level: 1,
1024                    negative: 0
1025                })),
1026                level: 1,
1027                negative: 0
1028            }]
1029        );
1030    }
1031    #[test]
1032    fn test_mixed_chain() {
1033        let consts = Consts::default();
1034        let jobs = parse(
1035            "a factorialchain !(15!)",
1036            true,
1037            &consts,
1038            &NumFormat { decimal: '.' },
1039        );
1040        assert_eq!(
1041            jobs,
1042            [CalculationJob {
1043                base: CalculationBase::Calc(Box::new(CalculationJob {
1044                    base: CalculationBase::Num(15.into()),
1045                    level: 1,
1046                    negative: 0
1047                })),
1048                level: 0,
1049                negative: 0
1050            }]
1051        );
1052    }
1053    #[test]
1054    fn test_postfix_chain() {
1055        let consts = Consts::default();
1056        let jobs = parse(
1057            "a factorialchain -15!?",
1058            true,
1059            &consts,
1060            &NumFormat { decimal: '.' },
1061        );
1062        assert_eq!(
1063            jobs,
1064            [CalculationJob {
1065                base: CalculationBase::Calc(Box::new(CalculationJob {
1066                    base: CalculationBase::Num(15.into()),
1067                    level: 1,
1068                    negative: 0
1069                })),
1070                level: -1,
1071                negative: 1
1072            }]
1073        );
1074    }
1075    #[test]
1076    fn test_negative() {
1077        let consts = Consts::default();
1078        let jobs = parse(
1079            "a factorial ---15!",
1080            true,
1081            &consts,
1082            &NumFormat { decimal: '.' },
1083        );
1084        assert_eq!(
1085            jobs,
1086            [CalculationJob {
1087                base: CalculationBase::Num(15.into()),
1088                level: 1,
1089                negative: 3
1090            }]
1091        );
1092    }
1093    #[test]
1094    fn test_negative_gap() {
1095        let consts = Consts::default();
1096        let jobs = parse(
1097            "a factorial --- 15!",
1098            true,
1099            &consts,
1100            &NumFormat { decimal: '.' },
1101        );
1102        assert_eq!(
1103            jobs,
1104            [CalculationJob {
1105                base: CalculationBase::Num(15.into()),
1106                level: 1,
1107                negative: 0
1108            }]
1109        );
1110    }
1111    #[test]
1112    fn test_paren() {
1113        let consts = Consts::default();
1114        let jobs = parse(
1115            "a factorial (15)!",
1116            true,
1117            &consts,
1118            &NumFormat { decimal: '.' },
1119        );
1120        assert_eq!(
1121            jobs,
1122            [CalculationJob {
1123                base: CalculationBase::Num(15.into()),
1124                level: 1,
1125                negative: 0
1126            }]
1127        );
1128    }
1129    #[test]
1130    fn test_in_paren() {
1131        let consts = Consts::default();
1132        let jobs = parse(
1133            "a factorial (15!)",
1134            true,
1135            &consts,
1136            &NumFormat { decimal: '.' },
1137        );
1138        assert_eq!(
1139            jobs,
1140            [CalculationJob {
1141                base: CalculationBase::Num(15.into()),
1142                level: 1,
1143                negative: 0
1144            }]
1145        );
1146    }
1147    #[test]
1148    fn test_decimal() {
1149        let consts = Consts::default();
1150        let jobs = parse(
1151            "a factorial 1.5!",
1152            true,
1153            &consts,
1154            &NumFormat { decimal: '.' },
1155        );
1156        assert_eq!(
1157            jobs,
1158            [CalculationJob {
1159                base: CalculationBase::Num(Float::with_val(FLOAT_PRECISION, 1.5).into()),
1160                level: 1,
1161                negative: 0
1162            }]
1163        );
1164    }
1165    #[test]
1166    fn test_negative_decimal() {
1167        let consts = Consts::default();
1168        let jobs = parse(
1169            "a factorial (-1.5)!",
1170            true,
1171            &consts,
1172            &NumFormat { decimal: '.' },
1173        );
1174        assert_eq!(
1175            jobs,
1176            [CalculationJob {
1177                base: CalculationBase::Num(Float::with_val(FLOAT_PRECISION, -1.5).into()),
1178                level: 1,
1179                negative: 0
1180            }]
1181        );
1182    }
1183    #[test]
1184    fn test_paren_negation() {
1185        let consts = Consts::default();
1186        let jobs = parse(
1187            "a factorial -(--(-(-(-3))!))!",
1188            true,
1189            &consts,
1190            &NumFormat { decimal: '.' },
1191        );
1192        assert_eq!(
1193            jobs,
1194            [CalculationJob {
1195                base: CalculationBase::Calc(Box::new(CalculationJob {
1196                    base: CalculationBase::Num(3.into()),
1197                    level: 1,
1198                    negative: 3
1199                })),
1200                level: 1,
1201                negative: 1
1202            }]
1203        );
1204    }
1205    #[test]
1206    fn test_tag() {
1207        let consts = Consts::default();
1208        let jobs = parse(
1209            ">!5 a factorial 15! !<",
1210            true,
1211            &consts,
1212            &NumFormat { decimal: '.' },
1213        );
1214        assert_eq!(jobs, []);
1215    }
1216    #[test]
1217    fn test_incomplete_tag() {
1218        let consts = Consts::default();
1219        let jobs = parse(
1220            ">!5 a factorial 15!",
1221            true,
1222            &consts,
1223            &NumFormat { decimal: '.' },
1224        );
1225        assert_eq!(
1226            jobs,
1227            [
1228                CalculationJob {
1229                    base: CalculationBase::Num(5.into()),
1230                    level: 0,
1231                    negative: 0
1232                },
1233                CalculationJob {
1234                    base: CalculationBase::Num(15.into()),
1235                    level: 1,
1236                    negative: 0
1237                }
1238            ]
1239        );
1240    }
1241    #[test]
1242    fn test_escaped_tag() {
1243        let consts = Consts::default();
1244        let jobs = parse(
1245            "\\>!5 a factorial 15! !<",
1246            true,
1247            &consts,
1248            &NumFormat { decimal: '.' },
1249        );
1250        assert_eq!(
1251            jobs,
1252            [
1253                CalculationJob {
1254                    base: CalculationBase::Num(5.into()),
1255                    level: 0,
1256                    negative: 0
1257                },
1258                CalculationJob {
1259                    base: CalculationBase::Num(15.into()),
1260                    level: 1,
1261                    negative: 0
1262                }
1263            ]
1264        );
1265    }
1266    #[test]
1267    fn test_escaped_tag2() {
1268        let consts = Consts::default();
1269        let jobs = parse(
1270            ">!5 a factorial 15! \\!<",
1271            true,
1272            &consts,
1273            &NumFormat { decimal: '.' },
1274        );
1275        assert_eq!(
1276            jobs,
1277            [
1278                CalculationJob {
1279                    base: CalculationBase::Num(5.into()),
1280                    level: 0,
1281                    negative: 0
1282                },
1283                CalculationJob {
1284                    base: CalculationBase::Num(15.into()),
1285                    level: 1,
1286                    negative: 0
1287                }
1288            ]
1289        );
1290    }
1291
1292    #[test]
1293    fn test_url() {
1294        let consts = Consts::default();
1295        let jobs = parse(
1296            "https://something.somewhere/with/path/and?tag=siufgiufgia3873844hi8743!hfsf",
1297            true,
1298            &consts,
1299            &NumFormat { decimal: '.' },
1300        );
1301        assert_eq!(jobs, []);
1302    }
1303
1304    #[test]
1305    fn test_uri_poi_doesnt_cause_infinite_loop() {
1306        let consts = Consts::default();
1307        let jobs = parse("84!:", true, &consts, &NumFormat { decimal: '.' });
1308        assert_eq!(
1309            jobs,
1310            [CalculationJob {
1311                base: Num(84.into()),
1312                level: 1,
1313                negative: 0
1314            }]
1315        );
1316    }
1317    #[test]
1318    fn test_escaped_url() {
1319        let consts = Consts::default();
1320        let jobs = parse(
1321            "\\://something.somewhere/with/path/and?tag=siufgiufgia3873844hi8743!hfsf",
1322            true,
1323            &consts,
1324            &NumFormat { decimal: '.' },
1325        );
1326        assert_eq!(
1327            jobs,
1328            [CalculationJob {
1329                base: CalculationBase::Num(8743.into()),
1330                level: 1,
1331                negative: 0
1332            }]
1333        );
1334    }
1335
1336    #[test]
1337    fn test_word_in_paren() {
1338        let consts = Consts::default();
1339        let jobs = parse(
1340            "(x-2)! (2 word)! ((x/k)-3)! (,x-4)!",
1341            true,
1342            &consts,
1343            &NumFormat { decimal: '.' },
1344        );
1345        assert_eq!(jobs, []);
1346    }
1347
1348    #[test]
1349    fn test_multi_number_paren() {
1350        let consts = Consts::default();
1351        let jobs = parse("(5-2)!", true, &consts, &NumFormat { decimal: '.' });
1352        assert_eq!(jobs, []);
1353    }
1354    #[test]
1355    fn test_arbitrary_input() {
1356        let consts = Consts::default();
1357        arbtest(|u| {
1358            let text: &str = u.arbitrary()?;
1359            let _ = parse(text, u.arbitrary()?, &consts, &NumFormat { decimal: '.' });
1360            Ok(())
1361        });
1362    }
1363
1364    #[test]
1365    fn test_constant() {
1366        let consts = Consts::default();
1367        let jobs = parse("!espi!", true, &consts, &NumFormat { decimal: '.' });
1368        assert_eq!(jobs, []);
1369        let jobs = parse(
1370            "some. pi!",
1371            true,
1372            &consts,
1373            &consts.locales.get("en").unwrap().format.number_format,
1374        );
1375        assert_eq!(
1376            jobs,
1377            [CalculationJob {
1378                base: CalculationBase::Num(Number::Float(
1379                    Float::with_val(FLOAT_PRECISION, factorion_math::rug::float::Constant::Pi)
1380                        .into()
1381                )),
1382                level: 1,
1383                negative: 0
1384            }]
1385        );
1386    }
1387
1388    #[test]
1389    fn test_fraction() {
1390        let consts = Consts::default();
1391        let jobs = parse("!5/6!", true, &consts, &NumFormat { decimal: '.' });
1392        assert_eq!(
1393            jobs,
1394            [
1395                CalculationJob {
1396                    base: CalculationBase::Num(Number::Exact(5.into())),
1397                    level: 0,
1398                    negative: 0
1399                },
1400                CalculationJob {
1401                    base: CalculationBase::Num(Number::Exact(6.into())),
1402                    level: 1,
1403                    negative: 0
1404                }
1405            ]
1406        );
1407        let jobs = parse("5/6!", true, &consts, &NumFormat { decimal: '.' });
1408        assert_eq!(
1409            jobs,
1410            [CalculationJob {
1411                base: CalculationBase::Num(Number::Exact(6.into())),
1412                level: 1,
1413                negative: 0
1414            }]
1415        );
1416        let jobs = parse("(10/2)!", true, &consts, &NumFormat { decimal: '.' });
1417        assert_eq!(
1418            jobs,
1419            [CalculationJob {
1420                base: CalculationBase::Num(Number::Exact(5.into())),
1421                level: 1,
1422                negative: 0
1423            },]
1424        );
1425    }
1426
1427    #[test]
1428    fn test_parse_num() {
1429        let consts = Consts::default();
1430        let num = parse_num(
1431            &mut "1.5more !",
1432            false,
1433            false,
1434            &consts,
1435            &NumFormat { decimal: '.' },
1436        );
1437        assert_eq!(
1438            num,
1439            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into()))
1440        );
1441        let num = parse_num(
1442            &mut "1,5more !",
1443            false,
1444            false,
1445            &consts,
1446            &NumFormat { decimal: ',' },
1447        );
1448        assert_eq!(
1449            num,
1450            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into()))
1451        );
1452        let num = parse_num(
1453            &mut ".5more !",
1454            false,
1455            false,
1456            &consts,
1457            &NumFormat { decimal: '.' },
1458        );
1459        assert_eq!(
1460            num,
1461            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1462        );
1463        let num = parse_num(
1464            &mut "1more !",
1465            false,
1466            true,
1467            &consts,
1468            &NumFormat { decimal: '.' },
1469        );
1470        assert_eq!(num, Some(1.into()));
1471        let num = parse_num(
1472            &mut "1_000more !",
1473            false,
1474            true,
1475            &consts,
1476            &NumFormat { decimal: '.' },
1477        );
1478        assert_eq!(num, Some(1000.into()));
1479        let num = parse_num(
1480            &mut "1,000more !",
1481            false,
1482            true,
1483            &consts,
1484            &NumFormat { decimal: '.' },
1485        );
1486        assert_eq!(num, Some(1000.into()));
1487        let num = parse_num(
1488            &mut "1'000more !",
1489            false,
1490            true,
1491            &consts,
1492            &NumFormat { decimal: '.' },
1493        );
1494        assert_eq!(num, Some(1000.into()));
1495        let num = parse_num(
1496            &mut "1.000more !",
1497            false,
1498            true,
1499            &consts,
1500            &NumFormat { decimal: ',' },
1501        );
1502        assert_eq!(num, Some(1000.into()));
1503        let num = parse_num(
1504            &mut "1.000more !",
1505            false,
1506            true,
1507            &consts,
1508            &NumFormat { decimal: '.' },
1509        );
1510        assert_eq!(num, Some(1.into()));
1511        let num = parse_num(
1512            &mut "1.0more !",
1513            true,
1514            false,
1515            &consts,
1516            &NumFormat { decimal: '.' },
1517        );
1518        assert_eq!(num, Some(1.into()));
1519        let num = parse_num(
1520            &mut "1.5e2more !",
1521            false,
1522            false,
1523            &consts,
1524            &NumFormat { decimal: '.' },
1525        );
1526        assert_eq!(num, Some(150.into()));
1527        let num = parse_num(
1528            &mut "1.5 ⨉ 10^2more !",
1529            false,
1530            false,
1531            &consts,
1532            &NumFormat { decimal: '.' },
1533        );
1534        assert_eq!(num, Some(150.into()));
1535        let num = parse_num(
1536            &mut "1e2more !",
1537            false,
1538            false,
1539            &consts,
1540            &NumFormat { decimal: '.' },
1541        );
1542        assert_eq!(num, Some(100.into()));
1543        let num = parse_num(
1544            &mut "1\\*10^(2)more !",
1545            false,
1546            false,
1547            &consts,
1548            &NumFormat { decimal: '.' },
1549        );
1550        assert_eq!(num, Some(100.into()));
1551        let num = parse_num(
1552            &mut "1.531e2more !",
1553            false,
1554            false,
1555            &consts,
1556            &NumFormat { decimal: '.' },
1557        );
1558        let Some(Number::Float(f)) = num else {
1559            panic!("Not a float")
1560        };
1561        assert!(Float::abs(f.as_float().clone() - 153.1) < 0.0000001);
1562        let num = parse_num(
1563            &mut "5e-1more !",
1564            false,
1565            false,
1566            &consts,
1567            &NumFormat { decimal: '.' },
1568        );
1569        assert_eq!(
1570            num,
1571            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1572        );
1573        let num = parse_num(
1574            &mut "e2more !",
1575            true,
1576            false,
1577            &consts,
1578            &NumFormat { decimal: '.' },
1579        );
1580        assert_eq!(num, None);
1581        let num = parse_num(
1582            &mut "es !",
1583            false,
1584            false,
1585            &consts,
1586            &NumFormat { decimal: '.' },
1587        );
1588        assert_eq!(num, None);
1589        let num = parse_num(
1590            &mut "e !",
1591            false,
1592            false,
1593            &consts,
1594            &NumFormat { decimal: '.' },
1595        );
1596        assert_eq!(num, Some(E(FLOAT_PRECISION)));
1597        let num = parse_num(
1598            &mut "pi !",
1599            false,
1600            false,
1601            &consts,
1602            &NumFormat { decimal: '.' },
1603        );
1604        assert_eq!(num, Some(PI(FLOAT_PRECISION)));
1605        let num = parse_num(
1606            &mut "π !",
1607            false,
1608            false,
1609            &consts,
1610            &NumFormat { decimal: '.' },
1611        );
1612        assert_eq!(num, Some(PI(FLOAT_PRECISION)));
1613        let num = parse_num(
1614            &mut "phi !",
1615            false,
1616            false,
1617            &consts,
1618            &NumFormat { decimal: '.' },
1619        );
1620        assert_eq!(num, Some(PHI(FLOAT_PRECISION)));
1621        let num = parse_num(
1622            &mut "ɸ !",
1623            false,
1624            false,
1625            &consts,
1626            &NumFormat { decimal: '.' },
1627        );
1628        assert_eq!(num, Some(PHI(FLOAT_PRECISION)));
1629        let num = parse_num(
1630            &mut "tau !",
1631            false,
1632            false,
1633            &consts,
1634            &NumFormat { decimal: '.' },
1635        );
1636        assert_eq!(num, Some(TAU(FLOAT_PRECISION)));
1637        let num = parse_num(
1638            &mut "τ !",
1639            false,
1640            false,
1641            &consts,
1642            &NumFormat { decimal: '.' },
1643        );
1644        assert_eq!(num, Some(TAU(FLOAT_PRECISION)));
1645        let num = parse_num(
1646            &mut "∞\u{0303} !",
1647            false,
1648            false,
1649            &consts,
1650            &NumFormat { decimal: '.' },
1651        );
1652        assert_eq!(num, Some(Number::ComplexInfinity));
1653        let num = parse_num(
1654            &mut "∞ !",
1655            false,
1656            false,
1657            &consts,
1658            &NumFormat { decimal: '.' },
1659        );
1660        assert_eq!(num, Some(Number::ComplexInfinity));
1661        let num = parse_num(
1662            &mut "1/2 !",
1663            false,
1664            false,
1665            &consts,
1666            &NumFormat { decimal: '.' },
1667        );
1668        assert_eq!(
1669            num,
1670            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1671        );
1672        let num = parse_num(
1673            &mut "10/2 !",
1674            false,
1675            false,
1676            &consts,
1677            &NumFormat { decimal: '.' },
1678        );
1679        assert_eq!(num, Some(Number::Exact(5.into())));
1680        let num = parse_num(
1681            &mut "1.5/2 !",
1682            false,
1683            false,
1684            &consts,
1685            &NumFormat { decimal: '.' },
1686        );
1687        assert_eq!(
1688            num,
1689            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.75).into()))
1690        );
1691        let num = parse_num(
1692            &mut "10e10000000000/2 !",
1693            false,
1694            false,
1695            &consts,
1696            &NumFormat { decimal: '.' },
1697        );
1698        assert_eq!(
1699            num,
1700            Some(Number::Approximate(
1701                Float::with_val(FLOAT_PRECISION, 5).into(),
1702                10000000000u64.into()
1703            ))
1704        );
1705        let num = parse_num(
1706            &mut "10/2 !",
1707            false,
1708            true,
1709            &consts,
1710            &NumFormat { decimal: '.' },
1711        );
1712        assert_eq!(num, Some(Number::Exact(10.into())));
1713        let num = parse_num(
1714            &mut "10/2!",
1715            false,
1716            false,
1717            &consts,
1718            &NumFormat { decimal: '.' },
1719        );
1720        assert_eq!(num, Some(Number::Exact(2.into())));
1721        let num = parse_num(
1722            &mut "^(11)10!",
1723            false,
1724            false,
1725            &consts,
1726            &NumFormat { decimal: '.' },
1727        );
1728        assert_eq!(num, None);
1729        let num = parse_num(
1730            &mut "^(50)100!",
1731            false,
1732            false,
1733            &consts,
1734            &NumFormat { decimal: '.' },
1735        );
1736        assert_eq!(num, None);
1737        let num = parse_num(
1738            &mut "^(50)1000!",
1739            false,
1740            false,
1741            &consts,
1742            &NumFormat { decimal: '.' },
1743        );
1744        assert_eq!(num, None);
1745        let num = parse_num(
1746            &mut "^(50)10,000!",
1747            false,
1748            false,
1749            &consts,
1750            &NumFormat { decimal: '.' },
1751        );
1752        assert_eq!(num, None);
1753        let num = parse_num(
1754            &mut "^(11)10",
1755            false,
1756            false,
1757            &consts,
1758            &NumFormat { decimal: '.' },
1759        );
1760        assert_eq!(
1761            num,
1762            Some(Number::ApproximateDigitsTower(
1763                false,
1764                false,
1765                10.into(),
1766                1.into()
1767            ))
1768        );
1769        let num = parse_num(
1770            &mut "^(11,000)10",
1771            false,
1772            false,
1773            &consts,
1774            &NumFormat { decimal: '.' },
1775        );
1776        assert_eq!(
1777            num,
1778            Some(Number::ApproximateDigitsTower(
1779                false,
1780                false,
1781                10999.into(),
1782                1.into()
1783            ))
1784        );
1785        let num = parse_num(
1786            &mut "10^10^10^5!",
1787            false,
1788            false,
1789            &consts,
1790            &NumFormat { decimal: '.' },
1791        );
1792        assert_eq!(num, None);
1793        let num = parse_num(
1794            &mut "10^10^10^5",
1795            false,
1796            false,
1797            &consts,
1798            &NumFormat { decimal: '.' },
1799        );
1800        assert_eq!(
1801            num,
1802            Some(Number::ApproximateDigitsTower(
1803                false,
1804                false,
1805                2.into(),
1806                5.into()
1807            ))
1808        );
1809        let num = parse_num(
1810            &mut "10^(10\\^10\\^\\(5\\))!",
1811            false,
1812            false,
1813            &consts,
1814            &NumFormat { decimal: '.' },
1815        );
1816        assert_eq!(
1817            num,
1818            Some(Number::ApproximateDigitsTower(
1819                false,
1820                false,
1821                2.into(),
1822                5.into()
1823            ))
1824        );
1825        let num = parse_num(
1826            &mut "10^50000000000",
1827            false,
1828            false,
1829            &consts,
1830            &NumFormat { decimal: '.' },
1831        );
1832        assert_eq!(
1833            num,
1834            Some(Number::ApproximateDigits(false, 50000000000u64.into()))
1835        );
1836        let num = parse_num(
1837            &mut "10^5!",
1838            false,
1839            false,
1840            &consts,
1841            &NumFormat { decimal: '.' },
1842        );
1843        assert_eq!(num, None);
1844        let num = parse_num(
1845            &mut "10^5",
1846            false,
1847            false,
1848            &consts,
1849            &NumFormat { decimal: '.' },
1850        );
1851        assert_eq!(num, Some(Number::Exact(100000.into())));
1852        let num = parse_num(
1853            &mut "10^5",
1854            false,
1855            true,
1856            &consts,
1857            &NumFormat { decimal: '.' },
1858        );
1859        assert_eq!(num, Some(Number::Exact(10.into())));
1860    }
1861    #[test]
1862    fn test_parse_num_revert() {
1863        // Note that we want one extra character when we get None, as in such a situation a char will always be skipped
1864        let consts = Consts::default();
1865        let mut text = "1 ⨉ 10^(5!)";
1866        let num = parse_num(
1867            &mut text,
1868            false,
1869            false,
1870            &consts,
1871            &NumFormat { decimal: '.' },
1872        );
1873        assert_eq!(num, None);
1874        assert_eq!(text, "^(5!)");
1875        let mut text = "^(10 10";
1876        let num = parse_num(
1877            &mut text,
1878            false,
1879            false,
1880            &consts,
1881            &NumFormat { decimal: '.' },
1882        );
1883        assert_eq!(num, None);
1884        assert_eq!(text, "^(10 10");
1885        let mut text = "^(10)1";
1886        let num = parse_num(
1887            &mut text,
1888            false,
1889            false,
1890            &consts,
1891            &NumFormat { decimal: '.' },
1892        );
1893        assert_eq!(num, None);
1894        assert_eq!(text, "^(10)1");
1895        let mut text = "10^(10^10^\\(5\\)";
1896        let num = parse_num(
1897            &mut text,
1898            false,
1899            false,
1900            &consts,
1901            &NumFormat { decimal: '.' },
1902        );
1903        assert_eq!(num, None);
1904        assert_eq!(text, "^(10^10^\\(5\\)");
1905        let mut text = "10^10^10^\\(5\\)!";
1906        let num = parse_num(
1907            &mut text,
1908            false,
1909            false,
1910            &consts,
1911            &NumFormat { decimal: '.' },
1912        );
1913        assert_eq!(num, None);
1914        assert_eq!(text, "^10^\\(5\\)!");
1915        let mut text = "10^30!";
1916        let num = parse_num(
1917            &mut text,
1918            false,
1919            false,
1920            &consts,
1921            &NumFormat { decimal: '.' },
1922        );
1923        assert_eq!(num, None);
1924        assert_eq!(text, "^30!");
1925    }
1926
1927    #[allow(clippy::uninlined_format_args)]
1928    #[test]
1929    fn test_biggest_num() {
1930        let consts = Consts::default();
1931        let num = parse_num(
1932            &mut format!("9e{}", recommended::INTEGER_CONSTRUCTION_LIMIT()).as_str(),
1933            true,
1934            false,
1935            &consts,
1936            &NumFormat { decimal: '.' },
1937        );
1938        assert!(matches!(num, Some(Number::Approximate(_, _))));
1939        let num = parse_num(
1940            &mut format!("9e{}", recommended::INTEGER_CONSTRUCTION_LIMIT() - 1).as_str(),
1941            false,
1942            false,
1943            &consts,
1944            &NumFormat { decimal: '.' },
1945        );
1946        assert!(num.is_some());
1947    }
1948}