move solver to it's own file
This commit is contained in:
		
							parent
							
								
									a63f063a05
								
							
						
					
					
						commit
						20f96ede2d
					
				
							
								
								
									
										903
									
								
								src/math.rs
									
									
									
									
									
								
							
							
						
						
									
										903
									
								
								src/math.rs
									
									
									
									
									
								
							@ -1,903 +0,0 @@
 | 
				
			|||||||
pub type Scalar = f64;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub type Vec2 = nalgebra::Vector2<Scalar>;
 | 
					 | 
				
			||||||
pub type Point2 = nalgebra::Point2<Scalar>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub type Rot2 = nalgebra::UnitComplex<Scalar>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub trait Region<T> {
 | 
					 | 
				
			||||||
    fn full() -> Self;
 | 
					 | 
				
			||||||
    fn singleton(value: T) -> Self;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn nearest(&self, value: &T) -> Option<T>;
 | 
					 | 
				
			||||||
    fn contains(&self, value: &T) -> bool;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Clone, Debug)]
 | 
					 | 
				
			||||||
pub enum Region1 {
 | 
					 | 
				
			||||||
    Empty,
 | 
					 | 
				
			||||||
    Singleton(Scalar),
 | 
					 | 
				
			||||||
    Range(Scalar, Scalar),
 | 
					 | 
				
			||||||
    Union(Box<Region1>, Box<Region1>),
 | 
					 | 
				
