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