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