number_diff/simplify/
classification.rs

1use crate::{
2    Elementary::{self, *},
3    Error,
4};
5
6#[derive(Debug, PartialEq)]
7pub enum Category {
8    Exponential,
9    Polynomial,
10    Trigonometric,
11    Constant,
12    ClusterFuck,
13}
14
15impl Elementary {
16    pub fn classify(&self) -> Result<Category, Error> {
17        if self.is_constant() {
18            Ok(Category::Constant)
19        } else if self.is_exponential()? {
20            Ok(Category::Exponential)
21        } else if self.is_polynomial()? {
22            Ok(Category::Polynomial)
23        } else if self.is_trig() {
24            Ok(Category::Trigonometric)
25        } else {
26            Ok(Category::ClusterFuck)
27        }
28    }
29
30    // checks if the provided function is constant, that is, it contains no independent variable
31    // of the type f(x) = C
32    pub fn is_constant(&self) -> bool {
33        match self {
34            Sin(func) => func.is_constant(),
35            Cos(func) => func.is_constant(),
36            Tan(func) => func.is_constant(),
37
38            Sec(func) => func.is_constant(),
39            Csc(func) => func.is_constant(),
40            Cot(func) => func.is_constant(),
41
42            Asin(func) => func.is_constant(),
43            Acos(func) => func.is_constant(),
44            Atan(func) => func.is_constant(),
45
46            Sinh(func) => func.is_constant(),
47            Cosh(func) => func.is_constant(),
48            Tanh(func) => func.is_constant(),
49
50            // for the operations, both of their functions must be constant for the whole function
51            // to be considered constant
52            Add(func1, func2) => func1.is_constant() && func2.is_constant(),
53            Sub(func1, func2) => func1.is_constant() && func2.is_constant(),
54            Mul(func1, func2) => func1.is_constant() && func2.is_constant(),
55            Div(func1, func2) => func1.is_constant() && func2.is_constant(),
56            Pow(func1, func2) => func1.is_constant() && func2.is_constant(),
57            Log(func1, func2) => func1.is_constant() && func2.is_constant(),
58
59            Factorial(func) => func.is_constant(),
60
61            Gamma(func) => func.is_constant(),
62            Polygamma(func, _) => func.is_constant(),
63
64            Abs(func) => func.is_constant(),
65
66            Con(_) => true,
67            X => false,
68        }
69    }
70
71    // returns true if the function is of type f(x) = Cx
72    fn is_linear(&self) -> bool {
73        if let Mul(func1, func2) = self {
74            if (func1.is_constant() && (func2.is_linear() || func2.clone() == X.into()))
75                || (func2.is_constant() && (func1.is_linear() || func1.clone() == X.into()))
76            {
77                return true;
78            }
79        }
80        false
81    }
82
83    // returns true if the function is a constant digit f(x) = C, C ∈ ℤ
84    pub fn is_digit(&self) -> Result<bool, Error> {
85        if let Con(numb) = self {
86            if numb.fract() == 0.0 {
87                return Ok(true);
88            }
89        }
90        Ok(false)
91    }
92
93    // returns true if the function is of type f(x) = a^(cx)
94    pub fn is_exponential(&self) -> Result<bool, Error> {
95        if let Pow(base, exp) = self {
96            if base.is_constant() && exp.is_linear() {
97                return Ok(true);
98            }
99        }
100        if let Mul(func1, func2) = self {
101            if (func1.is_exponential()? && func2.is_constant())
102                || (func1.is_constant() && func2.is_exponential()?)
103            {
104                return Ok(true);
105            }
106        }
107
108        Ok(false)
109    }
110
111    // returns true if the function is of type f(x) = ax^n + bx^(n-1) + cx^(n-2) + ...
112    // (a, b, c, ... ∈ ℝ)
113    fn is_polynomial(&self) -> Result<bool, Error> {
114        if let Pow(base, exp) = self {
115            if base.clone().is_polynomial()? && exp.is_digit()? {
116                return Ok(true);
117            }
118        } else if self.clone() == X.into() {
119            return Ok(true);
120        } else if let Con(_) = self {
121            return Ok(true);
122        } else if let Mul(func1, func2) = self {
123            if func1.is_polynomial()? && func2.is_polynomial()? {
124                return Ok(true);
125            }
126        } else if let Add(func1, func2) = self {
127            if func1.is_polynomial()? && func2.is_polynomial()? {
128                return Ok(true);
129            }
130        } else if let Sub(func1, func2) = self {
131            if func1.is_polynomial()? && func2.is_polynomial()? {
132                return Ok(true);
133            }
134        } else if let Div(func1, func2) = self {
135            if func1.is_polynomial()? && func2.is_polynomial()? {
136                return Ok(true);
137            }
138        }
139        Ok(false)
140    }
141
142    // returns true if the function is of type trig * trig, constant * trig, trig^constant, or
143    // log constant (trig)
144    fn is_trig(&self) -> bool {
145        match self {
146            Sin(_) => true,
147            Cos(_) => true,
148            Tan(_) => true,
149
150            Sec(_) => true,
151            Csc(_) => true,
152            Cot(_) => true,
153
154            Asin(_) => true,
155            Acos(_) => true,
156            Atan(_) => true,
157
158            Sinh(_) => true,
159            Cosh(_) => true,
160            Tanh(_) => true,
161
162            Add(func1, func2) => func1.is_trig() && func2.is_trig(),
163            Sub(func1, func2) => func1.is_trig() && func2.is_trig(),
164            Mul(func1, func2) => {
165                (func1.is_trig() && func2.is_trig()) // trig * trig
166                    || (func1.is_trig() && func2.is_constant()) // trig * constant
167                    || (func2.is_trig() && func1.is_constant()) // constant * trig
168            }
169            Div(func1, func2) => {
170                (func1.is_trig() && func2.is_trig())
171                    || (func1.is_trig() && func2.is_constant())
172                    || (func2.is_trig() && func1.is_constant())
173            }
174            Pow(func1, func2) => func1.is_trig() && func2.is_constant(), // trig^constant
175            Log(func1, func2) => func1.is_constant() && func2.is_trig(), // log constant (trig)
176
177            Factorial(_) => false,
178
179            Gamma(_) => false,
180            Polygamma(_, _) => false,
181
182            Abs(_) => false,
183            Con(_) => false,
184            X => false,
185        }
186    }
187}