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_negative_decimal() {
1160 let consts = Consts::default();
1161 let jobs = parse(
1162 "a factorial (-1.5)!",
1163 true,
1164 &consts,
1165 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1166 );
1167 assert_eq!(
1168 jobs,
1169 [CalculationJob {
1170 base: CalculationBase::Num(Float::with_val(FLOAT_PRECISION, -1.5).into()),
1171 level: 1,
1172 negative: 0
1173 }]
1174 );
1175 }
1176 #[test]
1177 fn test_paren_negation() {
1178 let consts = Consts::default();
1179 let jobs = parse(
1180 "a factorial -(--(-(-(-3))!))!",
1181 true,
1182 &consts,
1183 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1184 );
1185 assert_eq!(
1186 jobs,
1187 [CalculationJob {
1188 base: CalculationBase::Calc(Box::new(CalculationJob {
1189 base: CalculationBase::Num(3.into()),
1190 level: 1,
1191 negative: 3
1192 })),
1193 level: 1,
1194 negative: 1
1195 }]
1196 );
1197 }
1198 #[test]
1199 fn test_tag() {
1200 let consts = Consts::default();
1201 let jobs = parse(
1202 ">!5 a factorial 15! !<",
1203 true,
1204 &consts,
1205 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1206 );
1207 assert_eq!(jobs, []);
1208 }
1209 #[test]
1210 fn test_incomplete_tag() {
1211 let consts = Consts::default();
1212 let jobs = parse(
1213 ">!5 a factorial 15!",
1214 true,
1215 &consts,
1216 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1217 );
1218 assert_eq!(
1219 jobs,
1220 [
1221 CalculationJob {
1222 base: CalculationBase::Num(5.into()),
1223 level: 0,
1224 negative: 0
1225 },
1226 CalculationJob {
1227 base: CalculationBase::Num(15.into()),
1228 level: 1,
1229 negative: 0
1230 }
1231 ]
1232 );
1233 }
1234 #[test]
1235 fn test_escaped_tag() {
1236 let consts = Consts::default();
1237 let jobs = parse(
1238 "\\>!5 a factorial 15! !<",
1239 true,
1240 &consts,
1241 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1242 );
1243 assert_eq!(
1244 jobs,
1245 [
1246 CalculationJob {
1247 base: CalculationBase::Num(5.into()),
1248 level: 0,
1249 negative: 0
1250 },
1251 CalculationJob {
1252 base: CalculationBase::Num(15.into()),
1253 level: 1,
1254 negative: 0
1255 }
1256 ]
1257 );
1258 }
1259 #[test]
1260 fn test_escaped_tag2() {
1261 let consts = Consts::default();
1262 let jobs = parse(
1263 ">!5 a factorial 15! \\!<",
1264 true,
1265 &consts,
1266 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1267 );
1268 assert_eq!(
1269 jobs,
1270 [
1271 CalculationJob {
1272 base: CalculationBase::Num(5.into()),
1273 level: 0,
1274 negative: 0
1275 },
1276 CalculationJob {
1277 base: CalculationBase::Num(15.into()),
1278 level: 1,
1279 negative: 0
1280 }
1281 ]
1282 );
1283 }
1284
1285 #[test]
1286 fn test_url() {
1287 let consts = Consts::default();
1288 let jobs = parse(
1289 "https://something.somewhere/with/path/and?tag=siufgiufgia3873844hi8743!hfsf",
1290 true,
1291 &consts,
1292 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1293 );
1294 assert_eq!(jobs, []);
1295 }
1296
1297 #[test]
1298 fn test_uri_poi_doesnt_cause_infinite_loop() {
1299 let consts = Consts::default();
1300 let jobs = parse(
1301 "84!:",
1302 true,
1303 &consts,
1304 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1305 );
1306 assert_eq!(
1307 jobs,
1308 [CalculationJob {
1309 base: Num(84.into()),
1310 level: 1,
1311 negative: 0
1312 }]
1313 );
1314 }
1315 #[test]
1316 fn test_escaped_url() {
1317 let consts = Consts::default();
1318 let jobs = parse(
1319 "\\://something.somewhere/with/path/and?tag=siufgiufgia3873844hi8743!hfsf",
1320 true,
1321 &consts,
1322 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1323 );
1324 assert_eq!(
1325 jobs,
1326 [CalculationJob {
1327 base: CalculationBase::Num(8743.into()),
1328 level: 1,
1329 negative: 0
1330 }]
1331 );
1332 }
1333
1334 #[test]
1335 fn test_word_in_paren() {
1336 let consts = Consts::default();
1337 let jobs = parse(
1338 "(x-2)! (2 word)! ((x/k)-3)! (,x-4)!",
1339 true,
1340 &consts,
1341 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1342 );
1343 assert_eq!(jobs, []);
1344 }
1345
1346 #[test]
1347 fn test_multi_number_paren() {
1348 let consts = Consts::default();
1349 let jobs = parse(
1350 "(5-2)!",
1351 true,
1352 &consts,
1353 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1354 );
1355 assert_eq!(jobs, []);
1356 }
1357 #[test]
1358 fn test_arbitrary_input() {
1359 let consts = Consts::default();
1360 arbtest(|u| {
1361 let text: &str = u.arbitrary()?;
1362 let _ = parse(
1363 text,
1364 u.arbitrary()?,
1365 &consts,
1366 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1367 );
1368 Ok(())
1369 });
1370 }
1371
1372 #[test]
1373 fn test_constant() {
1374 let consts = Consts::default();
1375 let jobs = parse(
1376 "!espi!",
1377 true,
1378 &consts,
1379 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1380 );
1381 assert_eq!(jobs, []);
1382 let jobs = parse(
1383 "some. pi!",
1384 true,
1385 &consts,
1386 &consts.locales.get("en").unwrap().format().number_format(),
1387 );
1388 assert_eq!(
1389 jobs,
1390 [CalculationJob {
1391 base: CalculationBase::Num(Number::Float(
1392 Float::with_val(FLOAT_PRECISION, factorion_math::rug::float::Constant::Pi)
1393 .into()
1394 )),
1395 level: 1,
1396 negative: 0
1397 }]
1398 );
1399 }
1400
1401 #[test]
1402 fn test_fraction() {
1403 let consts = Consts::default();
1404 let jobs = parse(
1405 "!5/6!",
1406 true,
1407 &consts,
1408 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1409 );
1410 assert_eq!(
1411 jobs,
1412 [
1413 CalculationJob {
1414 base: CalculationBase::Num(Number::Exact(5.into())),
1415 level: 0,
1416 negative: 0
1417 },
1418 CalculationJob {
1419 base: CalculationBase::Num(Number::Exact(6.into())),
1420 level: 1,
1421 negative: 0
1422 }
1423 ]
1424 );
1425 let jobs = parse(
1426 "5/6!",
1427 true,
1428 &consts,
1429 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1430 );
1431 assert_eq!(
1432 jobs,
1433 [CalculationJob {
1434 base: CalculationBase::Num(Number::Exact(6.into())),
1435 level: 1,
1436 negative: 0
1437 }]
1438 );
1439 let jobs = parse(
1440 "(10/2)!",
1441 true,
1442 &consts,
1443 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1444 );
1445 assert_eq!(
1446 jobs,
1447 [CalculationJob {
1448 base: CalculationBase::Num(Number::Exact(5.into())),
1449 level: 1,
1450 negative: 0
1451 },]
1452 );
1453 }
1454
1455 #[test]
1456 fn test_parse_num() {
1457 let consts = Consts::default();
1458 let num = parse_num(
1459 &mut "1.5more !",
1460 false,
1461 false,
1462 &consts,
1463 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1464 );
1465 assert_eq!(
1466 num,
1467 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into()))
1468 );
1469 let num = parse_num(
1470 &mut "1,5more !",
1471 false,
1472 false,
1473 &consts,
1474 &NumFormat::V1(&locale::v1::NumFormat { decimal: ',' }),
1475 );
1476 assert_eq!(
1477 num,
1478 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 1.5).into()))
1479 );
1480 let num = parse_num(
1481 &mut ".5more !",
1482 false,
1483 false,
1484 &consts,
1485 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1486 );
1487 let num = parse_num(
1488 &mut "1more !",
1489 false,
1490 true,
1491 &consts,
1492 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1493 );
1494 assert_eq!(num, Some(1.into()));
1495 let num = parse_num(
1496 &mut "1_000more !",
1497 false,
1498 true,
1499 &consts,
1500 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1501 );
1502 assert_eq!(num, Some(1000.into()));
1503 let num = parse_num(
1504 &mut "1,000more !",
1505 false,
1506 true,
1507 &consts,
1508 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1509 );
1510 assert_eq!(num, Some(1000.into()));
1511 let num = parse_num(
1512 &mut "1'000more !",
1513 false,
1514 true,
1515 &consts,
1516 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1517 );
1518 assert_eq!(num, Some(1000.into()));
1519 let num = parse_num(
1520 &mut "1.000more !",
1521 false,
1522 true,
1523 &consts,
1524 &NumFormat::V1(&locale::v1::NumFormat { decimal: ',' }),
1525 );
1526 assert_eq!(num, Some(1000.into()));
1527 let num = parse_num(
1528 &mut "1.000more !",
1529 false,
1530 true,
1531 &consts,
1532 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1533 );
1534 assert_eq!(num, Some(1.into()));
1535 let num = parse_num(
1536 &mut "1.0more !",
1537 true,
1538 false,
1539 &consts,
1540 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1541 );
1542 assert_eq!(num, Some(1.into()));
1543 let num = parse_num(
1544 &mut "1.5e2more !",
1545 false,
1546 false,
1547 &consts,
1548 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1549 );
1550 assert_eq!(num, Some(150.into()));
1551 let num = parse_num(
1552 &mut "1.5 ⨉ 10^2more !",
1553 false,
1554 false,
1555 &consts,
1556 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1557 );
1558 assert_eq!(num, Some(150.into()));
1559 let num = parse_num(
1560 &mut "1e2more !",
1561 false,
1562 false,
1563 &consts,
1564 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1565 );
1566 assert_eq!(num, Some(100.into()));
1567 let num = parse_num(
1568 &mut "1\\*10^(2)more !",
1569 false,
1570 false,
1571 &consts,
1572 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1573 );
1574 assert_eq!(num, Some(100.into()));
1575 let num = parse_num(
1576 &mut "1.531e2more !",
1577 false,
1578 false,
1579 &consts,
1580 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1581 );
1582 let Some(Number::Float(f)) = num else {
1583 panic!("Not a float")
1584 };
1585 assert!(Float::abs(f.as_float().clone() - 153.1) < 0.0000001);
1586 let num = parse_num(
1587 &mut "5e-1more !",
1588 false,
1589 false,
1590 &consts,
1591 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1592 );
1593 assert_eq!(
1594 num,
1595 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1596 );
1597 let num = parse_num(
1598 &mut "e2more !",
1599 true,
1600 false,
1601 &consts,
1602 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1603 );
1604 assert_eq!(num, None);
1605 let num = parse_num(
1606 &mut "es !",
1607 false,
1608 false,
1609 &consts,
1610 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1611 );
1612 assert_eq!(num, None);
1613 let num = parse_num(
1614 &mut "e !",
1615 false,
1616 false,
1617 &consts,
1618 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1619 );
1620 assert_eq!(num, Some(E(FLOAT_PRECISION)));
1621 let num = parse_num(
1622 &mut "pi !",
1623 false,
1624 false,
1625 &consts,
1626 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1627 );
1628 assert_eq!(num, Some(PI(FLOAT_PRECISION)));
1629 let num = parse_num(
1630 &mut "π !",
1631 false,
1632 false,
1633 &consts,
1634 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1635 );
1636 assert_eq!(num, Some(PI(FLOAT_PRECISION)));
1637 let num = parse_num(
1638 &mut "phi !",
1639 false,
1640 false,
1641 &consts,
1642 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1643 );
1644 assert_eq!(num, Some(PHI(FLOAT_PRECISION)));
1645 let num = parse_num(
1646 &mut "ɸ !",
1647 false,
1648 false,
1649 &consts,
1650 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1651 );
1652 assert_eq!(num, Some(PHI(FLOAT_PRECISION)));
1653 let num = parse_num(
1654 &mut "tau !",
1655 false,
1656 false,
1657 &consts,
1658 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1659 );
1660 assert_eq!(num, Some(TAU(FLOAT_PRECISION)));
1661 let num = parse_num(
1662 &mut "τ !",
1663 false,
1664 false,
1665 &consts,
1666 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1667 );
1668 assert_eq!(num, Some(TAU(FLOAT_PRECISION)));
1669 let num = parse_num(
1670 &mut "∞\u{0303} !",
1671 false,
1672 false,
1673 &consts,
1674 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1675 );
1676 assert_eq!(num, Some(Number::ComplexInfinity));
1677 let num = parse_num(
1678 &mut "∞ !",
1679 false,
1680 false,
1681 &consts,
1682 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1683 );
1684 assert_eq!(num, Some(Number::ComplexInfinity));
1685 let num = parse_num(
1686 &mut "1/2 !",
1687 false,
1688 false,
1689 &consts,
1690 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1691 );
1692 assert_eq!(
1693 num,
1694 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1695 );
1696 let num = parse_num(
1697 &mut "10/2 !",
1698 false,
1699 false,
1700 &consts,
1701 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1702 );
1703 assert_eq!(num, Some(Number::Exact(5.into())));
1704 let num = parse_num(
1705 &mut "1.5/2 !",
1706 false,
1707 false,
1708 &consts,
1709 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1710 );
1711 assert_eq!(
1712 num,
1713 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.75).into()))
1714 );
1715 let num = parse_num(
1716 &mut "10e10000000000/2 !",
1717 false,
1718 false,
1719 &consts,
1720 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1721 );
1722 assert_eq!(
1723 num,
1724 Some(Number::Approximate(
1725 Float::with_val(FLOAT_PRECISION, 5).into(),
1726 10000000000u64.into()
1727 ))
1728 );
1729 let num = parse_num(
1730 &mut "10/2 !",
1731 false,
1732 true,
1733 &consts,
1734 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1735 );
1736 assert_eq!(num, Some(Number::Exact(10.into())));
1737 let num = parse_num(
1738 &mut "10/2!",
1739 false,
1740 false,
1741 &consts,
1742 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1743 );
1744 assert_eq!(num, Some(Number::Exact(2.into())));
1745 let num = parse_num(
1746 &mut "^(11)10!",
1747 false,
1748 false,
1749 &consts,
1750 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1751 );
1752 assert_eq!(num, None);
1753 let num = parse_num(
1754 &mut "^(50)100!",
1755 false,
1756 false,
1757 &consts,
1758 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1759 );
1760 assert_eq!(num, None);
1761 let num = parse_num(
1762 &mut "^(50)1000!",
1763 false,
1764 false,
1765 &consts,
1766 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1767 );
1768 assert_eq!(num, None);
1769 let num = parse_num(
1770 &mut "^(50)10,000!",
1771 false,
1772 false,
1773 &consts,
1774 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1775 );
1776 assert_eq!(num, None);
1777 let num = parse_num(
1778 &mut "^(11)10",
1779 false,
1780 false,
1781 &consts,
1782 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1783 );
1784 assert_eq!(
1785 num,
1786 Some(Number::ApproximateDigitsTower(
1787 false,
1788 false,
1789 10.into(),
1790 1.into()
1791 ))
1792 );
1793 let num = parse_num(
1794 &mut "^(11,000)10",
1795 false,
1796 false,
1797 &consts,
1798 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1799 );
1800 assert_eq!(
1801 num,
1802 Some(Number::ApproximateDigitsTower(
1803 false,
1804 false,
1805 10999.into(),
1806 1.into()
1807 ))
1808 );
1809 let num = parse_num(
1810 &mut "10^10^10^5!",
1811 false,
1812 false,
1813 &consts,
1814 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1815 );
1816 assert_eq!(num, None);
1817 let num = parse_num(
1818 &mut "10^10^10^5",
1819 false,
1820 false,
1821 &consts,
1822 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1823 );
1824 assert_eq!(
1825 num,
1826 Some(Number::ApproximateDigitsTower(
1827 false,
1828 false,
1829 2.into(),
1830 5.into()
1831 ))
1832 );
1833 let num = parse_num(
1834 &mut "10^(10\\^10\\^\\(5\\))!",
1835 false,
1836 false,
1837 &consts,
1838 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1839 );
1840 assert_eq!(
1841 num,
1842 Some(Number::ApproximateDigitsTower(
1843 false,
1844 false,
1845 2.into(),
1846 5.into()
1847 ))
1848 );
1849 let num = parse_num(
1850 &mut "10^5!",
1851 false,
1852 false,
1853 &consts,
1854 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1855 );
1856 assert_eq!(num, None);
1857 let num = parse_num(
1858 &mut "10^5",
1859 false,
1860 false,
1861 &consts,
1862 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1863 );
1864 assert_eq!(num, Some(Number::ApproximateDigits(false, 5.into())));
1865 let num = parse_num(
1866 &mut "10^5",
1867 false,
1868 true,
1869 &consts,
1870 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1871 );
1872 assert_eq!(num, Some(Number::Exact(10.into())));
1873 }
1874 #[test]
1875 fn test_parse_num_revert() {
1876 let consts = Consts::default();
1878 let mut text = "1 ⨉ 10^(5!)";
1879 let num = parse_num(
1880 &mut text,
1881 false,
1882 false,
1883 &consts,
1884 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1885 );
1886 assert_eq!(num, None);
1887 assert_eq!(text, "^(5!)");
1888 let mut text = "^(10 10";
1889 let num = parse_num(
1890 &mut text,
1891 false,
1892 false,
1893 &consts,
1894 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1895 );
1896 assert_eq!(num, None);
1897 assert_eq!(text, "^(10 10");
1898 let mut text = "^(10)1";
1899 let num = parse_num(
1900 &mut text,
1901 false,
1902 false,
1903 &consts,
1904 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1905 );
1906 assert_eq!(num, None);
1907 assert_eq!(text, "^(10)1");
1908 let mut text = "10^(10^10^\\(5\\)";
1909 let num = parse_num(
1910 &mut text,
1911 false,
1912 false,
1913 &consts,
1914 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1915 );
1916 assert_eq!(num, None);
1917 assert_eq!(text, "^(10^10^\\(5\\)");
1918 let mut text = "10^10^10^\\(5\\)!";
1919 let num = parse_num(
1920 &mut text,
1921 false,
1922 false,
1923 &consts,
1924 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1925 );
1926 assert_eq!(num, None);
1927 assert_eq!(text, "^10^\\(5\\)!");
1928 let mut text = "10^30!";
1929 let num = parse_num(
1930 &mut text,
1931 false,
1932 false,
1933 &consts,
1934 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1935 );
1936 assert_eq!(num, None);
1937 assert_eq!(text, "^30!");
1938 }
1939
1940 #[test]
1941 fn test_parse_num_absorb() {
1942 let consts = Consts::default();
1944 }
1945
1946 #[allow(clippy::uninlined_format_args)]
1947 #[test]
1948 fn test_biggest_num() {
1949 let consts = Consts::default();
1950 let num = parse_num(
1951 &mut format!("9e{}", recommended::INTEGER_CONSTRUCTION_LIMIT()).as_str(),
1952 true,
1953 false,
1954 &consts,
1955 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1956 );
1957 assert!(matches!(num, Some(Number::Approximate(_, _))));
1958 let num = parse_num(
1959 &mut format!("9e{}", recommended::INTEGER_CONSTRUCTION_LIMIT() - 1).as_str(),
1960 false,
1961 false,
1962 &consts,
1963 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1964 );
1965 assert!(num.is_some());
1966 }
1967}