Skip to main content

factorion_lib/
parse.rs

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