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