factorion_lib/
parse.rs

1//! Parses text and extracts calculations
2
3use crate::locale::{self, 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 = || 10_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.find(|c: char| !c.is_numeric()).unwrap_or(text.len());
569        let part = &text[..end];
570        *text = &text[end..];
571        if text.starts_with(")10") && !text[3..].starts_with(POSTFIX_OPS) {
572            *text = &text[3..];
573            let n = part.parse::<Integer>().ok()?;
574            return Some(Number::ApproximateDigitsTower(
575                false,
576                false,
577                n - 1,
578                1.into(),
579            ));
580        } else {
581            // Skip ^ only (because ret None)
582            *text = orig_text;
583            return None;
584        }
585    }
586
587    if text.starts_with("10^") || text.starts_with("10\\^") {
588        let orig_text = &text[..];
589        if had_op {
590            if &text[2..3] == "^" {
591                *text = &text[3..];
592            } else {
593                *text = &text[4..];
594            }
595            // Don't skip power if op won't be in exponent, so it will be skipped by tetration parsing
596            if text.starts_with("(") || text.starts_with("\\(") {
597                *text = &orig_text[2..];
598            }
599            return Some(Number::Exact(10.into()));
600        }
601        let mut cur_parens = 0;
602        let mut depth = 0;
603
604        let mut max_no_paren_level = 0;
605        let mut no_paren_inner = &text[0..];
606
607        while text.starts_with("10^") || text.starts_with("10\\^") {
608            let base_text;
609            if &text[2..3] == "^" {
610                base_text = &text[2..];
611                *text = &text[3..];
612            } else {
613                base_text = &text[3..];
614                *text = &text[4..];
615            }
616            if text.starts_with("\\(") {
617                *text = &text[2..];
618                cur_parens += 1;
619            } else if text.starts_with("(") {
620                *text = &text[1..];
621                cur_parens += 1;
622            }
623            // we're at base and pushed a paren
624            if depth == max_no_paren_level && cur_parens == 0 {
625                max_no_paren_level += 1;
626                no_paren_inner = base_text;
627            }
628            depth += 1;
629        }
630
631        let top = match parse_num_simple(text, had_op, consts, locale, prec) {
632            Some(Number::Exact(n)) => n,
633            Some(Number::Approximate(_, n)) => {
634                depth += 1;
635                n
636            }
637            _ => {
638                *text = &orig_text[3..];
639                return None;
640            }
641        };
642
643        for _ in 0..cur_parens {
644            if text.starts_with("\\)") {
645                *text = &text[2..];
646            } else if text.starts_with(")") {
647                *text = &text[1..];
648            } else {
649                *text = &orig_text[2..];
650                return None;
651            }
652        }
653
654        // If postfix op in exponent, ingore that it's in exponent
655        if max_no_paren_level != 0 && text.starts_with(POSTFIX_OPS) {
656            *text = no_paren_inner;
657            return None;
658        }
659
660        if depth == 1 {
661            return Some(Number::ApproximateDigits(false, top));
662        } else {
663            return Some(Number::ApproximateDigitsTower(
664                false,
665                false,
666                (depth - 1).into(),
667                top,
668            ));
669        }
670    }
671
672    parse_num_simple(text, had_op, consts, locale, prec)
673}
674
675fn parse_num_simple(
676    text: &mut &str,
677    had_op: bool,
678    consts: &Consts<'_>,
679    locale: &NumFormat<'_>,
680    prec: u32,
681) -> Option<crate::calculation_results::CalculationResult> {
682    let integer_part = {
683        let end = text
684            .find(|c: char| (!c.is_numeric() && !SEPARATORS.contains(&c)) || &c == locale.decimal())
685            .unwrap_or(text.len());
686        let part = &text[..end];
687        *text = &text[end..];
688        part
689    };
690    let decimal_part = if text.starts_with(*locale.decimal()) {
691        *text = &text[1..];
692        let end = text
693            .find(|c: char| (!c.is_numeric() && !SEPARATORS.contains(&c)) || &c == locale.decimal())
694            .unwrap_or(text.len());
695        let part = &text[..end];
696        *text = &text[end..];
697        part
698    } else {
699        &text[..0]
700    };
701    let exponent_part = if text.starts_with(['e', 'E']) {
702        *text = &text[1..];
703        let negative = if text.starts_with('+') {
704            *text = &text[1..];
705            false
706        } else if text.starts_with('-') {
707            *text = &text[1..];
708            true
709        } else {
710            false
711        };
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, negative)
718    } else if !had_op
719        && (text.trim_start().starts_with("\\*10^")
720            || text.trim_start().starts_with("\\* 10^")
721            || text.trim_start().starts_with("\\*10\\^")
722            || text.trim_start().starts_with("\\* 10\\^")
723            || text.trim_start().starts_with("⨉10^")
724            || text.trim_start().starts_with("⨉ 10^")
725            || text.trim_start().starts_with("⨉10\\^")
726            || text.trim_start().starts_with("⨉ 10\\^")
727            || text.trim_start().starts_with("x10^")
728            || text.trim_start().starts_with("x 10^")
729            || text.trim_start().starts_with("x10\\^")
730            || text.trim_start().starts_with("x 10\\^"))
731    {
732        let pre_orig_text = &text[..];
733        let start = text.find("^").unwrap();
734        let orig_text = &text[start..];
735        *text = &text[start + 1..];
736        let paren = if text.starts_with('(') {
737            *text = &text[1..];
738            true
739        } else {
740            false
741        };
742        let negative = if text.starts_with('+') {
743            *text = &text[1..];
744            false
745        } else if text.starts_with('-') {
746            *text = &text[1..];
747            true
748        } else {
749            false
750        };
751        let end = text.find(|c: char| !c.is_numeric()).unwrap_or(text.len());
752        let part = &text[..end];
753        *text = &text[end..];
754        if paren {
755            if text.starts_with(')') {
756                *text = &text[1..];
757            } else {
758                *text = orig_text;
759                return None;
760            }
761        }
762        if text.starts_with(POSTFIX_OPS) {
763            //  10^(50)! => (10^50)!, 10^50! => 50!
764            // if !paren text ohne 10^ else text mit 10^
765            if paren {
766                *text = pre_orig_text;
767            } else {
768                *text = orig_text;
769            }
770            return None;
771        }
772        (part, negative)
773    } else {
774        (&text[..0], false)
775    };
776    let fraction_part = if !had_op && text.starts_with(['/']) {
777        *text = &text[1..];
778        let end = text
779            .find(|c: char| (!c.is_numeric() && !SEPARATORS.contains(&c)) || &c == locale.decimal())
780            .unwrap_or(text.len());
781        let part = &text[..end];
782        *text = &text[end..];
783        part
784    } else {
785        &text[..0]
786    };
787    if text.starts_with(POSTFIX_OPS) && !fraction_part.is_empty() {
788        let fraction_part = fraction_part.replace(SEPARATORS, "");
789        let n = fraction_part.parse::<Integer>().ok()?;
790        return Some(Number::Exact(n));
791    }
792    if integer_part.is_empty() && decimal_part.is_empty() {
793        return None;
794    }
795    let exponent = if !exponent_part.0.is_empty() {
796        let exponent_part = (exponent_part.0.replace(SEPARATORS, ""), exponent_part.1);
797        let mut e = exponent_part.0.parse::<Integer>().ok()?;
798        if exponent_part.1 {
799            e *= -1;
800        }
801        e
802    } else {
803        0.into()
804    };
805    let divisor = if !fraction_part.is_empty() {
806        let fraction_part = fraction_part.replace(SEPARATORS, "");
807        fraction_part.parse::<Integer>().ok()?
808    } else {
809        Integer::ONE.clone()
810    };
811    let integer_part = integer_part.replace(SEPARATORS, "");
812    let decimal_part = decimal_part.replace(SEPARATORS, "");
813    if exponent >= decimal_part.len() as i64
814        && exponent <= consts.integer_construction_limit.clone() - integer_part.len() as i64
815        && (divisor == 1 || exponent >= consts.integer_construction_limit.clone() / 10)
816    {
817        let exponent = exponent - decimal_part.len();
818        let n = format!("{integer_part}{decimal_part}")
819            .parse::<Integer>()
820            .ok()?;
821        let num = (n * Integer::u64_pow_u64(10, exponent.to_u64().unwrap()).complete()) / divisor;
822        Some(Number::Exact(num))
823    } else if exponent <= consts.integer_construction_limit.clone() - integer_part.len() as i64 {
824        let x = Float::parse(format!(
825            "{integer_part}.{decimal_part}{}{}{}",
826            if !exponent_part.0.is_empty() { "e" } else { "" },
827            if exponent_part.1 { "-" } else { "" },
828            exponent_part.0
829        ))
830        .ok()?;
831        let x = Float::with_val(prec, x) / divisor;
832        if x.is_integer() {
833            let n = x.to_integer().unwrap();
834            Some(Number::Exact(n))
835        } else if x.is_finite() {
836            Some(Number::Float(x.into()))
837        } else {
838            None
839        }
840    } else {
841        let x = Float::parse(format!("{integer_part}.{decimal_part}")).ok()?;
842        let x = Float::with_val(prec, x) / divisor;
843        if x.is_finite() {
844            let (b, e) = crate::math::adjust_approximate((x, exponent));
845            Some(Number::Approximate(b.into(), e))
846        } else {
847            None
848        }
849    }
850}
851
852#[cfg(test)]
853mod test {
854    use super::*;
855    use crate::calculation_tasks::CalculationBase::Num;
856    use arbtest::arbtest;
857
858    use crate::recommended::FLOAT_PRECISION;
859
860    #[test]
861    fn test_text_only() {
862        let consts = Consts::default();
863        let jobs = parse(
864            "just some words of encouragement!",
865            true,
866            &consts,
867            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
868        );
869        assert_eq!(jobs, []);
870    }
871    #[test]
872    fn test_factorial() {
873        let consts = Consts::default();
874        let jobs = parse(
875            "a factorial 15!",
876            true,
877            &consts,
878            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
879        );
880        assert_eq!(
881            jobs,
882            [CalculationJob {
883                base: CalculationBase::Num(15.into()),
884                level: 1,
885                negative: 0
886            }]
887        );
888    }
889    #[test]
890    fn test_multifactorial() {
891        let consts = Consts::default();
892        let jobs = parse(
893            "a factorial 15!!! actually a multi",
894            true,
895            &consts,
896            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
897        );
898        assert_eq!(
899            jobs,
900            [CalculationJob {
901                base: CalculationBase::Num(15.into()),
902                level: 3,
903                negative: 0
904            }]
905        );
906    }
907    #[test]
908    fn test_subfactorial() {
909        let consts = Consts::default();
910        let jobs = parse(
911            "a factorial !15 actually a sub",
912            true,
913            &consts,
914            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
915        );
916        assert_eq!(
917            jobs,
918            [CalculationJob {
919                base: CalculationBase::Num(15.into()),
920                level: 0,
921                negative: 0
922            }]
923        );
924    }
925    #[test]
926    fn test_submultifactorial() {
927        let consts = Consts::default();
928        let jobs = parse(
929            "not well defined !!!15",
930            true,
931            &consts,
932            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
933        );
934        assert_eq!(jobs, []);
935    }
936    #[test]
937    fn test_termial() {
938        let consts = Consts::default();
939        let jobs = parse(
940            "a termial 15?",
941            true,
942            &consts,
943            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
944        );
945        assert_eq!(
946            jobs,
947            [CalculationJob {
948                base: CalculationBase::Num(15.into()),
949                level: -1,
950                negative: 0
951            }]
952        );
953    }
954    #[test]
955    fn test_no_termial() {
956        let consts = Consts::default();
957        let jobs = parse(
958            "not enabled 15?",
959            false,
960            &consts,
961            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
962        );
963        assert_eq!(jobs, []);
964    }
965    #[test]
966    fn test_multitermial() {
967        let consts = Consts::default();
968        let jobs = parse(
969            "a termial 15??? actually a multi",
970            true,
971            &consts,
972            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
973        );
974        assert_eq!(
975            jobs,
976            [CalculationJob {
977                base: CalculationBase::Num(15.into()),
978                level: -3,
979                negative: 0
980            }]
981        );
982    }
983    #[test]
984    fn test_subtermial() {
985        let consts = Consts::default();
986        let jobs = parse(
987            "a termial ?15 actually a sub",
988            true,
989            &consts,
990            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
991        );
992        assert_eq!(jobs, []);
993    }
994    #[test]
995    fn test_chain() {
996        let consts = Consts::default();
997        let jobs = parse(
998            "a factorialchain (15!)!",
999            true,
1000            &consts,
1001            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1002        );
1003        assert_eq!(
1004            jobs,
1005            [CalculationJob {
1006                base: CalculationBase::Calc(Box::new(CalculationJob {
1007                    base: CalculationBase::Num(15.into()),
1008                    level: 1,
1009                    negative: 0
1010                })),
1011                level: 1,
1012                negative: 0
1013            }]
1014        );
1015    }
1016    #[test]
1017    fn test_mixed_chain() {
1018        let consts = Consts::default();
1019        let jobs = parse(
1020            "a factorialchain !(15!)",
1021            true,
1022            &consts,
1023            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1024        );
1025        assert_eq!(
1026            jobs,
1027            [CalculationJob {
1028                base: CalculationBase::Calc(Box::new(CalculationJob {
1029                    base: CalculationBase::Num(15.into()),
1030                    level: 1,
1031                    negative: 0
1032                })),
1033                level: 0,
1034                negative: 0
1035            }]
1036        );
1037    }
1038    #[test]
1039    fn test_postfix_chain() {
1040        let consts = Consts::default();
1041        let jobs = parse(
1042            "a factorialchain -15!?",
1043            true,
1044            &consts,
1045            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1046        );
1047        assert_eq!(
1048            jobs,
1049            [CalculationJob {
1050                base: CalculationBase::Calc(Box::new(CalculationJob {
1051                    base: CalculationBase::Num(15.into()),
1052                    level: 1,
1053                    negative: 0
1054                })),
1055                level: -1,
1056                negative: 1
1057            }]
1058        );
1059    }
1060    #[test]
1061    fn test_negative() {
1062        let consts = Consts::default();
1063        let jobs = parse(
1064            "a factorial ---15!",
1065            true,
1066            &consts,
1067            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1068        );
1069        assert_eq!(
1070            jobs,
1071            [CalculationJob {
1072                base: CalculationBase::Num(15.into()),
1073                level: 1,
1074                negative: 3
1075            }]
1076        );
1077    }
1078    #[test]
1079    fn test_negative_gap() {
1080        let consts = Consts::default();
1081        let jobs = parse(
1082            "a factorial --- 15!",
1083            true,
1084            &consts,
1085            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1086        );
1087        assert_eq!(
1088            jobs,
1089            [CalculationJob {
1090                base: CalculationBase::Num(15.into()),
1091                level: 1,
1092                negative: 0
1093            }]
1094        );
1095    }
1096    #[test]
1097    fn test_paren() {
1098        let consts = Consts::default();
1099        let jobs = parse(
1100            "a factorial (15)!",
1101            true,
1102            &consts,
1103            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1104        );
1105        assert_eq!(
1106            jobs,
1107            [CalculationJob {
1108                base: CalculationBase::Num(15.into()),
1109                level: 1,
1110                negative: 0
1111            }]
1112        );
1113    }
1114    #[test]
1115    fn test_in_paren() {
1116        let consts = Consts::default();
1117        let jobs = parse(
1118            "a factorial (15!)",
1119            true,
1120            &consts,
1121            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1122        );
1123        assert_eq!(
1124            jobs,
1125            [CalculationJob {
1126                base: CalculationBase::Num(15.into()),
1127                level: 1,
1128                negative: 0
1129            }]
1130        );
1131    }
1132    #[test]
1133    fn test_decimal() {
1134        let consts = Consts::default();
1135        let jobs = parse(
1136            "a factorial 1.5!",
1137            true,
1138            &consts,
1139            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1140        );
1141        assert_eq!(
1142            jobs,
1143            [CalculationJob {
1144                base: CalculationBase::Num(Float::with_val(FLOAT_PRECISION, 1.5).into()),
1145                level: 1,
1146                negative: 0
1147            }]
1148        );
1149    }
1150    #[test]
1151    fn test_paren_negation() {
1152        let consts = Consts::default();
1153        let jobs = parse(
1154            "a factorial -(--(-(-(-3))!))!",
1155            true,
1156            &consts,
1157            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1158        );
1159        assert_eq!(
1160            jobs,
1161            [CalculationJob {
1162                base: CalculationBase::Calc(Box::new(CalculationJob {
1163                    base: CalculationBase::Num(3.into()),
1164                    level: 1,
1165                    negative: 3
1166                })),
1167                level: 1,
1168                negative: 1
1169            }]
1170        );
1171    }
1172    #[test]
1173    fn test_tag() {
1174        let consts = Consts::default();
1175        let jobs = parse(
1176            ">!5 a factorial 15! !<",
1177            true,
1178            &consts,
1179            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1180        );
1181        assert_eq!(jobs, []);
1182    }
1183    #[test]
1184    fn test_incomplete_tag() {
1185        let consts = Consts::default();
1186        let jobs = parse(
1187            ">!5 a factorial 15!",
1188            true,
1189            &consts,
1190            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1191        );
1192        assert_eq!(
1193            jobs,
1194            [
1195                CalculationJob {
1196                    base: CalculationBase::Num(5.into()),
1197                    level: 0,
1198                    negative: 0
1199                },
1200                CalculationJob {
1201                    base: CalculationBase::Num(15.into()),
1202                    level: 1,
1203                    negative: 0
1204                }
1205            ]
1206        );
1207    }
1208    #[test]
1209    fn test_escaped_tag() {
1210        let consts = Consts::default();
1211        let jobs = parse(
1212            "\\>!5 a factorial 15! !<",
1213            true,
1214            &consts,
1215            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1216        );
1217        assert_eq!(
1218            jobs,
1219            [
1220                CalculationJob {
1221                    base: CalculationBase::Num(5.into()),
1222                    level: 0,
1223                    negative: 0
1224                },
1225                CalculationJob {
1226                    base: CalculationBase::Num(15.into()),
1227                    level: 1,
1228                    negative: 0
1229                }
1230            ]
1231        );
1232    }
1233    #[test]
1234    fn test_escaped_tag2() {
1235        let consts = Consts::default();
1236        let jobs = parse(
1237            ">!5 a factorial 15! \\!<",
1238            true,
1239            &consts,
1240            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1241        );
1242        assert_eq!(
1243            jobs,
1244            [
1245                CalculationJob {
1246                    base: CalculationBase::Num(5.into()),
1247                    level: 0,
1248                    negative: 0
1249                },
1250                CalculationJob {
1251                    base: CalculationBase::Num(15.into()),
1252                    level: 1,
1253                    negative: 0
1254                }
1255            ]
1256        );
1257    }
1258
1259    #[test]
1260    fn test_url() {
1261        let consts = Consts::default();
1262        let jobs = parse(
1263            "https://something.somewhere/with/path/and?tag=siufgiufgia3873844hi8743!hfsf",
1264            true,
1265            &consts,
1266            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1267        );
1268        assert_eq!(jobs, []);
1269    }
1270
1271    #[test]
1272    fn test_uri_poi_doesnt_cause_infinite_loop() {
1273        let consts = Consts::default();
1274        let jobs = parse(
1275            "84!:",
1276            true,
1277            &consts,
1278            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1279        );
1280        assert_eq!(
1281            jobs,
1282            [CalculationJob {
1283                base: Num(84.into()),
1284                level: 1,
1285                negative: 0
1286            }]
1287        );
1288    }
1289    #[test]
1290    fn test_escaped_url() {
1291        let consts = Consts::default();
1292        let jobs = parse(
1293            "\\://something.somewhere/with/path/and?tag=siufgiufgia3873844hi8743!hfsf",
1294            true,
1295            &consts,
1296            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1297        );
1298        assert_eq!(
1299            jobs,
1300            [CalculationJob {
1301                base: CalculationBase::Num(8743.into()),
1302                level: 1,
1303                negative: 0
1304            }]
1305        );
1306    }
1307
1308    #[test]
1309    fn test_word_in_paren() {
1310        let consts = Consts::default();
1311        let jobs = parse(
1312            "(x-2)! (2 word)! ((x/k)-3)! (,x-4)!",
1313            true,
1314            &consts,
1315            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1316        );
1317        assert_eq!(jobs, []);
1318    }
1319
1320    #[test]
1321    fn test_multi_number_paren() {
1322        let consts = Consts::default();
1323        let jobs = parse(
1324            "(5-2)!",
1325            true,
1326            &consts,
1327            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1328        );
1329        assert_eq!(jobs, []);
1330    }
1331    #[test]
1332    fn test_arbitrary_input() {
1333        let consts = Consts::default();
1334        arbtest(|u| {
1335            let text: &str = u.arbitrary()?;
1336            let _ = parse(
1337                text,
1338                u.arbitrary()?,
1339                &consts,
1340                &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1341            );
1342            Ok(())
1343        });
1344    }
1345
1346    #[test]
1347    fn test_constant() {
1348        let consts = Consts::default();
1349        let jobs = parse(
1350            "!espi!",
1351            true,
1352            &consts,
1353            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1354        );
1355        assert_eq!(jobs, []);
1356        let jobs = parse(
1357            "some. pi!",
1358            true,
1359            &consts,
1360            &consts.locales.get("en").unwrap().format().number_format(),
1361        );
1362        assert_eq!(
1363            jobs,
1364            [CalculationJob {
1365                base: CalculationBase::Num(Number::Float(
1366                    Float::with_val(FLOAT_PRECISION, factorion_math::rug::float::Constant::Pi)
1367                        .into()
1368                )),
1369                level: 1,
1370                negative: 0
1371            }]
1372        );
1373    }
1374
1375    #[test]
1376    fn test_fraction() {
1377        let consts = Consts::default();
1378        let jobs = parse(
1379            "!5/6!",
1380            true,
1381            &consts,
1382            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1383        );
1384        assert_eq!(
1385            jobs,
1386            [
1387                CalculationJob {
1388                    base: CalculationBase::Num(Number::Exact(5.into())),
1389                    level: 0,
1390                    negative: 0
1391                },
1392                CalculationJob {
1393                    base: CalculationBase::Num(Number::Exact(6.into())),
1394                    level: 1,
1395                    negative: 0
1396                }
1397            ]
1398        );
1399        let jobs = parse(
1400            "5/6!",
1401            true,
1402            &consts,
1403            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1404        );
1405        assert_eq!(
1406            jobs,
1407            [CalculationJob {
1408                base: CalculationBase::Num(Number::Exact(6.into())),
1409                level: 1,
1410                negative: 0
1411            }]
1412        );
1413        let jobs = parse(
1414            "(10/2)!",
1415            true,
1416            &consts,
1417            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1418        );
1419        assert_eq!(
1420            jobs,
1421            [CalculationJob {
1422                base: CalculationBase::Num(Number::Exact(5.into())),
1423                level: 1,
1424                negative: 0
1425            },]
1426        );
1427    }
1428
1429    #[test]
1430    fn test_parse_num() {
1431        let consts = Consts::default();
1432        let num = parse_num(
1433            &mut "1.5more !",
1434            false,
1435            false,
1436            &consts,
1437            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1438        );
1439        assert_eq!(
1440            num,
1441            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into()))
1442        );
1443        let num = parse_num(
1444            &mut "1,5more !",
1445            false,
1446            false,
1447            &consts,
1448            &NumFormat::V1(&locale::v1::NumFormat { decimal: ',' }),
1449        );
1450        assert_eq!(
1451            num,
1452            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into()))
1453        );
1454        let num = parse_num(
1455            &mut ".5more !",
1456            false,
1457            false,
1458            &consts,
1459            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1460        );
1461        assert_eq!(
1462            num,
1463            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1464        );
1465        let num = parse_num(
1466            &mut "1more !",
1467            false,
1468            true,
1469            &consts,
1470            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1471        );
1472        assert_eq!(num, Some(1.into()));
1473        let num = parse_num(
1474            &mut "1_000more !",
1475            false,
1476            true,
1477            &consts,
1478            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1479        );
1480        assert_eq!(num, Some(1000.into()));
1481        let num = parse_num(
1482            &mut "1,000more !",
1483            false,
1484            true,
1485            &consts,
1486            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1487        );
1488        assert_eq!(num, Some(1000.into()));
1489        let num = parse_num(
1490            &mut "1'000more !",
1491            false,
1492            true,
1493            &consts,
1494            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1495        );
1496        assert_eq!(num, Some(1000.into()));
1497        let num = parse_num(
1498            &mut "1.000more !",
1499            false,
1500            true,
1501            &consts,
1502            &NumFormat::V1(&locale::v1::NumFormat { decimal: ',' }),
1503        );
1504        assert_eq!(num, Some(1000.into()));
1505        let num = parse_num(
1506            &mut "1.000more !",
1507            false,
1508            true,
1509            &consts,
1510            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1511        );
1512        assert_eq!(num, Some(1.into()));
1513        let num = parse_num(
1514            &mut "1.0more !",
1515            true,
1516            false,
1517            &consts,
1518            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1519        );
1520        assert_eq!(num, Some(1.into()));
1521        let num = parse_num(
1522            &mut "1.5e2more !",
1523            false,
1524            false,
1525            &consts,
1526            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1527        );
1528        assert_eq!(num, Some(150.into()));
1529        let num = parse_num(
1530            &mut "1.5 ⨉ 10^2more !",
1531            false,
1532            false,
1533            &consts,
1534            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1535        );
1536        assert_eq!(num, Some(150.into()));
1537        let num = parse_num(
1538            &mut "1e2more !",
1539            false,
1540            false,
1541            &consts,
1542            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1543        );
1544        assert_eq!(num, Some(100.into()));
1545        let num = parse_num(
1546            &mut "1\\*10^(2)more !",
1547            false,
1548            false,
1549            &consts,
1550            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1551        );
1552        assert_eq!(num, Some(100.into()));
1553        let num = parse_num(
1554            &mut "1.531e2more !",
1555            false,
1556            false,
1557            &consts,
1558            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1559        );
1560        let Some(Number::Float(f)) = num else {
1561            panic!("Not a float")
1562        };
1563        assert!(Float::abs(f.as_float().clone() - 153.1) < 0.0000001);
1564        let num = parse_num(
1565            &mut "5e-1more !",
1566            false,
1567            false,
1568            &consts,
1569            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1570        );
1571        assert_eq!(
1572            num,
1573            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1574        );
1575        let num = parse_num(
1576            &mut "e2more !",
1577            true,
1578            false,
1579            &consts,
1580            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1581        );
1582        assert_eq!(num, None);
1583        let num = parse_num(
1584            &mut "es !",
1585            false,
1586            false,
1587            &consts,
1588            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1589        );
1590        assert_eq!(num, None);
1591        let num = parse_num(
1592            &mut "e !",
1593            false,
1594            false,
1595            &consts,
1596            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1597        );
1598        assert_eq!(num, Some(E(FLOAT_PRECISION)));
1599        let num = parse_num(
1600            &mut "pi !",
1601            false,
1602            false,
1603            &consts,
1604            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1605        );
1606        assert_eq!(num, Some(PI(FLOAT_PRECISION)));
1607        let num = parse_num(
1608            &mut "π !",
1609            false,
1610            false,
1611            &consts,
1612            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1613        );
1614        assert_eq!(num, Some(PI(FLOAT_PRECISION)));
1615        let num = parse_num(
1616            &mut "phi !",
1617            false,
1618            false,
1619            &consts,
1620            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1621        );
1622        assert_eq!(num, Some(PHI(FLOAT_PRECISION)));
1623        let num = parse_num(
1624            &mut "ɸ !",
1625            false,
1626            false,
1627            &consts,
1628            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1629        );
1630        assert_eq!(num, Some(PHI(FLOAT_PRECISION)));
1631        let num = parse_num(
1632            &mut "tau !",
1633            false,
1634            false,
1635            &consts,
1636            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1637        );
1638        assert_eq!(num, Some(TAU(FLOAT_PRECISION)));
1639        let num = parse_num(
1640            &mut "τ !",
1641            false,
1642            false,
1643            &consts,
1644            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1645        );
1646        assert_eq!(num, Some(TAU(FLOAT_PRECISION)));
1647        let num = parse_num(
1648            &mut "∞\u{0303} !",
1649            false,
1650            false,
1651            &consts,
1652            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1653        );
1654        assert_eq!(num, Some(Number::ComplexInfinity));
1655        let num = parse_num(
1656            &mut "∞ !",
1657            false,
1658            false,
1659            &consts,
1660            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1661        );
1662        assert_eq!(num, Some(Number::ComplexInfinity));
1663        let num = parse_num(
1664            &mut "1/2 !",
1665            false,
1666            false,
1667            &consts,
1668            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1669        );
1670        assert_eq!(
1671            num,
1672            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1673        );
1674        let num = parse_num(
1675            &mut "10/2 !",
1676            false,
1677            false,
1678            &consts,
1679            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1680        );
1681        assert_eq!(num, Some(Number::Exact(5.into())));
1682        let num = parse_num(
1683            &mut "1.5/2 !",
1684            false,
1685            false,
1686            &consts,
1687            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1688        );
1689        assert_eq!(
1690            num,
1691            Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.75).into()))
1692        );
1693        let num = parse_num(
1694            &mut "10e10000000000/2 !",
1695            false,
1696            false,
1697            &consts,
1698            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1699        );
1700        assert_eq!(
1701            num,
1702            Some(Number::Approximate(
1703                Float::with_val(FLOAT_PRECISION, 5).into(),
1704                10000000000u64.into()
1705            ))
1706        );
1707        let num = parse_num(
1708            &mut "10/2 !",
1709            false,
1710            true,
1711            &consts,
1712            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1713        );
1714        assert_eq!(num, Some(Number::Exact(10.into())));
1715        let num = parse_num(
1716            &mut "10/2!",
1717            false,
1718            false,
1719            &consts,
1720            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1721        );
1722        assert_eq!(num, Some(Number::Exact(2.into())));
1723        let num = parse_num(
1724            &mut "^(11)10!",
1725            false,
1726            false,
1727            &consts,
1728            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1729        );
1730        assert_eq!(num, None);
1731        let num = parse_num(
1732            &mut "^(11)10",
1733            false,
1734            false,
1735            &consts,
1736            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1737        );
1738        assert_eq!(
1739            num,
1740            Some(Number::ApproximateDigitsTower(
1741                false,
1742                false,
1743                10.into(),
1744                1.into()
1745            ))
1746        );
1747        let num = parse_num(
1748            &mut "10^10^10^5!",
1749            false,
1750            false,
1751            &consts,
1752            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1753        );
1754        assert_eq!(num, None);
1755        let num = parse_num(
1756            &mut "10^10^10^5",
1757            false,
1758            false,
1759            &consts,
1760            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1761        );
1762        assert_eq!(
1763            num,
1764            Some(Number::ApproximateDigitsTower(
1765                false,
1766                false,
1767                2.into(),
1768                5.into()
1769            ))
1770        );
1771        let num = parse_num(
1772            &mut "10^(10\\^10\\^\\(5\\))!",
1773            false,
1774            false,
1775            &consts,
1776            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1777        );
1778        assert_eq!(
1779            num,
1780            Some(Number::ApproximateDigitsTower(
1781                false,
1782                false,
1783                2.into(),
1784                5.into()
1785            ))
1786        );
1787        let num = parse_num(
1788            &mut "10^5!",
1789            false,
1790            false,
1791            &consts,
1792            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1793        );
1794        assert_eq!(num, None);
1795        let num = parse_num(
1796            &mut "10^5",
1797            false,
1798            false,
1799            &consts,
1800            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1801        );
1802        assert_eq!(num, Some(Number::ApproximateDigits(false, 5.into())));
1803        let num = parse_num(
1804            &mut "10^5",
1805            false,
1806            true,
1807            &consts,
1808            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1809        );
1810        assert_eq!(num, Some(Number::Exact(10.into())));
1811    }
1812    #[test]
1813    fn test_parse_num_revert() {
1814        // Note that we want one extra character when we get None, as in such a situation a char will always be skipped
1815        let consts = Consts::default();
1816        let mut text = "1 ⨉ 10^(5!)";
1817        let num = parse_num(
1818            &mut text,
1819            false,
1820            false,
1821            &consts,
1822            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1823        );
1824        assert_eq!(num, None);
1825        assert_eq!(text, "^(5!)");
1826        let mut text = "^(10 10";
1827        let num = parse_num(
1828            &mut text,
1829            false,
1830            false,
1831            &consts,
1832            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1833        );
1834        assert_eq!(num, None);
1835        assert_eq!(text, "^(10 10");
1836        let mut text = "^(10)1";
1837        let num = parse_num(
1838            &mut text,
1839            false,
1840            false,
1841            &consts,
1842            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1843        );
1844        assert_eq!(num, None);
1845        assert_eq!(text, "^(10)1");
1846        let mut text = "10^(10^10^\\(5\\)";
1847        let num = parse_num(
1848            &mut text,
1849            false,
1850            false,
1851            &consts,
1852            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1853        );
1854        assert_eq!(num, None);
1855        assert_eq!(text, "^(10^10^\\(5\\)");
1856        let mut text = "10^10^10^\\(5\\)!";
1857        let num = parse_num(
1858            &mut text,
1859            false,
1860            false,
1861            &consts,
1862            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1863        );
1864        assert_eq!(num, None);
1865        assert_eq!(text, "^10^\\(5\\)!");
1866        let mut text = "10^30!";
1867        let num = parse_num(
1868            &mut text,
1869            false,
1870            false,
1871            &consts,
1872            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1873        );
1874        assert_eq!(num, None);
1875        assert_eq!(text, "^30!");
1876    }
1877    #[allow(clippy::uninlined_format_args)]
1878    #[test]
1879    fn test_biggest_num() {
1880        let consts = Consts::default();
1881        let num = parse_num(
1882            &mut format!("9e{}", recommended::INTEGER_CONSTRUCTION_LIMIT()).as_str(),
1883            true,
1884            false,
1885            &consts,
1886            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1887        );
1888        assert!(matches!(num, Some(Number::Approximate(_, _))));
1889        let num = parse_num(
1890            &mut format!("9e{}", recommended::INTEGER_CONSTRUCTION_LIMIT() - 1).as_str(),
1891            false,
1892            false,
1893            &consts,
1894            &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1895        );
1896        assert!(num.is_some());
1897    }
1898}