single_variable_algebra_compiler/
lib.rs

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