use crate::core::{Expression, Number, Symbol};
use crate::simplify::Simplify;
pub fn is_polynomial(expr: &Expression, var: &Symbol) -> bool {
match expr {
Expression::Number(_) => true,
Expression::Symbol(s) => s == var || !expr.contains_variable(var),
Expression::Add(terms) => terms.iter().all(|t| is_polynomial(t, var)),
Expression::Mul(factors) => factors.iter().all(|f| is_polynomial(f, var)),
Expression::Pow(base, exp) => {
if let Expression::Symbol(s) = base.as_ref() {
if s == var {
matches!(exp.as_ref(), Expression::Number(Number::Integer(n)) if *n >= 0)
} else {
is_polynomial(base, var)
}
} else {
is_polynomial(base, var)
&& matches!(exp.as_ref(), Expression::Number(Number::Integer(n)) if *n >= 0)
}
}
_ => false,
}
}
pub fn polynomial_degree(expr: &Expression, var: &Symbol) -> i64 {
match expr {
Expression::Symbol(s) if s == var => 1,
Expression::Number(_) => 0,
Expression::Pow(base, exp) => {
if let (Expression::Symbol(s), Expression::Number(Number::Integer(e))) =
(base.as_ref(), exp.as_ref())
{
if s == var {
return *e;
}
}
0
}
Expression::Add(terms) => terms
.iter()
.map(|t| polynomial_degree(t, var))
.max()
.unwrap_or(0),
Expression::Mul(factors) => factors.iter().map(|f| polynomial_degree(f, var)).sum(),
_ => 0,
}
}
pub fn substitute_variable(expr: &Expression, var: &Symbol, value: &Expression) -> Expression {
match expr {
Expression::Symbol(s) if s == var => value.clone(),
Expression::Add(terms) => Expression::add(
terms
.iter()
.map(|t| substitute_variable(t, var, value))
.collect(),
),
Expression::Mul(factors) => Expression::mul(
factors
.iter()
.map(|f| substitute_variable(f, var, value))
.collect(),
),
Expression::Pow(base, exp) => Expression::pow(
substitute_variable(base, var, value),
substitute_variable(exp, var, value),
),
Expression::Function { name, args } => Expression::function(
name,
args.iter()
.map(|a| substitute_variable(a, var, value))
.collect(),
),
_ => expr.clone(),
}
}
pub fn factorial(n: i64) -> i64 {
if n <= 0 {
1
} else {
(1..=n).product()
}
}
pub fn try_extract_quadratic(expr: &Expression, var: &Symbol) -> Option<(Expression, Expression)> {
if let Expression::Add(terms) = expr {
let mut x_squared_coeff = None;
let mut x_coeff = Expression::integer(0);
let mut constant = Expression::integer(0);
for term in terms.iter() {
match term {
Expression::Pow(base, exp) => {
if let (Expression::Symbol(s), Expression::Number(Number::Integer(2))) =
(base.as_ref(), exp.as_ref())
{
if *s == *var && x_squared_coeff.is_none() {
x_squared_coeff = Some(Expression::integer(1));
continue;
}
}
return None;
}
Expression::Mul(factors) => {
let mut has_x_squared = false;
let mut has_x = false;
let mut coeff = Expression::integer(1);
for factor in factors.iter() {
if let Expression::Pow(base, exp) = factor {
if let (Expression::Symbol(s), Expression::Number(Number::Integer(2))) =
(base.as_ref(), exp.as_ref())
{
if *s == *var {
has_x_squared = true;
continue;
}
}
}
if let Expression::Symbol(s) = factor {
if *s == *var {
has_x = true;
continue;
}
}
coeff = Expression::mul(vec![coeff, factor.clone()]);
}
if has_x_squared && x_squared_coeff.is_none() {
x_squared_coeff = Some(coeff);
} else if has_x {
x_coeff = Expression::add(vec![x_coeff, coeff]);
} else {
constant = Expression::add(vec![constant, term.clone()]);
}
}
Expression::Symbol(s) if *s == *var => {
x_coeff = Expression::add(vec![x_coeff, Expression::integer(1)]);
}
_ => {
constant = Expression::add(vec![constant, term.clone()]);
}
}
}
if x_squared_coeff == Some(Expression::integer(1)) {
return Some((x_coeff.simplify(), constant.simplify()));
}
}
None
}