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