use num_bigint::{BigInt, ToBigInt};
use num_rational::BigRational;
use bigdecimal::{BigDecimal, Zero, ToPrimitive, FromPrimitive};
use num_traits::{Signed};
use std::fmt::{self, Display, Debug};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Number {
Integer(BigInt),
Rational(BigRational),
Real(BigDecimal),
Complex {
real: Box<Number>,
imaginary: Box<Number>,
},
Constant(crate::core::MathConstant),
Symbolic(Box<crate::core::Expression>),
Float(f64),
}
impl Number {
pub fn integer(value: impl Into<BigInt>) -> Self {
Number::Integer(value.into())
}
pub fn rational(numerator: impl Into<BigInt>, denominator: impl Into<BigInt>) -> Self {
Number::Rational(BigRational::new(numerator.into(), denominator.into()))
}
pub fn real(value: impl Into<BigDecimal>) -> Self {
Number::Real(value.into())
}
pub fn complex(real: Number, imaginary: Number) -> Self {
Number::Complex {
real: Box::new(real),
imaginary: Box::new(imaginary),
}
}
pub fn float(value: f64) -> Self {
Number::Float(value)
}
pub fn to_exact(&self) -> Self {
match self {
Number::Float(f) => {
if let Some(rational) = Self::float_to_rational(*f) {
Number::Rational(rational)
} else {
self.clone()
}
}
_ => self.clone(),
}
}
pub fn is_exact(&self) -> bool {
!matches!(self, Number::Float(_))
}
pub fn is_zero(&self) -> bool {
match self {
Number::Integer(i) => i.is_zero(),
Number::Rational(r) => r.is_zero(),
Number::Real(r) => r.is_zero(),
Number::Complex { real, imaginary } => real.is_zero() && imaginary.is_zero(),
Number::Float(f) => *f == 0.0,
Number::Constant(_) => false,
Number::Symbolic(_) => false,
}
}
pub fn is_one(&self) -> bool {
match self {
Number::Integer(i) => i == &BigInt::from(1),
Number::Rational(r) => r == &BigRational::from(BigInt::from(1)),
Number::Real(r) => r == &BigDecimal::from(1),
Number::Float(f) => *f == 1.0,
Number::Complex { real, imaginary } => real.is_one() && imaginary.is_zero(),
Number::Constant(_) => false,
Number::Symbolic(_) => false,
}
}
pub fn is_two(&self) -> bool {
match self {
Number::Integer(i) => i == &BigInt::from(2),
Number::Rational(r) => r == &BigRational::from(BigInt::from(2)),
Number::Real(r) => r == &BigDecimal::from(2),
Number::Float(f) => *f == 2.0,
Number::Complex { real, imaginary } => real.is_two() && imaginary.is_zero(),
Number::Constant(_) => false,
Number::Symbolic(_) => false,
}
}
pub fn approximate(&self) -> f64 {
match self {
Number::Integer(i) => ToPrimitive::to_f64(i).unwrap_or(f64::INFINITY),
Number::Rational(r) => ToPrimitive::to_f64(r).unwrap_or(f64::NAN),
Number::Real(r) => ToPrimitive::to_f64(r).unwrap_or(f64::NAN),
Number::Complex { real, imaginary } => {
let r = real.approximate();
let i = imaginary.approximate();
(r * r + i * i).sqrt()
}
Number::Constant(c) => c.approximate_value(),
Number::Symbolic(_) => f64::NAN,
Number::Float(f) => *f,
}
}
fn float_to_rational(f: f64) -> Option<BigRational> {
if f.is_finite() {
let precision = 1e-10;
let mut num = f;
let mut denom = 1.0;
while (num - num.round()).abs() > precision && denom < 1e6 {
num *= 10.0;
denom *= 10.0;
}
let numerator = BigInt::from(num.round() as i64);
let denominator = BigInt::from(denom as i64);
Some(BigRational::new(numerator, denominator))
} else {
None
}
}
}
impl From<i32> for Number {
fn from(value: i32) -> Self {
Number::Integer(BigInt::from(value))
}
}
impl From<i64> for Number {
fn from(value: i64) -> Self {
Number::Integer(BigInt::from(value))
}
}
impl From<f64> for Number {
fn from(value: f64) -> Self {
Number::Float(value)
}
}
impl From<BigInt> for Number {
fn from(value: BigInt) -> Self {
Number::Integer(value)
}
}
impl From<BigRational> for Number {
fn from(value: BigRational) -> Self {
Number::Rational(value)
}
}
impl From<BigDecimal> for Number {
fn from(value: BigDecimal) -> Self {
Number::Real(value)
}
}
impl Display for Number {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Number::Integer(i) => write!(f, "{}", i),
Number::Rational(r) => {
if r.denom() == &BigInt::from(1) {
write!(f, "{}", r.numer())
} else {
write!(f, "{}/{}", r.numer(), r.denom())
}
}
Number::Real(r) => write!(f, "{}", r),
Number::Complex { real, imaginary } => {
if imaginary.is_zero() {
write!(f, "{}", real)
} else if real.is_zero() {
if imaginary.is_one() {
write!(f, "i")
} else if **imaginary == Number::integer(-1) {
write!(f, "-i")
} else {
write!(f, "{}i", imaginary)
}
} else {
let imag_str = if imaginary.is_one() {
"i".to_string()
} else if **imaginary == Number::integer(-1) {
"-i".to_string()
} else {
format!("{}i", imaginary)
};
if imag_str.starts_with('-') {
write!(f, "{}{}", real, imag_str)
} else {
write!(f, "{}+{}", real, imag_str)
}
}
}
Number::Constant(c) => write!(f, "{}", c.symbol()),
Number::Symbolic(expr) => write!(f, "{}", expr),
Number::Float(fl) => write!(f, "{}", fl),
}
}
}
impl Number {
pub fn zero() -> Self {
Number::Integer(BigInt::from(0))
}
pub fn one() -> Self {
Number::Integer(BigInt::from(1))
}
pub fn neg_one() -> Self {
Number::Integer(BigInt::from(-1))
}
pub fn i() -> Self {
Number::Complex {
real: Box::new(Number::zero()),
imaginary: Box::new(Number::one()),
}
}
pub fn is_negative(&self) -> bool {
match self {
Number::Integer(i) => i < &BigInt::from(0),
Number::Rational(r) => r < &BigRational::from(BigInt::from(0)),
Number::Real(r) => r < &BigDecimal::from(0),
Number::Float(f) => *f < 0.0,
Number::Complex { .. } => false, Number::Constant(c) => c.approximate_value() < 0.0,
Number::Symbolic(_) => false, }
}
pub fn is_positive(&self) -> bool {
match self {
Number::Integer(i) => i > &BigInt::from(0),
Number::Rational(r) => r > &BigRational::from(BigInt::from(0)),
Number::Real(r) => r > &BigDecimal::from(0),
Number::Float(f) => *f > 0.0,
Number::Complex { .. } => false, Number::Constant(c) => c.approximate_value() > 0.0,
Number::Symbolic(_) => false, }
}
pub fn is_integer(&self) -> bool {
match self {
Number::Integer(_) => true,
Number::Rational(r) => r.denom() == &BigInt::from(1),
Number::Real(r) => {
if let Some(int_val) = r.to_bigint() {
BigDecimal::from(int_val) == *r
} else {
false
}
},
Number::Float(f) => f.fract() == 0.0,
Number::Complex { real, imaginary } => {
imaginary.is_zero() && real.is_integer()
}
Number::Constant(_) => false,
Number::Symbolic(_) => false,
}
}
pub fn is_rational(&self) -> bool {
match self {
Number::Integer(_) | Number::Rational(_) => true,
Number::Complex { real, imaginary } => {
imaginary.is_zero() && real.is_rational()
}
Number::Constant(_) => false,
_ => false,
}
}
pub fn is_real(&self) -> bool {
match self {
Number::Integer(_) | Number::Rational(_) | Number::Real(_) | Number::Float(_) => true,
Number::Complex { imaginary, .. } => imaginary.is_zero(),
Number::Constant(c) => c.is_real(),
Number::Symbolic(_) => false,
}
}
pub fn is_complex(&self) -> bool {
match self {
Number::Complex { imaginary, .. } => !imaginary.is_zero(),
_ => false,
}
}
pub fn abs(&self) -> Result<Self, crate::engine::ComputeError> {
Ok(match self {
Number::Integer(i) => Number::Integer(i.abs()),
Number::Rational(r) => Number::Rational(r.abs()),
Number::Real(r) => Number::Real(r.abs()),
Number::Float(f) => Number::Float(f.abs()),
Number::Complex { real, imaginary } => {
let real_sq = match real.as_ref() {
Number::Integer(r) => Number::Integer(r * r),
Number::Rational(r) => Number::Rational(r * r),
Number::Real(r) => Number::Real(r * r),
Number::Float(r) => Number::Float(r * r),
_ => return Ok(Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Abs,
operand: Box::new(crate::core::Expression::Number(self.clone())),
}))),
};
let imag_sq = match imaginary.as_ref() {
Number::Integer(i) => Number::Integer(i * i),
Number::Rational(i) => Number::Rational(i * i),
Number::Real(i) => Number::Real(i * i),
Number::Float(i) => Number::Float(i * i),
_ => return Ok(Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Abs,
operand: Box::new(crate::core::Expression::Number(self.clone())),
}))),
};
Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Sqrt,
operand: Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Add,
left: Box::new(crate::core::Expression::Number(real_sq)),
right: Box::new(crate::core::Expression::Number(imag_sq)),
}),
}))
}
Number::Constant(_) => Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Abs,
operand: Box::new(crate::core::Expression::Number(self.clone())),
})),
Number::Symbolic(_) => Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Abs,
operand: Box::new(crate::core::Expression::Number(self.clone())),
})),
})
}
pub fn neg(&self) -> Self {
match self {
Number::Integer(i) => Number::Integer(-i),
Number::Rational(r) => Number::Rational(-r),
Number::Real(r) => Number::Real(-r),
Number::Float(f) => Number::Float(-f),
Number::Complex { real, imaginary } => Number::Complex {
real: Box::new(real.clone().neg()),
imaginary: Box::new(imaginary.clone().neg()),
},
Number::Constant(_) => Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Negate,
operand: Box::new(crate::core::Expression::Number(self.clone())),
})),
Number::Symbolic(expr) => Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Negate,
operand: expr.clone(),
})),
}
}
pub fn is_even(&self) -> bool {
match self {
Number::Integer(i) => i % BigInt::from(2) == BigInt::from(0),
Number::Rational(r) => {
if r.denom() == &BigInt::from(1) {
r.numer() % BigInt::from(2) == BigInt::from(0)
} else {
false
}
}
Number::Float(f) => f.fract() == 0.0 && (*f as i64) % 2 == 0,
Number::Complex { real, imaginary } => {
imaginary.is_zero() && real.is_even()
}
_ => false,
}
}
pub fn negate(&self) -> Result<Number, crate::engine::ComputeError> {
Ok(self.neg())
}
pub fn add(&self, other: &Number) -> Result<Number, crate::engine::ComputeError> {
Ok(self.clone() + other.clone())
}
pub fn subtract(&self, other: &Number) -> Result<Number, crate::engine::ComputeError> {
Ok(self.clone() - other.clone())
}
pub fn multiply(&self, other: &Number) -> Result<Number, crate::engine::ComputeError> {
Ok(self.clone() * other.clone())
}
pub fn divide(&self, other: &Number) -> Result<Number, crate::engine::ComputeError> {
if other.is_zero() {
return Err(crate::engine::ComputeError::DivisionByZero);
}
Ok(self.clone() / other.clone())
}
pub fn power(&self, other: &Number) -> Result<Number, crate::engine::ComputeError> {
use num_traits::Pow;
match (self, other) {
(Number::Integer(base), Number::Integer(exp)) => {
if exp >= &BigInt::from(0) {
if let Some(exp_u32) = exp.to_u32() {
Ok(Number::Integer(base.pow(exp_u32)))
} else {
Ok(Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Power,
left: Box::new(crate::core::Expression::Number(self.clone())),
right: Box::new(crate::core::Expression::Number(other.clone())),
})))
}
} else {
let base_rational = BigRational::from(base.clone());
let exp_abs = exp.abs();
if let Some(exp_u32) = exp_abs.to_u32() {
let result = base_rational.pow(exp_u32);
Ok(Number::Rational(BigRational::from(BigInt::from(1)) / result))
} else {
Ok(Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Power,
left: Box::new(crate::core::Expression::Number(self.clone())),
right: Box::new(crate::core::Expression::Number(other.clone())),
})))
}
}
}
(Number::Rational(base), Number::Integer(exp)) => {
if let Some(exp_i32) = exp.to_i32() {
Ok(Number::Rational(base.pow(exp_i32)))
} else {
Ok(Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Power,
left: Box::new(crate::core::Expression::Number(self.clone())),
right: Box::new(crate::core::Expression::Number(other.clone())),
})))
}
}
(Number::Float(base), Number::Float(exp)) => {
Ok(Number::Float(base.powf(*exp)))
}
_ => {
Ok(Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Power,
left: Box::new(crate::core::Expression::Number(self.clone())),
right: Box::new(crate::core::Expression::Number(other.clone())),
})))
}
}
}
pub fn to_integer(&self) -> Option<BigInt> {
match self {
Number::Integer(i) => Some(i.clone()),
Number::Rational(r) if r.denom() == &BigInt::from(1) => Some(r.numer().clone()),
Number::Real(r) => {
if let Some(int_val) = r.to_bigint() {
if BigDecimal::from(int_val.clone()) == *r {
Some(int_val)
} else {
None
}
} else {
None
}
}
Number::Float(f) if f.fract() == 0.0 => {
BigInt::from_f64(*f)
}
Number::Complex { real, imaginary } if imaginary.is_zero() => {
real.to_integer()
}
_ => None,
}
}
pub fn to_rational(&self) -> Option<BigRational> {
match self {
Number::Integer(i) => Some(BigRational::from(i.clone())),
Number::Rational(r) => Some(r.clone()),
Number::Float(f) => Self::float_to_rational(*f),
Number::Complex { real, imaginary } if imaginary.is_zero() => {
real.to_rational()
}
_ => None,
}
}
pub fn to_f64(&self) -> Option<f64> {
match self {
Number::Integer(i) => i.to_f64(),
Number::Rational(r) => {
let num = r.numer().to_f64()?;
let den = r.denom().to_f64()?;
Some(num / den)
}
Number::Real(r) => r.to_f64(),
Number::Float(f) => Some(*f),
Number::Complex { real, imaginary } if imaginary.is_zero() => {
real.to_f64()
}
Number::Constant(c) => Some(c.approximate_value()),
_ => None,
}
}
pub fn to_i64(&self) -> Option<i64> {
match self {
Number::Integer(i) => i.to_i64(),
Number::Rational(r) if r.denom() == &BigInt::from(1) => {
r.numer().to_i64()
}
Number::Real(r) => {
if let Some(int_val) = r.to_bigint() {
int_val.to_i64()
} else {
None
}
}
Number::Float(f) if f.fract() == 0.0 => Some(*f as i64),
Number::Complex { real, imaginary } if imaginary.is_zero() => {
real.to_i64()
}
_ => None,
}
}
pub fn get_numeric_type(&self) -> crate::core::NumericType {
match self {
Number::Integer(_) => crate::core::NumericType::Integer,
Number::Rational(_) => crate::core::NumericType::Rational,
Number::Real(_) => crate::core::NumericType::Real,
Number::Float(_) => crate::core::NumericType::Float,
Number::Complex { .. } => crate::core::NumericType::Complex,
Number::Constant(c) => {
if c.is_complex() {
crate::core::NumericType::Complex
} else {
crate::core::NumericType::Real
}
}
Number::Symbolic(_) => crate::core::NumericType::Real, }
}
pub fn promote_types(a: &Number, b: &Number) -> (Number, Number) {
match (a, b) {
(Number::Integer(_), Number::Integer(_)) => (a.clone(), b.clone()),
(Number::Rational(_), Number::Rational(_)) => (a.clone(), b.clone()),
(Number::Real(_), Number::Real(_)) => (a.clone(), b.clone()),
(Number::Float(_), Number::Float(_)) => (a.clone(), b.clone()),
(Number::Integer(i), Number::Rational(_)) => {
(Number::Rational(BigRational::from(i.clone())), b.clone())
}
(Number::Rational(_), Number::Integer(i)) => {
(a.clone(), Number::Rational(BigRational::from(i.clone())))
}
(Number::Integer(i), Number::Real(_)) => {
(Number::Real(BigDecimal::from(i.clone())), b.clone())
}
(Number::Real(_), Number::Integer(i)) => {
(a.clone(), Number::Real(BigDecimal::from(i.clone())))
}
(Number::Rational(r), Number::Real(_)) => {
let decimal = BigDecimal::new(r.numer().clone(), 0) / BigDecimal::new(r.denom().clone(), 0);
(Number::Real(decimal), b.clone())
}
(Number::Real(_), Number::Rational(r)) => {
let decimal = BigDecimal::new(r.numer().clone(), 0) / BigDecimal::new(r.denom().clone(), 0);
(a.clone(), Number::Real(decimal))
}
(Number::Float(_), _) => (a.clone(), Number::Float(b.approximate())),
(_, Number::Float(_)) => (Number::Float(a.approximate()), b.clone()),
(Number::Complex { .. }, _) if b.is_real() => {
(a.clone(), Number::Complex {
real: Box::new(b.clone()),
imaginary: Box::new(Number::zero()),
})
}
(_, Number::Complex { .. }) if a.is_real() => {
(Number::Complex {
real: Box::new(a.clone()),
imaginary: Box::new(Number::zero()),
}, b.clone())
}
_ => (a.clone(), b.clone()),
}
}
}
use std::ops::{Add, Sub, Mul, Div, Neg};
impl Add for Number {
type Output = Number;
fn add(self, other: Number) -> Number {
let (a, b) = Number::promote_types(&self, &other);
match (a, b) {
(Number::Integer(a), Number::Integer(b)) => {
Number::Integer(a + b)
}
(Number::Rational(a), Number::Rational(b)) => {
Number::Rational(a + b)
}
(Number::Real(a), Number::Real(b)) => {
Number::Real(a + b)
}
(Number::Float(a), Number::Float(b)) => {
Number::Float(a + b)
}
(Number::Complex { real: real_a, imaginary: imag_a },
Number::Complex { real: real_b, imaginary: imag_b }) => {
Number::Complex {
real: Box::new(*real_a + *real_b),
imaginary: Box::new(*imag_a + *imag_b),
}
}
_ => Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Add,
left: Box::new(crate::core::Expression::Number(self)),
right: Box::new(crate::core::Expression::Number(other)),
}))
}
}
}
impl Sub for Number {
type Output = Number;
fn sub(self, other: Number) -> Number {
let (a, b) = Number::promote_types(&self, &other);
match (a, b) {
(Number::Integer(a), Number::Integer(b)) => {
Number::Integer(a - b)
}
(Number::Rational(a), Number::Rational(b)) => {
Number::Rational(a - b)
}
(Number::Real(a), Number::Real(b)) => {
Number::Real(a - b)
}
(Number::Float(a), Number::Float(b)) => {
Number::Float(a - b)
}
(Number::Complex { real: real_a, imaginary: imag_a },
Number::Complex { real: real_b, imaginary: imag_b }) => {
Number::Complex {
real: Box::new(*real_a - *real_b),
imaginary: Box::new(*imag_a - *imag_b),
}
}
_ => Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Subtract,
left: Box::new(crate::core::Expression::Number(self)),
right: Box::new(crate::core::Expression::Number(other)),
}))
}
}
}
impl Mul for Number {
type Output = Number;
fn mul(self, other: Number) -> Number {
if self.is_zero() || other.is_zero() {
return Number::zero();
}
if self.is_one() {
return other;
}
if other.is_one() {
return self;
}
let (a, b) = Number::promote_types(&self, &other);
match (a, b) {
(Number::Integer(a), Number::Integer(b)) => {
Number::Integer(a * b)
}
(Number::Rational(a), Number::Rational(b)) => {
Number::Rational(a * b)
}
(Number::Real(a), Number::Real(b)) => {
Number::Real(a * b)
}
(Number::Float(a), Number::Float(b)) => {
Number::Float(a * b)
}
(Number::Complex { real: real_a, imaginary: imag_a },
Number::Complex { real: real_b, imaginary: imag_b }) => {
let real_part = (*real_a.clone() * *real_b.clone()) - (*imag_a.clone() * *imag_b.clone());
let imag_part = (*real_a * *imag_b) + (*imag_a * *real_b);
if imag_part.is_zero() {
real_part
} else {
Number::Complex {
real: Box::new(real_part),
imaginary: Box::new(imag_part),
}
}
}
_ => Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Multiply,
left: Box::new(crate::core::Expression::Number(self)),
right: Box::new(crate::core::Expression::Number(other)),
}))
}
}
}
impl Div for Number {
type Output = Number;
fn div(self, other: Number) -> Number {
if other.is_zero() {
return Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Divide,
left: Box::new(crate::core::Expression::Number(self)),
right: Box::new(crate::core::Expression::Number(other)),
}));
}
if self.is_zero() {
return Number::zero();
}
if other.is_one() {
return self;
}
let (a, b) = Number::promote_types(&self, &other);
match (a, b) {
(Number::Integer(a), Number::Integer(b)) => {
if &a % &b == BigInt::from(0) {
Number::Integer(a / b)
} else {
Number::Rational(BigRational::new(a, b))
}
}
(Number::Rational(a), Number::Rational(b)) => {
Number::Rational(a / b)
}
(Number::Real(a), Number::Real(b)) => {
Number::Real(a / b)
}
(Number::Float(a), Number::Float(b)) => {
Number::Float(a / b)
}
(Number::Complex { real: real_a, imaginary: imag_a },
Number::Complex { real: real_b, imaginary: imag_b }) => {
let denominator = (*real_b.clone() * *real_b.clone()) + (*imag_b.clone() * *imag_b.clone());
let numerator_real = (*real_a.clone() * *real_b.clone()) + (*imag_a.clone() * *imag_b.clone());
let numerator_imag = (*imag_a * *real_b) - (*real_a * *imag_b);
Number::Complex {
real: Box::new(numerator_real / denominator.clone()),
imaginary: Box::new(numerator_imag / denominator),
}
}
_ => Number::Symbolic(Box::new(crate::core::Expression::BinaryOp {
op: crate::core::BinaryOperator::Divide,
left: Box::new(crate::core::Expression::Number(self)),
right: Box::new(crate::core::Expression::Number(other)),
}))
}
}
}
impl Neg for Number {
type Output = Number;
fn neg(self) -> Number {
match self {
Number::Integer(i) => Number::Integer(-i),
Number::Rational(r) => Number::Rational(-r),
Number::Real(r) => Number::Real(-r),
Number::Float(f) => Number::Float(-f),
Number::Complex { real, imaginary } => Number::Complex {
real: Box::new(-*real),
imaginary: Box::new(-*imaginary),
},
Number::Constant(_) => Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Negate,
operand: Box::new(crate::core::Expression::Number(self)),
})),
Number::Symbolic(expr) => Number::Symbolic(Box::new(crate::core::Expression::UnaryOp {
op: crate::core::UnaryOperator::Negate,
operand: expr,
})),
}
}
}
impl Add<&Number> for &Number {
type Output = Number;
fn add(self, other: &Number) -> Number {
self.clone() + other.clone()
}
}
impl Sub<&Number> for &Number {
type Output = Number;
fn sub(self, other: &Number) -> Number {
self.clone() - other.clone()
}
}
impl Mul<&Number> for &Number {
type Output = Number;
fn mul(self, other: &Number) -> Number {
self.clone() * other.clone()
}
}
impl Div<&Number> for &Number {
type Output = Number;
fn div(self, other: &Number) -> Number {
self.clone() / other.clone()
}
}
impl Eq for Number {}
impl std::hash::Hash for Number {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use std::hash::Hash;
match self {
Number::Integer(i) => {
0u8.hash(state);
i.hash(state);
}
Number::Rational(r) => {
1u8.hash(state);
r.hash(state);
}
Number::Real(r) => {
2u8.hash(state);
r.to_string().hash(state);
}
Number::Float(f) => {
3u8.hash(state);
f.to_bits().hash(state);
}
Number::Complex { real, imaginary } => {
4u8.hash(state);
real.hash(state);
imaginary.hash(state);
}
Number::Constant(c) => {
5u8.hash(state);
c.hash(state);
}
Number::Symbolic(expr) => {
6u8.hash(state);
expr.hash(state);
}
}
}
}
#[cfg(test)]
#[path = "number_tests.rs"]
mod number_tests;
#[cfg(test)]
#[path = "arithmetic_tests.rs"]
mod arithmetic_tests;