mirror of
https://github.com/tiffany352/rink-rs
synced 2024-11-10 13:44:15 +00:00
Add computation of unit derivatives
This commit is contained in:
parent
a85532d914
commit
2e2c877bdb
3 changed files with 70 additions and 0 deletions
58
src/eval.rs
58
src/eval.rs
|
@ -177,6 +177,40 @@ impl Context {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn derivatives(&self, value: &Number, aliases: &BTreeMap<Unit, Rc<String>>)
|
||||
-> BTreeMap<usize, Vec<Rc<String>>> {
|
||||
if value.1.len() == 0 {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(0, vec![]);
|
||||
return map;
|
||||
}
|
||||
let mut candidates: BTreeMap<usize, Vec<Rc<String>>> = BTreeMap::new();
|
||||
let value_score = value.complexity_score();
|
||||
for (unit, name) in aliases.iter().rev() {
|
||||
use gmp::mpq::Mpq;
|
||||
|
||||
let res = (value / &Number(Mpq::one(), unit.clone())).unwrap();
|
||||
//if res.1.len() >= value.1.len() {
|
||||
let score = res.complexity_score();
|
||||
// we are not making the unit any simpler
|
||||
if score >= value_score {
|
||||
continue
|
||||
}
|
||||
let res = self.derivatives(&res, aliases);
|
||||
for (score, mut vec) in res {
|
||||
vec.push(name.clone());
|
||||
// more complicated than decomposition to base units
|
||||
/*if score + 1 > value.1.len() {
|
||||
continue
|
||||
}*/
|
||||
candidates.insert(score + 1, vec);
|
||||
}
|
||||
candidates = candidates.into_iter().take(10).collect();
|
||||
}
|
||||
assert!(candidates.len() <= 10);
|
||||
candidates
|
||||
}
|
||||
|
||||
/// Describes a value's unit, gives true if the unit is reciprocal
|
||||
/// (e.g. you should prefix "1.0 / " or replace "multiply" with
|
||||
/// "divide" when rendering it).
|
||||
|
@ -323,6 +357,7 @@ impl Context {
|
|||
})
|
||||
}),
|
||||
Expr::Convert(_, _) => Err(format!("Conversions (->) must be top-level expressions")),
|
||||
Expr::Derivatives(_) => Err(format!("Derivatives must be top-level expressions")),
|
||||
Expr::Equals(_, ref right) => self.eval(right),
|
||||
Expr::Call(ref name, ref args) => {
|
||||
let args = try!(args.iter().map(|x| self.eval(x)).collect::<Result<Vec<_>, _>>());
|
||||
|
@ -414,6 +449,7 @@ impl Context {
|
|||
Err(format!("Temperature conversions must not be compound units")),
|
||||
Expr::Date(_) => Err(format!("Dates are not allowed in the right hand side of conversions")),
|
||||
Expr::Convert(_, _) => Err(format!("Conversions are not allowed in the right hand of conversions")),
|
||||
Expr::Derivatives(_) => Err(format!("Derivatives are not allowed in the right hand of conversions")),
|
||||
Expr::Error(ref e) => Err(e.clone()),
|
||||
}
|
||||
}
|
||||
|
@ -558,6 +594,28 @@ impl Context {
|
|||
(Err(e), _, _) => Err(e),
|
||||
(_, _, Err(e)) => Err(e),
|
||||
},
|
||||
Expr::Derivatives(ref expr) => {
|
||||
let val = try!(self.eval(expr));
|
||||
let val = match val {
|
||||
Value::Number(val) => val,
|
||||
_ => return Err(format!("Cannot find derivatives of <{}>", val.show(self))),
|
||||
};
|
||||
let aliases = self.aliases.iter()
|
||||
.map(|(a, b)| (a.clone(), Rc::new(b.clone())))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let derivs = self.derivatives(&val, &aliases);
|
||||
let derivs = derivs.into_iter().map(|(_score, names)| {
|
||||
let first = names.first().cloned();
|
||||
names.into_iter().skip(1).fold(
|
||||
first.map(|x| (**x).to_owned()).unwrap_or(String::new()),
|
||||
|a, x| format!("{} {}", a, x))
|
||||
}).collect::<Vec<_>>();
|
||||
let first = derivs.first().cloned();
|
||||
let derivs = derivs.into_iter().skip(1).fold(
|
||||
first.unwrap_or(String::new()),
|
||||
|a, x| format!("{}; {}", a, x));
|
||||
Ok(format!("Possible derivatives: {}", derivs))
|
||||
},
|
||||
_ => {
|
||||
let val = try!(self.eval(expr));
|
||||
Ok(val.show(self))
|
||||
|
|
|
@ -245,6 +245,10 @@ impl Number {
|
|||
|
||||
String::from_utf8(out).unwrap()
|
||||
}
|
||||
|
||||
pub fn complexity_score(&self) -> i64 {
|
||||
self.1.iter().map(|(_, p)| 1 + p.abs()).fold(0, |a,x| a+x)
|
||||
}
|
||||
}
|
||||
|
||||
impl Show for Number {
|
||||
|
|
|
@ -301,6 +301,7 @@ pub enum Expr {
|
|||
Equals(Box<Expr>, Box<Expr>),
|
||||
Suffix(SuffixOp, Box<Expr>),
|
||||
Call(String, Vec<Expr>),
|
||||
Derivatives(Box<Expr>),
|
||||
DegC,
|
||||
DegF,
|
||||
DegRe,
|
||||
|
@ -500,6 +501,13 @@ fn parse_eq(mut iter: &mut Iter) -> Expr {
|
|||
}
|
||||
|
||||
pub fn parse_expr(mut iter: &mut Iter) -> Expr {
|
||||
match iter.peek().cloned() {
|
||||
Some(Token::Ident(ref s)) if s == "derivatives" => {
|
||||
iter.next();
|
||||
return Expr::Derivatives(Box::new(parse_eq(iter)))
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
let left = parse_eq(iter);
|
||||
match iter.peek().cloned().unwrap() {
|
||||
Token::DashArrow => {
|
||||
|
|
Loading…
Reference in a new issue