use crate::number::{Dimension, Number, Quantity};
use crate::numeric::Numeric;
use std::cmp;
use std::collections::{BTreeMap, BinaryHeap};
use std::rc::Rc;
#[derive(PartialEq, Eq, Debug)]
pub struct Factors(pub usize, pub Vec<Rc<String>>);
impl cmp::PartialOrd for Factors {
fn partial_cmp(&self, other: &Factors) -> Option<cmp::Ordering> {
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 fast_decompose(value: &Number, quantities: &BTreeMap<Quantity, String>) -> Quantity {
let mut best = None;
'outer: for (unit, name) in quantities.iter() {
for (dim, pow) in unit {
let vpow = value.unit.get(dim).cloned().unwrap_or(0);
let snum = (vpow - pow).signum();
if snum != 0 && snum != vpow.signum() {
continue 'outer;
}
}
let num = Number {
value: Numeric::one(),
unit: unit.clone(),
};
for &i in [-1, 1, 2].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 num = Number {
value: Numeric::one(),
unit: unit.clone(),
};
let mut res = (value / &num.powi(pow)).unwrap().unit;
res.insert(Dimension::new(&**name), pow as i64);
return res;
}
}
value.unit.clone()
}
pub fn factorize(
value: &Number,
quantities: &BTreeMap<Quantity, Rc<String>>,
) -> BinaryHeap<Factors> {
if value.dimless() {
let mut map = BinaryHeap::new();
map.push(Factors(0, vec![]));
return map;
}
let mut candidates: BinaryHeap<Factors> = BinaryHeap::new();
let value_score = value.complexity_score();
for (unit, name) in quantities.iter().rev() {
let num = Number {
value: Numeric::one(),
unit: unit.clone(),
};
let res = (value / &num).unwrap();
let score = res.complexity_score();
if score >= value_score {
continue;
}
let res = factorize(&res, quantities);
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
}