1use crate::functions::{functions_with_args, options_list, units_list};
2use crate::{
3 complex::{
4 NumStr,
5 NumStr::{
6 And, Comma, Converse, Conversion, Division, Equal, Exponent, Func, Greater,
7 GreaterEqual, Implies, InternalMultiplication, LeftBracket, LeftCurlyBracket, Lesser,
8 LesserEqual, Matrix, Minus, Modulo, Multiplication, Nand, NearEqual, Nor, Not,
9 NotEqual, Num, Or, Plus, PlusMinus, Range, RightBracket, RightCurlyBracket, Root,
10 ShiftLeft, ShiftRight, Tetration, Vector, Xor,
11 },
12 },
13 functions::functions,
14 math::do_math,
15 print::{custom_units, get_output},
16 units::{Auto, Colors, Options, Variable},
17};
18#[cfg(feature = "bin-deps")]
19use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, read};
20#[cfg(feature = "bin-deps")]
21#[cfg(unix)]
22use libc::{STDOUT_FILENO, TIOCGWINSZ, ioctl, winsize};
23use std::path::PathBuf;
24use std::process::Command;
25use std::{fs::File, io::Write};
26#[cfg(not(unix))]
27#[cfg(feature = "bin-deps")]
28use term_size::dimensions;
29#[cfg(feature = "bin-deps")]
30#[cfg(unix)]
31pub fn get_terminal_dimensions() -> (usize, usize) {
32 unsafe {
33 let mut size: winsize = std::mem::zeroed();
34 if ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) == 0 && size.ws_col != 0 {
35 (size.ws_col as usize, size.ws_row as usize)
36 } else {
37 (80, 80)
38 }
39 }
40}
41#[cfg(feature = "bin-deps")]
42#[cfg(unix)]
43pub fn get_terminal_dimensions_pixel() -> (usize, usize) {
44 unsafe {
45 let mut size: winsize = std::mem::zeroed();
46 if ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) == 0 && size.ws_col != 0 {
47 (size.ws_xpixel as usize, size.ws_ypixel as usize)
48 } else {
49 (80, 80)
50 }
51 }
52}
53#[cfg(feature = "bin-deps")]
54#[cfg(not(unix))]
55pub fn get_terminal_dimensions() -> (usize, usize) {
56 if let Some((width, height)) = dimensions() {
57 (width, height)
58 } else {
59 (80, 80)
60 }
61}
62#[cfg(not(feature = "bin-deps"))]
63pub fn get_terminal_dimensions() -> (usize, usize) {
64 (usize::MAX, usize::MAX)
65}
66#[cfg(not(feature = "bin-deps"))]
67pub fn get_terminal_dimensions_pixel() -> (usize, usize) {
68 (usize::MAX, usize::MAX)
69}
70#[cfg(feature = "bin-deps")]
71pub fn digraph(char: Option<char>) -> char {
72 match if let Some(c) = char {
73 c
74 } else {
75 read_single_char()
76 } {
77 ';' => '°',
78 'a' => 'α',
79 'A' => 'Α',
80 'b' => 'β',
81 'B' => 'Β',
82 'c' => 'ξ',
83 'C' => 'Ξ',
84 'd' => 'Δ',
85 'D' => 'δ',
86 'e' => 'ε',
87 'E' => 'Ε',
88 'f' => 'φ',
89 'F' => 'Φ',
90 'g' => 'γ',
91 'G' => 'Γ',
92 'h' => 'η',
93 'H' => 'Η',
94 'i' => 'ι',
95 'I' => 'Ι',
96 'k' => 'κ',
97 'K' => 'Κ',
98 'l' => 'λ',
99 'L' => 'Λ',
100 'm' => 'μ',
101 'M' => 'Μ',
102 'n' => 'ν',
103 'N' => 'Ν',
104 'o' => 'ο',
105 'O' => 'Ο',
106 'p' => 'π',
107 'P' => 'Π',
108 'q' => 'θ',
109 'Q' => 'Θ',
110 'r' => 'ρ',
111 'R' => 'Ρ',
112 's' => 'σ',
113 'S' => 'Σ',
114 't' => 'τ',
115 'T' => 'Τ',
116 'u' => 'υ',
117 'U' => 'Υ',
118 'w' => 'ω',
119 'W' => 'Ω',
120 'y' => 'ψ',
121 'Y' => 'Ψ',
122 'x' => 'χ',
123 'X' => 'Χ',
124 'z' => 'ζ',
125 'Z' => 'Ζ',
126 '0' => '⁰',
127 '9' => '⁹',
128 '8' => '⁸',
129 '7' => '⁷',
130 '6' => '⁶',
131 '5' => '⁵',
132 '4' => '⁴',
133 '3' => '³',
134 '2' => '²',
135 '1' => '¹',
136 '-' => '⁻',
137 '`' => 'ⁱ',
138 '+' => '±',
139 '=' => '≈',
140 '_' => '∞',
141 '\n' => '\n',
142 '\x08' => '\x08',
143 '\x7F' => '\x7F',
144 '\x1B' => '\x1B',
145 '\x1C' => '\x1C',
146 '\x1D' => '\x1D',
147 '\x1E' => '\x1E',
148 '\x1A' => '\x1A',
149 '\x10' => '\x10',
150 '\x11' => '\x11',
151 '\x12' => '\x12',
152 '\x13' => '\x13',
153 _ => '\0',
154 }
155}
156pub fn convert(c: &char) -> char {
157 let valid_chars = [
158 '+', '^', '(', ')', '.', '=', ',', '#', '|', '&', '!', '%', '_', '<', '>', ' ', '[', ']',
159 '{', '}', '√', '∛', '⁻', 'ⁱ', '`', '±', '∞', ';', ':', '\'', '⌊', '⌈', '⌉', '⌋', '∫', '°',
160 '$', '¢', '≈', '~', '¬',
161 ];
162 match c {
163 c if c.is_alphanumeric() || valid_chars.contains(c) => *c,
164 '∗' | '∙' | '·' | '⋅' | '*' | '×' => '*',
165 '∕' | '⁄' | '/' | '÷' => '/',
166 '−' | '-' => '-',
167 _ => '\0',
168 }
169}
170#[cfg(feature = "bin-deps")]
171pub fn read_single_char() -> char {
172 match match read() {
173 Ok(c) => c,
174 _ => return '\0',
175 } {
176 Event::Key(KeyEvent {
177 code,
178 modifiers,
179 kind,
180 ..
181 }) => {
182 if kind == KeyEventKind::Press {
183 match (code, modifiers) {
184 (KeyCode::Char('c'), KeyModifiers::CONTROL) => '\x14',
185 (KeyCode::Home, KeyModifiers::NONE)
186 | (KeyCode::Char('a'), KeyModifiers::CONTROL) => '\x10',
187 (KeyCode::End, KeyModifiers::NONE)
188 | (KeyCode::Char('e'), KeyModifiers::CONTROL) => '\x11',
189 (KeyCode::Left, KeyModifiers::CONTROL)
190 | (KeyCode::Char('b'), KeyModifiers::ALT) => '\x12',
191 (KeyCode::Right, KeyModifiers::CONTROL)
192 | (KeyCode::Char('f'), KeyModifiers::ALT) => '\x13',
193 (KeyCode::Char('l'), KeyModifiers::CONTROL) => '\x15',
194 (KeyCode::Char('t'), KeyModifiers::CONTROL) => '\x16',
195 (KeyCode::Char('u'), KeyModifiers::CONTROL) => '\x18',
196 (KeyCode::Char('k'), KeyModifiers::CONTROL) => '\x19',
197 (KeyCode::Char('y'), KeyModifiers::CONTROL) => '\x17',
198 (KeyCode::Char('d'), KeyModifiers::CONTROL) => '\x06',
199 (KeyCode::Char('p'), KeyModifiers::CONTROL) => '\x05',
200 (KeyCode::Char('n'), KeyModifiers::CONTROL) => '\x04',
201 (KeyCode::Char('d'), KeyModifiers::ALT) => '\x0C',
202 (KeyCode::Char('w'), KeyModifiers::CONTROL) => '\x0D',
203 (KeyCode::Char('x'), KeyModifiers::CONTROL) => '\x0E',
204 (KeyCode::Char('t'), KeyModifiers::ALT) => '\x0F',
205 (KeyCode::Char(c), KeyModifiers::NONE | KeyModifiers::SHIFT) => convert(&c),
206 (KeyCode::Char(c), KeyModifiers::ALT) => digraph(Some(c)),
207 (KeyCode::Esc, _) => digraph(None),
208 (KeyCode::Enter, KeyModifiers::NONE) => '\n',
209 (KeyCode::Enter, KeyModifiers::ALT) => '\x09',
210 (KeyCode::Char('h'), KeyModifiers::CONTROL) => '\x03',
211 (KeyCode::Backspace, KeyModifiers::NONE) => '\x08',
212 (KeyCode::Delete, KeyModifiers::NONE) => '\x7F',
213 (KeyCode::Left, KeyModifiers::NONE)
214 | (KeyCode::Char('b'), KeyModifiers::CONTROL) => '\x1B',
215 (KeyCode::Right, KeyModifiers::NONE)
216 | (KeyCode::Char('f'), KeyModifiers::CONTROL) => '\x1C',
217 (KeyCode::Up, KeyModifiers::NONE) => '\x1D',
218 (KeyCode::Down, KeyModifiers::NONE) => '\x1E',
219 (KeyCode::Tab, KeyModifiers::NONE) => '\x1F',
220 _ => '\0',
221 }
222 } else {
223 '\0'
224 }
225 }
226 _ => '\0',
227 }
228}
229pub fn end_word(c: char) -> bool {
230 matches!(
231 c,
232 '(' | '{'
233 | '['
234 | ')'
235 | '}'
236 | ']'
237 | '+'
238 | '-'
239 | '*'
240 | '/'
241 | '^'
242 | '<'
243 | '='
244 | '>'
245 | '|'
246 | '&'
247 | '!'
248 | '±'
249 | '%'
250 | ';'
251 )
252}
253pub fn no_col(input: &str, color: bool) -> Vec<char> {
254 if color {
255 let mut skip = false;
256 let mut output = String::new();
257 for c in input.chars() {
258 if skip {
259 if c == 'm' || c == 'G' || c == 'K' {
260 skip = false
261 }
262 } else if c == '\x1b' {
263 skip = true
264 } else {
265 output.push(c)
266 }
267 }
268 output.chars().collect::<Vec<char>>()
269 } else {
270 input.chars().collect::<Vec<char>>()
271 }
272}
273pub fn no_col_len(input: &str, color: bool) -> usize {
274 if color {
275 let mut skip = false;
276 let mut count = 0;
277 for c in input.chars() {
278 if skip {
279 if c == 'm' || c == 'G' || c == 'K' {
280 skip = false
281 }
282 } else if c == '\x1b' {
283 skip = true
284 } else {
285 count += 1
286 }
287 }
288 count
289 } else {
290 input.len()
291 }
292}
293pub fn write(
294 mut input: String,
295 file: &mut File,
296 lines: &mut Vec<String>,
297 slow: bool,
298 last: String,
299) {
300 if last != input && !input.replace(' ', "").is_empty() && !input.starts_with('#') {
301 if slow {
302 input.push('\t');
303 }
304 lines.push(input.clone());
305 writeln!(file, "{input}").unwrap();
306 }
307}
308pub fn clearln(
309 input: &[char],
310 vars: &[Variable],
311 start: usize,
312 end: usize,
313 options: Options,
314 colors: &Colors,
315) {
316 print!(
317 "\x1b[G{}{}\x1b[K{}",
318 prompt(options, colors),
319 to_output(
320 &input[start..end],
321 vars,
322 options.color == Auto::True,
323 colors
324 ),
325 if options.color == Auto::True {
326 "\x1b[0m"
327 } else {
328 ""
329 }
330 );
331}
332pub fn clear(
333 input: &[char],
334 vars: &[Variable],
335 start: usize,
336 end: usize,
337 options: Options,
338 colors: &Colors,
339) {
340 print!(
341 "\x1b[G{}{}\x1b[J{}",
342 prompt(options, colors),
343 to_output(
344 &input[start..end],
345 vars,
346 options.color == Auto::True,
347 colors
348 ),
349 if options.color == Auto::True {
350 "\x1b[0m"
351 } else {
352 ""
353 }
354 );
355}
356pub fn to_output(input: &[char], vars: &[Variable], color: bool, colors: &Colors) -> String {
357 if color {
358 let mut count: isize = (input
359 .iter()
360 .filter(|a| matches!(a, ')' | '}' | ']'))
361 .count() as isize
362 - input
363 .iter()
364 .filter(|a| matches!(a, '(' | '{' | '['))
365 .count() as isize)
366 .max(0);
367 let mut output = String::new();
368 let mut abs: Vec<(usize, isize)> = Vec::new();
369 let mut i = 0;
370 let mut ignore = false;
371 while i < input.len() {
372 let c = input[i];
373 match c {
374 '#' => {
375 count = 0;
376 output.push(c)
377 }
378 '\x1b' => {
379 ignore = true;
380 output.push(c)
381 }
382 '[' if ignore => output.push(c),
383 '|' => {
384 if !abs.is_empty()
385 && abs[0].1 == count
386 && input[abs[0].0 + 1..i]
387 .iter()
388 .filter(|c| !c.is_ascii_whitespace())
389 .count()
390 != 0
391 && match input[..i].iter().rev().position(|c| !c.is_alphabetic()) {
392 Some(n) => {
393 let s = input[i - n..i].iter().collect::<String>();
394 let sb = &(s.clone() + "(");
395 !(functions().contains(s.as_str())
396 || vars
397 .iter()
398 .any(|c| c.name.iter().collect::<String>().starts_with(sb)))
399 }
400 _ => true,
401 }
402 {
403 count -= 1;
404 abs.remove(0);
405 output.push_str(&format!(
406 "{}|{}",
407 colors.brackets[count as usize % colors.brackets.len()],
408 colors.text
409 ))
410 } else if i + 1 == input.len()
411 || input[i + 1] != '|'
412 || i == 0
413 || input[0..=i - 1]
414 .iter()
415 .rev()
416 .filter(|c| !c.is_ascii_whitespace())
417 .take(1)
418 .all(|c| matches!(c, '(' | '{' | '[' | '|'))
419 {
420 output.push_str(&format!(
421 "{}|{}",
422 colors.brackets[count as usize % colors.brackets.len()],
423 colors.text
424 ));
425 count += 1;
426 abs.insert(0, (i, count));
427 } else {
428 i += 1;
429 output.push_str("||")
430 }
431 }
432 '(' | '{' | '[' => {
433 output.push_str(&format!(
434 "{}{}{}",
435 colors.brackets[count as usize % colors.brackets.len()],
436 c,
437 colors.text
438 ));
439 count += 1
440 }
441 ')' | '}' | ']' => {
442 count -= 1;
443 output.push_str(&format!(
444 "{}{}{}",
445 colors.brackets[count as usize % colors.brackets.len()],
446 c,
447 colors.text
448 ))
449 }
450 '@' => {}
451 _ => output.push(c),
452 }
453 i += 1;
454 }
455 output
456 } else {
457 input.iter().collect::<String>()
458 }
459}
460pub fn handle_err(
461 err: &str,
462 vars: &[Variable],
463 input: &[char],
464 options: Options,
465 colors: &Colors,
466 start: usize,
467 end: usize,
468) {
469 let num = if cfg!(feature = "bin-deps") {
470 err.len().div_ceil(get_terminal_dimensions().0) - 1
471 } else {
472 err.len()
473 };
474 print!(
475 "\x1b[J\x1b[G\n{}{}\x1b[G\x1b[A\x1b[K{}{}{}",
476 err,
477 if num == 0 {
478 String::new()
479 } else {
480 format!("\x1b[{num}A")
481 },
482 prompt(options, colors),
483 to_output(
484 &input[start..end],
485 vars,
486 options.color == Auto::True,
487 colors
488 ),
489 if options.color == Auto::True {
490 "\x1b[0m"
491 } else {
492 ""
493 },
494 );
495}
496pub fn prompt(options: Options, colors: &Colors) -> String {
497 if !options.interactive {
498 String::new()
499 } else if options.prompt {
500 if options.color == Auto::True {
501 format!("{}>{} ", colors.prompt, colors.text)
502 } else {
503 "> ".to_string()
504 }
505 } else if options.color == Auto::True {
506 colors.text.to_string()
507 } else {
508 String::new()
509 }
510}
511pub fn place_funcvarxy(
512 mut funcvar: Vec<(String, Vec<NumStr>)>,
513 num: NumStr,
514) -> Vec<(String, Vec<NumStr>)> {
515 for i in funcvar.iter_mut() {
516 if !i.0.contains('(') {
517 let mut sum: Vec<(usize, String)> = Vec::new();
518 let mut bracket = 0;
519 let mut j = 0;
520 while i.1.len() > j {
521 match &i.1[j] {
522 LeftBracket => bracket += 1,
523 RightBracket => bracket -= 1,
524 Comma if !sum.is_empty() && sum[0].0 == bracket => {
525 sum.remove(0);
526 }
527 Func(s) => {
528 if matches!(s.as_str(), "x" | "y") && !sum.iter().any(|a| a.1 == *s) {
529 i.1[j] = num.clone();
530 } else {
531 match s.as_str() {
532 "sum" | "summation" | "prod" | "product" | "Σ" | "Π" | "vec"
533 | "mat" | "D" | "integrate" | "arclength" | "∫" | "area"
534 | "surfacearea" | "sarea" | "solve" | "length" | "slope"
535 | "lim" | "set" | "limit" | "iter" | "extrema" | "taylor"
536 | "isolate"
537 if j + 2 < i.1.len()
538 && if let Func(s) = &i.1[j + 2] {
539 matches!(s.as_str(), "x" | "y")
540 } else {
541 false
542 } =>
543 {
544 bracket += 1;
545 j += 3;
546 sum.push((
547 bracket,
548 if let Func(s) = &i.1[j - 1] {
549 s.to_string()
550 } else {
551 String::new()
552 },
553 ))
554 }
555 "surfacearea" | "sarea"
556 if j + 4 < i.1.len()
557 && if let Func(s) = &i.1[j + 4] {
558 matches!(s.as_str(), "x" | "y")
559 } else {
560 false
561 } =>
562 {
563 bracket += 1;
564 j += 5;
565 sum.push((
566 bracket,
567 if let Func(s) = &i.1[j - 1] {
568 s.to_string()
569 } else {
570 String::new()
571 },
572 ))
573 }
574 _ => {}
575 }
576 }
577 }
578 _ => {}
579 }
580 j += 1;
581 }
582 }
583 }
584 funcvar
585}
586pub fn place_varxy(mut func: Vec<NumStr>, num: NumStr) -> Vec<NumStr> {
587 let mut sum: Vec<(usize, String)> = Vec::new();
588 let mut bracket = 0;
589 let mut i = 0;
590 while func.len() > i {
591 match &func[i] {
592 LeftBracket => bracket += 1,
593 RightBracket => bracket -= 1,
594 Comma if !sum.is_empty() && sum[0].0 == bracket => {
595 sum.remove(0);
596 }
597 Func(s) => {
598 if matches!(s.as_str(), "x" | "y") && !sum.iter().any(|a| a.1 == *s) {
599 func[i] = num.clone();
600 } else {
601 match s.as_str() {
602 "sum" | "summation" | "prod" | "product" | "Σ" | "Π" | "vec" | "mat"
603 | "D" | "integrate" | "arclength" | "∫" | "area" | "surfacearea"
604 | "sarea" | "solve" | "length" | "slope" | "lim" | "limit" | "set"
605 | "iter" | "extrema" | "taylor" | "isolate"
606 if i + 2 < func.len()
607 && if let Func(s) = &func[i + 2] {
608 matches!(s.as_str(), "x" | "y")
609 } else {
610 false
611 } =>
612 {
613 bracket += 1;
614 i += 3;
615 sum.push((
616 bracket,
617 if let Func(s) = &func[i - 1] {
618 s.to_string()
619 } else {
620 String::new()
621 },
622 ))
623 }
624 "surfacearea" | "sarea"
625 if i + 4 < func.len()
626 && if let Func(s) = &func[i + 4] {
627 matches!(s.as_str(), "x" | "y")
628 } else {
629 false
630 } =>
631 {
632 bracket += 1;
633 i += 5;
634 sum.push((
635 bracket,
636 if let Func(s) = &func[i - 1] {
637 s.to_string()
638 } else {
639 String::new()
640 },
641 ))
642 }
643 _ => {}
644 }
645 }
646 }
647 _ => {}
648 }
649 i += 1;
650 }
651 func
652}
653pub fn place_funcvar(
654 mut funcvar: Vec<(String, Vec<NumStr>)>,
655 var: &str,
656 num: NumStr,
657) -> Vec<(String, Vec<NumStr>)> {
658 if !var.is_empty() {
659 for i in funcvar.iter_mut() {
660 if !i.0.contains('(') {
661 let mut sum = Vec::new();
662 let mut bracket = 0;
663 let mut j = 0;
664 while i.1.len() > j {
665 match &i.1[j] {
666 LeftBracket => bracket += 1,
667 RightBracket => bracket -= 1,
668 Comma if sum.contains(&bracket) => {
669 sum.remove(0);
670 }
671 Func(s) => {
672 if s == var && sum.is_empty() {
673 i.1[j] = num.clone();
674 } else {
675 match s.as_str() {
676 "sum" | "summation" | "prod" | "product" | "Σ" | "Π"
677 | "vec" | "mat" | "D" | "integrate" | "arclength" | "∫"
678 | "area" | "solve" | "length" | "slope" | "lim" | "limit"
679 | "set" | "iter" | "extrema" | "surfacearea" | "sarea"
680 | "taylor" | "isolate"
681 if j + 2 < i.1.len()
682 && i.1[j + 2] == Func(var.to_string()) =>
683 {
684 j += 3;
685 sum.push(bracket)
686 }
687 "surfacearea" | "sarea"
688 if j + 4 < i.1.len()
689 && i.1[j + 4] == Func(var.to_string()) =>
690 {
691 j += 5;
692 sum.push(bracket)
693 }
694 _ => {}
695 }
696 }
697 }
698 _ => {}
699 }
700 j += 1;
701 }
702 }
703 }
704 }
705 funcvar
706}
707pub fn place_var(mut func: Vec<NumStr>, var: &str, num: NumStr) -> Vec<NumStr> {
708 if !var.is_empty() {
709 let mut sum = Vec::new();
710 let mut bracket = 0;
711 let mut i = 0;
712 while func.len() > i {
713 match &func[i] {
714 LeftBracket => bracket += 1,
715 RightBracket => bracket -= 1,
716 Comma if sum.contains(&bracket) => {
717 sum.remove(0);
718 }
719 Func(s) => {
720 if s == var && sum.is_empty() {
721 func[i] = num.clone();
722 } else {
723 match s.as_str() {
724 "sum" | "summation" | "prod" | "product" | "Σ" | "Π" | "vec"
725 | "mat" | "D" | "integrate" | "arclength" | "∫" | "area" | "solve"
726 | "length" | "slope" | "lim" | "set" | "limit" | "iter" | "extrema"
727 | "surfacearea" | "sarea" | "taylor" | "isolate"
728 if i + 2 < func.len() && func[i + 2] == Func(var.to_string()) =>
729 {
730 i += 3;
731 sum.push(bracket)
732 }
733 "surfacearea" | "sarea"
734 if i + 4 < func.len() && func[i + 4] == Func(var.to_string()) =>
735 {
736 i += 5;
737 sum.push(bracket)
738 }
739 _ => {}
740 }
741 }
742 }
743 _ => {}
744 }
745 i += 1;
746 }
747 }
748 func
749}
750pub fn do_math_with_var(
751 function: Vec<NumStr>,
752 options: Options,
753 func_vars: Vec<(String, Vec<NumStr>)>,
754 var: &str,
755 num: NumStr,
756) -> Result<NumStr, &'static str> {
757 do_math(
758 place_var(function, var, num.clone()),
759 options,
760 place_funcvar(func_vars, var, num),
761 )
762}
763pub fn parsed_to_string(
764 mut input: Vec<NumStr>,
765 vars: &[Variable],
766 func_vars: Vec<(String, Vec<NumStr>)>,
767 options: &Options,
768 colors: &Colors,
769) -> String {
770 let mut i = 0;
771 'main: while i < input.len() {
772 if let Func(s) = &input[i] {
773 for v in &func_vars {
774 if *s == v.0 && !v.0.ends_with(')') {
775 if i != 0
776 && i + 1 < input.len()
777 && input[i - 1] == LeftBracket
778 && input[i + 1] == RightBracket
779 {
780 input.remove(i);
781 input.splice(i..i, v.1.clone());
782 } else {
783 input[i] = LeftBracket;
784 input.splice(i + 1..i + 1, v.1.clone());
785 input.insert(i + v.1.len() + 1, RightBracket);
786 }
787 continue 'main;
788 }
789 }
790 }
791 i += 1;
792 }
793 let mut out = String::new();
794 for i in input {
795 out.push_str(&match i {
796 Num(n) => {
797 let n = custom_units(*n, *options, colors);
798 let n = get_output(*options, colors, &n);
799 format!(
800 "{}{}{}{}",
801 n.0,
802 n.1,
803 n.2.unwrap_or_default(),
804 if options.color == Auto::True {
805 "\x1b[0m"
806 } else {
807 ""
808 }
809 )
810 }
811 Vector(n) => {
812 let mut str = String::new();
813 let mut num;
814 for i in n {
815 let i = custom_units(i, *options, colors);
816 num = get_output(*options, colors, &i);
817 str.push_str(&format!(
818 "{}{}{}{},",
819 num.0,
820 num.1,
821 num.2.unwrap_or_default(),
822 if options.color == Auto::True {
823 "\x1b[0m"
824 } else {
825 ""
826 }
827 ));
828 }
829 str.pop();
830 format!("{{{str}}}")
831 }
832 Matrix(n) => {
833 let mut str = String::new();
834 let mut num;
835 for i in n {
836 str.push('{');
837 for j in i {
838 let j = custom_units(j, *options, colors);
839 num = get_output(*options, colors, &j);
840 str.push_str(&format!(
841 "{}{}{}{},",
842 num.0,
843 num.1,
844 num.2.unwrap_or_default(),
845 if options.color == Auto::True {
846 "\x1b[0m"
847 } else {
848 ""
849 }
850 ));
851 }
852 str.insert(str.len().saturating_sub(1), '}');
853 }
854 str.pop();
855 format!("{{{str}}}")
856 }
857 Func(n) if n.starts_with('@') && n.contains('(') => {
858 n.split('(').next().unwrap().replace('@', "")
859 }
860 Func(n) => n.replace('@', ""),
861 LeftBracket => "(".to_string(),
862 RightBracket => ")".to_string(),
863 LeftCurlyBracket => "{".to_string(),
864 RightCurlyBracket => "}".to_string(),
865 Comma => ",".to_string(),
866 Plus => "+".to_string(),
867 Minus => "-".to_string(),
868 PlusMinus => "±".to_string(),
869 Multiplication => "*".to_string(),
870 InternalMultiplication => "×".to_string(),
871 Division => "/".to_string(),
872 Root => "//".to_string(),
873 Tetration => "^^".to_string(),
874 Exponent => "^".to_string(),
875 Equal => "==".to_string(),
876 NotEqual => "!=".to_string(),
877 Greater => ">".to_string(),
878 GreaterEqual => ">=".to_string(),
879 Lesser => "<".to_string(),
880 LesserEqual => "<=".to_string(),
881 Modulo => "%".to_string(),
882 Range => "..".to_string(),
883 And => "&&".to_string(),
884 Or => "||".to_string(),
885 ShiftLeft => "<<".to_string(),
886 ShiftRight => ">>".to_string(),
887 Conversion => "->".to_string(),
888 NearEqual => "≈".to_string(),
889 Xor => " Xor ".to_string(),
890 Implies => " Implies ".to_string(),
891 Nand => " Nand ".to_string(),
892 Not => "¬".to_string(),
893 Nor => " Nor ".to_string(),
894 Converse => " Converse ".to_string(),
895 })
896 }
897 to_output(
898 &out.chars().collect::<Vec<char>>(),
899 vars,
900 options.color == Auto::True,
901 colors,
902 )
903}
904pub fn insert_last(input: &[char], last: &str) -> String {
905 let mut output = String::new();
906 let mut word = String::new();
907 for c in input {
908 if c.is_alphanumeric() || matches!(c, '\'' | '`' | '_') {
909 output.push(*c);
910 word.push(*c)
911 } else {
912 if word.eq_ignore_ascii_case("ans") {
913 output.drain(output.len() - 3..);
914 output.push('(');
915 output.push_str(last);
916 output.push(')');
917 } else if word == "_" {
918 output.pop();
919 output.push('(');
920 output.push_str(last);
921 output.push(')');
922 }
923 output.push(*c);
924 word.clear()
925 }
926 }
927 if word.eq_ignore_ascii_case("ans") {
928 output.drain(output.len() - 3..);
929 if !last.contains('#') {
930 output.push('(');
931 }
932 output.push_str(last);
933 if !last.contains('#') {
934 output.push(')');
935 }
936 } else if word == "_" {
937 output.pop();
938 if !last.contains('#') {
939 output.push('(');
940 }
941 output.push_str(last);
942 if !last.contains('#') {
943 output.push(')');
944 }
945 }
946 output
947}
948pub fn get_word_bank(word: &str, vars: &[Variable], options: Options) -> Vec<String> {
949 let mut bank: Vec<String> = vars
950 .iter()
951 .filter_map(|v| {
952 v.name
953 .starts_with(&word.chars().collect::<Vec<char>>()[..])
954 .then_some(v.name.iter().collect())
955 })
956 .collect();
957 bank.extend(
958 functions_with_args()
959 .iter()
960 .chain(options_list().iter())
961 .chain(
962 if options.units {
963 units_list()
964 } else {
965 Default::default()
966 }
967 .iter(),
968 )
969 .filter_map(|f| {
970 (f.starts_with(word)
971 && !bank
972 .iter()
973 .any(|b| b.contains('(') && b.split('(').next() == f.split('(').next()))
974 .then_some(f.to_string())
975 })
976 .collect::<Vec<String>>(),
977 );
978 bank.sort_unstable();
979 bank
980}
981#[cfg(unix)]
982#[allow(clippy::missing_safety_doc)]
983pub unsafe fn spawn_cmd(func: PathBuf) -> Command {
984 let mut cmd = Command::new(func);
985 unsafe {
986 use std::os::unix::process::CommandExt;
987 cmd.pre_exec(|| {
988 #[cfg(feature = "bin-deps")]
989 libc::setsid();
990 Ok(())
991 });
992 }
993 cmd
994}
995#[cfg(not(unix))]
996pub fn spawn_cmd(func: PathBuf) -> Command {
997 Command::new(func)
998}