use std::{collections::HashMap, rc::Rc};
use derivative::*;
use num::{Signed, Zero};
use crate::evaluate::Error;
pub type Function =
dyn Fn(&Expression, &[Expression], &HashMap<String, Expression>) -> Result<Expression, Error>;
pub type Integer = num::bigint::BigInt;
pub type Rational = num::rational::Ratio<Integer>;
pub type Complex = num::complex::Complex<Rational>;
pub type Vector = nalgebra::DVector<Expression>;
pub type Matrix = nalgebra::DMatrix<Expression>;
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum RationalRepresentation {
Fraction,
Decimal,
}
impl RationalRepresentation {
pub(crate) fn merge(self, other: Self) -> Self {
use RationalRepresentation::*;
if self == Decimal || other == Decimal {
Decimal
} else {
Fraction
}
}
}
#[derive(Derivative)]
#[derivative(PartialEq, Eq, Clone, Debug)]
pub enum Expression {
Variable(String),
Function(
String,
#[derivative(PartialEq = "ignore", Debug = "ignore")] Rc<Function>,
),
FunctionValue(Box<Self>, Vec<Self>),
Integer(Integer),
Rational(Rational, RationalRepresentation),
Complex(Complex, RationalRepresentation),
Vector(Vector),
VectorElement(Box<Self>, Box<Self>),
Matrix(Matrix),
MatrixElement(Box<Self>, Box<Self>, Box<Self>),
Boolean(bool),
Negation(Box<Self>),
Not(Box<Self>),
Sum(Box<Self>, Box<Self>),
Difference(Box<Self>, Box<Self>),
Product(Box<Self>, Box<Self>),
Quotient(Box<Self>, Box<Self>),
Remainder(Box<Self>, Box<Self>),
Power(Box<Self>, Box<Self>),
Equal(Box<Self>, Box<Self>),
NotEqual(Box<Self>, Box<Self>),
LessThan(Box<Self>, Box<Self>),
LessThanOrEqual(Box<Self>, Box<Self>),
GreaterThan(Box<Self>, Box<Self>),
GreaterThanOrEqual(Box<Self>, Box<Self>),
And(Box<Self>, Box<Self>),
Or(Box<Self>, Box<Self>),
}
#[derive(Derivative)]
#[derivative(PartialEq, Eq, Clone, Debug)]
pub(crate) enum Type {
Function(
String,
#[derivative(PartialEq = "ignore", Debug = "ignore")] Rc<Function>,
),
Number(Complex, RationalRepresentation),
Matrix(Matrix),
Boolean(Option<bool>),
Arithmetic,
Unknown,
}
#[allow(clippy::enum_variant_names)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub(crate) enum Associativity {
LeftAssociative,
RightAssociative,
Associative,
}
impl Expression {
pub(crate) fn typ(&self) -> Type {
use Expression::*;
use RationalRepresentation::*;
use Type::{
Arithmetic, Boolean as Bool, Function as Fun, Matrix as Mat, Number as Num, Unknown,
};
match self {
Variable(_) => Unknown,
Function(identifier, f) => Fun(identifier.clone(), f.clone()),
FunctionValue(_, _) => Unknown,
Integer(n) => Num(self::Rational::from_integer(n.clone()).into(), Fraction),
Rational(x, representation) => Num(x.into(), *representation),
Complex(z, representation) => Num(z.clone(), *representation),
Vector(v) => Mat(self::Matrix::from_columns(&[v.clone()])),
VectorElement(_, _) => Unknown,
Matrix(m) => Mat(m.clone()),
MatrixElement(_, _, _) => Unknown,
Boolean(boolean) => Bool(Some(*boolean)),
Negation(_) => Arithmetic,
Not(_) => Bool(None),
Sum(_, _) => Arithmetic,
Difference(_, _) => Arithmetic,
Product(_, _) => Arithmetic,
Quotient(_, _) => Arithmetic,
Remainder(_, _) => Arithmetic,
Power(_, _) => Arithmetic,
Equal(_, _) => Bool(None),
NotEqual(_, _) => Bool(None),
LessThan(_, _) => Bool(None),
LessThanOrEqual(_, _) => Bool(None),
GreaterThan(_, _) => Bool(None),
GreaterThanOrEqual(_, _) => Bool(None),
And(_, _) => Bool(None),
Or(_, _) => Bool(None),
}
}
pub(crate) fn precedence_and_associativity(&self) -> (isize, Associativity) {
use Associativity::*;
use Expression::*;
match self {
Variable(_) => (isize::MAX, Associative),
Function(_, _) => (isize::MAX, Associative),
FunctionValue(_, _) => (5, Associative),
Integer(n) => {
if n.is_negative() {
(2, Associative)
} else {
(isize::MAX, Associative)
}
}
Rational(x, _) => {
if self.to_string().contains('/') {
(2, LeftAssociative)
} else if x.is_negative() {
(2, Associative)
} else {
(isize::MAX, Associative)
}
}
Complex(z, _) => {
if !z.re.is_zero() && !z.im.is_zero() {
if self.to_string().contains('+') {
(1, Associative)
} else {
(1, LeftAssociative)
}
} else if self.to_string().contains('/') {
(2, LeftAssociative)
} else if z.re.is_negative() || !z.im.is_zero() {
(2, Associative)
} else {
(isize::MAX, Associative)
}
}
Vector(_) => (isize::MAX, Associative),
VectorElement(_, _) => (5, Associative),
Matrix(_) => (isize::MAX, Associative),
MatrixElement(_, _, _) => (5, Associative),
Boolean(_) => (isize::MAX, Associative),
Negation(_) => (3, Associative),
Not(_) => (3, Associative),
Sum(_, _) => (1, Associative),
Difference(_, _) => (1, LeftAssociative),
Product(_, _) => (2, Associative),
Quotient(_, _) => (2, LeftAssociative),
Remainder(_, _) => (2, LeftAssociative),
Power(_, _) => (4, RightAssociative),
Equal(_, _) => (0, Associative),
NotEqual(_, _) => (0, Associative),
LessThan(_, _) => (0, Associative),
LessThanOrEqual(_, _) => (0, Associative),
GreaterThan(_, _) => (0, Associative),
GreaterThanOrEqual(_, _) => (0, Associative),
And(_, _) => (-1, Associative),
Or(_, _) => (-2, Associative),
}
}
pub(crate) fn precedence(&self) -> isize {
self.precedence_and_associativity().0
}
pub(crate) fn associativity(&self) -> Associativity {
self.precedence_and_associativity().1
}
}