use super::Number;
use num_bigint::BigInt;
use bigdecimal::BigDecimal;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum MathConstant {
Pi,
E,
I,
EulerGamma,
GoldenRatio,
Catalan,
PositiveInfinity,
NegativeInfinity,
Undefined,
}
impl MathConstant {
pub fn approximate_value(&self) -> f64 {
match self {
MathConstant::Pi => std::f64::consts::PI,
MathConstant::E => std::f64::consts::E,
MathConstant::I => f64::NAN, MathConstant::EulerGamma => 0.5772156649015329,
MathConstant::GoldenRatio => 1.618033988749895,
MathConstant::Catalan => 0.915965594177219,
MathConstant::PositiveInfinity => f64::INFINITY,
MathConstant::NegativeInfinity => f64::NEG_INFINITY,
MathConstant::Undefined => f64::NAN,
}
}
pub fn symbol(&self) -> &'static str {
match self {
MathConstant::Pi => "π",
MathConstant::E => "e",
MathConstant::I => "i",
MathConstant::EulerGamma => "γ",
MathConstant::GoldenRatio => "φ",
MathConstant::Catalan => "G",
MathConstant::PositiveInfinity => "∞",
MathConstant::NegativeInfinity => "-∞",
MathConstant::Undefined => "undefined",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"pi" | "π" => Some(MathConstant::Pi),
"e" => Some(MathConstant::E),
"i" | "j" => Some(MathConstant::I),
"gamma" | "γ" => Some(MathConstant::EulerGamma),
"phi" | "φ" => Some(MathConstant::GoldenRatio),
"catalan" | "g" => Some(MathConstant::Catalan),
"inf" | "infinity" | "∞" => Some(MathConstant::PositiveInfinity),
"-inf" | "-infinity" | "-∞" => Some(MathConstant::NegativeInfinity),
"nan" | "undefined" => Some(MathConstant::Undefined),
_ => None,
}
}
pub fn name(&self) -> &'static str {
match self {
MathConstant::Pi => "圆周率",
MathConstant::E => "自然常数",
MathConstant::I => "虚数单位",
MathConstant::EulerGamma => "欧拉-马歇罗尼常数",
MathConstant::GoldenRatio => "黄金比例",
MathConstant::Catalan => "卡塔兰常数",
MathConstant::PositiveInfinity => "正无穷",
MathConstant::NegativeInfinity => "负无穷",
MathConstant::Undefined => "未定义",
}
}
pub fn is_real(&self) -> bool {
match self {
MathConstant::I => false, MathConstant::Undefined => false, _ => true,
}
}
pub fn is_complex(&self) -> bool {
matches!(self, MathConstant::I)
}
pub fn to_exact_number(&self) -> Option<Number> {
match self {
MathConstant::I => {
Some(Number::Complex {
real: Box::new(Number::zero()),
imaginary: Box::new(Number::one()),
})
}
MathConstant::PositiveInfinity => Some(Number::Float(f64::INFINITY)),
MathConstant::NegativeInfinity => Some(Number::Float(f64::NEG_INFINITY)),
MathConstant::Undefined => Some(Number::Float(f64::NAN)),
_ => None, }
}
pub fn to_high_precision(&self, _precision: usize) -> Option<BigDecimal> {
use num_traits::FromPrimitive;
match self {
MathConstant::Pi => {
BigDecimal::from_f64(std::f64::consts::PI)
}
MathConstant::E => {
BigDecimal::from_f64(std::f64::consts::E)
}
MathConstant::EulerGamma => {
BigDecimal::from_f64(0.5772156649015329)
}
MathConstant::GoldenRatio => {
BigDecimal::from_f64(1.618033988749895)
}
MathConstant::Catalan => {
BigDecimal::from_f64(0.915965594177219)
}
_ => None,
}
}
pub fn is_finite(&self) -> bool {
match self {
MathConstant::PositiveInfinity | MathConstant::NegativeInfinity | MathConstant::Undefined => false,
_ => true,
}
}
pub fn is_infinite(&self) -> bool {
matches!(self, MathConstant::PositiveInfinity | MathConstant::NegativeInfinity)
}
pub fn is_undefined(&self) -> bool {
matches!(self, MathConstant::Undefined)
}
pub fn properties(&self) -> Vec<&'static str> {
match self {
MathConstant::Pi => vec!["无理数", "超越数", "正数"],
MathConstant::E => vec!["无理数", "超越数", "正数"],
MathConstant::I => vec!["虚数单位", "复数"],
MathConstant::EulerGamma => vec!["实数", "可能是无理数"],
MathConstant::GoldenRatio => vec!["无理数", "代数数", "正数"],
MathConstant::Catalan => vec!["实数", "可能是无理数", "正数"],
MathConstant::PositiveInfinity => vec!["无穷大", "正数"],
MathConstant::NegativeInfinity => vec!["无穷大", "负数"],
MathConstant::Undefined => vec!["未定义"],
}
}
pub fn all_constants() -> Vec<MathConstant> {
vec![
MathConstant::Pi,
MathConstant::E,
MathConstant::I,
MathConstant::EulerGamma,
MathConstant::GoldenRatio,
MathConstant::Catalan,
MathConstant::PositiveInfinity,
MathConstant::NegativeInfinity,
MathConstant::Undefined,
]
}
pub fn aliases(&self) -> Vec<&'static str> {
match self {
MathConstant::Pi => vec!["pi", "π", "PI"],
MathConstant::E => vec!["e", "E", "euler"],
MathConstant::I => vec!["i", "I", "j", "J"],
MathConstant::EulerGamma => vec!["gamma", "γ", "euler_gamma"],
MathConstant::GoldenRatio => vec!["phi", "φ", "golden", "golden_ratio"],
MathConstant::Catalan => vec!["catalan", "G", "catalan_constant"],
MathConstant::PositiveInfinity => vec!["inf", "infinity", "∞", "+inf"],
MathConstant::NegativeInfinity => vec!["-inf", "-infinity", "-∞"],
MathConstant::Undefined => vec!["nan", "undefined", "NaN"],
}
}
}
impl MathConstant {
pub fn add_rule(&self, other: &MathConstant) -> Option<super::Expression> {
use super::Expression;
match (self, other) {
(MathConstant::I, MathConstant::I) => {
Some(Expression::BinaryOp {
op: super::BinaryOperator::Multiply,
left: Box::new(Expression::Number(Number::Integer(BigInt::from(2)))),
right: Box::new(Expression::Constant(MathConstant::I)),
})
}
(MathConstant::PositiveInfinity, MathConstant::NegativeInfinity) |
(MathConstant::NegativeInfinity, MathConstant::PositiveInfinity) => {
Some(Expression::Constant(MathConstant::Undefined))
}
(MathConstant::PositiveInfinity, _) if other.is_finite() => {
Some(Expression::Constant(MathConstant::PositiveInfinity))
}
(MathConstant::NegativeInfinity, _) if other.is_finite() => {
Some(Expression::Constant(MathConstant::NegativeInfinity))
}
(_, MathConstant::PositiveInfinity) if self.is_finite() => {
Some(Expression::Constant(MathConstant::PositiveInfinity))
}
(_, MathConstant::NegativeInfinity) if self.is_finite() => {
Some(Expression::Constant(MathConstant::NegativeInfinity))
}
(MathConstant::Undefined, _) | (_, MathConstant::Undefined) => {
Some(Expression::Constant(MathConstant::Undefined))
}
_ => None,
}
}
pub fn multiply_rule(&self, other: &MathConstant) -> Option<super::Expression> {
use super::Expression;
match (self, other) {
(MathConstant::I, MathConstant::I) => {
Some(Expression::Number(Number::Integer(BigInt::from(-1))))
}
(MathConstant::PositiveInfinity, MathConstant::PositiveInfinity) |
(MathConstant::NegativeInfinity, MathConstant::NegativeInfinity) => {
Some(Expression::Constant(MathConstant::PositiveInfinity))
}
(MathConstant::PositiveInfinity, MathConstant::NegativeInfinity) |
(MathConstant::NegativeInfinity, MathConstant::PositiveInfinity) => {
Some(Expression::Constant(MathConstant::NegativeInfinity))
}
(MathConstant::PositiveInfinity, _) | (_, MathConstant::PositiveInfinity) => {
Some(Expression::Constant(MathConstant::PositiveInfinity))
}
(MathConstant::NegativeInfinity, _) | (_, MathConstant::NegativeInfinity) => {
Some(Expression::Constant(MathConstant::NegativeInfinity))
}
(MathConstant::Undefined, _) | (_, MathConstant::Undefined) => {
Some(Expression::Constant(MathConstant::Undefined))
}
_ => None,
}
}
pub fn power_rule(&self, exponent: &super::Expression) -> Option<super::Expression> {
use super::Expression;
match (self, exponent) {
(MathConstant::E, Expression::BinaryOp {
op: super::BinaryOperator::Multiply,
left,
right
}) => {
let is_i_pi = matches!(
(left.as_ref(), right.as_ref()),
(Expression::Constant(MathConstant::I), Expression::Constant(MathConstant::Pi)) |
(Expression::Constant(MathConstant::Pi), Expression::Constant(MathConstant::I))
);
if is_i_pi {
Some(Expression::Number(Number::Integer(BigInt::from(-1))))
} else {
None
}
}
(MathConstant::I, Expression::Number(Number::Integer(n))) if n == &BigInt::from(2) => {
Some(Expression::Number(Number::Integer(BigInt::from(-1))))
}
(MathConstant::I, Expression::Number(Number::Integer(n))) if n == &BigInt::from(4) => {
Some(Expression::Number(Number::Integer(BigInt::from(1))))
}
_ => None,
}
}
pub fn trigonometric_rule(&self, function: &str) -> Option<super::Expression> {
use super::Expression;
match (function, self) {
("sin", MathConstant::Pi) => {
Some(Expression::Number(Number::Integer(BigInt::from(0))))
}
("cos", MathConstant::Pi) => {
Some(Expression::Number(Number::Integer(BigInt::from(-1))))
}
_ => None,
}
}
}
pub struct ConstantCache {
precision_cache: HashMap<(MathConstant, usize), BigDecimal>,
operation_cache: HashMap<String, super::Expression>,
}
impl ConstantCache {
pub fn new() -> Self {
Self {
precision_cache: HashMap::new(),
operation_cache: HashMap::new(),
}
}
pub fn get_high_precision(&mut self, constant: &MathConstant, precision: usize) -> Option<BigDecimal> {
let key = (constant.clone(), precision);
if let Some(cached) = self.precision_cache.get(&key) {
Some(cached.clone())
} else if let Some(value) = constant.to_high_precision(precision) {
self.precision_cache.insert(key, value.clone());
Some(value)
} else {
None
}
}
pub fn clear(&mut self) {
self.precision_cache.clear();
self.operation_cache.clear();
}
pub fn stats(&self) -> (usize, usize) {
(self.precision_cache.len(), self.operation_cache.len())
}
}
impl Default for ConstantCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "constants_tests.rs"]
mod constants_tests;