resistor_calc/
expr_builder.rs1extern crate meval;
2
3use std::{f64::EPSILON, str::FromStr};
4
5use RSet;
6
7lazy_static!(
8 static ref RNAMES: Vec<String> = (1..=100).map(|i| format!("R{}", i)).collect();
9);
10
11enum Bounds {
12 Cmp(Box<Fn(f64, f64) -> bool>, meval::Expr, f64),
13 Err(meval::Expr, f64),
14}
15
16fn split_expr(expr: &str, pat: &str) -> (meval::Expr, f64) {
17 let mut split = expr.split(pat);
18 (
19 split.next().unwrap().trim().parse::<meval::Expr>().unwrap(),
20 split.next().unwrap().trim().parse::<f64>().unwrap(),
21 )
22}
23
24impl FromStr for Bounds {
25 type Err = &'static str;
26
27 fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
28 if s.contains("<=") {
29 let (ex, trg) = split_expr(s, "<=");
30 Ok(Bounds::Cmp(Box::new(|a, b| a <= b), ex, trg))
31 } else if s.contains('<') {
32 let (ex, trg) = split_expr(s, "<");
33 Ok(Bounds::Cmp(Box::new(|a, b| a < b), ex, trg))
34 } else if s.contains(">=") {
35 let (ex, trg) = split_expr(s, ">=");
36 Ok(Bounds::Cmp(Box::new(|a, b| a >= b), ex, trg))
37 } else if s.contains('>') {
38 let (ex, trg) = split_expr(s, ">");
39 Ok(Bounds::Cmp(Box::new(|a, b| a > b), ex, trg))
40 } else if s.contains("==") {
41 let (ex, trg) = split_expr(s, "==");
42 Ok(Bounds::Cmp(
43 Box::new(|a, b| (a - b).abs() < EPSILON),
44 ex,
45 trg,
46 ))
47 } else if s.contains("!=") {
48 let (ex, trg) = split_expr(s, "!=");
49 Ok(Bounds::Cmp(
50 Box::new(|a, b| (a - b).abs() > EPSILON),
51 ex,
52 trg,
53 ))
54 } else if s.contains('~') {
55 let (ex, trg) = split_expr(s, "~");
56 Ok(Bounds::Err(ex, trg))
57 } else {
58 Err("Err: Bound must contain either <, <=, >, >=, ==, != or ~")
59 }
60 }
61}
62
63#[derive(Default)]
65pub struct ROpBuilder {
66 ops: Vec<Bounds>,
67}
68
69impl ROpBuilder {
70 pub fn new() -> Self {
72 ROpBuilder { ops: Vec::new() }
73 }
74
75 pub fn bound(mut self, expr: &str) -> Self {
82 self.ops.push(expr.parse().unwrap());
83 self
84 }
85
86 fn cmp_bound_fn(&mut self) -> Box<Fn(&meval::Context) -> Option<f64>> {
87 match self.ops.pop() {
88 Some(b) => match b {
89 Bounds::Cmp(op, expr, target) => {
90 let inner_bound = self.cmp_bound_fn();
91 Box::new(move |ctx| {
92 if op(expr.eval_with_context(ctx).unwrap(), target) {
93 inner_bound(ctx)
94 } else {
95 None
96 }
97 })
98 }
99 Bounds::Err(expr, target) => {
100 let inner_bound = self.cmp_bound_fn();
101 Box::new(move |ctx| {
102 let val = expr.eval_with_context(ctx).unwrap();
103 inner_bound(ctx).map(|v| v + (target - val).abs())
104 })
105 }
106 },
107 None => Box::new(|_| Some(0.0)),
108 }
109 }
110
111 pub fn finish(mut self) -> impl Fn(&RSet) -> Option<f64> {
113 let bound = self.cmp_bound_fn();
114 move |rs: &RSet| {
115 let mut ctx = meval::Context::new();
116 for (i, v) in rs.0.iter().enumerate() {
117 ctx.var(RNAMES[i].clone(), *v as f64);
118 }
119 bound(&ctx)
120 }
121 }
122}