From a6c49067737ffb0b5d93a6f6fc1403b41821be02 Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Wed, 22 May 2019 16:50:58 -0700 Subject: [PATCH] refactor out math stuff --- src/main.rs | 12 +- src/math/eqn.rs | 504 +------------------------------------------- src/math/expr.rs | 460 ++++++++++++++++++++++++++++++++++++++++ src/math/mod.rs | 440 +------------------------------------- src/math/ops.rs | 3 +- src/math/region.rs | 432 +++++++++++++++++++++++++++++++++++++ src/math/unknown.rs | 47 +++++ 7 files changed, 959 insertions(+), 939 deletions(-) create mode 100644 src/math/expr.rs create mode 100644 src/math/region.rs create mode 100644 src/math/unknown.rs diff --git a/src/main.rs b/src/main.rs index cb9fa32..3e7889d 100644 --- a/src/main.rs +++ b/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); } diff --git a/src/math/eqn.rs b/src/math/eqn.rs index 860a6ff..154e767 100644 --- a/src/math/eqn.rs +++ b/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; - -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) - } -} - -#[derive(Clone, Debug, PartialEq)] -pub enum Expr { - Unkn(Unknown), - Const(Scalar), - Sum(Exprs), - Neg(Box), - Product(Exprs), - Div(Box, Box), -} - -pub type Exprs = Vec; - -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, r: &mut Vec) -> Vec { - 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, term: &Expr) -> Option { - 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 = 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 = 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), - } - } -} +use super::Scalar; +use super::expr::Expr; +use super::unknown::*; #[derive(Clone, Debug, PartialEq)] -pub struct Eqn(Expr, Expr); +pub struct Eqn(pub Expr, pub Expr); impl Unknowns for Eqn { fn unknowns(&self) -> UnknownSet { diff --git a/src/math/expr.rs b/src/math/expr.rs new file mode 100644 index 0000000..2ab68e8 --- /dev/null +++ b/src/math/expr.rs @@ -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), + Product(Exprs), + Div(Box, Box), +} + +pub type Exprs = Vec; + +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, r: &mut Vec) -> Vec { + 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, term: &Expr) -> Option { + 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 = 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 = 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), + } + } +} \ No newline at end of file diff --git a/src/math/mod.rs b/src/math/mod.rs index 7fe208b..e36076f 100644 --- a/src/math/mod.rs +++ b/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; -// pub type Point2 = nalgebra::Point2; - -// pub type Rot2 = nalgebra::UnitComplex; - -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: GenericRegion { - fn singleton(value: T) -> Self; - - fn nearest(&self, value: &T) -> Option; - fn contains(&self, value: &T) -> Option; -} - -#[derive(Clone, Debug)] -pub enum Region1 { - Empty, - Singleton(Value), - Range(Value, Value), - Intersection(Box, Box), - // Union(Box, Box), - 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 for Region1 { - fn singleton(value: Scalar) -> Self { - Region1::Singleton(value.into()) - } - - fn contains(&self, n: &Scalar) -> Option { - 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 { - 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, - dir: Rot2, - extent: Region1, -} - -impl fmt::Display for Line2 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{{ = {} + {} * {} }}", self.start, self.dir, self.extent) - } -} - -impl Line2 { - pub fn new(start: Point2, dir: Rot2, extent: Region1) -> Self { - Self { start, dir, extent } - } - - pub fn evaluate(&self, t: Value) -> Point2 { - self.start.clone() + self.dir.clone() * t - } - - pub fn evaluate_extent(&self) -> Option> { - 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) -> Point2 { - // 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), - Line(Line2), - // #[allow(dead_code)] - // Union(Box, Box), - Intersection(Box, Box), - 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> for Region2 { - fn singleton(value: Point2) -> Self { - Region2::Singleton(value.into()) - } - - fn contains(&self, p: &Point2) -> Option { - self.nearest(p).map(|n| n == *p) - } - - fn nearest(&self, p: &Point2) -> Option> { - 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 = 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> for Region2 { - fn singleton(value: Point2) -> Self { - Region2::Singleton(value) - } - - fn contains(&self, p: &Point2) -> Option { - self.nearest(p).map(|n| n.simplify() == p.clone().simplify()) - } - - fn nearest(&self, p: &Point2) -> Option> { - 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; diff --git a/src/math/ops.rs b/src/math/ops.rs index 9e2859c..6a03ed7 100644 --- a/src/math/ops.rs +++ b/src/math/ops.rs @@ -1,7 +1,6 @@ use std::ops; -use super::eqn::{Expr, Unknown}; -use super::Scalar; +use super::{Expr, Scalar, Unknown}; impl From for Expr { fn from(c: Scalar) -> Expr { diff --git a/src/math/region.rs b/src/math/region.rs new file mode 100644 index 0000000..09a1cae --- /dev/null +++ b/src/math/region.rs @@ -0,0 +1,432 @@ +use std::fmt; + +use super::{eqn, Value, Scalar, Expr, Point2, Rot2}; + +// pub type Vec2 = nalgebra::Vector2; +// pub type Point2 = nalgebra::Point2; + +// pub type Rot2 = nalgebra::UnitComplex; + +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: GenericRegion { + fn singleton(value: T) -> Self; + + fn nearest(&self, value: &T) -> Option; + fn contains(&self, value: &T) -> Option; +} + +#[derive(Clone, Debug)] +pub enum Region1 { + Empty, + Singleton(Value), + Range(Value, Value), + Intersection(Box, Box), + // Union(Box, Box), + 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 for Region1 { + fn singleton(value: Scalar) -> Self { + Region1::Singleton(value.into()) + } + + fn contains(&self, n: &Scalar) -> Option { + 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 { + 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, + dir: Rot2, + extent: Region1, +} + +impl fmt::Display for Line2 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{{ = {} + {} * {} }}", self.start, self.dir, self.extent) + } +} + +impl Line2 { + pub fn new(start: Point2, dir: Rot2, extent: Region1) -> Self { + Self { start, dir, extent } + } + + pub fn evaluate(&self, t: Value) -> Point2 { + self.start.clone() + self.dir.clone() * t + } + + pub fn evaluate_extent(&self) -> Option> { + 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) -> Point2 { + // 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), + Line(Line2), + // #[allow(dead_code)] + // Union(Box, Box), + Intersection(Box, Box), + 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> for Region2 { + fn singleton(value: Point2) -> Self { + Region2::Singleton(value.into()) + } + + fn contains(&self, p: &Point2) -> Option { + self.nearest(p).map(|n| n == *p) + } + + fn nearest(&self, p: &Point2) -> Option> { + 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 = 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> for Region2 { + fn singleton(value: Point2) -> Self { + Region2::Singleton(value) + } + + fn contains(&self, p: &Point2) -> Option { + self.nearest(p).map(|n| n.simplify() == p.clone().simplify()) + } + + fn nearest(&self, p: &Point2) -> Option> { + 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)), + } + } +} \ No newline at end of file diff --git a/src/math/unknown.rs b/src/math/unknown.rs new file mode 100644 index 0000000..55a5f92 --- /dev/null +++ b/src/math/unknown.rs @@ -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; + +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) + } +} \ No newline at end of file