			||||||
    Full,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Region<Scalar> for Region1 {
 | 
					 | 
				
			||||||
    fn full() -> Self {
 | 
					 | 
				
			||||||
        Region1::Full
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn singleton(value: Scalar) -> Self {
 | 
					 | 
				
			||||||
        Region1::Singleton(value)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn contains(&self, n: &Scalar) -> bool {
 | 
					 | 
				
			||||||
        use Region1::*;
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Empty => false,
 | 
					 | 
				
			||||||
            Singleton(n1) => relative_eq!(n1, n),
 | 
					 | 
				
			||||||
            Range(l, u) => *l <= *n && *n <= *u,
 | 
					 | 
				
			||||||
            Union(r1, r2) => r1.contains(n) || r2.contains(n),
 | 
					 | 
				
			||||||
            Full => true,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn nearest(&self, s: &Scalar) -> Option<Scalar> {
 | 
					 | 
				
			||||||
        use Region1::*;
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Empty => None,
 | 
					 | 
				
			||||||
            Full => Some(*s),
 | 
					 | 
				
			||||||
            Singleton(n) => Some(*n),
 | 
					 | 
				
			||||||
            Range(l, u) => match (l < s, s < u) {
 | 
					 | 
				
			||||||
                (true, true) => Some(*s),
 | 
					 | 
				
			||||||
                (true, false) => Some(*u),
 | 
					 | 
				
			||||||
                (false, true) => Some(*l),
 | 
					 | 
				
			||||||
                _ => None,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            Union(r1, r2) => {
 | 
					 | 
				
			||||||
                let distance = |a: Scalar, b: Scalar| (a - b).abs();
 | 
					 | 
				
			||||||
                match (r1.nearest(s), r2.nearest(s)) {
 | 
					 | 
				
			||||||
                    (None, None) => None,
 | 
					 | 
				
			||||||
                    (Some(n), None) | (None, Some(n)) => Some(n),
 | 
					 | 
				
			||||||
                    (Some(n1), Some(n2)) => Some({
 | 
					 | 
				
			||||||
                        if distance(*s, n1) <= distance(*s, n2) {
 | 
					 | 
				
			||||||
                            n1
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            n2
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }),
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// line starting at start, point at angle dir, with range extent
 | 
					 | 
				
			||||||
// ie. start + (cos dir, sin dir) * t for t in extent
 | 
					 | 
				
			||||||
#[derive(Clone, Debug)]
 | 
					 | 
				
			||||||
pub struct Line2 {
 | 
					 | 
				
			||||||
    start: Point2,
 | 
					 | 
				
			||||||
    dir: Rot2,
 | 
					 | 
				
			||||||
    extent: Region1,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Line2 {
 | 
					 | 
				
			||||||
    pub fn new(start: Point2, dir: Rot2, extent: Region1) -> Self {
 | 
					 | 
				
			||||||
        Self { start, dir, extent }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn evaluate(&self, t: Scalar) -> Point2 {
 | 
					 | 
				
			||||||
        self.start + self.dir * Vec2::new(t, 0.)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn nearest(&self, p: &Point2) -> Point2 {
 | 
					 | 
				
			||||||
        // rotate angle 90 degrees
 | 
					 | 
				
			||||||
        let perp_dir = self.dir * Rot2::from_cos_sin_unchecked(0., 1.);
 | 
					 | 
				
			||||||
        let perp = Line2::new(*p, perp_dir, Region1::Full);
 | 
					 | 
				
			||||||
        if let Region2::Singleton(np) = self.intersect(&perp) {
 | 
					 | 
				
			||||||
            np
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            panic!("Line2::nearest not found!");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn intersect(&self, other: &Line2) -> Region2 {
 | 
					 | 
				
			||||||
        // if the two lines are parallel...
 | 
					 | 
				
			||||||
        let dirs = self.dir / other.dir;
 | 
					 | 
				
			||||||
        if relative_eq!(dirs.sin_angle(), 0.) {
 | 
					 | 
				
			||||||
            let starts = self.dir.to_rotation_matrix().inverse() * (other.start - self.start);
 | 
					 | 
				
			||||||
            return if relative_eq!(starts.y, 0.) {
 | 
					 | 
				
			||||||
                // and they are colinear
 | 
					 | 
				
			||||||
                Region2::Line(self.clone())
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                // they are parallel and never intersect
 | 
					 | 
				
			||||||
                Region2::Empty
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // TODO: respect extent
 | 
					 | 
				
			||||||
        let (a, b) = (self, other);
 | 
					 | 
				
			||||||
        let (a_0, a_v, b_0, b_v) = (a.start, a.dir, b.start, b.dir);
 | 
					 | 
				
			||||||
        let (a_c, a_s, b_c, b_s) = (
 | 
					 | 
				
			||||||
            a_v.cos_angle(),
 | 
					 | 
				
			||||||
            a_v.sin_angle(),
 | 
					 | 
				
			||||||
            b_v.cos_angle(),
 | 
					 | 
				
			||||||
            b_v.sin_angle(),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        let t_b = (a_0.x * a_s - a_0.y * a_c + a_0.x * a_s + b_0.y * a_c) / (a_s * b_c - a_c * b_s);
 | 
					 | 
				
			||||||
        Region2::Singleton(b.evaluate(t_b))
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Clone, Debug)]
 | 
					 | 
				
			||||||
pub enum Region2 {
 | 
					 | 
				
			||||||
    Empty,
 | 
					 | 
				
			||||||
    // single point at 0
 | 
					 | 
				
			||||||
    Singleton(Point2),
 | 
					 | 
				
			||||||
    Line(Line2),
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    Union(Box<Region2>, Box<Region2>),
 | 
					 | 
				
			||||||
    Full,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Region<Point2> for Region2 {
 | 
					 | 
				
			||||||
    fn full() -> Self {
 | 
					 | 
				
			||||||
        Region2::Full
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn singleton(value: Point2) -> Self {
 | 
					 | 
				
			||||||
        Region2::Singleton(value)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn contains(&self, p: &Point2) -> bool {
 | 
					 | 
				
			||||||
        self.nearest(p).map_or(false, |n| relative_eq!(n, p))
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn nearest(&self, p: &Point2) -> Option<Point2> {
 | 
					 | 
				
			||||||
        use Region2::*;
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Empty => None,
 | 
					 | 
				
			||||||
            Full => Some(*p),
 | 
					 | 
				
			||||||
            Singleton(n) => Some(*n),
 | 
					 | 
				
			||||||
            Line(line) => Some(line.nearest(p)),
 | 
					 | 
				
			||||||
            Union(r1, r2) => {
 | 
					 | 
				
			||||||
                use nalgebra::distance;
 | 
					 | 
				
			||||||
                match (r1.nearest(p), r2.nearest(p)) {
 | 
					 | 
				
			||||||
                    (None, None) => None,
 | 
					 | 
				
			||||||
                    (Some(n), None) | (None, Some(n)) => Some(n),
 | 
					 | 
				
			||||||
                    (Some(n1), Some(n2)) => Some({
 | 
					 | 
				
			||||||
                        if distance(p, &n1) <= distance(p, &n2) {
 | 
					 | 
				
			||||||
                            n1
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            n2
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }),
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Region2 {
 | 
					 | 
				
			||||||
    pub fn union(r1: Region2, r2: Region2) -> Region2 {
 | 
					 | 
				
			||||||
        use Region2::*;
 | 
					 | 
				
			||||||
        match (r1, r2) {
 | 
					 | 
				
			||||||
            (Empty, r) | (r, Empty) => r,
 | 
					 | 
				
			||||||
            (Full, _) | (_, Full) => Full,
 | 
					 | 
				
			||||||
            (r1, r2) => Union(Box::new(r1), Box::new(r2)),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn intersect(&self, other: &Region2) -> Region2 {
 | 
					 | 
				
			||||||
        use Region2::*;
 | 
					 | 
				
			||||||
        match (self, other) {
 | 
					 | 
				
			||||||
            (Empty, _) | (_, Empty) => Empty,
 | 
					 | 
				
			||||||
            (Full, r) | (r, Full) => r.clone(),
 | 
					 | 
				
			||||||
            (Singleton(n1), Singleton(n2)) => {
 | 
					 | 
				
			||||||
                if n1 == n2 {
 | 
					 | 
				
			||||||
                    Singleton(*n1)
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    Empty
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            (Singleton(n), o) | (o, Singleton(n)) => {
 | 
					 | 
				
			||||||
                if o.contains(n) {
 | 
					 | 
				
			||||||
                    Singleton(*n)
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    Empty
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            (Line(l1), Line(l2)) => l1.intersect(l2),
 | 
					 | 
				
			||||||
            (Union(un1, un2), o) | (o, Union(un1, un2)) => {
 | 
					 | 
				
			||||||
                Self::union(un1.intersect(o), un2.intersect(o))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub mod solve {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    use std::collections::{BTreeMap, BTreeSet};
 | 
					 | 
				
			||||||
    use std::fmt;
 | 
					 | 
				
			||||||
    use std::iter::FromIterator;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    use crate::math::Scalar;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // an unknown variable with an id
 | 
					 | 
				
			||||||
    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 | 
					 | 
				
			||||||
    struct Unknown(i64);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    type UnknownSet = BTreeSet<Unknown>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    trait Unknowns {
 | 
					 | 
				
			||||||
        fn unknowns(&self) -> UnknownSet;
 | 
					 | 
				
			||||||
        fn has_unknowns(&self) -> bool;
 | 
					 | 
				
			||||||
        fn has_unknown(&self, u: Unknown) -> bool;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl Unknowns for Scalar {
 | 
					 | 
				
			||||||
        fn unknowns(&self) -> UnknownSet {
 | 
					 | 
				
			||||||
            UnknownSet::new()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn has_unknowns(&self) -> bool {
 | 
					 | 
				
			||||||
            false
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn has_unknown(&self, _: Unknown) -> bool {
 | 
					 | 
				
			||||||
            false
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl Unknowns for Unknown {
 | 
					 | 
				
			||||||
        fn unknowns(&self) -> UnknownSet {
 | 
					 | 
				
			||||||
            FromIterator::from_iter(Some(*self))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn has_unknowns(&self) -> bool {
 | 
					 | 
				
			||||||
            true
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn has_unknown(&self, u: Unknown) -> bool {
 | 
					 | 
				
			||||||
            *self == u
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl fmt::Display for Unknown {
 | 
					 | 
				
			||||||
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
					 | 
				
			||||||
            write!(f, "u{}", self.0)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[derive(Clone, Debug, PartialEq)]
 | 
					 | 
				
			||||||
    enum Expr {
 | 
					 | 
				
			||||||
        Unkn(Unknown),
 | 
					 | 
				
			||||||
        Const(Scalar),
 | 
					 | 
				
			||||||
        Sum(Exprs),
 | 
					 | 
				
			||||||
        Neg(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 {
 | 
					 | 
				
			||||||
        fn unknowns(&self) -> UnknownSet {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            match self {
 | 
					 | 
				
			||||||
                Unkn(u) => u.unknowns(),
 | 
					 | 
				
			||||||
                Const(_) => UnknownSet::default(),
 | 
					 | 
				
			||||||
                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 {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            match self {
 | 
					 | 
				
			||||||
                Unkn(u) => u.has_unknowns(),
 | 
					 | 
				
			||||||
                Const(_) => false,
 | 
					 | 
				
			||||||
                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 {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            match self {
 | 
					 | 
				
			||||||
                Unkn(u1) => u1.has_unknown(u),
 | 
					 | 
				
			||||||
                Const(_) => false,
 | 
					 | 
				
			||||||
                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_sum(e1: Expr, e2: Expr) -> Expr {
 | 
					 | 
				
			||||||
            Expr::Sum(vec![e1, 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_div(num: Expr, den: Expr) -> Expr {
 | 
					 | 
				
			||||||
            Expr::Div(Box::new(num), Box::new(den))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn new_minus(e1: Expr, e2: Expr) -> Expr {
 | 
					 | 
				
			||||||
            Expr::Sum(vec![e1, Expr::new_neg(e2)])
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn new_inv(den: Expr) -> Expr {
 | 
					 | 
				
			||||||
            Expr::new_div(Expr::Const(1.), den)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn is_zero(&self) -> bool {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            match self {
 | 
					 | 
				
			||||||
                Const(c) => relative_eq!(*c, 0.),
 | 
					 | 
				
			||||||
                _ => false,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn is_one(&self) -> bool {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            match self {
 | 
					 | 
				
			||||||
                Const(c) => relative_eq!(*c, 1.),
 | 
					 | 
				
			||||||
                _ => false,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn simplify(self) -> Expr {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            match self {
 | 
					 | 
				
			||||||
                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_product(*num, Const(1. / den))
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        (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),
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        (num, den) => {
 | 
					 | 
				
			||||||
                            if num == den {
 | 
					 | 
				
			||||||
                                Expr::Const(1.)
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                Div(num, den)
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                e => e,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn distribute(self) -> Expr {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            trace!("distribute {}", self);
 | 
					 | 
				
			||||||
            match self {
 | 
					 | 
				
			||||||
                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,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl fmt::Display for Expr {
 | 
					 | 
				
			||||||
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            match self {
 | 
					 | 
				
			||||||
                Unkn(u) => write!(f, "{}", u),
 | 
					 | 
				
			||||||
                Const(c) => write!(f, "{}", c),
 | 
					 | 
				
			||||||
                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),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[derive(Clone, Debug, PartialEq)]
 | 
					 | 
				
			||||||
    struct Eqn(Expr, Expr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl Unknowns for Eqn {
 | 
					 | 
				
			||||||
        fn unknowns(&self) -> UnknownSet {
 | 
					 | 
				
			||||||
            self.0
 | 
					 | 
				
			||||||
                .unknowns()
 | 
					 | 
				
			||||||
                .union(&self.1.unknowns())
 | 
					 | 
				
			||||||
                .cloned()
 | 
					 | 
				
			||||||
                .collect()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn has_unknowns(&self) -> bool {
 | 
					 | 
				
			||||||
            self.0.has_unknowns() || self.1.has_unknowns()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn has_unknown(&self, u: Unknown) -> bool {
 | 
					 | 
				
			||||||
            self.0.has_unknown(u) || self.1.has_unknown(u)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn ord_by_unkn(a: Expr, b: Expr, u: Unknown) -> Option<(Expr, Expr)> {
 | 
					 | 
				
			||||||
        if a.has_unknown(u) {
 | 
					 | 
				
			||||||
            Some((a, b))
 | 
					 | 
				
			||||||
        } else if b.has_unknown(u) {
 | 
					 | 
				
			||||||
            Some((b, a))
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            None
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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() /*.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 },
 | 
					 | 
				
			||||||
                    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(),
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    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)),
 | 
					 | 
				
			||||||
                    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;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl Unknowns for Eqns {
 | 
					 | 
				
			||||||
        fn unknowns(&self) -> UnknownSet {
 | 
					 | 
				
			||||||
            self.0.iter().flat_map(|eqn: &Eqn| eqn.unknowns()).collect()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn has_unknowns(&self) -> bool {
 | 
					 | 
				
			||||||
            self.0.iter().any(|eqn: &Eqn| eqn.has_unknowns())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        fn has_unknown(&self, u: Unknown) -> bool {
 | 
					 | 
				
			||||||
            self.0.iter().any(|eqn: &Eqn| eqn.has_unknown(u))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[cfg(test)]
 | 
					 | 
				
			||||||
    mod tests {
 | 
					 | 
				
			||||||
        use super::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[test]
 | 
					 | 
				
			||||||
        fn test_unknowns() {
 | 
					 | 
				
			||||||
            let u1 = Unknown(1);
 | 
					 | 
				
			||||||
            let u2 = Unknown(2);
 | 
					 | 
				
			||||||
            let u3 = Unknown(3);
 | 
					 | 
				
			||||||
            assert!(u1.has_unknowns());
 | 
					 | 
				
			||||||
            assert!(u2.has_unknowns());
 | 
					 | 
				
			||||||
            assert!(u1.has_unknown(u1));
 | 
					 | 
				
			||||||
            assert!(!u1.has_unknown(u2));
 | 
					 | 
				
			||||||
            assert!(u1.unknowns().contains(&u1));
 | 
					 | 
				
			||||||
            assert!(!u2.unknowns().contains(&u1));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let e1 = Expr::new_minus(Expr::Unkn(u1), Expr::Unkn(u2));
 | 
					 | 
				
			||||||
            assert!(e1.has_unknowns());
 | 
					 | 
				
			||||||
            assert!(e1.has_unknown(u1));
 | 
					 | 
				
			||||||
            assert!(e1.has_unknown(u2));
 | 
					 | 
				
			||||||
            assert!(!e1.has_unknown(u3));
 | 
					 | 
				
			||||||
            assert!(e1.unknowns().len() == 2);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fn const_expr(e: Expr) -> Option<Scalar> {
 | 
					 | 
				
			||||||
            match e {
 | 
					 | 
				
			||||||
                Expr::Const(c) => Some(c),
 | 
					 | 
				
			||||||
                _ => None,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[test]
 | 
					 | 
				
			||||||
        fn test_solve() {
 | 
					 | 
				
			||||||
            use Expr::*;
 | 
					 | 
				
			||||||
            let _ = env_logger::try_init();
 | 
					 | 
				
			||||||
            let u1 = Unknown(1);
 | 
					 | 
				
			||||||
            let e1 = Unkn(u1);
 | 
					 | 
				
			||||||
            let e2 = Const(1.);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let eqn = Eqn(e1.clone(), e2.clone());
 | 
					 | 
				
			||||||
            assert_eq!(eqn.solve(u1), Some(Const(1.)));
 | 
					 | 
				
			||||||
            let eqn = Eqn(e2.clone(), e1.clone());
 | 
					 | 
				
			||||||
            assert_eq!(eqn.solve(u1), Some(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(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.)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            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_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()
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            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().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(), -9.));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										217
									
								
								src/math/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/math/mod.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,217 @@
 | 
				
			|||||||
 | 
					pub mod solver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub type Scalar = f64;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub type Vec2 = nalgebra::Vector2<Scalar>;
 | 
				
			||||||
 | 
					pub type Point2 = nalgebra::Point2<Scalar>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub type Rot2 = nalgebra::UnitComplex<Scalar>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait Region<T> {
 | 
				
			||||||
 | 
					    fn full() -> Self;
 | 
				
			||||||
 | 
					    fn singleton(value: T) -> Self;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nearest(&self, value: &T) -> Option<T>;
 | 
				
			||||||
 | 
					    fn contains(&self, value: &T) -> bool;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					pub enum Region1 {
 | 
				
			||||||
 | 
					    Empty,
 | 
				
			||||||
 | 
					    Singleton(Scalar),
 | 
				
			||||||
 | 
					    Range(Scalar, Scalar),
 | 
				
			||||||
 | 
					    Union(Box<Region1>, Box<Region1>),
 | 
				
			||||||
 | 
					    Full,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Region<Scalar> for Region1 {
 | 
				
			||||||
 | 
					    fn full() -> Self {
 | 
				
			||||||
 | 
					        Region1::Full
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn singleton(value: Scalar) -> Self {
 | 
				
			||||||
 | 
					        Region1::Singleton(value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn contains(&self, n: &Scalar) -> bool {
 | 
				
			||||||
 | 
					        use Region1::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Empty => false,
 | 
				
			||||||
 | 
					            Singleton(n1) => relative_eq!(n1, n),
 | 
				
			||||||
 | 
					            Range(l, u) => *l <= *n && *n <= *u,
 | 
				
			||||||
 | 
					            Union(r1, r2) => r1.contains(n) || r2.contains(n),
 | 
				
			||||||
 | 
					            Full => true,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nearest(&self, s: &Scalar) -> Option<Scalar> {
 | 
				
			||||||
 | 
					        use Region1::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Empty => None,
 | 
				
			||||||
 | 
					            Full => Some(*s),
 | 
				
			||||||
 | 
					            Singleton(n) => Some(*n),
 | 
				
			||||||
 | 
					            Range(l, u) => match (l < s, s < u) {
 | 
				
			||||||
 | 
					                (true, true) => Some(*s),
 | 
				
			||||||
 | 
					                (true, false) => Some(*u),
 | 
				
			||||||
 | 
					                (false, true) => Some(*l),
 | 
				
			||||||
 | 
					                _ => None,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Union(r1, r2) => {
 | 
				
			||||||
 | 
					                let distance = |a: Scalar, b: Scalar| (a - b).abs();
 | 
				
			||||||
 | 
					                match (r1.nearest(s), r2.nearest(s)) {
 | 
				
			||||||
 | 
					                    (None, None) => None,
 | 
				
			||||||
 | 
					                    (Some(n), None) | (None, Some(n)) => Some(n),
 | 
				
			||||||
 | 
					                    (Some(n1), Some(n2)) => Some({
 | 
				
			||||||
 | 
					                        if distance(*s, n1) <= distance(*s, n2) {
 | 
				
			||||||
 | 
					                            n1
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            n2
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// line starting at start, point at angle dir, with range extent
 | 
				
			||||||
 | 
					// ie. start + (cos dir, sin dir) * t for t in extent
 | 
				
			||||||
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					pub struct Line2 {
 | 
				
			||||||
 | 
					    start: Point2,
 | 
				
			||||||
 | 
					    dir: Rot2,
 | 
				
			||||||
 | 
					    extent: Region1,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Line2 {
 | 
				
			||||||
 | 
					    pub fn new(start: Point2, dir: Rot2, extent: Region1) -> Self {
 | 
				
			||||||
 | 
					        Self { start, dir, extent }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn evaluate(&self, t: Scalar) -> Point2 {
 | 
				
			||||||
 | 
					        self.start + self.dir * Vec2::new(t, 0.)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn nearest(&self, p: &Point2) -> Point2 {
 | 
				
			||||||
 | 
					        // rotate angle 90 degrees
 | 
				
			||||||
 | 
					        let perp_dir = self.dir * Rot2::from_cos_sin_unchecked(0., 1.);
 | 
				
			||||||
 | 
					        let perp = Line2::new(*p, perp_dir, Region1::Full);
 | 
				
			||||||
 | 
					        if let Region2::Singleton(np) = self.intersect(&perp) {
 | 
				
			||||||
 | 
					            np
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            panic!("Line2::nearest not found!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn intersect(&self, other: &Line2) -> Region2 {
 | 
				
			||||||
 | 
					        // if the two lines are parallel...
 | 
				
			||||||
 | 
					        let dirs = self.dir / other.dir;
 | 
				
			||||||
 | 
					        if relative_eq!(dirs.sin_angle(), 0.) {
 | 
				
			||||||
 | 
					            let starts = self.dir.to_rotation_matrix().inverse() * (other.start - self.start);
 | 
				
			||||||
 | 
					            return if relative_eq!(starts.y, 0.) {
 | 
				
			||||||
 | 
					                // and they are colinear
 | 
				
			||||||
 | 
					                Region2::Line(self.clone())
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // they are parallel and never intersect
 | 
				
			||||||
 | 
					                Region2::Empty
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // TODO: respect extent
 | 
				
			||||||
 | 
					        let (a, b) = (self, other);
 | 
				
			||||||
 | 
					        let (a_0, a_v, b_0, b_v) = (a.start, a.dir, b.start, b.dir);
 | 
				
			||||||
 | 
					        let (a_c, a_s, b_c, b_s) = (
 | 
				
			||||||
 | 
					            a_v.cos_angle(),
 | 
				
			||||||
 | 
					            a_v.sin_angle(),
 | 
				
			||||||
 | 
					            b_v.cos_angle(),
 | 
				
			||||||
 | 
					            b_v.sin_angle(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let t_b = (a_0.x * a_s - a_0.y * a_c + a_0.x * a_s + b_0.y * a_c) / (a_s * b_c - a_c * b_s);
 | 
				
			||||||
 | 
					        Region2::Singleton(b.evaluate(t_b))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					pub enum Region2 {
 | 
				
			||||||
 | 
					    Empty,
 | 
				
			||||||
 | 
					    // single point at 0
 | 
				
			||||||
 | 
					    Singleton(Point2),
 | 
				
			||||||
 | 
					    Line(Line2),
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    Union(Box<Region2>, Box<Region2>),
 | 
				
			||||||
 | 
					    Full,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Region<Point2> for Region2 {
 | 
				
			||||||
 | 
					    fn full() -> Self {
 | 
				
			||||||
 | 
					        Region2::Full
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn singleton(value: Point2) -> Self {
 | 
				
			||||||
 | 
					        Region2::Singleton(value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn contains(&self, p: &Point2) -> bool {
 | 
				
			||||||
 | 
					        self.nearest(p).map_or(false, |n| relative_eq!(n, p))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nearest(&self, p: &Point2) -> Option<Point2> {
 | 
				
			||||||
 | 
					        use Region2::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Empty => None,
 | 
				
			||||||
 | 
					            Full => Some(*p),
 | 
				
			||||||
 | 
					            Singleton(n) => Some(*n),
 | 
				
			||||||
 | 
					            Line(line) => Some(line.nearest(p)),
 | 
				
			||||||
 | 
					            Union(r1, r2) => {
 | 
				
			||||||
 | 
					                use nalgebra::distance;
 | 
				
			||||||
 | 
					                match (r1.nearest(p), r2.nearest(p)) {
 | 
				
			||||||
 | 
					                    (None, None) => None,
 | 
				
			||||||
 | 
					                    (Some(n), None) | (None, Some(n)) => Some(n),
 | 
				
			||||||
 | 
					                    (Some(n1), Some(n2)) => Some({
 | 
				
			||||||
 | 
					                        if distance(p, &n1) <= distance(p, &n2) {
 | 
				
			||||||
 | 
					                            n1
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            n2
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Region2 {
 | 
				
			||||||
 | 
					    pub fn union(r1: Region2, r2: Region2) -> Region2 {
 | 
				
			||||||
 | 
					        use Region2::*;
 | 
				
			||||||
 | 
					        match (r1, r2) {
 | 
				
			||||||
 | 
					            (Empty, r) | (r, Empty) => r,
 | 
				
			||||||
 | 
					            (Full, _) | (_, Full) => Full,
 | 
				
			||||||
 | 
					            (r1, r2) => Union(Box::new(r1), Box::new(r2)),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn intersect(&self, other: &Region2) -> Region2 {
 | 
				
			||||||
 | 
					        use Region2::*;
 | 
				
			||||||
 | 
					        match (self, other) {
 | 
				
			||||||
 | 
					            (Empty, _) | (_, Empty) => Empty,
 | 
				
			||||||
 | 
					            (Full, r) | (r, Full) => r.clone(),
 | 
				
			||||||
 | 
					            (Singleton(n1), Singleton(n2)) => {
 | 
				
			||||||
 | 
					                if n1 == n2 {
 | 
				
			||||||
 | 
					                    Singleton(*n1)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Empty
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            (Singleton(n), o) | (o, Singleton(n)) => {
 | 
				
			||||||
 | 
					                if o.contains(n) {
 | 
				
			||||||
 | 
					                    Singleton(*n)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Empty
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            (Line(l1), Line(l2)) => l1.intersect(l2),
 | 
				
			||||||
 | 
					            (Union(un1, un2), o) | (o, Union(un1, un2)) => {
 | 
				
			||||||
 | 
					                Self::union(un1.intersect(o), un2.intersect(o))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										684
									
								
								src/math/solver.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										684
									
								
								src/math/solver.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,684 @@
 | 
				
			|||||||
 | 
					use std::collections::{BTreeMap, BTreeSet};
 | 
				
			||||||
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					use std::iter::FromIterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::math::Scalar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// an unknown variable with an id
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 | 
				
			||||||
 | 
					struct Unknown(i64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UnknownSet = BTreeSet<Unknown>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trait Unknowns {
 | 
				
			||||||
 | 
					    fn unknowns(&self) -> UnknownSet;
 | 
				
			||||||
 | 
					    fn has_unknowns(&self) -> bool;
 | 
				
			||||||
 | 
					    fn has_unknown(&self, u: Unknown) -> bool;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Unknowns for Scalar {
 | 
				
			||||||
 | 
					    fn unknowns(&self) -> UnknownSet {
 | 
				
			||||||
 | 
					        UnknownSet::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn has_unknowns(&self) -> bool {
 | 
				
			||||||
 | 
					        false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn has_unknown(&self, _: Unknown) -> bool {
 | 
				
			||||||
 | 
					        false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Unknowns for Unknown {
 | 
				
			||||||
 | 
					    fn unknowns(&self) -> UnknownSet {
 | 
				
			||||||
 | 
					        FromIterator::from_iter(Some(*self))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn has_unknowns(&self) -> bool {
 | 
				
			||||||
 | 
					        true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn has_unknown(&self, u: Unknown) -> bool {
 | 
				
			||||||
 | 
					        *self == u
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for Unknown {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
				
			||||||
 | 
					        write!(f, "u{}", self.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Debug, PartialEq)]
 | 
				
			||||||
 | 
					enum Expr {
 | 
				
			||||||
 | 
					    Unkn(Unknown),
 | 
				
			||||||
 | 
					    Const(Scalar),
 | 
				
			||||||
 | 
					    Sum(Exprs),
 | 
				
			||||||
 | 
					    Neg(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 {
 | 
				
			||||||
 | 
					    fn unknowns(&self) -> UnknownSet {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Unkn(u) => u.unknowns(),
 | 
				
			||||||
 | 
					            Const(_) => UnknownSet::default(),
 | 
				
			||||||
 | 
					            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 {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Unkn(u) => u.has_unknowns(),
 | 
				
			||||||
 | 
					            Const(_) => false,
 | 
				
			||||||
 | 
					            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 {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Unkn(u1) => u1.has_unknown(u),
 | 
				
			||||||
 | 
					            Const(_) => false,
 | 
				
			||||||
 | 
					            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_sum(e1: Expr, e2: Expr) -> Expr {
 | 
				
			||||||
 | 
					        Expr::Sum(vec![e1, 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_div(num: Expr, den: Expr) -> Expr {
 | 
				
			||||||
 | 
					        Expr::Div(Box::new(num), Box::new(den))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn new_minus(e1: Expr, e2: Expr) -> Expr {
 | 
				
			||||||
 | 
					        Expr::Sum(vec![e1, Expr::new_neg(e2)])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn new_inv(den: Expr) -> Expr {
 | 
				
			||||||
 | 
					        Expr::new_div(Expr::Const(1.), den)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn is_zero(&self) -> bool {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Const(c) => relative_eq!(*c, 0.),
 | 
				
			||||||
 | 
					            _ => false,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn is_one(&self) -> bool {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Const(c) => relative_eq!(*c, 1.),
 | 
				
			||||||
 | 
					            _ => false,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn simplify(self) -> Expr {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            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_product(*num, Const(1. / den))
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    (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),
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    (num, den) => {
 | 
				
			||||||
 | 
					                        if num == den {
 | 
				
			||||||
 | 
					                            Expr::Const(1.)
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            Div(num, den)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            e => e,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn distribute(self) -> Expr {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        trace!("distribute {}", self);
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            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,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for Expr {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Unkn(u) => write!(f, "{}", u),
 | 
				
			||||||
 | 
					            Const(c) => write!(f, "{}", c),
 | 
				
			||||||
 | 
					            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),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Debug, PartialEq)]
 | 
				
			||||||
 | 
					struct Eqn(Expr, Expr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Unknowns for Eqn {
 | 
				
			||||||
 | 
					    fn unknowns(&self) -> UnknownSet {
 | 
				
			||||||
 | 
					        self.0
 | 
				
			||||||
 | 
					            .unknowns()
 | 
				
			||||||
 | 
					            .union(&self.1.unknowns())
 | 
				
			||||||
 | 
					            .cloned()
 | 
				
			||||||
 | 
					            .collect()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn has_unknowns(&self) -> bool {
 | 
				
			||||||
 | 
					        self.0.has_unknowns() || self.1.has_unknowns()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn has_unknown(&self, u: Unknown) -> bool {
 | 
				
			||||||
 | 
					        self.0.has_unknown(u) || self.1.has_unknown(u)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn ord_by_unkn(a: Expr, b: Expr, u: Unknown) -> Option<(Expr, Expr)> {
 | 
				
			||||||
 | 
					    if a.has_unknown(u) {
 | 
				
			||||||
 | 
					        Some((a, b))
 | 
				
			||||||
 | 
					    } else if b.has_unknown(u) {
 | 
				
			||||||
 | 
					        Some((b, a))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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() /*.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 },
 | 
				
			||||||
 | 
					                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(),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                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)),
 | 
				
			||||||
 | 
					                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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Unknowns for Eqns {
 | 
				
			||||||
 | 
					    fn unknowns(&self) -> UnknownSet {
 | 
				
			||||||
 | 
					        self.0.iter().flat_map(|eqn: &Eqn| eqn.unknowns()).collect()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn has_unknowns(&self) -> bool {
 | 
				
			||||||
 | 
					        self.0.iter().any(|eqn: &Eqn| eqn.has_unknowns())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn has_unknown(&self, u: Unknown) -> bool {
 | 
				
			||||||
 | 
					        self.0.iter().any(|eqn: &Eqn| eqn.has_unknown(u))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_unknowns() {
 | 
				
			||||||
 | 
					        let u1 = Unknown(1);
 | 
				
			||||||
 | 
					        let u2 = Unknown(2);
 | 
				
			||||||
 | 
					        let u3 = Unknown(3);
 | 
				
			||||||
 | 
					        assert!(u1.has_unknowns());
 | 
				
			||||||
 | 
					        assert!(u2.has_unknowns());
 | 
				
			||||||
 | 
					        assert!(u1.has_unknown(u1));
 | 
				
			||||||
 | 
					        assert!(!u1.has_unknown(u2));
 | 
				
			||||||
 | 
					        assert!(u1.unknowns().contains(&u1));
 | 
				
			||||||
 | 
					        assert!(!u2.unknowns().contains(&u1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let e1 = Expr::new_minus(Expr::Unkn(u1), Expr::Unkn(u2));
 | 
				
			||||||
 | 
					        assert!(e1.has_unknowns());
 | 
				
			||||||
 | 
					        assert!(e1.has_unknown(u1));
 | 
				
			||||||
 | 
					        assert!(e1.has_unknown(u2));
 | 
				
			||||||
 | 
					        assert!(!e1.has_unknown(u3));
 | 
				
			||||||
 | 
					        assert!(e1.unknowns().len() == 2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn const_expr(e: Expr) -> Option<Scalar> {
 | 
				
			||||||
 | 
					        match e {
 | 
				
			||||||
 | 
					            Expr::Const(c) => Some(c),
 | 
				
			||||||
 | 
					            _ => None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_solve() {
 | 
				
			||||||
 | 
					        use Expr::*;
 | 
				
			||||||
 | 
					        let _ = env_logger::try_init();
 | 
				
			||||||
 | 
					        let u1 = Unknown(1);
 | 
				
			||||||
 | 
					        let e1 = Unkn(u1);
 | 
				
			||||||
 | 
					        let e2 = Const(1.);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let eqn = Eqn(e1.clone(), e2.clone());
 | 
				
			||||||
 | 
					        assert_eq!(eqn.solve(u1), Some(Const(1.)));
 | 
				
			||||||
 | 
					        let eqn = Eqn(e2.clone(), e1.clone());
 | 
				
			||||||
 | 
					        assert_eq!(eqn.solve(u1), Some(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(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.)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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_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()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        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().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(), -9.));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user