use std::cmp;
use std::fmt;
use std::ops;
use super::xi::partial_str;
use crate::algebra::{ar_product, Alpha, Form, Magnitude, Sign, Xi, AR};
#[derive(Hash, Eq, Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Term {
magnitude: Magnitude,
alpha: Alpha,
partials: Vec<Form>,
numerator: Xi,
denominator: Xi,
}
impl AR for Term {
type Output = Self;
fn as_terms(&self) -> Vec<Term> {
vec![self.clone()]
}
fn from_terms(terms: Vec<Term>) -> Self {
if terms.len() != 1 {
panic!("Can only construct an Term from a single term")
};
terms[0].clone()
}
fn inverse(&self) -> Self::Output {
Term {
magnitude: 1 / self.magnitude,
alpha: self.alpha.inverse(),
partials: self.partials.clone(),
numerator: self.denominator.clone(),
denominator: self.numerator.clone(),
}
}
}
impl Term {
pub fn new(val: Option<&str>, alpha: Alpha) -> Term {
let xi = if let Some(v) = val {
Xi::new(v)
} else {
Xi::new(&format!("{}", alpha.form()))
};
Term {
magnitude: 1.into(),
alpha: alpha,
partials: vec![],
numerator: xi,
denominator: Xi::empty(),
}
}
pub fn from_xis_and_alpha(xis: Vec<&str>, alpha: Alpha) -> Term {
Term {
magnitude: 1.into(),
alpha: alpha,
partials: vec![],
numerator: Xi::merge(&xis.iter().map(|s| Xi::new(s)).collect()),
denominator: Xi::empty(),
}
}
pub fn form(&self) -> Form {
self.alpha.form()
}
pub fn sign(&self) -> Sign {
self.alpha.sign()
}
pub fn alpha(&self) -> Alpha {
self.alpha.clone()
}
pub fn magnitude(&self) -> Magnitude {
self.magnitude
}
pub fn set_alpha(&mut self, a: Alpha) {
self.alpha = a;
}
pub fn add_partial(&mut self, wrt: &Alpha) {
self.partials.push(wrt.form());
self.partials.sort();
}
pub fn set_partials(&mut self, partials: Vec<Form>) {
self.partials = partials;
self.partials.sort();
}
pub fn xi_str(&self) -> String {
let numerator = if self.numerator.is_empty() {
"1".to_string()
} else {
self.numerator.dotted_string()
};
let s = if self.denominator.is_empty() {
format!("{}", numerator)
} else {
format!("{}/{}", numerator, self.denominator.dotted_string())
};
format!("{}{}", partial_str(&self.partials), s)
}
pub fn try_add(&self, other: &Term) -> Option<Term> {
fn sub_mag(a: &Term, b: &Term) -> Term {
let mut t = a.clone();
if t.magnitude > b.magnitude {
t.magnitude -= b.magnitude;
} else {
t.magnitude = b.magnitude - t.magnitude;
t.alpha = -t.alpha;
}
return t;
}
if self.summation_key() == other.summation_key() {
Some(match (self.sign(), other.sign()) {
(Sign::Pos, Sign::Pos) | (Sign::Neg, Sign::Neg) => {
let mut t = self.clone();
t.magnitude += other.magnitude;
t
}
(Sign::Pos, Sign::Neg) => sub_mag(self, other), (Sign::Neg, Sign::Pos) => sub_mag(other, self), })
} else {
None
}
}
pub fn form_product_with(&self, other: &Term) -> Term {
Term {
magnitude: self.magnitude * other.magnitude,
alpha: ar_product(&self.alpha, &other.alpha),
partials: Vec::new(),
numerator: Xi::merge(&vec![self.numerator.clone(), other.numerator.clone()]),
denominator: Xi::merge(&vec![self.denominator.clone(), other.denominator.clone()]),
}
}
pub fn summation_key(&self) -> (Form, String) {
(self.form(), self.xi_str())
}
}
impl ops::Mul<usize> for Term {
type Output = Self;
fn mul(self, rhs: usize) -> Self::Output {
let mut t = self.clone();
t.magnitude = t.magnitude * rhs;
return t;
}
}
impl ops::Mul<isize> for Term {
type Output = Self;
fn mul(self, rhs: isize) -> Self::Output {
let mut t = self.clone();
if rhs < 0 {
t.magnitude = t.magnitude * (-rhs) as usize;
t.alpha = -t.alpha;
} else {
t.magnitude = t.magnitude * rhs as usize;
}
return t;
}
}
impl ops::Mul<Magnitude> for Term {
type Output = Self;
fn mul(self, rhs: Magnitude) -> Self::Output {
let mut t = self.clone();
t.magnitude = t.magnitude * rhs;
return t;
}
}
impl ops::Div<Magnitude> for Term {
type Output = Term;
fn div(self, rhs: Magnitude) -> Self::Output {
let mut t = self.clone();
t.magnitude = t.magnitude / rhs;
return t;
}
}
impl ops::Neg for Term {
type Output = Term;
fn neg(self) -> Self::Output {
let mut t = self.clone();
t.alpha = -t.alpha;
return t;
}
}
impl ops::Mul<Term> for usize {
type Output = Term;
fn mul(self, rhs: Term) -> Self::Output {
rhs * self
}
}
impl ops::Mul<Term> for isize {
type Output = Term;
fn mul(self, rhs: Term) -> Self::Output {
rhs * self
}
}
impl ops::Mul<Term> for Magnitude {
type Output = Term;
fn mul(self, rhs: Term) -> Self::Output {
rhs * self
}
}
impl fmt::Display for Term {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let m_str = if self.magnitude != 1 {
format!("({})", self.magnitude)
} else {
String::new()
};
let p_str = partial_str(&self.partials);
write!(f, "{}{}{}({})", self.alpha, m_str, p_str, self.xi_str())
}
}
impl cmp::Ord for Term {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.form()
.cmp(&other.form())
.then(self.numerator.cmp(&other.numerator))
.then(self.denominator.cmp(&other.denominator))
.then(self.partials.cmp(&other.partials))
.then(self.sign().cmp(&other.sign()))
.then(self.magnitude.cmp(&other.magnitude))
}
}
impl cmp::PartialOrd for Term {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(test)]
mod tests {
use crate::*;
use super::*;
use test_case::test_case;
#[test_case(Term::new(None, alpha!(0 1)), Term::new(None, alpha!(0 1)))]
#[test_case(Term::new(None, alpha!(2 3)), Term::new(None, -alpha!(2 3)))]
#[test_case(term!(0 3 1), -term!(0 3 1))]
#[test_case(term!("foo", 1 2 3), term!("foo", 1 2 3))]
#[test_case(term!(["foo", "bar"], 0 1 2 3), -term!(["foo", "bar"], 0 1 2 3))]
fn inversion_works(t: Term, u: Term) {
let mut expected = u.clone();
expected.numerator = u.denominator;
expected.denominator = u.numerator;
assert_eq!(t.inverse(), expected);
}
#[test_case(term!("foo", 1 2 3), term!("foo", 1 2 3), true)]
#[test_case(term!("foo", 1), -term!("foo", 1), true)]
#[test_case(term!("foo", 1), term!("foo", 2), false)]
#[test_case(term!("foo", 0 2), term!("bar", 0 2), false)]
fn summation_key_correctly_identifies_terms(t: Term, u: Term, expected: bool) {
assert_eq!(t.summation_key() == u.summation_key(), expected);
}
#[test_case(term!(1 2 3), term!(1 2 3), 2 as usize * term!(1 2 3))]
#[test_case(term!(3 1), 2 as usize * -term!(3 1), -1 as isize * term!(3 1))]
fn try_add_correctly_sums_terms(t: Term, u: Term, expected: Term) {
assert_eq!(t.try_add(&u).unwrap(), expected);
}
#[test_case(term!("a", 2 3), term!("b", 1 2 3), -term!(["a", "b"], 1))]
#[test_case(term!("a", 2 3), term!("a", 2 3), -term!(["a", "a"], ))]
fn form_product_with_works_with_no_inversion(left: Term, right: Term, expected: Term) {
assert_eq!(left.form_product_with(&right), expected)
}
}