kalc_lib/
misc.rs

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}