diff --git a/src/derivatives.rs b/src/derivatives.rs deleted file mode 100644 index 0f593eb..0000000 --- a/src/derivatives.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::collections::{BTreeMap, BinaryHeap}; -use eval::Context; -use std::rc::Rc; -use number::{Number, Unit}; -use std::cmp; - -#[derive(PartialEq, Eq, Debug)] -pub struct Deriv(pub usize, pub Vec>); - -impl cmp::PartialOrd for Deriv { - fn partial_cmp(&self, other: &Deriv) -> Option { - Some(self.0.cmp(&other.0).reverse()) - } -} - -impl cmp::Ord for Deriv { - fn cmp(&self, other: &Deriv) -> cmp::Ordering { - self.partial_cmp(other).unwrap() - } -} - -pub fn derivatives(ctx: &Context, value: &Number, aliases: &BTreeMap>) - -> BinaryHeap { - if value.1.len() == 0 { - let mut map = BinaryHeap::new(); - map.push(Deriv(0, vec![])); - return map; - } - let mut candidates: BinaryHeap = BinaryHeap::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 = derivatives(ctx, &res, aliases); - for Deriv(score, mut vec) in res { - vec.push(name.clone()); - candidates.push(Deriv(score + 1, vec)); - } - let mut next = BinaryHeap::new(); - for _ in 0..10 { - if let Some(v) = candidates.pop() { - next.push(v); - } else { - break - } - } - candidates = next; - } - assert!(candidates.len() <= 10); - candidates -} diff --git a/src/eval.rs b/src/eval.rs index 7902600..47de64c 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -7,7 +7,7 @@ use date; use unit_defs::DatePattern; use std::ops::{Add, Div, Mul, Neg, Sub}; use std::rc::Rc; -use derivatives::{derivatives, Deriv}; +use factorize::{factorize, Factors}; #[derive(Clone)] pub enum Value { @@ -324,7 +324,7 @@ impl Context { }) }), Expr::Convert(_, _) => Err(format!("Conversions (->) must be top-level expressions")), - Expr::Derivatives(_) => Err(format!("Derivatives must be top-level expressions")), + Expr::Factorize(_) => 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::, _>>()); @@ -416,7 +416,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::Factorize(_) => Err(format!("Derivatives are not allowed in the right hand of conversions")), Expr::Error(ref e) => Err(e.clone()), } } @@ -561,7 +561,7 @@ impl Context { (Err(e), _, _) => Err(e), (_, _, Err(e)) => Err(e), }, - Expr::Derivatives(ref expr) => { + Expr::Factorize(ref expr) => { let val = try!(self.eval(expr)); let val = match val { Value::Number(val) => val, @@ -570,18 +570,21 @@ impl Context { let aliases = self.aliases.iter() .map(|(a, b)| (a.clone(), Rc::new(b.clone()))) .collect::>(); - let derivs = derivatives(self, &val, &aliases); - let derivs = derivs.into_iter().map(|Deriv(_score, names)| { + let results = factorize(self, &val, &aliases); + let mut results = results.into_sorted_vec(); + results.dedup(); + let results = results.into_iter().map(|Factors(_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::>(); - let first = derivs.first().cloned(); - let derivs = derivs.into_iter().skip(1).fold( + let first = results.first().cloned(); + let len = results.len(); + let results = results.into_iter().skip(1).fold( first.unwrap_or(String::new()), |a, x| format!("{}; {}", a, x)); - Ok(format!("Possible derivatives: {}", derivs)) + Ok(format!("Factorizations: {}{}", results, if len < 10 {""} else {"; ..."})) }, _ => { let val = try!(self.eval(expr)); diff --git a/src/factorize.rs b/src/factorize.rs new file mode 100644 index 0000000..beecd59 --- /dev/null +++ b/src/factorize.rs @@ -0,0 +1,53 @@ +use std::collections::{BTreeMap, BinaryHeap}; +use eval::Context; +use std::rc::Rc; +use number::{Number, Unit}; +use std::cmp; + +#[derive(PartialEq, Eq, Debug)] +pub struct Factors(pub usize, pub Vec>); + +impl cmp::PartialOrd for Factors { + fn partial_cmp(&self, other: &Factors) -> Option { + Some(self.0.cmp(&other.0)) + } +} + +impl cmp::Ord for Factors { + fn cmp(&self, other: &Factors) -> cmp::Ordering { + self.partial_cmp(other).unwrap() + } +} + +pub fn factorize(ctx: &Context, value: &Number, aliases: &BTreeMap>) + -> BinaryHeap { + if value.1.len() == 0 { + let mut map = BinaryHeap::new(); + map.push(Factors(0, vec![])); + return map; + } + let mut candidates: BinaryHeap = BinaryHeap::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 = factorize(ctx, &res, aliases); + for Factors(score, mut vec) in res { + vec.push(name.clone()); + vec.sort(); + candidates.push(Factors(score + 1, vec)); + } + let mut next = candidates.into_sorted_vec(); + next.dedup(); + candidates = next.into_iter().take(10).collect(); + } + assert!(candidates.len() <= 10); + candidates +} diff --git a/src/lib.rs b/src/lib.rs index 462a5f6..779ab08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,7 @@ pub mod unit_defs; pub mod eval; pub mod number; pub mod date; -pub mod derivatives; +pub mod factorize; pub use number::Number; pub use eval::{Context, Value}; diff --git a/src/unit_defs.rs b/src/unit_defs.rs index a46f79d..dc1e25b 100644 --- a/src/unit_defs.rs +++ b/src/unit_defs.rs @@ -301,7 +301,7 @@ pub enum Expr { Equals(Box, Box), Suffix(SuffixOp, Box), Call(String, Vec), - Derivatives(Box), + Factorize(Box), DegC, DegF, DegRe, @@ -502,9 +502,9 @@ 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" => { + Some(Token::Ident(ref s)) if s == "factorize" => { iter.next(); - return Expr::Derivatives(Box::new(parse_eq(iter))) + return Expr::Factorize(Box::new(parse_eq(iter))) }, _ => () }