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