refactor out math stuff
This commit is contained in:
		
							parent
							
								
									233c8eaad5
								
							
						
					
					
						commit
						a6c4906773
					
				
							
								
								
									
										12
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/main.rs
									
									
									
									
									
								
							@ -18,14 +18,14 @@ mod relation;
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    use entity::{CPoint, PointRef};
 | 
			
		||||
    use math::{Point2, eqn, Region2, Rot2};
 | 
			
		||||
    use math::{Point2, Expr, Region2, Rot2, Eqn, Eqns};
 | 
			
		||||
    use relation::{Relation, ResolveResult};
 | 
			
		||||
 | 
			
		||||
    env_logger::init();
 | 
			
		||||
 | 
			
		||||
    println!("Hello, world!");
 | 
			
		||||
    let u1 = math::eqn::Unknown(1);
 | 
			
		||||
    let u2 = math::eqn::Unknown(2);
 | 
			
		||||
    let u1 = math::Unknown(1);
 | 
			
		||||
    let u2 = math::Unknown(2);
 | 
			
		||||
    // let u1 = eqn::Expr::from(1.);
 | 
			
		||||
    // let u2 = eqn::Expr::from(1.);
 | 
			
		||||
    let origin = CPoint::new_single(Point2::new((0.).into(), (0.).into())).into_ref();
 | 
			
		||||
@ -90,9 +90,9 @@ fn main() {
 | 
			
		||||
    } else {
 | 
			
		||||
        println!("All constraints have been solved")
 | 
			
		||||
    }
 | 
			
		||||
    let e1 = eqn::Eqn::new(eqn::Expr::Unkn(u1), eqn::Expr::Const(1.));
 | 
			
		||||
    let e2 = eqn::Eqn::new(eqn::Expr::Unkn(u2), eqn::Expr::Const(1.));
 | 
			
		||||
    let eqns = eqn::Eqns(vec![e1, e2]);
 | 
			
		||||
    let e1 = Eqn::new(Expr::Unkn(u1), Expr::Const(1.));
 | 
			
		||||
    let e2 = Eqn::new(Expr::Unkn(u2), Expr::Const(1.));
 | 
			
		||||
    let eqns = Eqns(vec![e1, e2]);
 | 
			
		||||
    for p in &mut points {
 | 
			
		||||
        p.borrow_mut().resolve_with(&eqns);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										504
									
								
								src/math/eqn.rs
									
									
									
									
									
								
							
							
						
						
									
										504
									
								
								src/math/eqn.rs
									
									
									
									
									
								
							@ -1,507 +1,11 @@
 | 
			
		||||
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)]
 | 
			
		||||
pub struct Unknown(pub i64);
 | 
			
		||||
 | 
			
		||||
pub type UnknownSet = BTreeSet<Unknown>;
 | 
			
		||||
 | 
			
		||||
pub 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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
use super::Scalar;
 | 
			
		||||
use super::expr::Expr;
 | 
			
		||||
use super::unknown::*;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
pub enum Expr {
 | 
			
		||||
    Unkn(Unknown),
 | 
			
		||||
    Const(Scalar),
 | 
			
		||||
    Sum(Exprs),
 | 
			
		||||
    Neg(Box<Expr>),
 | 
			
		||||
    Product(Exprs),
 | 
			
		||||
    Div(Box<Expr>, Box<Expr>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub 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 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);
 | 
			
