|
|
|
@ -214,9 +214,9 @@ impl Region2 {
@@ -214,9 +214,9 @@ impl Region2 {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mod solve { |
|
|
|
|
pub mod solve { |
|
|
|
|
|
|
|
|
|
use std::collections::BTreeSet; |
|
|
|
|
use std::collections::{BTreeMap, BTreeSet}; |
|
|
|
|
use std::fmt; |
|
|
|
|
use std::iter::FromIterator; |
|
|
|
|
|
|
|
|
@ -268,10 +268,179 @@ mod solve {
@@ -268,10 +268,179 @@ mod solve {
|
|
|
|
|
enum Expr { |
|
|
|
|
Unkn(Unknown), |
|
|
|
|
Const(Scalar), |
|
|
|
|
Plus(Box<Expr>, Box<Expr>), |
|
|
|
|
Sum(Exprs), |
|
|
|
|
Neg(Box<Expr>), |
|
|
|
|
Times(Box<Expr>, Box<Expr>), |
|
|
|
|
Inv(Box<Expr>), |
|
|
|
|
Product(Exprs), |
|
|
|
|
Div(Box<Expr>, Box<Expr>), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type Exprs = Vec<Expr>; |
|
|
|
|
|
|
|
|
|
impl Unknowns for Exprs { |
|
|
|
|
fn unknowns(&self) -> UnknownSet { |
|
|
|
|
self.iter().flat_map(|e: &Expr| e.unknowns()).collect() |
|
|
|
|
} |
|
|
|
|
fn has_unknowns(&self) -> bool { |
|
|
|
|
self.iter().any(|e: &Expr| e.has_unknowns()) |
|
|
|
|
} |
|
|
|
|
fn has_unknown(&self, u: Unknown) -> bool { |
|
|
|
|
self.iter().any(|e: &Expr| e.has_unknown(u)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn write_separated_exprs(es: &Exprs, f: &mut fmt::Formatter, sep: &str) -> fmt::Result { |
|
|
|
|
let mut is_first = true; |
|
|
|
|
for e in es { |
|
|
|
|
if is_first { |
|
|
|
|
is_first = false; |
|
|
|
|
} else { |
|
|
|
|
write!(f, "{}", sep)? |
|
|
|
|
} |
|
|
|
|
write!(f, "({})", e)? |
|
|
|
|
} |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn remove_common_terms(l: &mut Vec<Expr>, r: &mut Vec<Expr>) -> Vec<Expr> { |
|
|
|
|
let common: Vec<_> = l.drain_filter(|e| r.contains(e)).collect(); |
|
|
|
|
common.iter().for_each(|e| { |
|
|
|
|
r.remove_item(e); |
|
|
|
|
}); |
|
|
|
|
common |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn remove_term(terms: &mut Vec<Expr>, term: &Expr) -> Option<Expr> { |
|
|
|
|
terms.remove_item(term) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn sum_fold(l: Expr, r: Expr) -> Expr { |
|
|
|
|
use itertools::Itertools; |
|
|
|
|
use Expr::*; |
|
|
|
|
match (l, r) { |
|
|
|
|
(Const(lc), Const(rc)) => Const(lc + rc), |
|
|
|
|
(Const(c), o) | (o, Const(c)) if relative_eq!(c, 0.) => o, |
|
|
|
|
(Product(mut l), Product(mut r)) => { |
|
|
|
|
let comm = remove_common_terms(&mut l, &mut r); |
|
|
|
|
Expr::new_product(Sum(comm), Expr::new_sum(Product(l), Product(r))).simplify() |
|
|
|
|
} |
|
|
|
|
(Product(mut l), r) | (r, Product(mut l)) => { |
|
|
|
|
let comm = remove_term(&mut l, &r); |
|
|
|
|
match comm { |
|
|
|
|
Some(_) => { |
|
|
|
|
Expr::new_product(r, Expr::new_sum(Product(l), Const(1.))).simplify() |
|
|
|
|
} |
|
|
|
|
None => Expr::new_sum(Product(l), r), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
(l, r) => Expr::new_sum(l, r), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn group_sum(es: Exprs) -> Exprs { |
|
|
|
|
use Expr::*; |
|
|
|
|
let mut common: BTreeMap<UnknownSet, Expr> = BTreeMap::new(); |
|
|
|
|
for e in es { |
|
|
|
|
let unkns = e.unknowns(); |
|
|
|
|
match common.get_mut(&unkns) { |
|
|
|
|
None => { |
|
|
|
|
match e { |
|
|
|
|
Const(c) if relative_eq!(c, 0.) => (), |
|
|
|
|
e => { |
|
|
|
|
common.insert(unkns, e); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
Some(existing) => { |
|
|
|
|
match existing { |
|
|
|
|
Sum(ref mut es) => { |
|
|
|
|
// already failed at merging, so just add it to the list
|
|
|
|
|
es.push(e); |
|
|
|
|
} |
|
|
|
|
other => { |
|
|
|
|
*other = sum_fold(other.clone(), e); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
common.into_iter().map(|(_, v)| v).collect() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn product_fold(l: Expr, r: Expr) -> Expr { |
|
|
|
|
use itertools::Itertools; |
|
|
|
|
use Expr::*; |
|
|
|
|
match (l, r) { |
|
|
|
|
(Const(lc), Const(rc)) => Const(lc * rc), |
|
|
|
|
(Const(c), o) | (o, Const(c)) if relative_eq!(c, 1.) => o, |
|
|
|
|
(Div(num, den), mul) | (mul, Div(num, den)) => { |
|
|
|
|
if mul == *den { |
|
|
|
|
*num |
|
|
|
|
} else { |
|
|
|
|
Expr::Div(Box::new(Expr::Product(vec![*num, mul])), den).simplify() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
(l, r) => Expr::new_product(l, r), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn group_product(es: Exprs) -> Exprs { |
|
|
|
|
use Expr::*; |
|
|
|
|
let mut common: BTreeMap<UnknownSet, Expr> = BTreeMap::new(); |
|
|
|
|
for e in es { |
|
|
|
|
let unkns = e.unknowns(); |
|
|
|
|
match common.get_mut(&unkns) { |
|
|
|
|
None => { |
|
|
|
|
match e { |
|
|
|
|
Const(c) if relative_eq!(c, 1.) => (), |
|
|
|
|
e => { |
|
|
|
|
common.insert(unkns, e); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
Some(existing) => { |
|
|
|
|
match existing { |
|
|
|
|
Sum(ref mut es) => { |
|
|
|
|
// already failed at merging, so just add it to the list
|
|
|
|
|
es.push(e); |
|
|
|
|
} |
|
|
|
|
other => *other = product_fold(other.clone(), e), |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
common.into_iter().map(|(_, v)| v).collect() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn distribute_product_sums(mut es: Exprs) -> Expr { |
|
|
|
|
trace!("distribute_product_sums: {}", Product(es.clone())); |
|
|
|
|
use itertools::Itertools; |
|
|
|
|
use Expr::*; |
|
|
|
|
let sums = es |
|
|
|
|
.drain_filter(|e| match e { |
|
|
|
|
Sum(_) => true, |
|
|
|
|
_ => false, |
|
|
|
|
}) |
|
|
|
|
.map(|e| { |
|
|
|
|
trace!("sum in product: {}", e); |
|
|
|
|
match e { |
|
|
|
|
Sum(es) => es, |
|
|
|
|
_ => unreachable!(), |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
let products: Vec<_> = sums.multi_cartesian_product().collect(); |
|
|
|
|
if products.is_empty() { |
|
|
|
|
trace!("no sums to distribute"); |
|
|
|
|
return Product(es); |
|
|
|
|
} |
|
|
|
|
let sums = products |
|
|
|
|
.into_iter() |
|
|
|
|
.map(|mut prod| { |
|
|
|
|
prod.extend(es.clone()); |
|
|
|
|
trace!("prod: {}", Product(prod.clone())); |
|
|
|
|
Product(prod) |
|
|
|
|
}) |
|
|
|
|
.collect(); |
|
|
|
|
Sum(sums) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Unknowns for Expr { |
|
|
|
@ -280,8 +449,9 @@ mod solve {
@@ -280,8 +449,9 @@ mod solve {
|
|
|
|
|
match self { |
|
|
|
|
Unkn(u) => u.unknowns(), |
|
|
|
|
Const(_) => UnknownSet::default(), |
|
|
|
|
Plus(l, r) | Times(l, r) => l.unknowns().union(&r.unknowns()).cloned().collect(), |
|
|
|
|
Neg(e) | Inv(e) => e.unknowns(), |
|
|
|
|
Sum(es) | Product(es) => es.unknowns(), |
|
|
|
|
Div(l, r) => l.unknowns().union(&r.unknowns()).cloned().collect(), |
|
|
|
|
Neg(e) => e.unknowns(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
fn has_unknowns(&self) -> bool { |
|
|
|
@ -289,8 +459,9 @@ mod solve {
@@ -289,8 +459,9 @@ mod solve {
|
|
|
|
|
match self { |
|
|
|
|
Unkn(u) => u.has_unknowns(), |
|
|
|
|
Const(_) => false, |
|
|
|
|
Plus(l, r) | Times(l, r) => l.has_unknowns() || r.has_unknowns(), |
|
|
|
|
Neg(e) | Inv(e) => e.has_unknowns(), |
|
|
|
|
Sum(es) | Product(es) => es.has_unknowns(), |
|
|
|
|
Div(l, r) => l.has_unknowns() || r.has_unknowns(), |
|
|
|
|
Neg(e) => e.has_unknowns(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
fn has_unknown(&self, u: Unknown) -> bool { |
|
|
|
@ -298,30 +469,31 @@ mod solve {
@@ -298,30 +469,31 @@ mod solve {
|
|
|
|
|
match self { |
|
|
|
|
Unkn(u1) => u1.has_unknown(u), |
|
|
|
|
Const(_) => false, |
|
|
|
|
Plus(l, r) | Times(l, r) => l.has_unknown(u) || r.has_unknown(u), |
|
|
|
|
Neg(e) | Inv(e) => e.has_unknown(u), |
|
|
|
|
Sum(es) | Product(es) => es.has_unknown(u), |
|
|
|
|
Div(l, r) => l.has_unknown(u) || r.has_unknown(u), |
|
|
|
|
Neg(e) => e.has_unknown(u), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Expr { |
|
|
|
|
fn new_plus(e1: Expr, e2: Expr) -> Expr { |
|
|
|
|
Expr::Plus(Box::new(e1), Box::new(e2)) |
|
|
|
|
fn new_sum(e1: Expr, e2: Expr) -> Expr { |
|
|
|
|
Expr::Sum(vec![e1, e2]) |
|
|
|
|
} |
|
|
|
|
fn new_times(e1: Expr, e2: Expr) -> Expr { |
|
|
|
|
Expr::Times(Box::new(e1), Box::new(e2)) |
|
|
|
|
fn new_product(e1: Expr, e2: Expr) -> Expr { |
|
|
|
|
Expr::Product(vec![e1, e2]) |
|
|
|
|
} |
|
|
|
|
fn new_neg(e1: Expr) -> Expr { |
|
|
|
|
Expr::Neg(Box::new(e1)) |
|
|
|
|
} |
|
|
|
|
fn new_inv(e1: Expr) -> Expr { |
|
|
|
|
Expr::Inv(Box::new(e1)) |
|
|
|
|
fn new_div(num: Expr, den: Expr) -> Expr { |
|
|
|
|
Expr::Div(Box::new(num), Box::new(den)) |
|
|
|
|
} |
|
|
|
|
fn new_minus(e1: Expr, e2: Expr) -> Expr { |
|
|
|
|
Expr::Plus(Box::new(e1), Box::new(Expr::new_neg(e2))) |
|
|
|
|
Expr::Sum(vec![e1, Expr::new_neg(e2)]) |
|
|
|
|
} |
|
|
|
|
fn new_div(e1: Expr, e2: Expr) -> Expr { |
|
|
|
|
Expr::Times(Box::new(e1), Box::new(Expr::new_inv(e2))) |
|
|
|
|
fn new_inv(den: Expr) -> Expr { |
|
|
|
|
Expr::new_div(Expr::Const(1.), den) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn is_zero(&self) -> bool { |
|
|
|
@ -343,90 +515,132 @@ mod solve {
@@ -343,90 +515,132 @@ mod solve {
|
|
|
|
|
fn simplify(self) -> Expr { |
|
|
|
|
use Expr::*; |
|
|
|
|
match self { |
|
|
|
|
Plus(l, r) => match (l.simplify(), r.simplify()) { |
|
|
|
|
(Const(lc), Const(rc)) => Const(lc + rc), |
|
|
|
|
(Const(c), ref o) | (ref o, Const(c)) if relative_eq!(c, 0.) => o.clone(), |
|
|
|
|
(Times(l1, l2), Times(r1, r2)) => { |
|
|
|
|
if l2 == r2 { |
|
|
|
|
Expr::new_times(Expr::Plus(l1, r1), *l2).simplify() |
|
|
|
|
} else if l1 == r1 { |
|
|
|
|
Expr::new_times(Expr::Plus(l2, r2), *l1).simplify() |
|
|
|
|
} else if l1 == r2 { |
|
|
|
|
Expr::new_times(Expr::Plus(l2, r1), *l1).simplify() |
|
|
|
|
} else if l2 == r1 { |
|
|
|
|
Expr::new_times(Expr::Plus(l1, r2), *l2).simplify() |
|
|
|
|
Sum(es) => { |
|
|
|
|
let mut new_es: Vec<_> = es |
|
|
|
|
.into_iter() |
|
|
|
|
.map(|e| e.simplify()) |
|
|
|
|
.flat_map(|e| match e { |
|
|
|
|
Sum(more_es) => more_es, |
|
|
|
|
other => vec![other], |
|
|
|
|
}) |
|
|
|
|
.collect(); |
|
|
|
|
let pre_new_es = new_es.clone(); |
|
|
|
|
new_es = group_sum(new_es); |
|
|
|
|
trace!( |
|
|
|
|
"simplify sum {} => {}", |
|
|
|
|
Sum(pre_new_es), |
|
|
|
|
Sum(new_es.clone()) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
match new_es.len() { |
|
|
|
|
0 => Const(0.), // none
|
|
|
|
|
1 => new_es.into_iter().next().unwrap(), // one
|
|
|
|
|
_ => Sum(new_es), // many
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Product(es) => { |
|
|
|
|
let new_es: Vec<_> = es |
|
|
|
|
.into_iter() |
|
|
|
|
.map(|e| e.simplify()) |
|
|
|
|
.flat_map(|e| match e { |
|
|
|
|
Product(more_es) => more_es, |
|
|
|
|
other => vec![other], |
|
|
|
|
}) |
|
|
|
|
.collect(); |
|
|
|
|
let pre_new_es = new_es.clone(); |
|
|
|
|
let new_es = group_product(new_es); |
|
|
|
|
trace!( |
|
|
|
|
"simplify product {} => {}", |
|
|
|
|
Product(pre_new_es), |
|
|
|
|
Product(new_es.clone()) |
|
|
|
|
); |
|
|
|
|
match new_es.len() { |
|
|
|
|
0 => Const(1.), // none
|
|
|
|
|
1 => new_es.into_iter().next().unwrap(), // one
|
|
|
|
|
_ => Product(new_es), // many
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Neg(mut v) => { |
|
|
|
|
*v = v.simplify(); |
|
|
|
|
trace!("simplify neg {}", Neg(v.clone())); |
|
|
|
|
match v { |
|
|
|
|
box Const(c) => Const(-c), |
|
|
|
|
box Neg(v) => *v, |
|
|
|
|
e => Product(vec![Const(-1.), *e]), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Div(mut num, mut den) => { |
|
|
|
|
*num = num.simplify(); |
|
|
|
|
*den = den.simplify(); |
|
|
|
|
trace!("simplify div {}", Div(num.clone(), den.clone())); |
|
|
|
|
match (num, den) { |
|
|
|
|
(box Const(num), box Const(den)) => Const(num / den), |
|
|
|
|
(num, box Const(den)) => { |
|
|
|
|
if relative_eq!(den, 1.) { |
|
|
|
|
*num |
|
|
|
|
} else { |
|
|
|
|
Expr::new_plus(Times(l1, l2), Times(r1, r2)) |
|
|
|
|
Expr::new_product(*num, Const(1. / den)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
(l, r) => Self::new_plus(l, r), |
|
|
|
|
}, |
|
|
|
|
Times(l, r) => match (l.simplify(), r.simplify()) { |
|
|
|
|
(Const(lc), Const(rc)) => Const(lc * rc), |
|
|
|
|
(Const(c), ref o) | (ref o, Const(c)) if relative_eq!(c, 1.) => o.clone(), |
|
|
|
|
(Inv(ref den), ref num) | (ref num, Inv(ref den)) if *num == **den => Const(1.), |
|
|
|
|
(l, r) => Self::new_times(l, r), |
|
|
|
|
}, |
|
|
|
|
Neg(v) => match v.simplify() { |
|
|
|
|
Const(c) => Const(-c), |
|
|
|
|
Neg(v) => *v, |
|
|
|
|
e => Self::new_times(Const(-1.), e), |
|
|
|
|
}, |
|
|
|
|
Inv(v) => match v.simplify() { |
|
|
|
|
Const(c) => Const(1. / c), |
|
|
|
|
Inv(v) => *v, |
|
|
|
|
e => Self::new_inv(e), |
|
|
|
|
(num, box Div(dennum, denden)) => { |
|
|
|
|
Div(Box::new(Product(vec![*num, *denden])), dennum).simplify() |
|
|
|
|
} |
|
|
|
|
(box Product(mut es), box den) => match es.remove_item(&den) { |
|
|
|
|
Some(_) => Product(es), |
|
|
|
|
None => Expr::new_div(Product(es), den), |
|
|
|
|
}, |
|
|
|
|
e => e, |
|
|
|
|
(num, den) => { |
|
|
|
|
if num == den { |
|
|
|
|
Expr::Const(1.) |
|
|
|
|
} else { |
|
|
|
|
Div(num, den) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn distrubute(self) -> Expr { |
|
|
|
|
use Expr::*; |
|
|
|
|
match self { |
|
|
|
|
Plus(l, r) => Expr::new_plus(l.distrubute(), r.distrubute()), |
|
|
|
|
Times(l, r) => match (*l, *r) { |
|
|
|
|
(Plus(l, r), o) | (o, Plus(l, r)) => Expr::new_plus( |
|
|
|
|
Expr::Times(Box::new(o.clone()), l), |
|
|
|
|
Expr::Times(Box::new(o), r), |
|
|
|
|
) |
|
|
|
|
.distrubute(), |
|
|
|
|
(l, r) => Expr::new_times(l, r), |
|
|
|
|
}, |
|
|
|
|
Neg(v) => match *v { |
|
|
|
|
Plus(l, r) => Expr::new_plus(Neg(l).distrubute(), Neg(r).distrubute()), |
|
|
|
|
Times(l, r) => Expr::new_times(Neg(l).distrubute(), *r), |
|
|
|
|
Neg(v) => v.distrubute(), |
|
|
|
|
Inv(v) => Expr::new_inv(Neg(v).distrubute()), |
|
|
|
|
e => Expr::new_neg(e), |
|
|
|
|
}, |
|
|
|
|
Inv(v) => match *v { |
|
|
|
|
Plus(l, r) => Expr::new_plus(Inv(l).distrubute(), Inv(r).distrubute()), |
|
|
|
|
Times(l, r) => Expr::new_times(Inv(l).distrubute(), Inv(r).distrubute()), |
|
|
|
|
Inv(v) => v.distrubute(), |
|
|
|
|
e => Expr::new_inv(e), |
|
|
|
|
}, |
|
|
|
|
e => e, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn reduce(self, for_u: Unknown) -> Expr { |
|
|
|
|
fn distribute(self) -> Expr { |
|
|
|
|
use Expr::*; |
|
|
|
|
trace!("distribute {}", self); |
|
|
|
|
match self { |
|
|
|
|
Plus(l, r) => match (l.reduce(for_u), r.reduce(for_u)) { |
|
|
|
|
(Const(lc), Const(rc)) => Const(lc + rc), |
|
|
|
|
(l, r) => Self::new_plus(l, r), |
|
|
|
|
}, |
|
|
|
|
Times(l, r) => match (l.reduce(for_u), r.reduce(for_u)) { |
|
|
|
|
(Const(lc), Const(rc)) => Const(lc * rc), |
|
|
|
|
(l, r) => Self::new_times(l, r), |
|
|
|
|
}, |
|
|
|
|
Neg(v) => match v.reduce(for_u) { |
|
|
|
|
Unkn(u) if u == for_u => Expr::new_times(Const(-1.), Unkn(u)), |
|
|
|
|
e => Self::new_neg(e), |
|
|
|
|
}, |
|
|
|
|
Inv(v) => match v.reduce(for_u) { |
|
|
|
|
e => Self::new_inv(e), |
|
|
|
|
Sum(mut es) => { |
|
|
|
|
for e in &mut es { |
|
|
|
|
*e = e.clone().distribute(); |
|
|
|
|
} |
|
|
|
|
Sum(es) |
|
|
|
|
} |
|
|
|
|
Product(es) => distribute_product_sums(es), |
|
|
|
|
Div(mut num, mut den) => { |
|
|
|
|
*num = num.distribute(); |
|
|
|
|
*den = den.distribute(); |
|
|
|
|
match (num, den) { |
|
|
|
|
(box Sum(es), box den) => Sum(es |
|
|
|
|
.into_iter() |
|
|
|
|
.map(|e| Expr::new_div(e, den.clone())) |
|
|
|
|
.collect()), |
|
|
|
|
(mut num, mut den) => Div(num, den), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Neg(v) => match v { |
|
|
|
|
// box Sum(mut l, mut r) => {
|
|
|
|
|
// *l = Neg(l.clone()).distribute();
|
|
|
|
|
// *r = Neg(r.clone()).distribute();
|
|
|
|
|
// Sum(l, r)
|
|
|
|
|
// }
|
|
|
|
|
// box Product(mut l, r) => {
|
|
|
|
|
// *l = Neg(l.clone()).distribute();
|
|
|
|
|
// Product(l, r)
|
|
|
|
|
// }
|
|
|
|
|
box Neg(v) => v.distribute(), |
|
|
|
|
box Div(mut num, mut den) => { |
|
|
|
|
*num = Neg(num.clone()).distribute(); |
|
|
|
|
*den = Neg(den.clone()).distribute(); |
|
|
|
|
Div(num, den) |
|
|
|
|
} |
|
|
|
|
e => Neg(e), |
|
|
|
|
}, |
|
|
|
|
e => e, |
|
|
|
|
} |
|
|
|
@ -439,10 +653,10 @@ mod solve {
@@ -439,10 +653,10 @@ mod solve {
|
|
|
|
|
match self { |
|
|
|
|
Unkn(u) => write!(f, "{}", u), |
|
|
|
|
Const(c) => write!(f, "{}", c), |
|
|
|
|
Plus(l, r) => write!(f, "({}) + ({})", l, r), |
|
|
|
|
Times(l, r) => write!(f, "({}) * ({})", l, r), |
|
|
|
|
Sum(es) => write_separated_exprs(es, f, " + "), |
|
|
|
|
Product(es) => write_separated_exprs(es, f, " * "), |
|
|
|
|
Div(num, den) => write!(f, "({}) / ({})", num, den), |
|
|
|
|
Neg(e) => write!(f, "-({})", e), |
|
|
|
|
Inv(e) => write!(f, "1 / ({})", e), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -477,27 +691,62 @@ mod solve {
@@ -477,27 +691,62 @@ mod solve {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Eqn { |
|
|
|
|
fn simplify(self) -> Eqn { |
|
|
|
|
Eqn(self.0.simplify(), self.1.simplify()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn solve(&self, for_u: Unknown) -> Option<Expr> { |
|
|
|
|
use Expr::*; |
|
|
|
|
if !self.has_unknown(for_u) { |
|
|
|
|
return None; |
|
|
|
|
} |
|
|
|
|
let (l, r) = (self.0.clone().simplify(), self.1.clone().simplify()); |
|
|
|
|
let (l, r) = ( |
|
|
|
|
self.0 |
|
|
|
|
.clone() /*.distribute()*/ |
|
|
|
|
.simplify(), |
|
|
|
|
self.1 |
|
|
|
|
.clone() /*.distribute()*/ |
|
|
|
|
.simplify(), |
|
|
|
|
); |
|
|
|
|
let (mut l, mut r) = ord_by_unkn(l, r, for_u)?; |
|
|
|
|
loop { |
|
|
|
|
trace!("solve: {} == {}", l, r); |
|
|
|
|
let (new_l, new_r): (Expr, Expr) = match l { |
|
|
|
|
Unkn(u) => return if u == for_u { Some(r.simplify()) } else { None }, |
|
|
|
|
Plus(a, b) => { |
|
|
|
|
let (a, b) = ord_by_unkn(*a, *b, for_u)?; |
|
|
|
|
(a, Expr::new_minus(r, b)) |
|
|
|
|
Sum(es) => { |
|
|
|
|
let (us, not_us): (Vec<_>, Vec<_>) = |
|
|
|
|
es.into_iter().partition(|e| e.has_unknown(for_u)); |
|
|
|
|
if us.len() != 1 { |
|
|
|
|
return None; |
|
|
|
|
} |
|
|
|
|
( |
|
|
|
|
Sum(us).simplify(), |
|
|
|
|
Expr::new_minus(r, Sum(not_us)).simplify(), |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
Times(a, b) => { |
|
|
|
|
let (a, b) = ord_by_unkn(*a, *b, for_u)?; |
|
|
|
|
(a, Expr::new_div(r, b)) |
|
|
|
|
Product(es) => { |
|
|
|
|
let (us, not_us): (Vec<_>, Vec<_>) = |
|
|
|
|
es.into_iter().partition(|e| e.has_unknown(for_u)); |
|
|
|
|
if us.len() != 1 { |
|
|
|
|
return None; |
|
|
|
|
} |
|
|
|
|
( |
|
|
|
|
Product(us).simplify(), |
|
|
|
|
Expr::new_div(r, Product(not_us)).simplify(), |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
Neg(v) => (*v, Expr::new_neg(r)), |
|
|
|
|
Inv(v) => (*v, Expr::new_inv(r)), |
|
|
|
|
Div(num, den) => { |
|
|
|
|
let (nu, du) = (num.has_unknown(for_u), den.has_unknown(for_u)); |
|
|
|
|
match (nu, du) { |
|
|
|
|
(true, false) => (*num, Expr::new_product(r, *den)), |
|
|
|
|
(false, true) => (Expr::new_product(r, *den), *num), |
|
|
|
|
(true, true) => return None, // TODO: simplify
|
|
|
|
|
(false, false) => return None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Const(_) => return None, |
|
|
|
|
_ => return None, |
|
|
|
|
}; |
|
|
|
|
l = new_l; |
|
|
|
|
r = new_r; |
|
|
|
@ -505,6 +754,12 @@ mod solve {
@@ -505,6 +754,12 @@ mod solve {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl fmt::Display for Eqn { |
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
write!(f, "{} == {}", self.0, self.1) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
|
|
|
struct Eqns(Vec<Eqn>); |
|
|
|
|
|
|
|
|
@ -554,6 +809,7 @@ mod solve {
@@ -554,6 +809,7 @@ mod solve {
|
|
|
|
|
#[test] |
|
|
|
|
fn test_solve() { |
|
|
|
|
use Expr::*; |
|
|
|
|
let _ = env_logger::try_init(); |
|
|
|
|
let u1 = Unknown(1); |
|
|
|
|
let e1 = Unkn(u1); |
|
|
|
|
let e2 = Const(1.); |
|
|
|
@ -563,9 +819,9 @@ mod solve {
@@ -563,9 +819,9 @@ mod solve {
|
|
|
|
|
let eqn = Eqn(e2.clone(), e1.clone()); |
|
|
|
|
assert_eq!(eqn.solve(u1), Some(Const(1.))); |
|
|
|
|
|
|
|
|
|
let e3 = Expr::new_plus(Const(1.), Const(1.)); |
|
|
|
|
let e3 = Expr::new_sum(Const(1.), Expr::new_sum(Const(1.), Const(2.))); |
|
|
|
|
let eqn = Eqn(e1.clone(), e3.clone()); |
|
|
|
|
assert_eq!(eqn.solve(u1), Some(Const(2.))); |
|
|
|
|
assert_eq!(eqn.solve(u1), Some(Const(4.))); |
|
|
|
|
let e3 = Expr::new_minus(Const(1.), Const(1.)); |
|
|
|
|
let eqn = Eqn(e1.clone(), e3.clone()); |
|
|
|
|
assert_eq!(eqn.solve(u1), Some(Const(0.))); |
|
|
|
@ -573,30 +829,74 @@ mod solve {
@@ -573,30 +829,74 @@ mod solve {
|
|
|
|
|
let e1 = Expr::new_div(Const(2.), Expr::new_minus(Const(1.), Const(4.))); |
|
|
|
|
let e2 = Expr::new_minus(Const(1.), Unkn(u1)); |
|
|
|
|
let eqn = Eqn(e1, e2); |
|
|
|
|
info!("eqn: {} => {}", eqn, eqn.clone().simplify()); |
|
|
|
|
let e = eqn.solve(u1).unwrap(); |
|
|
|
|
assert!(const_expr(e.clone()).is_some()); |
|
|
|
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), 5. / 3.)); |
|
|
|
|
|
|
|
|
|
let e1 = Expr::new_times(Const(2.), Expr::new_minus(Const(1.), Const(4.))); |
|
|
|
|
let e2 = Expr::new_minus(Expr::new_times(Unkn(u1), Const(2.)), Unkn(u1)); |
|
|
|
|
println!( |
|
|
|
|
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.))); |
|
|
|
|
let e2 = Expr::new_minus(Expr::new_product(Unkn(u1), Const(2.)), Unkn(u1)); |
|
|
|
|
info!( |
|
|
|
|
"e1==e2: {}=={} => {}=={}", |
|
|
|
|
e1, |
|
|
|
|
e2, |
|
|
|
|
e1.clone().simplify(), |
|
|
|
|
e2.clone().simplify() |
|
|
|
|
); |
|
|
|
|
println!( |
|
|
|
|
let eqn = Eqn(e1, e2); |
|
|
|
|
let e = eqn.solve(u1).unwrap(); |
|
|
|
|
assert!(const_expr(e.clone()).is_some()); |
|
|
|
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -6.)); |
|
|
|
|
|
|
|
|
|
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.))); |
|
|
|
|
let e2 = Expr::new_div( |
|
|
|
|
Expr::new_sum( |
|
|
|
|
Expr::new_product(Unkn(u1), Const(2.)), |
|
|
|
|
Expr::new_product(Unkn(u1), Unkn(u1)), |
|
|
|
|
), |
|
|
|
|
Unkn(u1), |
|
|
|
|
); |
|
|
|
|
info!( |
|
|
|
|
"{}=={} distrib=> {}=={}", |
|
|
|
|
e1, |
|
|
|
|
e2, |
|
|
|
|
e1.clone().distribute(), |
|
|
|
|
e2.clone().distribute() |
|
|
|
|
); |
|
|
|
|
info!( |
|
|
|
|
"{}=={} simplify=> {}=={}", |
|
|
|
|
e1, |
|
|
|
|
e2, |
|
|
|
|
e1.clone().distribute().simplify(), |
|
|
|
|
e2.clone().distribute().simplify() |
|
|
|
|
); |
|
|
|
|
let eqn = Eqn(e1, e2); |
|
|
|
|
let e = eqn.solve(u1).unwrap(); |
|
|
|
|
assert!(const_expr(e.clone()).is_some()); |
|
|
|
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -8.)); |
|
|
|
|
|
|
|
|
|
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.))); |
|
|
|
|
let e2 = Expr::new_div( |
|
|
|
|
Expr::new_sum( |
|
|
|
|
Expr::new_product(Unkn(u1), Const(2.)), |
|
|
|
|
Expr::new_sum( |
|
|
|
|
Expr::new_sum(Expr::new_product(Unkn(u1), Unkn(u1)), Unkn(u1)), |
|
|
|
|
Expr::new_minus(Const(2.), Const(1. + 1.)), |
|
|
|
|
), |
|
|
|
|
), |
|
|
|
|
Unkn(u1), |
|
|
|
|
); |
|
|
|
|
info!( |
|
|
|
|
"e1==e2: {}=={} => {}=={}", |
|
|
|
|
e1, |
|
|
|
|
e2, |
|
|
|
|
e1.clone().distrubute(), |
|
|
|
|
e2.clone().distrubute() |
|
|
|
|
e1.clone().distribute().simplify(), |
|
|
|
|
e2.clone().distribute().simplify().simplify() |
|
|
|
|
); |
|
|
|
|
let eqn = Eqn(e1, e2); |
|
|
|
|
let e = eqn.solve(u1).unwrap(); |
|
|
|
|
assert!(const_expr(e.clone()).is_some()); |
|
|
|
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -6.)); |
|
|
|
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -9.)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|