single_variable_algebra_compiler/
lib.rs

1use serde::{Deserialize, Serialize};
2use std::io::{self, Read};
3use std::sync::OnceLock;
4
5// This const is used for all test cases.
6const MAX_DECIMAL_PLACES: usize = 100;
7// If we use a different Decimal crate, this is the most important line to change.
8// Sadly, it is not that easy to make Dec dynamic since Decimal<CONST> expects a const and no LazyLock.
9type Dec = dec::Decimal<MAX_DECIMAL_PLACES>;
10
11static DECIMAL_PLACES: OnceLock<usize> = OnceLock::new();
12static NAN: OnceLock<String> = OnceLock::new();
13
14#[derive(Debug, Deserialize, Serialize, Clone)]
15pub enum TreeNode {
16    Op(char, Box<TreeNode>, Box<TreeNode>),
17    Num(i32),
18    Var(String),
19    Fun(String, Box<TreeNode>), // TODO: For iterated functions -> Fun(String, usize, Box<TreeNode>). Defaults to 1. f(x) = f^1(x). INFINITY(x) = 10^80. f^INFINITY(x).
20    Paren(Box<TreeNode>),
21    Empty,
22}
23
24/// The main struct of this crate. A binary algebraic expression tree is a TreeNode.
25#[derive(Debug, Deserialize, Serialize, Clone)]
26pub struct BinaryAlgebraicExpressionTree {
27    pub name: String,
28    pub root_node: TreeNode,
29}
30
31/// A TestCase is useful for unit testing in `mod tests`.
32#[derive(Clone, Debug, Serialize, Deserialize)]
33pub struct TestCase {
34    // A short description of what the binary tree is doing.
35    pub description: Option<String>,
36    // A vector full of examples. The first String is the input and the second the expected output.
37    pub examples: Vec<[String; 2]>,
38    pub solution: Vec<BinaryAlgebraicExpressionTree>,
39}
40
41impl Default for BinaryAlgebraicExpressionTree {
42    fn default() -> Self {
43        BinaryAlgebraicExpressionTree {
44            name: "NEW".to_string(),
45            root_node: parse_expression("1"),
46        }
47    }
48}
49
50fn get_decimal_places() -> usize {
51    *DECIMAL_PLACES.get_or_init(|| MAX_DECIMAL_PLACES)
52}
53
54fn get_nan() -> &'static String {
55    NAN.get_or_init(|| format!("-0.{}1", "0".repeat(get_decimal_places())))
56}
57
58/// For CLI.
59pub fn read_terminal_input() {
60    let mut use_math_tricks = false;
61    let args: Vec<String> = std::env::args().skip(1).collect();
62    if args.len() == 1
63        && let Some(first_line) = args[0].trim().lines().next()
64        && first_line.trim().starts_with("DECIMAL_PLACES(x)=")
65    {
66        use_math_tricks = true;
67        if let Some(k_str) = first_line.trim().strip_prefix("DECIMAL_PLACES(x)=") {
68            let k_clean = k_str.split_whitespace().next().unwrap_or(k_str);
69            if let Ok(k) = k_clean.parse::<usize>() {
70                let _ = DECIMAL_PLACES.set(k);
71                let _ = NAN.set(format!("-0.{}1", "0".repeat(k)));
72            }
73        }
74    }
75    let input = if args.len() == 1 && args[0].contains('\n') {
76        args[0]
77            .split('\n')
78            .map(|s| s.trim())
79            .filter(|s| !s.is_empty())
80            .map(|s| s.to_string())
81            .collect()
82    } else if args.is_empty() {
83        let mut buffer = String::new();
84        io::stdin().read_to_string(&mut buffer).unwrap();
85        buffer
86            .split('\n')
87            .map(|s| s.trim())
88            .filter(|s| !s.is_empty())
89            .map(|s| s.to_string())
90            .collect()
91    } else {
92        args
93    };
94    if input.is_empty() {
95        println!(
96            "Usage:\n  single-variable-algebra-compiler FUNC1(x)=expr1 FUNC2(x)=expr2 ... FUNCN(INPUT)\nOR\n  echo 'F(x)=4+4\nG(x)=F(x)*2\nG(1)' | single-variable-algebra-compiler"
97        );
98        return;
99    }
100    let mut trees = Vec::new();
101    for arg in input.iter().take(input.len() - 1) {
102        if let Some((name, expr)) = parse_function(arg) {
103            let tree = BinaryAlgebraicExpressionTree {
104                name: name.to_string(),
105                root_node: parse_expression(expr),
106            };
107            trees.push(tree);
108        } else {
109            println!("Invalid function definition: {arg}");
110            return;
111        }
112    }
113    if let Some((func_name, input_val)) = parse_function_call(input.last().unwrap()) {
114        if let Some(tree) = trees.iter().find(|t| t.name == func_name) {
115            let x: Dec = input_val.parse().unwrap_or_else(|_| {
116                println!("Invalid input value: {input_val}");
117                std::process::exit(1);
118            });
119            let result = apply_algebra_to_tree_node(&tree.root_node, &x, &trees, use_math_tricks);
120            println!("{}", trim2(result));
121        } else {
122            println!("Function {func_name} not defined");
123        }
124    } else {
125        println!("Invalid function call: {}", input.last().unwrap());
126    }
127}
128
129/// Calculate the result of a binary tree.
130pub fn apply_algebra_to_tree_node(
131    node: &TreeNode,
132    x: &Dec,
133    tablets: &Vec<BinaryAlgebraicExpressionTree>,
134    use_math_tricks: bool,
135) -> Dec {
136    match node {
137        TreeNode::Num(n) => Dec::from(*n),
138        TreeNode::Var(s) => {
139            if s == "x" {
140                *x
141            } else {
142                s.parse::<Dec>()
143                    .unwrap_or_else(|_| panic!("Unexpected variable: {s}"))
144            }
145        }
146        TreeNode::Fun(name, arg) => {
147            let arg_value = apply_algebra_to_tree_node(arg, x, tablets, use_math_tricks);
148            if name.as_str() == "ABS" && use_math_tricks {
149                math_trick::abs(arg_value).parse().unwrap()
150            } else if name.as_str() == "GE0" && use_math_tricks {
151                math_trick::ge0(arg_value).parse().unwrap()
152            } else if name.as_str() == "IS0" && use_math_tricks {
153                math_trick::is0(arg_value).parse().unwrap()
154            } else if name.as_str() == "FLOOR1" && use_math_tricks {
155                math_trick::floor1(arg_value).parse().unwrap()
156            } else if name.as_str() == "LEFT" && use_math_tricks {
157                math_trick::left(arg_value.to_standard_notation_string())
158                    .parse()
159                    .unwrap()
160            } else {
161                let tablet = tablets
162                    .iter()
163                    .find(|tablet| name == &tablet.name)
164                    .unwrap_or_else(|| panic!("There is no tree called {name}"));
165                apply_algebra_to_tree_node(&tablet.root_node, &arg_value, tablets, use_math_tricks)
166            }
167        }
168        TreeNode::Op(op, left, right) => {
169            let mut left_val = apply_algebra_to_tree_node(left, x, tablets, use_math_tricks);
170            let right_val = apply_algebra_to_tree_node(right, x, tablets, use_math_tricks);
171            match op {
172                '+' => left_val + right_val,
173                '-' => left_val - right_val,
174                '*' => left_val * right_val,
175                '/' => left_val / right_val,
176                '^' => {
177                    let mut ctx = dec::Context::<Dec>::default();
178                    ctx.set_min_exponent(-1000).unwrap();
179                    ctx.set_max_exponent(1000).unwrap();
180                    ctx.pow(&mut left_val, &right_val);
181                    left_val
182                }
183                _ => panic!("Unknown operator: {op}"),
184            }
185        }
186        TreeNode::Paren(expr) => apply_algebra_to_tree_node(expr, x, tablets, use_math_tricks),
187        TreeNode::Empty => Dec::zero(),
188    }
189}
190
191/// Converts a String like 3*x+5 to a binary tree.
192pub fn parse_expression(s: &str) -> TreeNode {
193    let tokens: Vec<char> = s.chars().filter(|c| !c.is_whitespace()).collect();
194    let mut index = 0;
195    parse_additive(&tokens, &mut index)
196}
197
198/// Converts a binary tree back to a String like 3*x+5.
199pub fn create_expression(node: TreeNode) -> String {
200    fn build_expr(node: TreeNode, parent_prec: u8, is_root: bool) -> String {
201        match node {
202            TreeNode::Op(op, left, right) => {
203                let (prec, is_left_assoc) = match op {
204                    '^' => (4, false),
205                    '*' | '/' => (3, true),
206                    '+' | '-' => (2, true),
207                    _ => (0, true),
208                };
209                let left_str = build_expr(*left, prec, false);
210                let right_str = build_expr(*right, prec + !is_left_assoc as u8, false);
211                let expr = format!("{left_str}{op}{right_str}");
212                if prec < parent_prec && !is_root {
213                    format!("({expr})")
214                } else {
215                    expr
216                }
217            }
218            TreeNode::Num(n) => n.to_string(),
219            TreeNode::Var(v) => v,
220            TreeNode::Fun(name, arg) => format!("{}({})", name, build_expr(*arg, 0, false)),
221            TreeNode::Paren(expr) => {
222                let inner = build_expr(*expr, 0, true);
223                if parent_prec > 0 {
224                    format!("({inner})")
225                } else {
226                    inner
227                }
228            }
229            TreeNode::Empty => String::new(),
230        }
231    }
232    build_expr(node, 0, true)
233}
234
235pub fn level_order_to_array(root: TreeNode) -> [String; 15] {
236    let mut result = std::array::from_fn(|_| String::new());
237    let mut queue = std::collections::VecDeque::with_capacity(15);
238    queue.push_back((0, root));
239    while let Some((i, node)) = queue.pop_front() {
240        if i >= 15 {
241            continue;
242        }
243        match node {
244            TreeNode::Op(op, left, right) => {
245                result[i] = op.to_string();
246                queue.push_back((2 * i + 1, *left));
247                queue.push_back((2 * i + 2, *right));
248            }
249            TreeNode::Num(n) => result[i] = n.to_string(),
250            TreeNode::Var(v) => result[i] = v,
251            TreeNode::Fun(name, arg) => {
252                result[i] = name;
253                queue.push_back((2 * i + 2, *arg));
254            }
255            TreeNode::Paren(expr) => {
256                result[i] = "()".to_string();
257                queue.push_back((2 * i + 1, *expr));
258            }
259            TreeNode::Empty => {}
260        }
261    }
262    result
263}
264
265fn trim2(mut dec: Dec) -> String {
266    dec.trim();
267    let result = dec.to_standard_notation_string();
268    if result == "-0" {
269        "0".to_string()
270    } else {
271        result
272    }
273}
274
275fn parse_function(s: &str) -> Option<(&str, &str)> {
276    let s = s.trim();
277    if let Some((func_part, expr)) = s.split_once('=') {
278        let func_part = func_part.trim();
279        if let Some((name, _)) = func_part.split_once('(') {
280            return Some((name, expr.trim()));
281        }
282    }
283    None
284}
285
286fn parse_function_call(s: &str) -> Option<(&str, &str)> {
287    if !s.ends_with(')') || !s.contains('(') {
288        return None;
289    }
290    let open_paren = s.find('(')?;
291    let name = &s[..open_paren];
292    let input = &s[open_paren + 1..s.len() - 1];
293    Some((name, input))
294}
295
296fn trim_zeros(s: &str) -> String {
297    if s.contains('.') {
298        let trimmed = s.trim_end_matches('0');
299        trimmed.trim_end_matches('.').to_string()
300    } else {
301        s.to_string()
302    }
303}
304
305fn parse_additive(tokens: &[char], index: &mut usize) -> TreeNode {
306    let mut left = parse_multiplicative(tokens, index);
307    while *index < tokens.len() {
308        match tokens[*index] {
309            '+' | '-' => {
310                let op = tokens[*index];
311                *index += 1;
312                let right = parse_multiplicative(tokens, index);
313                left = TreeNode::Op(op, Box::new(left), Box::new(right));
314            }
315            _ => break,
316        }
317    }
318    left
319}
320
321fn parse_multiplicative(tokens: &[char], index: &mut usize) -> TreeNode {
322    let mut left = parse_power(tokens, index);
323    while *index < tokens.len() {
324        match tokens[*index] {
325            '*' | '/' => {
326                let op = tokens[*index];
327                *index += 1;
328                let right = parse_power(tokens, index);
329                left = TreeNode::Op(op, Box::new(left), Box::new(right));
330            }
331            _ => break,
332        }
333    }
334    left
335}
336
337fn parse_power(tokens: &[char], index: &mut usize) -> TreeNode {
338    let mut left = parse_atomic(tokens, index);
339    while *index < tokens.len() && tokens[*index] == '^' {
340        let op = tokens[*index];
341        *index += 1;
342        let right = parse_atomic(tokens, index);
343        left = TreeNode::Op(op, Box::new(left), Box::new(right));
344    }
345    left
346}
347
348fn parse_atomic(tokens: &[char], index: &mut usize) -> TreeNode {
349    if *index >= tokens.len() {
350        return TreeNode::Empty;
351    }
352    let c = tokens[*index];
353    match c {
354        '(' => {
355            *index += 1;
356            let node = parse_additive(tokens, index);
357            if *index < tokens.len() && tokens[*index] == ')' {
358                *index += 1;
359            }
360            TreeNode::Paren(Box::new(node))
361        }
362        '0'..='9' => {
363            let mut num = 0;
364            while *index < tokens.len() && tokens[*index].is_ascii_digit() {
365                num = num * 10 + tokens[*index].to_digit(10).unwrap() as i32;
366                *index += 1;
367            }
368            TreeNode::Num(num)
369        }
370        'x' => {
371            *index += 1;
372            TreeNode::Var("x".to_string())
373        }
374        'A'..='Z' => {
375            let mut name = String::new();
376            while *index < tokens.len()
377                && (tokens[*index].is_alphanumeric() || tokens[*index] == '_')
378            {
379                name.push(tokens[*index]);
380                *index += 1;
381            }
382            if *index < tokens.len() && tokens[*index] == '(' {
383                *index += 1;
384                let arg = parse_additive(tokens, index);
385                if *index < tokens.len() && tokens[*index] == ')' {
386                    *index += 1;
387                }
388                TreeNode::Fun(name, Box::new(arg))
389            } else {
390                TreeNode::Var(name)
391            }
392        }
393        _ => TreeNode::Empty,
394    }
395}
396
397pub mod math_trick {
398    use super::*;
399
400    pub fn abs(mut x: Dec) -> String {
401        let mut ctx = dec::Context::<Dec>::default();
402        ctx.set_min_exponent(-1000).unwrap();
403        ctx.set_max_exponent(1000).unwrap();
404        ctx.abs(&mut x);
405        x.to_string()
406    }
407
408    pub fn ge0(x: Dec) -> String {
409        let nan: Dec = get_nan().parse().unwrap();
410        match x {
411            _ if x > nan => "1".to_string(),
412            _ if x < nan => "0".to_string(),
413            _ => "NaN".to_string(),
414        }
415    }
416
417    pub fn is0(x: Dec) -> String {
418        let nan: Dec = get_nan().parse().unwrap();
419        match x {
420            _ if x < nan => "0".to_string(),
421            _ if x > nan && x < "1".parse::<Dec>().unwrap() + nan => "1".to_string(),
422            _ if x > "1".parse::<Dec>().unwrap() + nan => "0".to_string(),
423            _ => "NaN".to_string(),
424        }
425    }
426
427    pub fn floor1(x: Dec) -> String {
428        let nan: Dec = get_nan().parse().unwrap();
429        match x {
430            _ if x < nan => "0".to_string(),
431            _ if x > nan && x < "1".parse::<Dec>().unwrap() + nan => "0".to_string(),
432            _ if x > "1".parse::<Dec>().unwrap() + nan && x < "2".parse::<Dec>().unwrap() + nan => {
433                "1".to_string()
434            }
435            _ if x > "2".parse::<Dec>().unwrap() + nan && x < "3".parse::<Dec>().unwrap() + nan => {
436                "2".to_string()
437            }
438            _ if x > "3".parse::<Dec>().unwrap() + nan && x < "4".parse::<Dec>().unwrap() + nan => {
439                "3".to_string()
440            }
441            _ if x > "4".parse::<Dec>().unwrap() + nan && x < "5".parse::<Dec>().unwrap() + nan => {
442                "4".to_string()
443            }
444            _ if x > "5".parse::<Dec>().unwrap() + nan && x < "6".parse::<Dec>().unwrap() + nan => {
445                "5".to_string()
446            }
447            _ if x > "6".parse::<Dec>().unwrap() + nan && x < "7".parse::<Dec>().unwrap() + nan => {
448                "6".to_string()
449            }
450            _ if x > "7".parse::<Dec>().unwrap() + nan && x < "8".parse::<Dec>().unwrap() + nan => {
451                "7".to_string()
452            }
453            _ if x > "8".parse::<Dec>().unwrap() + nan && x < "9".parse::<Dec>().unwrap() + nan => {
454                "8".to_string()
455            }
456            _ if x > "9".parse::<Dec>().unwrap() + nan
457                && x < "10".parse::<Dec>().unwrap() + nan =>
458            {
459                "9".to_string()
460            }
461            _ if x > "10".parse::<Dec>().unwrap() + nan => "0".to_string(),
462            _ => "NaN".to_string(),
463        }
464    }
465
466    /// num should be a to_standard_notation_string().
467    pub fn left(mut num: String) -> String {
468        // left(x) and right(x) only consist of several floor(x*10). That means this here should ne enough to get all NaNs.
469        if floor1(num.parse::<Dec>().unwrap() * "10".parse::<Dec>().unwrap()) == "NaN" {
470            return "NaN".to_string();
471        }
472        if !num.contains('.') {
473            num += ".0";
474        }
475        if num.ends_with('0') {
476            num = num.trim_end_matches('0').to_string()
477        }
478        if num.ends_with('.') {
479            num += "0";
480        }
481        let decimal_pos = num.find('.').unwrap();
482        let (integer_part, fractional_part) = num.split_at(decimal_pos + 1);
483        let mut chars: Vec<_> = fractional_part.chars().collect();
484        let len = get_decimal_places() - chars.len();
485        if len > 0 {
486            chars.extend(vec!['0'; len]);
487        }
488        chars.rotate_right(1);
489        let rotated_fractional_part: String = chars.into_iter().collect();
490        let result = format!("{integer_part}{rotated_fractional_part}");
491        trim_zeros(&result)
492    }
493}
494
495#[cfg(test)]
496mod tests {
497    use super::*;
498
499    fn get_test_cases() -> &'static Vec<TestCase> {
500        static INSTANCE: std::sync::OnceLock<Vec<TestCase>> = std::sync::OnceLock::new();
501        INSTANCE.get_or_init(|| {
502        vec![
503            TestCase {
504                description: None,
505                examples: vec![
506                    ["2".to_string(), get_decimal_places().to_string()],
507                    ["-0.2424".to_string(), get_decimal_places().to_string()],
508                    ["100".to_string(), get_decimal_places().to_string()],
509                ],
510                solution: vec![BinaryAlgebraicExpressionTree {
511                    name: "DECIMAL_PLACES".to_string(),
512                    root_node: parse_expression(&get_decimal_places().to_string())
513                }],
514            },
515            TestCase {
516                description: None,
517                examples: vec![
518                    ["-1".to_string(), "1".to_string()],
519                    //["11.2".to_string(), "11.2".to_string()], // The operation (x^2)^0.5 takes a lot of time. But using a HashMap cache for alle Node -> String wouldn't help here.
520                    ["0".to_string(), "0".to_string()],
521                    //["-0.0025".to_string(), "0.0025".to_string()], // The operation (x^2)^0.5 takes a lot of time. But using a HashMap cache for alle Node -> String wouldn't help here.
522                    ["1".to_string(), "1".to_string()],
523                ],
524                solution: vec![BinaryAlgebraicExpressionTree {
525                    name: "ABS".to_string(),
526                    root_node: parse_expression("(x^2)^(1/2)")
527                }],
528            },
529            TestCase {
530                description: None,
531                examples: vec![
532                    ["0".to_string(), "NaN".to_string()],
533                    ["0.3".to_string(), "1".to_string()],
534                    ["-0.3".to_string(), "0".to_string()],
535                    ["1.0".to_string(), "1".to_string()],
536                    ["400.0".to_string(), "1".to_string()],
537                ],
538                solution: vec![BinaryAlgebraicExpressionTree {
539                    name: "H".to_string(),
540                    root_node: parse_expression("(x+ABS(x))/(2*x)")
541                }],
542            },
543            TestCase {
544                description: None,
545                examples: vec![
546                    [
547                        "55".to_string(),
548                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "1",
549                    ],
550                    [
551                        "-11.9".to_string(),
552                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "1",
553                    ],
554                    [
555                        "0.0".to_string(),
556                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "1",
557                    ],
558                    [
559                        "-0.95".to_string(),
560                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "1",
561                    ],
562                ],
563                solution: vec![BinaryAlgebraicExpressionTree {
564                    name: "TINY".to_string(),
565                    root_node: parse_expression("10^(-DECIMAL_PLACES(x)))")
566                }],
567            },
568            TestCase {
569                description: None,
570                examples: vec![
571                    [
572                        "0.".to_string() + &"9".repeat(get_decimal_places()),
573                        "1".to_string(),
574                    ],
575                    [
576                        "-0.".to_string() + &"0".repeat(get_decimal_places()) + "1",
577                        "NaN".to_string(),
578                    ],
579                    ["0.3".to_string(), "1".to_string()],
580                    ["-0.3".to_string(), "0".to_string()],
581                    ["1.0".to_string(), "1".to_string()],
582                    ["400.0".to_string(), "1".to_string()],
583                ],
584                solution: vec![BinaryAlgebraicExpressionTree {
585                    name: "GE0".to_string(),
586                    root_node: parse_expression("H(x+TINY(x)/10)")
587                }],
588            },
589            TestCase {
590                description: None,
591                examples: vec![
592                    ["0".to_string(), "1".to_string()],
593                    ["-6.4".to_string(), "1".to_string()],
594                    ["1.0".to_string(), "0".to_string()],
595                    ["0.999".to_string(), "1".to_string()],
596                    ["50".to_string(), "0".to_string()],
597                ],
598                solution: vec![BinaryAlgebraicExpressionTree {
599                    name: "LT1".to_string(),
600                    root_node: parse_expression("1-GE0(x-1)")
601                }],
602            },
603            TestCase {
604                description: None,
605                examples: vec![
606                    ["0".to_string(), "1".to_string()],
607                    ["0.5".to_string(), "1".to_string()],
608                    ["1".to_string(), "0".to_string()],
609                ],
610                solution: vec![BinaryAlgebraicExpressionTree {
611                    name: "IS0".to_string(),
612                    root_node: parse_expression("GE0(x)*LT1(x)")
613                }],
614            },
615            TestCase {
616                description: None,
617                examples: vec![
618                    ["1".to_string(), "1".to_string()],
619                    ["1.5".to_string(), "1".to_string()],
620                    ["2".to_string(), "0".to_string()],
621                ],
622                solution: vec![BinaryAlgebraicExpressionTree {
623                    name: "IS1".to_string(),
624                    root_node: parse_expression("IS0(x-1)")
625                }],
626            },
627            TestCase {
628                description: None,
629                examples: vec![
630                    ["2".to_string(), "1".to_string()],
631                    ["2.5".to_string(), "1".to_string()],
632                    ["3".to_string(), "0".to_string()],
633                ],
634                solution: vec![BinaryAlgebraicExpressionTree {
635                    name: "IS2".to_string(),
636                    root_node: parse_expression("IS0(x-2)")
637                }],
638            },
639            TestCase {
640                description: None,
641                examples: vec![
642                    ["3".to_string(), "1".to_string()],
643                    ["3.5".to_string(), "1".to_string()],
644                    ["4".to_string(), "0".to_string()],
645                ],
646                solution: vec![BinaryAlgebraicExpressionTree {
647                    name: "IS3".to_string(),
648                    root_node: parse_expression("IS0(x-3)")
649                }],
650            },
651            TestCase {
652                description: None,
653                examples: vec![
654                    ["4".to_string(), "1".to_string()],
655                    ["4.5".to_string(), "1".to_string()],
656                    ["5".to_string(), "0".to_string()],
657                ],
658                solution: vec![BinaryAlgebraicExpressionTree {
659                    name: "IS4".to_string(),
660                    root_node: parse_expression("IS0(x-4)")
661                }],
662            },
663            TestCase {
664                description: None,
665                examples: vec![
666                    ["5".to_string(), "1".to_string()],
667                    ["5.5".to_string(), "1".to_string()],
668                    ["6".to_string(), "0".to_string()],
669                ],
670                solution: vec![BinaryAlgebraicExpressionTree {
671                    name: "IS5".to_string(),
672                    root_node: parse_expression("IS0(x-5)")
673                }],
674            },
675            TestCase {
676                description: None,
677                examples: vec![
678                    ["6".to_string(), "1".to_string()],
679                    ["6.5".to_string(), "1".to_string()],
680                    ["7".to_string(), "0".to_string()],
681                ],
682                solution: vec![BinaryAlgebraicExpressionTree {
683                    name: "IS6".to_string(),
684                    root_node: parse_expression("IS0(x-6)"),
685                }],
686            },
687            TestCase {
688                description: None,
689                examples: vec![
690                    ["7".to_string(), "1".to_string()],
691                    ["7.5".to_string(), "1".to_string()],
692                    ["8".to_string(), "0".to_string()],
693                ],
694                solution: vec![BinaryAlgebraicExpressionTree {
695                    name: "IS7".to_string(),
696                    root_node: parse_expression("IS0(x-7)"),
697                }],
698            },
699            TestCase {
700                description: None,
701                examples: vec![
702                    ["8".to_string(), "1".to_string()],
703                    ["8.5".to_string(), "1".to_string()],
704                    ["9".to_string(), "0".to_string()],
705                ],
706                solution: vec![BinaryAlgebraicExpressionTree {
707                    name: "IS8".to_string(),
708                    root_node: parse_expression("IS0(x-8)"),
709                }],
710            },
711            TestCase {
712                description: None,
713                examples: vec![
714                    ["9".to_string(), "1".to_string()],
715                    ["9.5".to_string(), "1".to_string()],
716                    ["10".to_string(), "0".to_string()],
717                ],
718                solution: vec![BinaryAlgebraicExpressionTree {
719                    name: "IS9".to_string(),
720                    root_node: parse_expression("IS0(x-9)"),
721                }],
722            },
723            TestCase {
724                description: None,
725                examples: vec![
726                    ["0".to_string(), "0".to_string()],
727                    ["0.2".to_string(), "0".to_string()],
728                    ["1".to_string(), "1".to_string()],
729                    ["1.2".to_string(), "1".to_string()],
730                    ["2".to_string(), "2".to_string()],
731                    ["2.2".to_string(), "2".to_string()],
732                    ["3".to_string(), "3".to_string()],
733                    ["3.2".to_string(), "3".to_string()],
734                    ["4".to_string(), "4".to_string()],
735                    ["4.2".to_string(), "4".to_string()],
736                    ["5".to_string(), "5".to_string()],
737                    ["5.2".to_string(), "5".to_string()],
738                    ["6".to_string(), "6".to_string()],
739                    ["6.2".to_string(), "6".to_string()],
740                    ["7".to_string(), "7".to_string()],
741                    ["7.2".to_string(), "7".to_string()],
742                    ["8".to_string(), "8".to_string()],
743                    ["8.2".to_string(), "8".to_string()],
744                    ["9".to_string(), "9".to_string()],
745                    ["9.2".to_string(), "9".to_string()],
746                ],
747                solution: vec![BinaryAlgebraicExpressionTree {
748                    name: "FLOOR1".to_string(),
749                    root_node: parse_expression(
750                        "IS1(x)+2*IS2(x)+3*IS3(x)+4*IS4(x)+5*IS5(x)+6*IS6(x)+7*IS7(x)+8*IS8(x)+9*IS9(x)",
751                    ),
752                }],
753            },
754            TestCase {
755                description: None,
756                examples: vec![
757                    ["0.06".to_string(), "0.6".to_string()],
758                    [
759                        "0.12345678".to_string(),
760                        "0.2345678".to_string() + &"0".repeat(get_decimal_places() - 8) + "1",
761                    ],
762                    [
763                        "0.7".to_string(),
764                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "7",
765                    ],
766                ],
767                solution: vec![
768                    BinaryAlgebraicExpressionTree {
769                        name: "RIGHT".to_string(),
770                        root_node: parse_expression("x*10-FLOOR1(x*10)+FLOOR1(x*10)*TINY(x)")
771                    },
772                ],
773            },
774            TestCase {
775                description: None,
776                examples: vec![
777                    [
778                        "0.2345678".to_string() + &"0".repeat(get_decimal_places() - 8) + "1",
779                        "0.12345678".to_string(),
780                    ],
781                    [
782                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "7",
783                        "0.7".to_string(),
784                    ],
785                ],
786                solution: vec![
787                    BinaryAlgebraicExpressionTree {
788                        name: "LEFT".to_string(),
789                        root_node: parse_expression(
790                            &("RIGHT(".repeat(get_decimal_places() - 1) + "(x)" + &")".repeat(get_decimal_places() - 1))
791                        )
792                    },
793                ],
794            },
795        ]
796    })
797    }
798
799    #[test]
800    fn test_solutions() {
801        let tasks = get_test_cases();
802        let mut trees: Vec<BinaryAlgebraicExpressionTree> = vec![];
803        for task in tasks {
804            for tree in &task.solution {
805                trees.push(tree.clone());
806            }
807        }
808        for i in 0..tasks.len() {
809            for [input, output] in &tasks[i].examples {
810                let name_function = &tasks[i].solution.last().unwrap().name;
811                let name = format!("{}({}) = ", name_function, input);
812                let result = trim2(apply_algebra_to_tree_node(
813                    &tasks[i].solution.last().unwrap().root_node,
814                    &input.parse::<Dec>().unwrap(),
815                    &trees,
816                    true,
817                ));
818                assert_eq!(name.clone() + output.as_str(), name + &result);
819            }
820        }
821    }
822
823    #[test]
824    fn test_math_tricks() {
825        let tasks = get_test_cases();
826        let mut trees: Vec<BinaryAlgebraicExpressionTree> = vec![];
827        for task in tasks {
828            for tree in &task.solution {
829                trees.push(tree.clone());
830            }
831        }
832        for i in 0..tasks.len() {
833            for [input, output] in &tasks[i].examples {
834                let name_function = &tasks[i].solution.last().unwrap().name;
835                let name = format!("{}({}) = ", name_function, input);
836                let input_dec = input.parse().unwrap();
837                if name_function == "ABS" {
838                    assert_eq!(
839                        name.clone() + output.as_str(),
840                        name + &math_trick::abs(input_dec)
841                    );
842                } else if name_function == "GE0" {
843                    assert_eq!(
844                        name.clone() + output.as_str(),
845                        name + &math_trick::ge0(input_dec)
846                    );
847                } else if name_function == "IS0" {
848                    assert_eq!(
849                        name.clone() + output.as_str(),
850                        name + &math_trick::is0(input_dec)
851                    );
852                } else if name_function == "FLOOR1" {
853                    assert_eq!(
854                        name.clone() + output.as_str(),
855                        name + &math_trick::floor1(input_dec)
856                    );
857                } else if name_function == "LEFT" {
858                    assert_eq!(
859                        name.clone() + output.as_str(),
860                        name + &math_trick::left(input_dec.to_standard_notation_string())
861                    );
862                }
863            }
864        }
865    }
866}