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