single_variable_algebra_compiler/
lib.rs

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