		||||
            if comm.is_empty() {
 | 
			
		||||
                Expr::new_sum(Product(l), Product(r))
 | 
			
		||||
            } else {
 | 
			
		||||
                Expr::new_product(Product(comm), Expr::new_sum(Product(l), Product(r)))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (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.))),
 | 
			
		||||
                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);
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    for c in common.values() {
 | 
			
		||||
        trace!("group sum value: {}", c);
 | 
			
		||||
    }
 | 
			
		||||
    common.into_iter().map(|(_, v)| v).collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn product_fold(l: Expr, r: Expr) -> Expr {
 | 
			
		||||
    use Expr::*;
 | 
			
		||||
    match (l, r) {
 | 
			
		||||
        (Const(lc), Const(rc)) => Const(lc * rc),
 | 
			
		||||
        (Const(c), o) | (o, Const(c)) if relative_eq!(c, 1.) => o,
 | 
			
		||||
        (Const(c), _) | (_, Const(c)) if relative_eq!(c, 0.) => Const(0.),
 | 
			
		||||
        (Div(num, den), mul) | (mul, Div(num, den)) => {
 | 
			
		||||
            if mul == *den {
 | 
			
		||||
                *num
 | 
			
		||||
            } else {
 | 
			
		||||
                Expr::Div(Box::new(Expr::Product(vec![*num, mul])), den).simplify()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (Product(mut ls), Product(mut rs)) => {
 | 
			
		||||
            ls.append(&mut rs);
 | 
			
		||||
            Product(ls)
 | 
			
		||||
        },
 | 
			
		||||
        (Product(mut ps), o) | (o, Product(mut ps)) => {
 | 
			
		||||
            ps.push(o);
 | 
			
		||||
            Product(ps)
 | 
			
		||||
        },
 | 
			
		||||
        (l, r) => Expr::new_product(l, r),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn group_product(es: Exprs) -> Exprs {
 | 
			
		||||
    use Expr::*;
 | 
			
		||||
    let es2 = es.clone();
 | 
			
		||||
    let mut consts: Option<Scalar> = None;
 | 
			
		||||
    let mut other = Exprs::new();
 | 
			
		||||
    for e in es {
 | 
			
		||||
        let unkns = e.unknowns();
 | 
			
		||||
        match e { 
 | 
			
		||||
            Const(c) => match consts {
 | 
			
		||||
                None => consts = Some(c),
 | 
			
		||||
                Some(cs) => consts = Some(c * cs),
 | 
			
		||||
            }
 | 
			
		||||
            e => {
 | 
			
		||||
                other.push(e)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if let Some(cs) = consts { 
 | 
			
		||||
        if relative_eq!(cs, 0.0) {
 | 
			
		||||
            other.clear();
 | 
			
		||||
            other.push(Const(0.0))
 | 
			
		||||
        } else if relative_ne!(cs, 1.0) {
 | 
			
		||||
            other.push(Const(cs)) 
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    trace!("group product: {:?} => {:?}", es2, other);
 | 
			
		||||
    other
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn distribute_product_sums(mut es: Exprs) -> Expr {
 | 
			
		||||
    let es_pre = es.clone();
 | 
			
		||||
    use itertools::Itertools;
 | 
			
		||||
    use Expr::*;
 | 
			
		||||
    for e in &mut es {
 | 
			
		||||
        *e = e.clone().distribute();
 | 
			
		||||
    }
 | 
			
		||||
    let sums = es
 | 
			
		||||
        .drain_filter(|e| match e {
 | 
			
		||||
            Sum(_) => true,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        })
 | 
			
		||||
        .map(|e| {
 | 
			
		||||
            trace!("sum in product: {}", e);
 | 
			
		||||
            match e.simplify() {
 | 
			
		||||
                Sum(es) => es,
 | 
			
		||||
                o => vec![o],
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    let products: Vec<_> = sums.multi_cartesian_product().collect();
 | 
			
		||||
    if products.is_empty() {
 | 
			
		||||
        trace!("distribute_product_sums: 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();
 | 
			
		||||
    let res = Sum(sums);
 | 
			
		||||
    trace!("distribute_product_sums: {} => {}", Product(es_pre), res);
 | 
			
		||||
    res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
    pub fn new_sum(e1: Expr, e2: Expr) -> Expr {
 | 
			
		||||
        Expr::Sum(vec![e1, e2])
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_product(e1: Expr, e2: Expr) -> Expr {
 | 
			
		||||
        Expr::Product(vec![e1, e2])
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_neg(e1: Expr) -> Expr {
 | 
			
		||||
        Expr::Neg(Box::new(e1))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_div(num: Expr, den: Expr) -> Expr {
 | 
			
		||||
        Expr::Div(Box::new(num), Box::new(den))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_minus(e1: Expr, e2: Expr) -> Expr {
 | 
			
		||||
        Expr::Sum(vec![e1, Expr::new_neg(e2)])
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_inv(den: Expr) -> Expr {
 | 
			
		||||
        Expr::new_div(Expr::Const(1.), den)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_zero(self) -> bool {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        match self.simplify() {
 | 
			
		||||
            Const(c) => relative_eq!(c, 0.),
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_one(self) -> bool {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        match self.simplify() {
 | 
			
		||||
            Const(c) => relative_eq!(c, 1.),
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn evaluate_with(self, eqns: &Eqns) -> Expr {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        for eqn in &eqns.0 {
 | 
			
		||||
            if self == eqn.0 {
 | 
			
		||||
                return eqn.1.clone();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        match self {
 | 
			
		||||
            Sum(mut es) => {
 | 
			
		||||
                for e in &mut es {
 | 
			
		||||
                    *e = e.clone().evaluate_with(eqns);
 | 
			
		||||
                }
 | 
			
		||||
                Sum(es)
 | 
			
		||||
            }
 | 
			
		||||
            Product(mut es) => {
 | 
			
		||||
                for e in &mut es {
 | 
			
		||||
                    *e = e.clone().evaluate_with(eqns);
 | 
			
		||||
                }
 | 
			
		||||
                Product(es)
 | 
			
		||||
            }
 | 
			
		||||
            Neg(mut e) => {
 | 
			
		||||
                *e = e.evaluate_with(eqns);
 | 
			
		||||
                Neg(e)
 | 
			
		||||
            }
 | 
			
		||||
            Div(mut num, mut den) => {
 | 
			
		||||
                *num = num.evaluate_with(eqns);
 | 
			
		||||
                *den = den.evaluate_with(eqns);
 | 
			
		||||
                Div(num, den)
 | 
			
		||||
            }
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn simplify(self) -> Expr {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Sum(es) => {
 | 
			
		||||
                let pre_new_es = es.clone();
 | 
			
		||||
                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();
 | 
			
		||||
                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 pre_new_es = es.clone();
 | 
			
		||||
                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 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,
 | 
			
		||||
                    box Product(mut es) => {
 | 
			
		||||
                        es.push(Const(-1.));
 | 
			
		||||
                        Product(es).simplify()
 | 
			
		||||
                    }
 | 
			
		||||
                    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,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn distribute(self) -> Expr {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Sum(mut es) => {
 | 
			
		||||
                let es_pre = es.clone();
 | 
			
		||||
                for e in &mut es {
 | 
			
		||||
                    *e = e.clone().distribute();
 | 
			
		||||
                }
 | 
			
		||||
                let res = Sum(es);
 | 
			
		||||
                trace!("distribute sum {} => {}", Sum(es_pre), res);
 | 
			
		||||
                res
 | 
			
		||||
            }
 | 
			
		||||
            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)]
 | 
			
		||||
pub struct Eqn(Expr, Expr);
 | 
			
		||||
pub struct Eqn(pub Expr, pub Expr);
 | 
			
		||||
 | 
			
		||||
impl Unknowns for Eqn {
 | 
			
		||||
    fn unknowns(&self) -> UnknownSet {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										460
									
								
								src/math/expr.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								src/math/expr.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,460 @@
 | 
			
		||||
use std::collections::BTreeMap;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
use super::Scalar;
 | 
			
		||||
use super::eqn::Eqns;
 | 
			
		||||
use super::unknown::{Unknown, Unknowns, UnknownSet};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
pub enum Expr {
 | 
			
		||||
    Unkn(Unknown),
 | 
			
		||||
    Const(Scalar),
 | 
			
		||||
    Sum(Exprs),
 | 
			
		||||
    Neg(Box<Expr>),
 | 
			
		||||
    Product(Exprs),
 | 
			
		||||
    Div(Box<Expr>, Box<Expr>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub 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 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);
 | 
			
		||||
            if comm.is_empty() {
 | 
			
		||||
                Expr::new_sum(Product(l), Product(r))
 | 
			
		||||
            } else {
 | 
			
		||||
                Expr::new_product(Product(comm), Expr::new_sum(Product(l), Product(r)))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (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.))),
 | 
			
		||||
                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);
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    for c in common.values() {
 | 
			
		||||
        trace!("group sum value: {}", c);
 | 
			
		||||
    }
 | 
			
		||||
    common.into_iter().map(|(_, v)| v).collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn product_fold(l: Expr, r: Expr) -> Expr {
 | 
			
		||||
    use Expr::*;
 | 
			
		||||
    match (l, r) {
 | 
			
		||||
        (Const(lc), Const(rc)) => Const(lc * rc),
 | 
			
		||||
        (Const(c), o) | (o, Const(c)) if relative_eq!(c, 1.) => o,
 | 
			
		||||
        (Const(c), _) | (_, Const(c)) if relative_eq!(c, 0.) => Const(0.),
 | 
			
		||||
        (Div(num, den), mul) | (mul, Div(num, den)) => {
 | 
			
		||||
            if mul == *den {
 | 
			
		||||
                *num
 | 
			
		||||
            } else {
 | 
			
		||||
                Expr::Div(Box::new(Expr::Product(vec![*num, mul])), den).simplify()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (Product(mut ls), Product(mut rs)) => {
 | 
			
		||||
            ls.append(&mut rs);
 | 
			
		||||
            Product(ls)
 | 
			
		||||
        },
 | 
			
		||||
        (Product(mut ps), o) | (o, Product(mut ps)) => {
 | 
			
		||||
            ps.push(o);
 | 
			
		||||
            Product(ps)
 | 
			
		||||
        },
 | 
			
		||||
        (l, r) => Expr::new_product(l, r),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn group_product(es: Exprs) -> Exprs {
 | 
			
		||||
    use Expr::*;
 | 
			
		||||
    let es2 = es.clone();
 | 
			
		||||
    let mut consts: Option<Scalar> = None;
 | 
			
		||||
    let mut other = Exprs::new();
 | 
			
		||||
    for e in es {
 | 
			
		||||
        let unkns = e.unknowns();
 | 
			
		||||
        match e { 
 | 
			
		||||
            Const(c) => match consts {
 | 
			
		||||
                None => consts = Some(c),
 | 
			
		||||
                Some(cs) => consts = Some(c * cs),
 | 
			
		||||
            }
 | 
			
		||||
            e => {
 | 
			
		||||
                other.push(e)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if let Some(cs) = consts { 
 | 
			
		||||
        if relative_eq!(cs, 0.0) {
 | 
			
		||||
            other.clear();
 | 
			
		||||
            other.push(Const(0.0))
 | 
			
		||||
        } else if relative_ne!(cs, 1.0) {
 | 
			
		||||
            other.push(Const(cs)) 
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    trace!("group product: {:?} => {:?}", es2, other);
 | 
			
		||||
    other
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn distribute_product_sums(mut es: Exprs) -> Expr {
 | 
			
		||||
    let es_pre = es.clone();
 | 
			
		||||
    use itertools::Itertools;
 | 
			
		||||
    use Expr::*;
 | 
			
		||||
    for e in &mut es {
 | 
			
		||||
        *e = e.clone().distribute();
 | 
			
		||||
    }
 | 
			
		||||
    let sums = es
 | 
			
		||||
        .drain_filter(|e| match e {
 | 
			
		||||
            Sum(_) => true,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        })
 | 
			
		||||
        .map(|e| {
 | 
			
		||||
            trace!("sum in product: {}", e);
 | 
			
		||||
            match e.simplify() {
 | 
			
		||||
                Sum(es) => es,
 | 
			
		||||
                o => vec![o],
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    let products: Vec<_> = sums.multi_cartesian_product().collect();
 | 
			
		||||
    if products.is_empty() {
 | 
			
		||||
        trace!("distribute_product_sums: 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();
 | 
			
		||||
    let res = Sum(sums);
 | 
			
		||||
    trace!("distribute_product_sums: {} => {}", Product(es_pre), res);
 | 
			
		||||
    res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
    pub fn new_sum(e1: Expr, e2: Expr) -> Expr {
 | 
			
		||||
        Expr::Sum(vec![e1, e2])
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_product(e1: Expr, e2: Expr) -> Expr {
 | 
			
		||||
        Expr::Product(vec![e1, e2])
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_neg(e1: Expr) -> Expr {
 | 
			
		||||
        Expr::Neg(Box::new(e1))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_div(num: Expr, den: Expr) -> Expr {
 | 
			
		||||
        Expr::Div(Box::new(num), Box::new(den))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_minus(e1: Expr, e2: Expr) -> Expr {
 | 
			
		||||
        Expr::Sum(vec![e1, Expr::new_neg(e2)])
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_inv(den: Expr) -> Expr {
 | 
			
		||||
        Expr::new_div(Expr::Const(1.), den)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_zero(self) -> bool {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        match self.simplify() {
 | 
			
		||||
            Const(c) => relative_eq!(c, 0.),
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_one(self) -> bool {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        match self.simplify() {
 | 
			
		||||
            Const(c) => relative_eq!(c, 1.),
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn evaluate_with(self, eqns: &Eqns) -> Expr {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        for eqn in &eqns.0 {
 | 
			
		||||
            if self == eqn.0 {
 | 
			
		||||
                return eqn.1.clone();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        match self {
 | 
			
		||||
            Sum(mut es) => {
 | 
			
		||||
                for e in &mut es {
 | 
			
		||||
                    *e = e.clone().evaluate_with(eqns);
 | 
			
		||||
                }
 | 
			
		||||
                Sum(es)
 | 
			
		||||
            }
 | 
			
		||||
            Product(mut es) => {
 | 
			
		||||
                for e in &mut es {
 | 
			
		||||
                    *e = e.clone().evaluate_with(eqns);
 | 
			
		||||
                }
 | 
			
		||||
                Product(es)
 | 
			
		||||
            }
 | 
			
		||||
            Neg(mut e) => {
 | 
			
		||||
                *e = e.evaluate_with(eqns);
 | 
			
		||||
                Neg(e)
 | 
			
		||||
            }
 | 
			
		||||
            Div(mut num, mut den) => {
 | 
			
		||||
                *num = num.evaluate_with(eqns);
 | 
			
		||||
                *den = den.evaluate_with(eqns);
 | 
			
		||||
                Div(num, den)
 | 
			
		||||
            }
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn simplify(self) -> Expr {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Sum(es) => {
 | 
			
		||||
                let pre_new_es = es.clone();
 | 
			
		||||
                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();
 | 
			
		||||
                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 pre_new_es = es.clone();
 | 
			
		||||
                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 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,
 | 
			
		||||
                    box Product(mut es) => {
 | 
			
		||||
                        es.push(Const(-1.));
 | 
			
		||||
                        Product(es).simplify()
 | 
			
		||||
                    }
 | 
			
		||||
                    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,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn distribute(self) -> Expr {
 | 
			
		||||
        use Expr::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Sum(mut es) => {
 | 
			
		||||
                let es_pre = es.clone();
 | 
			
		||||
                for e in &mut es {
 | 
			
		||||
                    *e = e.clone().distribute();
 | 
			
		||||
                }
 | 
			
		||||
                let res = Sum(es);
 | 
			
		||||
                trace!("distribute sum {} => {}", Sum(es_pre), res);
 | 
			
		||||
                res
 | 
			
		||||
            }
 | 
			
		||||
            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),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										440
									
								
								src/math/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										440
									
								
								src/math/mod.rs
									
									
									
									
									
								
							@ -1,446 +1,24 @@
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
pub mod eqn;
 | 
			
		||||
pub mod expr;
 | 
			
		||||
pub mod ops;
 | 
			
		||||
pub mod region;
 | 
			
		||||
pub mod unknown;
 | 
			
		||||
pub mod vec;
 | 
			
		||||
 | 
			
		||||
pub use eqn::{Expr, Unknown};
 | 
			
		||||
pub use eqn::{Eqn, Eqns};
 | 
			
		||||
pub use expr::{Expr, Exprs};
 | 
			
		||||
pub use unknown::{Unknown, Unknowns, UnknownSet};
 | 
			
		||||
pub use region::{Region, Region1, Line2, Region2, GenericRegion};
 | 
			
		||||
pub use ops::*;
 | 
			
		||||
pub use vec::*;
 | 
			
		||||
 | 
			
		||||
pub type Scalar = f64;
 | 
			
		||||
 | 
			
		||||
// #[derive(Clone, Copy, PartialEq, Debug)]
 | 
			
		||||
// pub enum Value {
 | 
			
		||||
//     Known(Scalar),
 | 
			
		||||
//     Unkn(Unknown),
 | 
			
		||||
// }
 | 
			
		||||
pub type Value = eqn::Expr;
 | 
			
		||||
 | 
			
		||||
// pub type Vec2 = nalgebra::Vector2<Value>;
 | 
			
		||||
// pub type Point2 = nalgebra::Point2<Value>;
 | 
			
		||||
 | 
			
		||||
// pub type Rot2 = nalgebra::UnitComplex<Value>;
 | 
			
		||||
 | 
			
		||||
pub trait GenericRegion {
 | 
			
		||||
    fn full() -> Self;
 | 
			
		||||
    fn intersection(self, other: Self) -> Self;
 | 
			
		||||
    fn simplify(self) -> Self;
 | 
			
		||||
    fn evaluate_with(self, eqns: &eqn::Eqns) -> Self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Region<T>: GenericRegion {
 | 
			
		||||
    fn singleton(value: T) -> Self;
 | 
			
		||||
 | 
			
		||||
    fn nearest(&self, value: &T) -> Option<T>;
 | 
			
		||||
    fn contains(&self, value: &T) -> Option<bool>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub enum Region1 {
 | 
			
		||||
    Empty,
 | 
			
		||||
    Singleton(Value),
 | 
			
		||||
    Range(Value, Value),
 | 
			
		||||
    Intersection(Box<Region1>, Box<Region1>),
 | 
			
		||||
    // Union(Box<Region1>, Box<Region1>),
 | 
			
		||||
    Full,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Region1 {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => write!(f, "Ø"),
 | 
			
		||||
            Singleton(v) => write!(f, "{{ {} }}", v),
 | 
			
		||||
            Range(l, u) => write!(f, "[ {}, {} ]", l, u),
 | 
			
		||||
            Intersection(r1, r2) => write!(f, "{} ∩ {}", r1, r2),
 | 
			
		||||
            Full => write!(f, "ℝ")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl GenericRegion for Region1 {
 | 
			
		||||
    fn intersection(self, other: Region1) -> Self {
 | 
			
		||||
        Region1::Intersection(Box::new(self), Box::new(other))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn full() -> Self {
 | 
			
		||||
        Region1::Full
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn simplify(self) -> Self {
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Singleton(n) => Singleton(n.simplify()),
 | 
			
		||||
            Range(l, u) => Range(l.simplify(), u.simplify()),
 | 
			
		||||
            Intersection(r1, r2) => r1.simplify().intersection(r2.simplify()),
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn evaluate_with(self, eqns: &eqn::Eqns) -> Self {
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Singleton(n) => Singleton(n.evaluate_with(eqns)),
 | 
			
		||||
            Range(l, u) => Range(l.evaluate_with(eqns), u.evaluate_with(eqns)),
 | 
			
		||||
            Intersection(r1, r2) => r1.evaluate_with(eqns).intersection(r2.evaluate_with(eqns)),
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Region<Scalar> for Region1 {
 | 
			
		||||
    fn singleton(value: Scalar) -> Self {
 | 
			
		||||
        Region1::Singleton(value.into())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn contains(&self, n: &Scalar) -> Option<bool> {
 | 
			
		||||
        use Expr::Const;
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => Some(false),
 | 
			
		||||
            Singleton(n1) => match n1 {
 | 
			
		||||
                Const(c) => Some(relative_eq!(c, n)),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Range(l, u) => match (l, u) {
 | 
			
		||||
                (Const(cl), Const(cu)) => Some(*cl <= *n && *n <= *cu),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Intersection(r1, r2) => r1
 | 
			
		||||
                .contains(n)
 | 
			
		||||
                .and_then(|c1| r2.contains(n).map(|c2| c1 && c2)),
 | 
			
		||||
            // Union(r1, r2) => r1.contains(n) || r2.contains(n),
 | 
			
		||||
            Full => Some(true),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn nearest(&self, s: &Scalar) -> Option<Scalar> {
 | 
			
		||||
        use Expr::Const;
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => None,
 | 
			
		||||
            Full => Some(*s),
 | 
			
		||||
            Singleton(n) => match n {
 | 
			
		||||
                Const(c) => Some(*c),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Range(l, u) => match (l, u) {
 | 
			
		||||
                (Const(cl), Const(cu)) => match (cl < s, s < cu) {
 | 
			
		||||
                    (true, true) => Some(*s),
 | 
			
		||||
                    (true, false) => Some(*cu),
 | 
			
		||||
                    (false, true) => Some(*cl),
 | 
			
		||||
                    _ => None,
 | 
			
		||||
                },
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Intersection(r1, r2) => {
 | 
			
		||||
                unimplemented!()
 | 
			
		||||
            }
 | 
			
		||||
            /*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<Value>,
 | 
			
		||||
    dir: Rot2,
 | 
			
		||||
    extent: Region1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Line2 {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        write!(f, "{{ <x, y> = {} + {} * {} }}", self.start, self.dir, self.extent)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Line2 {
 | 
			
		||||
    pub fn new(start: Point2<Value>, dir: Rot2, extent: Region1) -> Self {
 | 
			
		||||
        Self { start, dir, extent }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn evaluate(&self, t: Value) -> Point2<Value> {
 | 
			
		||||
        self.start.clone() + self.dir.clone() * t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn evaluate_extent(&self) -> Option<Point2<Value>> {
 | 
			
		||||
        match &self.extent {
 | 
			
		||||
            Region1::Singleton(t) => Some(self.evaluate(t.clone())),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    pub fn with_extent(self, new_extent: Region1) -> Line2 {
 | 
			
		||||
        Line2 { start: self.start, dir: self.dir, extent: new_extent }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn nearest(&self, p: &Point2<Value>) -> Point2<Value> {
 | 
			
		||||
        // rotate angle 90 degrees
 | 
			
		||||
        let perp_dir = self.dir.clone() + Rot2::cardinal(1);
 | 
			
		||||
        let perp = Line2::new(p.clone(), perp_dir, Region1::Full);
 | 
			
		||||
        match self.intersect(&perp) {
 | 
			
		||||
            Region2::Singleton(np) => np,
 | 
			
		||||
            Region2::Line(l) => l.evaluate_extent().expect("Line2::nearest not found"),
 | 
			
		||||
            _ => panic!("Line2::nearest not found!")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn intersect(&self, other: &Line2) -> Region2 {
 | 
			
		||||
        // if the two lines are parallel...
 | 
			
		||||
        let dirs = self.dir.clone() - other.dir.clone();
 | 
			
		||||
        if relative_eq!(dirs.sin(), 0.) {
 | 
			
		||||
            let starts = self.dir.conj() * (other.start.clone() - self.start.clone());
 | 
			
		||||
            return if starts.y.simplify().is_zero() {
 | 
			
		||||
                // 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.clone(),
 | 
			
		||||
            a.dir.clone(),
 | 
			
		||||
            b.start.clone(),
 | 
			
		||||
            b.dir.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let (a_c, a_s, b_c, b_s) = (a_v.cos(), a_v.sin(), b_v.cos(), b_v.sin());
 | 
			
		||||
        let t_b = (a_0.x.clone() * a_s.clone() - a_0.y.clone() * a_c.clone()
 | 
			
		||||
            - b_0.x.clone() * a_s.clone()
 | 
			
		||||
            + b_0.y.clone() * a_c.clone())
 | 
			
		||||
            / (a_s.clone() * b_c.clone() - a_c.clone() * b_s.clone());
 | 
			
		||||
        // Region2::Singleton(b.evaluate(t_b))
 | 
			
		||||
        trace!("intersect a: {}, b: {}, t_b = {}", a, b, t_b);
 | 
			
		||||
        let res = Region2::Line(b.clone().with_extent(Region1::Singleton(t_b.simplify())));
 | 
			
		||||
        trace!("intersect a: {}, b: {} = {}", a, b, res);
 | 
			
		||||
        res
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn simplify(self) -> Region2 {
 | 
			
		||||
        let new_l = Line2 {
 | 
			
		||||
            start: self.start.simplify(),
 | 
			
		||||
            dir: self.dir,
 | 
			
		||||
            extent: self.extent.simplify(),
 | 
			
		||||
        };
 | 
			
		||||
        trace!("line {}: simplify evaluate extent: {:?}", new_l, new_l.evaluate_extent());
 | 
			
		||||
        if let Some(p) = new_l.evaluate_extent() {
 | 
			
		||||
            return Region2::Singleton(p.simplify());
 | 
			
		||||
        }
 | 
			
		||||
        Region2::Line(new_l)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn evaluate_with(self, eqns: &eqn::Eqns) -> Self {
 | 
			
		||||
        Line2 {
 | 
			
		||||
            start: self.start.evaluate_with(eqns),
 | 
			
		||||
            dir: self.dir,
 | 
			
		||||
            extent: self.extent.evaluate_with(eqns),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub enum Region2 {
 | 
			
		||||
    Empty,
 | 
			
		||||
    // single point at 0
 | 
			
		||||
    Singleton(Point2<Value>),
 | 
			
		||||
    Line(Line2),
 | 
			
		||||
    // #[allow(dead_code)]
 | 
			
		||||
    // Union(Box<Region2>, Box<Region2>),
 | 
			
		||||
    Intersection(Box<Region2>, Box<Region2>),
 | 
			
		||||
    Full,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Region2 {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => write!(f, "ز"),
 | 
			
		||||
            Singleton(v) => write!(f, "{{ {} }}", v),
 | 
			
		||||
            Line(l) => l.fmt(f),
 | 
			
		||||
            Intersection(r1, r2) => write!(f, "{} ∩ {}", r1, r2),
 | 
			
		||||
            Full => write!(f, "ℝ²")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl GenericRegion for Region2 {
 | 
			
		||||
    fn full() -> Self {
 | 
			
		||||
        Region2::Full
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn intersection(self, other: Self) -> Self {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match (self, other) {
 | 
			
		||||
            (Empty, _) | (_, Empty) => Empty,
 | 
			
		||||
            (Full, r) | (r, Full) => r,
 | 
			
		||||
            (r1, r2) => Intersection(Box::new(r1), Box::new(r2)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn simplify(self) -> Region2 {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Singleton(n) => Singleton(n.simplify()),
 | 
			
		||||
            Line(l) => l.simplify(),
 | 
			
		||||
            Intersection(r1, r2) => r1.simplify().intersect(r2.simplify()),
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn evaluate_with(self, eqns: &eqn::Eqns) -> Self {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Singleton(n) => Singleton(n.evaluate_with(eqns)),
 | 
			
		||||
            Line(l) => Line(l.evaluate_with(eqns)),
 | 
			
		||||
            Intersection(r1, r2) => r1.evaluate_with(eqns).intersection(r2.evaluate_with(eqns)),
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Region<Point2<Scalar>> for Region2 {
 | 
			
		||||
    fn singleton(value: Point2<Scalar>) -> Self {
 | 
			
		||||
        Region2::Singleton(value.into())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn contains(&self, p: &Point2<Scalar>) -> Option<bool> {
 | 
			
		||||
        self.nearest(p).map(|n| n == *p)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn nearest(&self, p: &Point2<Scalar>) -> Option<Point2<Scalar>> {
 | 
			
		||||
        use Expr::Const;
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => None,
 | 
			
		||||
            Full => Some(p.clone()),
 | 
			
		||||
            Singleton(n) => match (&n.x, &n.y) {
 | 
			
		||||
                (Const(cx), Const(cy)) => Some(Point2::new(*cx, *cy)),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Line(line) => {
 | 
			
		||||
                let pv: Point2<Value> = p.clone().into();
 | 
			
		||||
                let n = line.nearest(&pv).simplify();
 | 
			
		||||
                trace!("line {} nearest to {}: {}", line, pv, n);
 | 
			
		||||
                match (n.x, n.y) {
 | 
			
		||||
                    (Const(cx), Const(cy)) => Some(Point2::new(cx, cy)),
 | 
			
		||||
                    _ => None,
 | 
			
		||||
                }
 | 
			
		||||
            } 
 | 
			
		||||
            Intersection(r1, r2) => {
 | 
			
		||||
                None
 | 
			
		||||
                // r1.clone().intersect((**r2).clone()).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 Region<Point2<Value>> for Region2 {
 | 
			
		||||
    fn singleton(value: Point2<Value>) -> Self {
 | 
			
		||||
        Region2::Singleton(value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn contains(&self, p: &Point2<Value>) -> Option<bool> {
 | 
			
		||||
        self.nearest(p).map(|n| n.simplify() == p.clone().simplify())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn nearest(&self, p: &Point2<Value>) -> Option<Point2<Value>> {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => None,
 | 
			
		||||
            Full => Some(p.clone()),
 | 
			
		||||
            Singleton(n) => Some(n.clone()),
 | 
			
		||||
            Line(line) => Some(line.nearest(p)),
 | 
			
		||||
            Intersection(r1, r2) => {
 | 
			
		||||
                r1.clone().intersect((**r2).clone()).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 {
 | 
			
		||||
                    Region2::intersection(Singleton(n1), Singleton(n2))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            (Singleton(n), o) | (o, Singleton(n)) => {
 | 
			
		||||
                if o.contains(&n).unwrap_or(false) {
 | 
			
		||||
                    Singleton(n)
 | 
			
		||||
                } else {
 | 
			
		||||
                    Region2::intersection(Singleton(n), o)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            (Intersection(r1, r2), o) | (o, Intersection(r1, r2)) => {
 | 
			
		||||
                r1.intersect(*r2).intersect(o)
 | 
			
		||||
            }
 | 
			
		||||
            (Line(l1), Line(l2)) => l1.intersect(&l2).simplify(),
 | 
			
		||||
            /*(Union(un1, un2), o) | (o, Union(un1, un2)) => {
 | 
			
		||||
                Self::union(un1.intersect(o), un2.intersect(o))
 | 
			
		||||
            }*/
 | 
			
		||||
            (r1, r2) => Intersection(Box::new(r1), Box::new(r2)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
pub type Value = Expr;
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
use std::ops;
 | 
			
		||||
 | 
			
		||||
use super::eqn::{Expr, Unknown};
 | 
			
		||||
use super::Scalar;
 | 
			
		||||
use super::{Expr, Scalar, Unknown};
 | 
			
		||||
 | 
			
		||||
impl From<Scalar> for Expr {
 | 
			
		||||
  fn from(c: Scalar) -> Expr {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										432
									
								
								src/math/region.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										432
									
								
								src/math/region.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,432 @@
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
use super::{eqn, Value, Scalar, Expr, Point2, Rot2};
 | 
			
		||||
 | 
			
		||||
// pub type Vec2 = nalgebra::Vector2<Value>;
 | 
			
		||||
// pub type Point2 = nalgebra::Point2<Value>;
 | 
			
		||||
 | 
			
		||||
// pub type Rot2 = nalgebra::UnitComplex<Value>;
 | 
			
		||||
 | 
			
		||||
pub trait GenericRegion {
 | 
			
		||||
    fn full() -> Self;
 | 
			
		||||
    fn intersection(self, other: Self) -> Self;
 | 
			
		||||
    fn simplify(self) -> Self;
 | 
			
		||||
    fn evaluate_with(self, eqns: &eqn::Eqns) -> Self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Region<T>: GenericRegion {
 | 
			
		||||
    fn singleton(value: T) -> Self;
 | 
			
		||||
 | 
			
		||||
    fn nearest(&self, value: &T) -> Option<T>;
 | 
			
		||||
    fn contains(&self, value: &T) -> Option<bool>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub enum Region1 {
 | 
			
		||||
    Empty,
 | 
			
		||||
    Singleton(Value),
 | 
			
		||||
    Range(Value, Value),
 | 
			
		||||
    Intersection(Box<Region1>, Box<Region1>),
 | 
			
		||||
    // Union(Box<Region1>, Box<Region1>),
 | 
			
		||||
    Full,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Region1 {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => write!(f, "Ø"),
 | 
			
		||||
            Singleton(v) => write!(f, "{{ {} }}", v),
 | 
			
		||||
            Range(l, u) => write!(f, "[ {}, {} ]", l, u),
 | 
			
		||||
            Intersection(r1, r2) => write!(f, "{} ∩ {}", r1, r2),
 | 
			
		||||
            Full => write!(f, "ℝ")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl GenericRegion for Region1 {
 | 
			
		||||
    fn intersection(self, other: Region1) -> Self {
 | 
			
		||||
        Region1::Intersection(Box::new(self), Box::new(other))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn full() -> Self {
 | 
			
		||||
        Region1::Full
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn simplify(self) -> Self {
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Singleton(n) => Singleton(n.simplify()),
 | 
			
		||||
            Range(l, u) => Range(l.simplify(), u.simplify()),
 | 
			
		||||
            Intersection(r1, r2) => r1.simplify().intersection(r2.simplify()),
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn evaluate_with(self, eqns: &eqn::Eqns) -> Self {
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Singleton(n) => Singleton(n.evaluate_with(eqns)),
 | 
			
		||||
            Range(l, u) => Range(l.evaluate_with(eqns), u.evaluate_with(eqns)),
 | 
			
		||||
            Intersection(r1, r2) => r1.evaluate_with(eqns).intersection(r2.evaluate_with(eqns)),
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Region<Scalar> for Region1 {
 | 
			
		||||
    fn singleton(value: Scalar) -> Self {
 | 
			
		||||
        Region1::Singleton(value.into())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn contains(&self, n: &Scalar) -> Option<bool> {
 | 
			
		||||
        use Expr::Const;
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => Some(false),
 | 
			
		||||
            Singleton(n1) => match n1 {
 | 
			
		||||
                Const(c) => Some(relative_eq!(c, n)),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Range(l, u) => match (l, u) {
 | 
			
		||||
                (Const(cl), Const(cu)) => Some(*cl <= *n && *n <= *cu),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Intersection(r1, r2) => r1
 | 
			
		||||
                .contains(n)
 | 
			
		||||
                .and_then(|c1| r2.contains(n).map(|c2| c1 && c2)),
 | 
			
		||||
            // Union(r1, r2) => r1.contains(n) || r2.contains(n),
 | 
			
		||||
            Full => Some(true),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn nearest(&self, s: &Scalar) -> Option<Scalar> {
 | 
			
		||||
        use Expr::Const;
 | 
			
		||||
        use Region1::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => None,
 | 
			
		||||
            Full => Some(*s),
 | 
			
		||||
            Singleton(n) => match n {
 | 
			
		||||
                Const(c) => Some(*c),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Range(l, u) => match (l, u) {
 | 
			
		||||
                (Const(cl), Const(cu)) => match (cl < s, s < cu) {
 | 
			
		||||
                    (true, true) => Some(*s),
 | 
			
		||||
                    (true, false) => Some(*cu),
 | 
			
		||||
                    (false, true) => Some(*cl),
 | 
			
		||||
                    _ => None,
 | 
			
		||||
                },
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Intersection(r1, r2) => {
 | 
			
		||||
                unimplemented!()
 | 
			
		||||
            }
 | 
			
		||||
            /*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<Value>,
 | 
			
		||||
    dir: Rot2,
 | 
			
		||||
    extent: Region1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Line2 {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        write!(f, "{{ <x, y> = {} + {} * {} }}", self.start, self.dir, self.extent)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Line2 {
 | 
			
		||||
    pub fn new(start: Point2<Value>, dir: Rot2, extent: Region1) -> Self {
 | 
			
		||||
        Self { start, dir, extent }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn evaluate(&self, t: Value) -> Point2<Value> {
 | 
			
		||||
        self.start.clone() + self.dir.clone() * t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn evaluate_extent(&self) -> Option<Point2<Value>> {
 | 
			
		||||
        match &self.extent {
 | 
			
		||||
            Region1::Singleton(t) => Some(self.evaluate(t.clone())),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    pub fn with_extent(self, new_extent: Region1) -> Line2 {
 | 
			
		||||
        Line2 { start: self.start, dir: self.dir, extent: new_extent }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn nearest(&self, p: &Point2<Value>) -> Point2<Value> {
 | 
			
		||||
        // rotate angle 90 degrees
 | 
			
		||||
        let perp_dir = self.dir.clone() + Rot2::cardinal(1);
 | 
			
		||||
        let perp = Line2::new(p.clone(), perp_dir, Region1::Full);
 | 
			
		||||
        match self.intersect(&perp) {
 | 
			
		||||
            Region2::Singleton(np) => np,
 | 
			
		||||
            Region2::Line(l) => l.evaluate_extent().expect("Line2::nearest not found"),
 | 
			
		||||
            _ => panic!("Line2::nearest not found!")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn intersect(&self, other: &Line2) -> Region2 {
 | 
			
		||||
        // if the two lines are parallel...
 | 
			
		||||
        let dirs = self.dir.clone() - other.dir.clone();
 | 
			
		||||
        if relative_eq!(dirs.sin(), 0.) {
 | 
			
		||||
            let starts = self.dir.conj() * (other.start.clone() - self.start.clone());
 | 
			
		||||
            return if starts.y.simplify().is_zero() {
 | 
			
		||||
                // 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.clone(),
 | 
			
		||||
            a.dir.clone(),
 | 
			
		||||
            b.start.clone(),
 | 
			
		||||
            b.dir.clone(),
 | 
			
		||||
        );
 | 
			
		||||
        let (a_c, a_s, b_c, b_s) = (a_v.cos(), a_v.sin(), b_v.cos(), b_v.sin());
 | 
			
		||||
        let t_b = (a_0.x.clone() * a_s.clone() - a_0.y.clone() * a_c.clone()
 | 
			
		||||
            - b_0.x.clone() * a_s.clone()
 | 
			
		||||
            + b_0.y.clone() * a_c.clone())
 | 
			
		||||
            / (a_s.clone() * b_c.clone() - a_c.clone() * b_s.clone());
 | 
			
		||||
        // Region2::Singleton(b.evaluate(t_b))
 | 
			
		||||
        trace!("intersect a: {}, b: {}, t_b = {}", a, b, t_b);
 | 
			
		||||
        let res = Region2::Line(b.clone().with_extent(Region1::Singleton(t_b.simplify())));
 | 
			
		||||
        trace!("intersect a: {}, b: {} = {}", a, b, res);
 | 
			
		||||
        res
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn simplify(self) -> Region2 {
 | 
			
		||||
        let new_l = Line2 {
 | 
			
		||||
            start: self.start.simplify(),
 | 
			
		||||
            dir: self.dir,
 | 
			
		||||
            extent: self.extent.simplify(),
 | 
			
		||||
        };
 | 
			
		||||
        trace!("line {}: simplify evaluate extent: {:?}", new_l, new_l.evaluate_extent());
 | 
			
		||||
        if let Some(p) = new_l.evaluate_extent() {
 | 
			
		||||
            return Region2::Singleton(p.simplify());
 | 
			
		||||
        }
 | 
			
		||||
        Region2::Line(new_l)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn evaluate_with(self, eqns: &eqn::Eqns) -> Self {
 | 
			
		||||
        Line2 {
 | 
			
		||||
            start: self.start.evaluate_with(eqns),
 | 
			
		||||
            dir: self.dir,
 | 
			
		||||
            extent: self.extent.evaluate_with(eqns),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub enum Region2 {
 | 
			
		||||
    Empty,
 | 
			
		||||
    // single point at 0
 | 
			
		||||
    Singleton(Point2<Value>),
 | 
			
		||||
    Line(Line2),
 | 
			
		||||
    // #[allow(dead_code)]
 | 
			
		||||
    // Union(Box<Region2>, Box<Region2>),
 | 
			
		||||
    Intersection(Box<Region2>, Box<Region2>),
 | 
			
		||||
    Full,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Region2 {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => write!(f, "ز"),
 | 
			
		||||
            Singleton(v) => write!(f, "{{ {} }}", v),
 | 
			
		||||
            Line(l) => l.fmt(f),
 | 
			
		||||
            Intersection(r1, r2) => write!(f, "{} ∩ {}", r1, r2),
 | 
			
		||||
            Full => write!(f, "ℝ²")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl GenericRegion for Region2 {
 | 
			
		||||
    fn full() -> Self {
 | 
			
		||||
        Region2::Full
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn intersection(self, other: Self) -> Self {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match (self, other) {
 | 
			
		||||
            (Empty, _) | (_, Empty) => Empty,
 | 
			
		||||
            (Full, r) | (r, Full) => r,
 | 
			
		||||
            (r1, r2) => Intersection(Box::new(r1), Box::new(r2)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn simplify(self) -> Region2 {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Singleton(n) => Singleton(n.simplify()),
 | 
			
		||||
            Line(l) => l.simplify(),
 | 
			
		||||
            Intersection(r1, r2) => r1.simplify().intersect(r2.simplify()),
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn evaluate_with(self, eqns: &eqn::Eqns) -> Self {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Singleton(n) => Singleton(n.evaluate_with(eqns)),
 | 
			
		||||
            Line(l) => Line(l.evaluate_with(eqns)),
 | 
			
		||||
            Intersection(r1, r2) => r1.evaluate_with(eqns).intersection(r2.evaluate_with(eqns)),
 | 
			
		||||
            other => other,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Region<Point2<Scalar>> for Region2 {
 | 
			
		||||
    fn singleton(value: Point2<Scalar>) -> Self {
 | 
			
		||||
        Region2::Singleton(value.into())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn contains(&self, p: &Point2<Scalar>) -> Option<bool> {
 | 
			
		||||
        self.nearest(p).map(|n| n == *p)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn nearest(&self, p: &Point2<Scalar>) -> Option<Point2<Scalar>> {
 | 
			
		||||
        use Expr::Const;
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => None,
 | 
			
		||||
            Full => Some(p.clone()),
 | 
			
		||||
            Singleton(n) => match (&n.x, &n.y) {
 | 
			
		||||
                (Const(cx), Const(cy)) => Some(Point2::new(*cx, *cy)),
 | 
			
		||||
                _ => None,
 | 
			
		||||
            },
 | 
			
		||||
            Line(line) => {
 | 
			
		||||
                let pv: Point2<Value> = p.clone().into();
 | 
			
		||||
                let n = line.nearest(&pv).simplify();
 | 
			
		||||
                trace!("line {} nearest to {}: {}", line, pv, n);
 | 
			
		||||
                match (n.x, n.y) {
 | 
			
		||||
                    (Const(cx), Const(cy)) => Some(Point2::new(cx, cy)),
 | 
			
		||||
                    _ => None,
 | 
			
		||||
                }
 | 
			
		||||
            } 
 | 
			
		||||
            Intersection(r1, r2) => {
 | 
			
		||||
                None
 | 
			
		||||
                // r1.clone().intersect((**r2).clone()).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 Region<Point2<Value>> for Region2 {
 | 
			
		||||
    fn singleton(value: Point2<Value>) -> Self {
 | 
			
		||||
        Region2::Singleton(value)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn contains(&self, p: &Point2<Value>) -> Option<bool> {
 | 
			
		||||
        self.nearest(p).map(|n| n.simplify() == p.clone().simplify())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn nearest(&self, p: &Point2<Value>) -> Option<Point2<Value>> {
 | 
			
		||||
        use Region2::*;
 | 
			
		||||
        match self {
 | 
			
		||||
            Empty => None,
 | 
			
		||||
            Full => Some(p.clone()),
 | 
			
		||||
            Singleton(n) => Some(n.clone()),
 | 
			
		||||
            Line(line) => Some(line.nearest(p)),
 | 
			
		||||
            Intersection(r1, r2) => {
 | 
			
		||||
                r1.clone().intersect((**r2).clone()).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 {
 | 
			
		||||
                    Region2::intersection(Singleton(n1), Singleton(n2))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            (Singleton(n), o) | (o, Singleton(n)) => {
 | 
			
		||||
                if o.contains(&n).unwrap_or(false) {
 | 
			
		||||
                    Singleton(n)
 | 
			
		||||
                } else {
 | 
			
		||||
                    Region2::intersection(Singleton(n), o)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            (Intersection(r1, r2), o) | (o, Intersection(r1, r2)) => {
 | 
			
		||||
                r1.intersect(*r2).intersect(o)
 | 
			
		||||
            }
 | 
			
		||||
            (Line(l1), Line(l2)) => l1.intersect(&l2).simplify(),
 | 
			
		||||
            /*(Union(un1, un2), o) | (o, Union(un1, un2)) => {
 | 
			
		||||
                Self::union(un1.intersect(o), un2.intersect(o))
 | 
			
		||||
            }*/
 | 
			
		||||
            (r1, r2) => Intersection(Box::new(r1), Box::new(r2)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								src/math/unknown.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/math/unknown.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
use std::collections::BTreeSet;
 | 
			
		||||
use std::iter::FromIterator;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
use super::Scalar;
 | 
			
		||||
 | 
			
		||||
// an unknown variable with an id
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 | 
			
		||||
pub struct Unknown(pub i64);
 | 
			
		||||
 | 
			
		||||
pub type UnknownSet = BTreeSet<Unknown>;
 | 
			
		||||
 | 
			
		||||
pub 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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user