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 assert_eq!(
1488 num,
1489 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1490 );
1491 let num = parse_num(
1492 &mut "1more !",
1493 false,
1494 true,
1495 &consts,
1496 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1497 );
1498 assert_eq!(num, Some(1.into()));
1499 let num = parse_num(
1500 &mut "1_000more !",
1501 false,
1502 true,
1503 &consts,
1504 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1505 );
1506 assert_eq!(num, Some(1000.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(1.into()));
1539 let num = parse_num(
1540 &mut "1.0more !",
1541 true,
1542 false,
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.5e2more !",
1549 false,
1550 false,
1551 &consts,
1552 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1553 );
1554 assert_eq!(num, Some(150.into()));
1555 let num = parse_num(
1556 &mut "1.5 ⨉ 10^2more !",
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 "1e2more !",
1565 false,
1566 false,
1567 &consts,
1568 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1569 );
1570 assert_eq!(num, Some(100.into()));
1571 let num = parse_num(
1572 &mut "1\\*10^(2)more !",
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.531e2more !",
1581 false,
1582 false,
1583 &consts,
1584 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1585 );
1586 let Some(Number::Float(f)) = num else {
1587 panic!("Not a float")
1588 };
1589 assert!(Float::abs(f.as_float().clone() - 153.1) < 0.0000001);
1590 let num = parse_num(
1591 &mut "5e-1more !",
1592 false,
1593 false,
1594 &consts,
1595 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1596 );
1597 assert_eq!(
1598 num,
1599 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1600 );
1601 let num = parse_num(
1602 &mut "e2more !",
1603 true,
1604 false,
1605 &consts,
1606 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1607 );
1608 assert_eq!(num, None);
1609 let num = parse_num(
1610 &mut "es !",
1611 false,
1612 false,
1613 &consts,
1614 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1615 );
1616 assert_eq!(num, None);
1617 let num = parse_num(
1618 &mut "e !",
1619 false,
1620 false,
1621 &consts,
1622 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1623 );
1624 assert_eq!(num, Some(E(FLOAT_PRECISION)));
1625 let num = parse_num(
1626 &mut "pi !",
1627 false,
1628 false,
1629 &consts,
1630 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1631 );
1632 assert_eq!(num, Some(PI(FLOAT_PRECISION)));
1633 let num = parse_num(
1634 &mut "π !",
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 "phi !",
1643 false,
1644 false,
1645 &consts,
1646 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1647 );
1648 assert_eq!(num, Some(PHI(FLOAT_PRECISION)));
1649 let num = parse_num(
1650 &mut "ɸ !",
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 "tau !",
1659 false,
1660 false,
1661 &consts,
1662 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1663 );
1664 assert_eq!(num, Some(TAU(FLOAT_PRECISION)));
1665 let num = parse_num(
1666 &mut "τ !",
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 "∞\u{0303} !",
1675 false,
1676 false,
1677 &consts,
1678 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1679 );
1680 assert_eq!(num, Some(Number::ComplexInfinity));
1681 let num = parse_num(
1682 &mut "∞ !",
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 "1/2 !",
1691 false,
1692 false,
1693 &consts,
1694 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1695 );
1696 assert_eq!(
1697 num,
1698 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.5).into()))
1699 );
1700 let num = parse_num(
1701 &mut "10/2 !",
1702 false,
1703 false,
1704 &consts,
1705 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1706 );
1707 assert_eq!(num, Some(Number::Exact(5.into())));
1708 let num = parse_num(
1709 &mut "1.5/2 !",
1710 false,
1711 false,
1712 &consts,
1713 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1714 );
1715 assert_eq!(
1716 num,
1717 Some(Number::Float(Float::with_val(FLOAT_PRECISION, 0.75).into()))
1718 );
1719 let num = parse_num(
1720 &mut "10e10000000000/2 !",
1721 false,
1722 false,
1723 &consts,
1724 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1725 );
1726 assert_eq!(
1727 num,
1728 Some(Number::Approximate(
1729 Float::with_val(FLOAT_PRECISION, 5).into(),
1730 10000000000u64.into()
1731 ))
1732 );
1733 let num = parse_num(
1734 &mut "10/2 !",
1735 false,
1736 true,
1737 &consts,
1738 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1739 );
1740 assert_eq!(num, Some(Number::Exact(10.into())));
1741 let num = parse_num(
1742 &mut "10/2!",
1743 false,
1744 false,
1745 &consts,
1746 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1747 );
1748 assert_eq!(num, Some(Number::Exact(2.into())));
1749 let num = parse_num(
1750 &mut "^(11)10!",
1751 false,
1752 false,
1753 &consts,
1754 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1755 );
1756 assert_eq!(num, None);
1757 let num = parse_num(
1758 &mut "^(50)100!",
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)1000!",
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)10,000!",
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 "^(11)10",
1783 false,
1784 false,
1785 &consts,
1786 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1787 );
1788 assert_eq!(
1789 num,
1790 Some(Number::ApproximateDigitsTower(
1791 false,
1792 false,
1793 10.into(),
1794 1.into()
1795 ))
1796 );
1797 let num = parse_num(
1798 &mut "^(11,000)10",
1799 false,
1800 false,
1801 &consts,
1802 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1803 );
1804 assert_eq!(
1805 num,
1806 Some(Number::ApproximateDigitsTower(
1807 false,
1808 false,
1809 10999.into(),
1810 1.into()
1811 ))
1812 );
1813 let num = parse_num(
1814 &mut "10^10^10^5!",
1815 false,
1816 false,
1817 &consts,
1818 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1819 );
1820 assert_eq!(num, None);
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!(
1829 num,
1830 Some(Number::ApproximateDigitsTower(
1831 false,
1832 false,
1833 2.into(),
1834 5.into()
1835 ))
1836 );
1837 let num = parse_num(
1838 &mut "10^(10\\^10\\^\\(5\\))!",
1839 false,
1840 false,
1841 &consts,
1842 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1843 );
1844 assert_eq!(
1845 num,
1846 Some(Number::ApproximateDigitsTower(
1847 false,
1848 false,
1849 2.into(),
1850 5.into()
1851 ))
1852 );
1853 let num = parse_num(
1854 &mut "10^5!",
1855 false,
1856 false,
1857 &consts,
1858 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1859 );
1860 assert_eq!(num, None);
1861 let num = parse_num(
1862 &mut "10^5",
1863 false,
1864 false,
1865 &consts,
1866 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1867 );
1868 assert_eq!(num, Some(Number::ApproximateDigits(false, 5.into())));
1869 let num = parse_num(
1870 &mut "10^5",
1871 false,
1872 true,
1873 &consts,
1874 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1875 );
1876 assert_eq!(num, Some(Number::Exact(10.into())));
1877 }
1878 #[test]
1879 fn test_parse_num_revert() {
1880 let consts = Consts::default();
1882 let mut text = "1 ⨉ 10^(5!)";
1883 let num = parse_num(
1884 &mut text,
1885 false,
1886 false,
1887 &consts,
1888 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1889 );
1890 assert_eq!(num, None);
1891 assert_eq!(text, "^(5!)");
1892 let mut text = "^(10 10";
1893 let num = parse_num(
1894 &mut text,
1895 false,
1896 false,
1897 &consts,
1898 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1899 );
1900 assert_eq!(num, None);
1901 assert_eq!(text, "^(10 10");
1902 let mut text = "^(10)1";
1903 let num = parse_num(
1904 &mut text,
1905 false,
1906 false,
1907 &consts,
1908 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1909 );
1910 assert_eq!(num, None);
1911 assert_eq!(text, "^(10)1");
1912 let mut text = "10^(10^10^\\(5\\)";
1913 let num = parse_num(
1914 &mut text,
1915 false,
1916 false,
1917 &consts,
1918 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1919 );
1920 assert_eq!(num, None);
1921 assert_eq!(text, "^(10^10^\\(5\\)");
1922 let mut text = "10^10^10^\\(5\\)!";
1923 let num = parse_num(
1924 &mut text,
1925 false,
1926 false,
1927 &consts,
1928 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1929 );
1930 assert_eq!(num, None);
1931 assert_eq!(text, "^10^\\(5\\)!");
1932 let mut text = "10^30!";
1933 let num = parse_num(
1934 &mut text,
1935 false,
1936 false,
1937 &consts,
1938 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1939 );
1940 assert_eq!(num, None);
1941 assert_eq!(text, "^30!");
1942 }
1943
1944 #[test]
1945 fn test_parse_num_absorb() {
1946 let consts = Consts::default();
1948 }
1949
1950 #[allow(clippy::uninlined_format_args)]
1951 #[test]
1952 fn test_biggest_num() {
1953 let consts = Consts::default();
1954 let num = parse_num(
1955 &mut format!("9e{}", recommended::INTEGER_CONSTRUCTION_LIMIT()).as_str(),
1956 true,
1957 false,
1958 &consts,
1959 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1960 );
1961 assert!(matches!(num, Some(Number::Approximate(_, _))));
1962 let num = parse_num(
1963 &mut format!("9e{}", recommended::INTEGER_CONSTRUCTION_LIMIT() - 1).as_str(),
1964 false,
1965 false,
1966 &consts,
1967 &NumFormat::V1(&locale::v1::NumFormat { decimal: '.' }),
1968 );
1969 assert!(num.is_some());
1970 }
1971}