Skip to main content

single_variable_algebra_compiler/
lib.rs

1use serde::{Deserialize, Serialize};
2use std::io::{self, Read};
3use std::sync::OnceLock;
4
5mod decimal_crate;
6use decimal_crate::*;
7
8static DECIMAL_PLACES: OnceLock<usize> = OnceLock::new();
9static NAN: OnceLock<String> = OnceLock::new();
10
11#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
12pub enum TreeNode {
13    Op(char, Box<TreeNode>, Box<TreeNode>),
14    Num(String),
15    Var(String),
16    Fun(String, usize, Box<TreeNode>),
17    Paren(Box<TreeNode>),
18    Empty,
19}
20
21/// The main struct of this crate. A binary algebraic expression tree is a TreeNode.
22#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
23pub struct BinaryAlgebraicExpressionTree {
24    pub name: String,
25    pub root_node: TreeNode,
26}
27
28/// A TestCase is useful for unit testing in `mod tests`.
29#[derive(Clone, Debug, Serialize, Deserialize)]
30pub struct TestCase {
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
46fn get_decimal_places() -> usize {
47    *DECIMAL_PLACES.get_or_init(|| MAX_DECIMAL_PLACES)
48}
49
50fn get_nan() -> &'static String {
51    NAN.get_or_init(|| format!("-0.{}1", "0".repeat(get_decimal_places())))
52}
53
54#[cfg(not(target_arch = "wasm32"))]
55fn output(s: String) {
56    println!("{s}");
57}
58
59#[cfg(target_arch = "wasm32")]
60fn output(s: String) {
61    use web_sys::wasm_bindgen::JsCast;
62    let window = web_sys::window().unwrap();
63    let document = window.document().unwrap();
64    let textarea = document
65        .get_element_by_id("output")
66        .unwrap()
67        .dyn_into::<web_sys::HtmlTextAreaElement>()
68        .unwrap();
69    textarea.set_value(&s);
70}
71
72#[cfg(not(target_arch = "wasm32"))]
73fn read_args() -> Vec<String> {
74    std::env::args().skip(1).collect()
75}
76
77#[cfg(target_arch = "wasm32")]
78fn read_args() -> Vec<String> {
79    use web_sys::wasm_bindgen::JsCast;
80    let window = web_sys::window().unwrap();
81    let document = window.document().unwrap();
82    vec![
83        document
84            .get_element_by_id("sva")
85            .unwrap()
86            .dyn_into::<web_sys::HtmlTextAreaElement>()
87            .unwrap()
88            .value(),
89    ]
90}
91
92/// For CLI.
93pub fn read_input() {
94    let mut use_math_tricks = false;
95    let args: Vec<String> = read_args();
96    if args.len() == 1
97        && let Some(first_line) = args[0].trim().lines().next()
98        && first_line.trim().starts_with("decimals(x) =")
99    {
100        use_math_tricks = true;
101        if let Some(k_str) = first_line.trim().strip_prefix("decimals(x) =") {
102            let k_clean = k_str.split_whitespace().next().unwrap_or(k_str);
103            if let Ok(k) = k_clean.parse::<usize>() {
104                let _ = DECIMAL_PLACES.set(k);
105                let _ = NAN.set(format!("-0.{}1", "0".repeat(k)));
106            }
107        }
108    }
109    let input = if args.len() == 1 && args[0].contains('\n') {
110        args[0]
111            .split('\n')
112            .map(|s| s.trim())
113            .filter(|s| !s.is_empty())
114            .map(|s| s.to_string())
115            .collect()
116    } else if args.is_empty() {
117        let mut buffer = String::new();
118        io::stdin().read_to_string(&mut buffer).unwrap();
119        buffer
120            .split('\n')
121            .map(|s| s.trim())
122            .filter(|s| !s.is_empty())
123            .map(|s| s.to_string())
124            .collect()
125    } else {
126        args
127    };
128    if input.is_empty() {
129        println!(
130            "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"
131        );
132        return;
133    }
134    let mut trees = Vec::new();
135    for arg in input.iter().take(input.len() - 1) {
136        if let Some((name, expr)) = parse_function(arg) {
137            let tree = BinaryAlgebraicExpressionTree {
138                name: name.to_string(),
139                root_node: parse_expression(expr),
140            };
141            trees.push(tree);
142        } else {
143            output(format!("Invalid function definition: {arg}"));
144            return;
145        }
146    }
147    if let Some((func_name, input_val)) = parse_function_call(input.last().unwrap()) {
148        if let Some(tree) = trees.iter().find(|t| t.name == func_name) {
149            let x: Dec = input_val.parse().unwrap_or_else(|_| {
150                output(format!("Invalid input value: {input_val}"));
151                std::process::exit(1);
152            });
153            let result = apply_algebra_to_tree_node(&tree.root_node, &x, &trees, use_math_tricks);
154            output(result.map_or("Undefined".to_string(), trim2));
155        } else {
156            output(format!("Function {func_name} not defined"));
157        }
158    } else {
159        output(format!("Invalid function call: {}", input.last().unwrap()));
160    }
161}
162
163/// Calculate the result of a binary tree.
164pub fn apply_algebra_to_tree_node(
165    node: &TreeNode,
166    x: &Dec,
167    tablets: &Vec<BinaryAlgebraicExpressionTree>,
168    use_math_tricks: bool,
169) -> Option<Dec> {
170    match node {
171        TreeNode::Num(n) => Some(n.parse::<Dec>().unwrap()),
172        TreeNode::Var(s) => {
173            if s == "x" {
174                Some(x.clone())
175            } else {
176                Some(
177                    s.parse::<Dec>()
178                        .unwrap_or_else(|_| panic!("Unexpected variable: {s}")),
179                )
180            }
181        }
182        TreeNode::Fun(name, iterate, arg) => {
183            let mut arg_value = apply_algebra_to_tree_node(arg, x, tablets, use_math_tricks);
184            let mut old_value = arg_value.clone();
185            for _ in 0..*iterate {
186                if name.as_str() == "abs" && use_math_tricks {
187                    arg_value = Some(math_trick::abs(arg_value.unwrap()).parse().unwrap());
188                } else if name.as_str() == "ge0" && use_math_tricks {
189                    arg_value = Some(math_trick::ge0(arg_value.unwrap()).parse().unwrap());
190                } else if name.as_str() == "is0" && use_math_tricks {
191                    arg_value = Some(math_trick::is0(arg_value.unwrap()).parse().unwrap());
192                } else if name.as_str() == "floor1" && use_math_tricks {
193                    arg_value = Some(math_trick::floor1(arg_value.unwrap()).parse().unwrap());
194                } else if name.as_str() == "left" && use_math_tricks {
195                    arg_value = Some(math_trick::left(arg_value.unwrap()).parse().unwrap());
196                } else {
197                    let tablet = tablets
198                        .iter()
199                        .find(|tablet| name == &tablet.name)
200                        .unwrap_or_else(|| panic!("There is no tree called {name}"));
201                    arg_value = apply_algebra_to_tree_node(
202                        &tablet.root_node,
203                        &arg_value.unwrap(),
204                        tablets,
205                        use_math_tricks,
206                    );
207                }
208                if arg_value == old_value {
209                    break;
210                }
211                old_value = arg_value.clone();
212            }
213            arg_value
214        }
215        TreeNode::Op(op, left, right) => {
216            let left_val = apply_algebra_to_tree_node(left, x, tablets, use_math_tricks);
217            let right_val = apply_algebra_to_tree_node(right, x, tablets, use_math_tricks);
218            let left_str = trim2(left_val.clone().unwrap());
219            let right_str = trim2(right_val.clone().unwrap());
220            match op {
221                '+' => Some(left_val.unwrap() + right_val.unwrap()),
222                '-' => Some(left_val.unwrap() - right_val.unwrap()),
223                '*' => Some(left_val.unwrap() * right_val.unwrap()),
224                '/' => {
225                    if right_str == "0" {
226                        return None;
227                    }
228                    Some(left_val.unwrap() / right_val.unwrap())
229                }
230                '^' => {
231                    if (left_str == "0" && (right_val <= Some(zero())))
232                        || (left_val < Some(zero()) && right_str.contains('.'))
233                    {
234                        return None;
235                    }
236                    Some(pow(left_val.unwrap(), right_val.unwrap()))
237                }
238                _ => panic!("Unknown operator: {op}"),
239            }
240        }
241        TreeNode::Paren(expr) => apply_algebra_to_tree_node(expr, x, tablets, use_math_tricks),
242        TreeNode::Empty => Some(zero()),
243    }
244}
245
246/// Converts a String like 3*x+5 to a binary tree.
247pub fn parse_expression(s: &str) -> TreeNode {
248    let tokens: Vec<char> = s.chars().filter(|c| !c.is_whitespace()).collect();
249    let mut index = 0;
250    parse_additive(&tokens, &mut index)
251}
252
253/// Converts a binary tree back to a String like 3*x+5.
254pub fn create_expression(node: TreeNode) -> String {
255    fn build_expr(node: TreeNode, parent_prec: u8, is_root: bool) -> String {
256        match node {
257            TreeNode::Op(op, left, right) => {
258                let (prec, is_left_assoc) = match op {
259                    '^' => (4, false),
260                    '*' | '/' => (3, true),
261                    '+' | '-' => (2, true),
262                    _ => (0, true),
263                };
264                let left_str = build_expr(*left, prec, false);
265                let right_str = build_expr(*right, prec + !is_left_assoc as u8, false);
266                let expr = format!("{left_str}{op}{right_str}");
267                if prec < parent_prec && !is_root {
268                    format!("({expr})")
269                } else {
270                    expr
271                }
272            }
273            TreeNode::Num(n) => n.to_string(),
274            TreeNode::Var(v) => v,
275            TreeNode::Fun(name, iterate, arg) => {
276                let mut iterating = "".to_string();
277                if iterate > 1 {
278                    iterating = "^[".to_string() + &iterate.to_string() + "]";
279                }
280                format!("{}{}({})", name, iterating, build_expr(*arg, 0, false))
281            }
282            TreeNode::Paren(expr) => {
283                let inner = build_expr(*expr, 0, true);
284                if parent_prec > 0 {
285                    format!("({inner})")
286                } else {
287                    inner
288                }
289            }
290            TreeNode::Empty => String::new(),
291        }
292    }
293    build_expr(node, 0, true)
294}
295
296pub fn level_order_to_array(root: TreeNode) -> [String; 15] {
297    let mut result = std::array::from_fn(|_| String::new());
298    let mut queue = std::collections::VecDeque::with_capacity(15);
299    queue.push_back((0, root));
300    while let Some((i, node)) = queue.pop_front() {
301        if i >= 15 {
302            continue;
303        }
304        match node {
305            TreeNode::Paren(expr) => {
306                queue.push_front((i, *expr));
307            }
308            TreeNode::Op(op, left, right) => {
309                result[i] = op.to_string();
310                queue.push_back((2 * i + 1, *left));
311                queue.push_back((2 * i + 2, *right));
312            }
313            TreeNode::Num(n) => result[i] = n.to_string(),
314            TreeNode::Var(v) => result[i] = v,
315            TreeNode::Fun(name, _iterate, arg) => {
316                result[i] = name;
317                queue.push_back((2 * i + 2, *arg));
318            }
319            TreeNode::Empty => {}
320        }
321    }
322    result
323}
324
325pub fn trim2(dec: Dec) -> String {
326    let mut x = dec_to_string(dec);
327    if x.contains('.') {
328        x = x.trim_end_matches('0').trim_end_matches('.').to_string();
329    }
330    if x.starts_with("-0") {
331        x.remove(0);
332    }
333    x
334}
335
336fn parse_function(s: &str) -> Option<(&str, &str)> {
337    let s = s.trim();
338    if let Some((func_part, expr)) = s.split_once('=') {
339        let func_part = func_part.trim();
340        if let Some((name, _)) = func_part.split_once('(') {
341            return Some((name, expr.trim()));
342        }
343    }
344    None
345}
346
347fn parse_function_call(s: &str) -> Option<(&str, &str)> {
348    if !s.ends_with(')') || !s.contains('(') {
349        return None;
350    }
351    let open_paren = s.find('(')?;
352    let name = &s[..open_paren];
353    let input = &s[open_paren + 1..s.len() - 1];
354    Some((name, input))
355}
356
357fn trim_zeros(s: &str) -> String {
358    if s.contains('.') {
359        let trimmed = s.trim_end_matches('0');
360        trimmed.trim_end_matches('.').to_string()
361    } else {
362        s.to_string()
363    }
364}
365
366fn parse_additive(tokens: &[char], index: &mut usize) -> TreeNode {
367    let mut left = parse_multiplicative(tokens, index);
368    while *index < tokens.len() {
369        match tokens[*index] {
370            '+' | '-' => {
371                let op = tokens[*index];
372                *index += 1;
373                let right = parse_multiplicative(tokens, index);
374                left = TreeNode::Op(op, Box::new(left), Box::new(right));
375            }
376            _ => break,
377        }
378    }
379    left
380}
381
382fn parse_multiplicative(tokens: &[char], index: &mut usize) -> TreeNode {
383    let mut left = parse_power(tokens, index);
384    while *index < tokens.len() {
385        match tokens[*index] {
386            '*' | '/' => {
387                let op = tokens[*index];
388                *index += 1;
389                let right = parse_power(tokens, index);
390                left = TreeNode::Op(op, Box::new(left), Box::new(right));
391            }
392            _ => break,
393        }
394    }
395    left
396}
397
398fn parse_atomic(tokens: &[char], index: &mut usize) -> TreeNode {
399    if *index >= tokens.len() {
400        return TreeNode::Empty;
401    }
402    let c = tokens[*index];
403
404    match c {
405        '(' => {
406            *index += 1;
407            let node = parse_additive(tokens, index);
408            if *index < tokens.len() && tokens[*index] == ')' {
409                *index += 1;
410            }
411            TreeNode::Paren(Box::new(node))
412        }
413        '0'..='9' => {
414            let mut num_str = String::new();
415            while *index < tokens.len() && tokens[*index].is_ascii_digit() {
416                num_str.push(tokens[*index]);
417                *index += 1;
418            }
419            if *index < tokens.len() && tokens[*index] == '.' {
420                num_str.push(tokens[*index]);
421                *index += 1;
422                while *index < tokens.len() && tokens[*index].is_ascii_digit() {
423                    num_str.push(tokens[*index]);
424                    *index += 1;
425                }
426            }
427            TreeNode::Num(num_str)
428        }
429        'A'..='Z' | 'a'..='z' => {
430            let mut name = String::new();
431            let mut iterate: usize = 1;
432            while *index < tokens.len()
433                && (tokens[*index].is_alphanumeric() || tokens[*index] == '_')
434            {
435                name.push(tokens[*index]);
436                *index += 1;
437            }
438            if *index < tokens.len() && tokens[*index] == '^' {
439                let saved_index = *index;
440                *index += 1;
441                if *index < tokens.len() && tokens[*index] == '[' {
442                    *index += 1;
443                    let mut num_str = String::new();
444                    while *index < tokens.len() && tokens[*index].is_ascii_digit() {
445                        num_str.push(tokens[*index]);
446                        *index += 1;
447                    }
448                    if *index < tokens.len() && tokens[*index] == ']' {
449                        *index += 1;
450                        if let Ok(parsed_iterate) = num_str.parse::<usize>() {
451                            iterate = parsed_iterate;
452                        }
453                    } else {
454                        *index = saved_index;
455                    }
456                } else {
457                    *index = saved_index;
458                }
459            }
460
461            if *index < tokens.len() && tokens[*index] == '(' {
462                *index += 1;
463                let arg = parse_additive(tokens, index);
464                if *index < tokens.len() && tokens[*index] == ')' {
465                    *index += 1;
466                }
467                TreeNode::Fun(name, iterate, Box::new(arg))
468            } else if name == "x" {
469                TreeNode::Var("x".to_string())
470            } else {
471                TreeNode::Empty
472            }
473        }
474        _ => {
475            *index += 1;
476            TreeNode::Empty
477        }
478    }
479}
480
481fn parse_unary(tokens: &[char], index: &mut usize) -> TreeNode {
482    if *index >= tokens.len() {
483        return TreeNode::Empty;
484    }
485
486    // Check for unary operators: +, -
487    match tokens[*index] {
488        '+' => {
489            *index += 1;
490            parse_unary(tokens, index) // Unary plus, just skip it
491        }
492        '-' => {
493            *index += 1;
494            // Check if this is a negative number
495            if *index < tokens.len() && (tokens[*index].is_ascii_digit() || tokens[*index] == '.') {
496                // Parse as negative number
497                let mut num_str = String::from("-");
498                while *index < tokens.len() && tokens[*index].is_ascii_digit() {
499                    num_str.push(tokens[*index]);
500                    *index += 1;
501                }
502                if *index < tokens.len() && tokens[*index] == '.' {
503                    num_str.push(tokens[*index]);
504                    *index += 1;
505                    while *index < tokens.len() && tokens[*index].is_ascii_digit() {
506                        num_str.push(tokens[*index]);
507                        *index += 1;
508                    }
509                }
510                TreeNode::Num(num_str)
511            } else {
512                // Unary minus operator
513                TreeNode::Op(
514                    '-',
515                    Box::new(TreeNode::Num("0".to_string())),
516                    Box::new(parse_unary(tokens, index)),
517                )
518            }
519        }
520        _ => parse_atomic(tokens, index),
521    }
522}
523
524// Update parse_power to use parse_unary instead of parse_atomic
525fn parse_power(tokens: &[char], index: &mut usize) -> TreeNode {
526    let mut left = parse_unary(tokens, index);
527    while *index < tokens.len() && tokens[*index] == '^' {
528        let op = tokens[*index];
529        *index += 1;
530        let right = parse_unary(tokens, index);
531        left = TreeNode::Op(op, Box::new(left), Box::new(right));
532    }
533    left
534}
535
536pub mod math_trick {
537    use super::*;
538
539    pub fn abs(x: Dec) -> String {
540        let mut res = x.to_string();
541        if res.starts_with('-') {
542            res.remove(0);
543        }
544        res
545    }
546
547    pub fn ge0(x: Dec) -> String {
548        let nan: Dec = get_nan().parse().unwrap();
549        match x {
550            _ if x > nan => "1".to_string(),
551            _ if x < nan => "0".to_string(),
552            _ => "NaN".to_string(),
553        }
554    }
555
556    pub fn is0(x: Dec) -> String {
557        let nan: Dec = get_nan().parse().unwrap();
558        match x {
559            _ if x < nan => "0".to_string(),
560            _ if x > nan && x < "1".parse::<Dec>().unwrap() + &nan => "1".to_string(),
561            _ if x > "1".parse::<Dec>().unwrap() + nan => "0".to_string(),
562            _ => "NaN".to_string(),
563        }
564    }
565
566    pub fn floor1(x: Dec) -> String {
567        let nan: Dec = get_nan().parse().unwrap();
568        match x {
569            _ if x < nan => "0".to_string(),
570            _ if x > nan && x < "1".parse::<Dec>().unwrap() + &nan => "0".to_string(),
571            _ if x > "1".parse::<Dec>().unwrap() + &nan
572                && x < "2".parse::<Dec>().unwrap() + &nan =>
573            {
574                "1".to_string()
575            }
576            _ if x > "2".parse::<Dec>().unwrap() + &nan
577                && x < "3".parse::<Dec>().unwrap() + &nan =>
578            {
579                "2".to_string()
580            }
581            _ if x > "3".parse::<Dec>().unwrap() + &nan
582                && x < "4".parse::<Dec>().unwrap() + &nan =>
583            {
584                "3".to_string()
585            }
586            _ if x > "4".parse::<Dec>().unwrap() + &nan
587                && x < "5".parse::<Dec>().unwrap() + &nan =>
588            {
589                "4".to_string()
590            }
591            _ if x > "5".parse::<Dec>().unwrap() + &nan
592                && x < "6".parse::<Dec>().unwrap() + &nan =>
593            {
594                "5".to_string()
595            }
596            _ if x > "6".parse::<Dec>().unwrap() + &nan
597                && x < "7".parse::<Dec>().unwrap() + &nan =>
598            {
599                "6".to_string()
600            }
601            _ if x > "7".parse::<Dec>().unwrap() + &nan
602                && x < "8".parse::<Dec>().unwrap() + &nan =>
603            {
604                "7".to_string()
605            }
606            _ if x > "8".parse::<Dec>().unwrap() + &nan
607                && x < "9".parse::<Dec>().unwrap() + &nan =>
608            {
609                "8".to_string()
610            }
611            _ if x > "9".parse::<Dec>().unwrap() + &nan
612                && x < "10".parse::<Dec>().unwrap() + &nan =>
613            {
614                "9".to_string()
615            }
616            _ if x > "10".parse::<Dec>().unwrap() + &nan => "0".to_string(),
617            _ => "NaN".to_string(),
618        }
619    }
620
621    /// num should be a to_standard_notation_string().
622    pub fn left(x: Dec) -> String {
623        // left(x) and right(x) only consist of several floor(x*10). That means this here should ne enough to get all NaNs.
624        let mut num = dec_to_string(x);
625        if floor1(num.parse::<Dec>().unwrap() * "10".parse::<Dec>().unwrap()) == "NaN" {
626            return "NaN".to_string();
627        }
628        if !num.contains('.') {
629            num += ".0";
630        }
631        if num.ends_with('0') {
632            num = num.trim_end_matches('0').to_string()
633        }
634        if num.ends_with('.') {
635            num += "0";
636        }
637        let decimal_pos = num.find('.').unwrap();
638        let (integer_part, fractional_part) = num.split_at(decimal_pos + 1);
639        let mut chars: Vec<_> = fractional_part.chars().collect();
640        let len = get_decimal_places() - chars.len();
641        if len > 0 {
642            chars.extend(vec!['0'; len]);
643        }
644        chars.rotate_right(1);
645        let rotated_fractional_part: String = chars.into_iter().collect();
646        let result = format!("{integer_part}{rotated_fractional_part}");
647        trim_zeros(&result)
648    }
649}
650
651#[cfg(test)]
652mod tests {
653    use super::*;
654
655    fn get_test_cases() -> &'static Vec<TestCase> {
656        static INSTANCE: std::sync::OnceLock<Vec<TestCase>> = std::sync::OnceLock::new();
657        INSTANCE.get_or_init(|| {
658        vec![
659            TestCase {
660                description: None,
661                examples: vec![
662                    ["2".to_string(), get_decimal_places().to_string()],
663                    ["-0.2424".to_string(), get_decimal_places().to_string()],
664                    ["100".to_string(), get_decimal_places().to_string()],
665                ],
666                solution: vec![BinaryAlgebraicExpressionTree {
667                    name: "decimal_places".to_string(),
668                    root_node: parse_expression(&get_decimal_places().to_string())
669                }],
670            },
671            TestCase {
672                description: None,
673                examples: vec![
674                    ["-1".to_string(), "1".to_string()],
675                    ["11.2".to_string(), "11.2".to_string()],
676                    ["0".to_string(), "0".to_string()],
677                    ["-0.0025".to_string(), "0.0025".to_string()],
678                    ["1".to_string(), "1".to_string()],
679                ],
680                solution: vec![BinaryAlgebraicExpressionTree {
681                    name: "abs".to_string(),
682                    root_node: parse_expression("(x^2)^0.5")
683                }],
684            },
685            TestCase {
686                description: None,
687                examples: vec![
688                    //["0".to_string(), "NaN".to_string()],
689                    ["0.3".to_string(), "1".to_string()],
690                    ["-0.3".to_string(), "0".to_string()],
691                    ["1.0".to_string(), "1".to_string()],
692                    ["400.0".to_string(), "1".to_string()],
693                ],
694                solution: vec![BinaryAlgebraicExpressionTree {
695                    name: "H".to_string(),
696                    root_node: parse_expression("(x+abs(x))/(2*x)")
697                }],
698            },
699            TestCase {
700                description: None,
701                examples: vec![
702                    [
703                        "55".to_string(),
704                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "1",
705                    ],
706                    [
707                        "-11.9".to_string(),
708                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "1",
709                    ],
710                    [
711                        "0.0".to_string(),
712                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "1",
713                    ],
714                    [
715                        "-0.95".to_string(),
716                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "1",
717                    ],
718                ],
719                solution: vec![BinaryAlgebraicExpressionTree {
720                    name: "tiny".to_string(),
721                    root_node: parse_expression("10^(-decimal_places(x)))")
722                }],
723            },
724            TestCase {
725                description: None,
726                examples: vec![
727                    [
728                        "0.".to_string() + &"9".repeat(get_decimal_places()),
729                        "1".to_string(),
730                    ],
731                    /*[
732                        "-0.".to_string() + &"0".repeat(get_decimal_places()) + "1",
733                        "NaN".to_string(),
734                    ],*/
735                    ["0.3".to_string(), "1".to_string()],
736                    ["-0.3".to_string(), "0".to_string()],
737                    ["1.0".to_string(), "1".to_string()],
738                    ["400.0".to_string(), "1".to_string()],
739                ],
740                solution: vec![BinaryAlgebraicExpressionTree {
741                    name: "ge0".to_string(),
742                    root_node: parse_expression("H(x+tiny(x)/10)")
743                }],
744            },
745            TestCase {
746                description: None,
747                examples: vec![
748                    ["0".to_string(), "1".to_string()],
749                    ["-6.4".to_string(), "1".to_string()],
750                    ["1.0".to_string(), "0".to_string()],
751                    ["0.999".to_string(), "1".to_string()],
752                    ["50".to_string(), "0".to_string()],
753                ],
754                solution: vec![BinaryAlgebraicExpressionTree {
755                    name: "lt1".to_string(),
756                    root_node: parse_expression("1-ge0(x-1)")
757                }],
758            },
759            TestCase {
760                description: None,
761                examples: vec![
762                    ["0".to_string(), "1".to_string()],
763                    ["0.5".to_string(), "1".to_string()],
764                    ["1".to_string(), "0".to_string()],
765                ],
766                solution: vec![BinaryAlgebraicExpressionTree {
767                    name: "is0".to_string(),
768                    root_node: parse_expression("ge0(x)*lt1(x)")
769                }],
770            },
771            TestCase {
772                description: None,
773                examples: vec![
774                    ["1".to_string(), "1".to_string()],
775                    ["1.5".to_string(), "1".to_string()],
776                    ["2".to_string(), "0".to_string()],
777                ],
778                solution: vec![BinaryAlgebraicExpressionTree {
779                    name: "is1".to_string(),
780                    root_node: parse_expression("is0(x-1)")
781                }],
782            },
783            TestCase {
784                description: None,
785                examples: vec![
786                    ["2".to_string(), "1".to_string()],
787                    ["2.5".to_string(), "1".to_string()],
788                    ["3".to_string(), "0".to_string()],
789                ],
790                solution: vec![BinaryAlgebraicExpressionTree {
791                    name: "is2".to_string(),
792                    root_node: parse_expression("is0(x-2)")
793                }],
794            },
795            TestCase {
796                description: None,
797                examples: vec![
798                    ["3".to_string(), "1".to_string()],
799                    ["3.5".to_string(), "1".to_string()],
800                    ["4".to_string(), "0".to_string()],
801                ],
802                solution: vec![BinaryAlgebraicExpressionTree {
803                    name: "is3".to_string(),
804                    root_node: parse_expression("is0(x-3)")
805                }],
806            },
807            TestCase {
808                description: None,
809                examples: vec![
810                    ["4".to_string(), "1".to_string()],
811                    ["4.5".to_string(), "1".to_string()],
812                    ["5".to_string(), "0".to_string()],
813                ],
814                solution: vec![BinaryAlgebraicExpressionTree {
815                    name: "is4".to_string(),
816                    root_node: parse_expression("is0(x-4)")
817                }],
818            },
819            TestCase {
820                description: None,
821                examples: vec![
822                    ["5".to_string(), "1".to_string()],
823                    ["5.5".to_string(), "1".to_string()],
824                    ["6".to_string(), "0".to_string()],
825                ],
826                solution: vec![BinaryAlgebraicExpressionTree {
827                    name: "is5".to_string(),
828                    root_node: parse_expression("is0(x-5)")
829                }],
830            },
831            TestCase {
832                description: None,
833                examples: vec![
834                    ["6".to_string(), "1".to_string()],
835                    ["6.5".to_string(), "1".to_string()],
836                    ["7".to_string(), "0".to_string()],
837                ],
838                solution: vec![BinaryAlgebraicExpressionTree {
839                    name: "is6".to_string(),
840                    root_node: parse_expression("is0(x-6)"),
841                }],
842            },
843            TestCase {
844                description: None,
845                examples: vec![
846                    ["7".to_string(), "1".to_string()],
847                    ["7.5".to_string(), "1".to_string()],
848                    ["8".to_string(), "0".to_string()],
849                ],
850                solution: vec![BinaryAlgebraicExpressionTree {
851                    name: "is7".to_string(),
852                    root_node: parse_expression("is0(x-7)"),
853                }],
854            },
855            TestCase {
856                description: None,
857                examples: vec![
858                    ["8".to_string(), "1".to_string()],
859                    ["8.5".to_string(), "1".to_string()],
860                    ["9".to_string(), "0".to_string()],
861                ],
862                solution: vec![BinaryAlgebraicExpressionTree {
863                    name: "is8".to_string(),
864                    root_node: parse_expression("is0(x-8)"),
865                }],
866            },
867            TestCase {
868                description: None,
869                examples: vec![
870                    ["9".to_string(), "1".to_string()],
871                    ["9.5".to_string(), "1".to_string()],
872                    ["10".to_string(), "0".to_string()],
873                ],
874                solution: vec![BinaryAlgebraicExpressionTree {
875                    name: "is9".to_string(),
876                    root_node: parse_expression("is0(x-9)"),
877                }],
878            },
879            TestCase {
880                description: None,
881                examples: vec![
882                    ["0".to_string(), "0".to_string()],
883                    ["0.2".to_string(), "0".to_string()],
884                    ["1".to_string(), "1".to_string()],
885                    ["1.2".to_string(), "1".to_string()],
886                    ["2".to_string(), "2".to_string()],
887                    ["2.2".to_string(), "2".to_string()],
888                    ["3".to_string(), "3".to_string()],
889                    ["3.2".to_string(), "3".to_string()],
890                    ["4".to_string(), "4".to_string()],
891                    ["4.2".to_string(), "4".to_string()],
892                    ["5".to_string(), "5".to_string()],
893                    ["5.2".to_string(), "5".to_string()],
894                    ["6".to_string(), "6".to_string()],
895                    ["6.2".to_string(), "6".to_string()],
896                    ["7".to_string(), "7".to_string()],
897                    ["7.2".to_string(), "7".to_string()],
898                    ["8".to_string(), "8".to_string()],
899                    ["8.2".to_string(), "8".to_string()],
900                    ["9".to_string(), "9".to_string()],
901                    ["9.2".to_string(), "9".to_string()],
902                ],
903                solution: vec![BinaryAlgebraicExpressionTree {
904                    name: "floor1".to_string(),
905                    root_node: parse_expression(
906                        "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)",
907                    ),
908                }],
909            },
910            TestCase {
911                description: None,
912                examples: vec![
913                    ["0.06".to_string(), "0.6".to_string()],
914                    [
915                        "0.12345678".to_string(),
916                        "0.2345678".to_string() + &"0".repeat(get_decimal_places() - 8) + "1",
917                    ],
918                    [
919                        "0.7".to_string(),
920                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "7",
921                    ],
922                ],
923                solution: vec![
924                    BinaryAlgebraicExpressionTree {
925                        name: "right".to_string(),
926                        root_node: parse_expression("x*10-floor1(x*10)+floor1(x*10)*tiny(x)")
927                    },
928                ],
929            },
930            TestCase {
931                description: None,
932                examples: vec![
933                    [
934                        "0.2345678".to_string() + &"0".repeat(get_decimal_places() - 8) + "1",
935                        "0.12345678".to_string(),
936                    ],
937                    [
938                        "0.".to_string() + &"0".repeat(get_decimal_places() - 1) + "7",
939                        "0.7".to_string(),
940                    ],
941                ],
942                solution: vec![
943                    BinaryAlgebraicExpressionTree {
944                        name: "left".to_string(),
945                        root_node: parse_expression(
946                            &("right(".repeat(get_decimal_places() - 1) + "(x)" + &")".repeat(get_decimal_places() - 1))
947                        )
948                    },
949                ],
950            },
951        ]
952    })
953    }
954
955    #[test]
956    fn test_solutions() {
957        let tasks = get_test_cases();
958        let trees: Vec<BinaryAlgebraicExpressionTree> = tasks
959            .iter()
960            .flat_map(|task| &task.solution)
961            .cloned()
962            .collect();
963        for i in 0..tasks.len() {
964            for [input, output] in &tasks[i].examples {
965                let name_function = &tasks[i].solution.last().unwrap().name;
966                let result = trim2(
967                    apply_algebra_to_tree_node(
968                        &tasks[i].solution.last().unwrap().root_node,
969                        &input.parse::<Dec>().unwrap(),
970                        &trees,
971                        true,
972                    )
973                    .unwrap(),
974                );
975                assert_eq!(
976                    format!("{}({}) = {}", name_function, input, output),
977                    format!("{}({}) = {}", name_function, input, result)
978                );
979            }
980        }
981    }
982
983    #[test]
984    fn test_math_tricks() {
985        let tasks = get_test_cases();
986        let mut trees: Vec<BinaryAlgebraicExpressionTree> = vec![];
987        for task in tasks {
988            for tree in &task.solution {
989                trees.push(tree.clone());
990            }
991        }
992        for i in 0..tasks.len() {
993            for [input, output] in &tasks[i].examples {
994                let name_function = &tasks[i].solution.last().unwrap().name;
995                let input_dec = input.parse().unwrap();
996                let result = match name_function.as_str() {
997                    "abs" => math_trick::abs(input_dec),
998                    "ge0" => math_trick::ge0(input_dec),
999                    "is0" => math_trick::is0(input_dec),
1000                    "floor1" => math_trick::floor1(input_dec),
1001                    "left" => math_trick::left(input_dec),
1002                    _ => continue,
1003                };
1004                assert_eq!(
1005                    format!("{}({}) = {}", name_function, input, output),
1006                    format!("{}({}) = {}", name_function, input, result)
1007                );
1008            }
1009        }
1010    }
1011}