1use crate::locale::NumFormat;
4use crate::rug::{Complete, Float, Integer, integer::IntegerExt64};
5
6use crate::Consts;
7use crate::{
8 calculation_results::Number,
9 calculation_tasks::{CalculationBase, CalculationJob},
10};
11
12pub mod recommended {
13 use factorion_math::rug::Integer;
14
15 pub static INTEGER_CONSTRUCTION_LIMIT: fn() -> Integer = || 200_000_000u128.into();
16}
17
18const POI_STARTS: &[char] = &[
21 NEGATION,
22 '!', '.', ESCAPE,
25 '0', '1',
27 '2',
28 '3',
29 '4',
30 '5',
31 '6',
32 '7',
33 '8',
34 '9',
35 'p', 'e',
37 't',
38 'i',
39 'π',
40 'ɸ',
41 'τ',
42 '∞',
43 '^', 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 = ">!";
61const SPOILER_HTML_END: &str = "!<";
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 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 let Some(position_of_interest) = text.find(POI_STARTS) else {
155 break;
156 };
157 if position_of_interest != 0 {
158 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 text = &text[position_of_interest..];
170 if text.starts_with(ESCAPE) {
171 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 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 let mut end = 0;
192 loop {
193 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 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 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 let mut end = 0;
220 loop {
221 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 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 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 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_steps.push((current_negative, None, false));
254 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 text = &text[PAREN_END.len_utf8()..];
264 current_negative = 0;
265 let Some(step) = paren_steps.pop() else {
267 continue;
268 };
269 if step.2 {
271 if let Some(CalculationBase::Calc(job)) = base.take() {
272 jobs.push(*job);
273 }
274 if let Some(step) = paren_steps.last_mut() {
276 step.2 = true;
277 }
278 continue;
279 }
280 let mut had_op = false;
281 if let Some(level) = step.1 {
283 let Some(inner) = base.take() else {
285 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 let Some(levels) = parse_ops(&mut text, false, do_termial) else {
305 base.take();
306 if let Some(step) = paren_steps.last_mut() {
308 step.2 = true;
309 }
310 continue;
311 };
312 if !levels.is_empty() {
313 for level in levels {
315 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 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 let Ok(level) = parse_op(&mut text, true, do_termial) else {
357 parse_num(&mut text, false, true, consts, locale);
359 continue;
360 };
361 if let Some(num) = parse_num(&mut text, false, true, consts, locale) {
363 if let Some(CalculationBase::Calc(job)) = base.take() {
365 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 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 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 if text.starts_with('.') && !text[1..].starts_with(char::is_numeric) {
406 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 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 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 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 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 if let Some(step) = paren_steps.last_mut() {
463 step.2 = true;
464 }
465 }
466 }
467 }
468 current_negative = 0;
469 };
470 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 && !(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 *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 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 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 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 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 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}