Skip to main content

factorion_lib/
parse.rs

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