mirror of
https://github.com/tiffany352/rink-rs
synced 2024-09-20 22:21:57 +00:00
Add output of computed derived units
E.g. a calculation with a result of W^2 will show as 1 watt^2 = kg^2 m^4 / s^6
This commit is contained in:
parent
6432f0b864
commit
a5ddd614bc
3 changed files with 89 additions and 25 deletions
21
src/eval.rs
21
src/eval.rs
|
@ -21,6 +21,7 @@ pub struct Context {
|
|||
pub dimensions: Vec<Rc<String>>,
|
||||
pub units: HashMap<String, Number>,
|
||||
pub aliases: HashMap<Unit, String>,
|
||||
pub reverse: HashMap<Unit, String>,
|
||||
pub prefixes: Vec<(String, Number)>,
|
||||
pub datepatterns: Vec<Vec<DatePattern>>,
|
||||
pub short_output: bool,
|
||||
|
@ -570,7 +571,7 @@ impl Context {
|
|||
let aliases = self.aliases.iter()
|
||||
.map(|(a, b)| (a.clone(), Rc::new(b.clone())))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let results = factorize(self, &val, &aliases);
|
||||
let results = factorize(&val, &aliases);
|
||||
let mut results = results.into_sorted_vec();
|
||||
results.dedup();
|
||||
let results = results.into_iter().map(|Factors(_score, names)| {
|
||||
|
@ -611,6 +612,7 @@ impl Context {
|
|||
dimensions: Vec::new(),
|
||||
units: HashMap::new(),
|
||||
aliases: HashMap::new(),
|
||||
reverse: HashMap::new(),
|
||||
prefixes: Vec::new(),
|
||||
datepatterns: Vec::new(),
|
||||
short_output: false,
|
||||
|
@ -759,6 +761,20 @@ impl Context {
|
|||
(name, res)
|
||||
});
|
||||
|
||||
let mut reverse = HashSet::new();
|
||||
reverse.insert("newton");
|
||||
reverse.insert("pascal");
|
||||
reverse.insert("joule");
|
||||
reverse.insert("watt");
|
||||
reverse.insert("coulomb");
|
||||
reverse.insert("volt");
|
||||
reverse.insert("ohm");
|
||||
reverse.insert("siemens");
|
||||
reverse.insert("farad");
|
||||
reverse.insert("weber");
|
||||
reverse.insert("henry");
|
||||
reverse.insert("tesla");
|
||||
|
||||
for (name, def) in udefs {
|
||||
let name = match name {
|
||||
Name::Unit(name) => (*name).clone(),
|
||||
|
@ -772,6 +788,9 @@ impl Context {
|
|||
},
|
||||
Def::Unit(ref expr) => match ctx.eval(expr) {
|
||||
Ok(Value::Number(v)) => {
|
||||
if v.0 == Mpq::one() && reverse.contains(&*name) {
|
||||
ctx.reverse.insert(v.1.clone(), name.clone());
|
||||
}
|
||||
ctx.units.insert(name.clone(), v);
|
||||
},
|
||||
Ok(_) => println!("Unit {} is not a number", name),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::{BTreeMap, BinaryHeap};
|
||||
use eval::Context;
|
||||
use std::collections::{BTreeMap, BinaryHeap, HashMap};
|
||||
use std::rc::Rc;
|
||||
use number::{Number, Unit};
|
||||
use std::cmp;
|
||||
use gmp::mpq::Mpq;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct Factors(pub usize, pub Vec<Rc<String>>);
|
||||
|
@ -19,7 +19,30 @@ impl cmp::Ord for Factors {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn factorize(ctx: &Context, value: &Number, aliases: &BTreeMap<Unit, Rc<String>>)
|
||||
pub fn fast_decompose(value: &Number, aliases: &HashMap<Unit, String>) -> Unit {
|
||||
let mut best = None;
|
||||
for (unit, name) in aliases.iter() {
|
||||
let num = Number(Mpq::one(), unit.clone());
|
||||
for &i in [-1, 1, 2].into_iter() {
|
||||
let res = (value / &num.powi(i)).unwrap();
|
||||
let score = res.complexity_score();
|
||||
let better = best.as_ref().map(|&(_, _, _, current)| score < current).unwrap_or(true);
|
||||
if better {
|
||||
best = Some((name, unit, i, score));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((name, unit, pow, score)) = best {
|
||||
if score < value.complexity_score() {
|
||||
let mut res = (value / &Number(Mpq::one(), unit.clone()).powi(pow)).unwrap().1;
|
||||
res.insert(Rc::new(name.clone()), pow as i64);
|
||||
return res
|
||||
}
|
||||
}
|
||||
value.1.clone()
|
||||
}
|
||||
|
||||
pub fn factorize(value: &Number, aliases: &BTreeMap<Unit, Rc<String>>)
|
||||
-> BinaryHeap<Factors> {
|
||||
if value.1.len() == 0 {
|
||||
let mut map = BinaryHeap::new();
|
||||
|
@ -29,8 +52,6 @@ pub fn factorize(ctx: &Context, value: &Number, aliases: &BTreeMap<Unit, Rc<Stri
|
|||
let mut candidates: BinaryHeap<Factors> = 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();
|
||||
|
@ -38,7 +59,7 @@ pub fn factorize(ctx: &Context, value: &Number, aliases: &BTreeMap<Unit, Rc<Stri
|
|||
if score >= value_score {
|
||||
continue
|
||||
}
|
||||
let res = factorize(ctx, &res, aliases);
|
||||
let res = factorize(&res, aliases);
|
||||
for Factors(score, mut vec) in res {
|
||||
vec.push(name.clone());
|
||||
vec.sort();
|
||||
|
|
|
@ -247,30 +247,15 @@ 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 fmt::Debug for Number {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.show_number_part())
|
||||
}
|
||||
}
|
||||
|
||||
impl Show for Number {
|
||||
fn show(&self, context: &::eval::Context) -> String {
|
||||
fn unit_to_string(unit: &Unit) -> String {
|
||||
use std::io::Write;
|
||||
|
||||
let mut out = vec![];
|
||||
let mut frac = vec![];
|
||||
let mut value = self.clone();
|
||||
value.0.canonicalize();
|
||||
|
||||
write!(out, "{}", self.show_number_part()).unwrap();
|
||||
for (dim, &exp) in &value.1 {
|
||||
for (dim, &exp) in unit {
|
||||
if exp < 0 {
|
||||
frac.push((dim.clone(), exp));
|
||||
frac.push((dim, exp));
|
||||
} else {
|
||||
write!(out, " {}", dim).unwrap();
|
||||
if exp != 1 {
|
||||
|
@ -288,6 +273,45 @@ impl Show for Number {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.remove(0);
|
||||
String::from_utf8(out).unwrap()
|
||||
}
|
||||
|
||||
pub fn unit_name(&self, context: &::eval::Context) -> String {
|
||||
let raw = Number::unit_to_string(&self.1);
|
||||
|
||||
let pretty = ::factorize::fast_decompose(self, &context.reverse);
|
||||
|
||||
if self.1 != pretty {
|
||||
let pretty = Number::unit_to_string(&pretty);
|
||||
format!("{} = {}", pretty, raw)
|
||||
} else {
|
||||
raw
|
||||
}
|
||||
}
|
||||
|
||||
pub fn complexity_score(&self) -> i64 {
|
||||
self.1.iter().map(|(_, p)| 1 + p.abs()).fold(0, |a,x| a+x)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Number {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.show_number_part())
|
||||
}
|
||||
}
|
||||
|
||||
impl Show for Number {
|
||||
fn show(&self, context: &::eval::Context) -> String {
|
||||
use std::io::Write;
|
||||
|
||||
let mut out = vec![];
|
||||
let mut value = self.clone();
|
||||
value.0.canonicalize();
|
||||
|
||||
write!(out, "{} {}", self.show_number_part(), self.unit_name(context)).unwrap();
|
||||
|
||||
let alias = context.aliases.get(&value.1).cloned().or_else(|| {
|
||||
if value.1.len() == 1 {
|
||||
let e = value.1.iter().next().unwrap();
|
||||
|
|
Loading…
Reference in a new issue