use crate::kernel::{ExprData, ExprId, ExprPool};
use rug::Integer;
pub fn is_zero_expr(expr: ExprId, pool: &ExprPool) -> bool {
match pool.get(expr) {
ExprData::Integer(n) => n.0 == 0,
ExprData::Rational(r) => r.0 == 0,
_ => false,
}
}
#[allow(dead_code)]
pub fn is_one_expr(expr: ExprId, pool: &ExprPool) -> bool {
match pool.get(expr) {
ExprData::Integer(n) => n.0 == 1,
_ => false,
}
}
pub fn is_free_of_subexpr(expr: ExprId, sub: ExprId, pool: &ExprPool) -> bool {
if expr == sub {
return false;
}
let children: Vec<ExprId> = pool.with(expr, |data| match data {
ExprData::Add(args) | ExprData::Mul(args) => args.clone(),
ExprData::Pow { base, exp } => vec![*base, *exp],
ExprData::Func { args, .. } => args.clone(),
_ => vec![],
});
children.iter().all(|&c| is_free_of_subexpr(c, sub, pool))
}
pub fn is_free_of(expr: ExprId, var: ExprId, pool: &ExprPool) -> bool {
is_free_of_subexpr(expr, var, pool)
}
pub fn poly_degree_in(expr: ExprId, var: ExprId, pool: &ExprPool) -> Option<u32> {
if expr == var {
return Some(1);
}
if is_free_of(expr, var, pool) {
return Some(0);
}
match pool.get(expr) {
ExprData::Add(args) => {
let mut max_d = 0u32;
for a in &args {
let d = poly_degree_in(*a, var, pool)?;
max_d = max_d.max(d);
}
Some(max_d)
}
ExprData::Mul(args) => {
let mut total = 0u32;
for a in &args {
let d = poly_degree_in(*a, var, pool)?;
total = total.checked_add(d)?;
}
Some(total)
}
ExprData::Pow { base, exp } if base == var => match pool.get(exp) {
ExprData::Integer(n) => {
let k: Option<u32> = n.0.to_u32();
k
}
_ => None,
},
ExprData::Pow { base, exp } if is_free_of(base, var, pool) => {
if is_free_of(exp, var, pool) {
Some(0)
} else {
None
}
}
_ => None,
}
}
pub fn as_integer(expr: ExprId, pool: &ExprPool) -> Option<i64> {
match pool.get(expr) {
ExprData::Integer(n) => n.0.to_i64(),
_ => None,
}
}
#[allow(dead_code)]
pub fn as_rational(expr: ExprId, pool: &ExprPool) -> Option<(i64, i64)> {
match pool.get(expr) {
ExprData::Rational(r) => {
let p = r.0.numer().to_i64()?;
let q = r.0.denom().to_i64()?;
Some((p, q))
}
ExprData::Integer(n) => {
let k = n.0.to_i64()?;
Some((k, 1))
}
_ => None,
}
}
#[allow(dead_code)]
pub fn extract_poly_coeffs(
expr: ExprId,
var: ExprId,
max_degree: u32,
pool: &ExprPool,
) -> Option<Vec<ExprId>> {
let deg = poly_degree_in(expr, var, pool)?;
if deg > max_degree {
return None;
}
let zero = pool.integer(0_i32);
let mut coeffs: Vec<ExprId> = vec![zero; (deg + 1) as usize];
fill_coeffs(expr, var, &mut coeffs, pool);
Some(coeffs)
}
fn fill_coeffs(expr: ExprId, var: ExprId, coeffs: &mut Vec<ExprId>, pool: &ExprPool) {
let n = coeffs.len() as u32;
let deg = poly_degree_in(expr, var, pool).unwrap_or(0);
if deg as usize >= coeffs.len() {
let extra = deg as usize + 1 - coeffs.len();
let zero = pool.integer(0_i32);
coeffs.extend(std::iter::repeat(zero).take(extra));
}
let _ = n;
fn add_to_slot(slot: &mut ExprId, term: ExprId, pool: &ExprPool) {
if is_zero_expr(*slot, pool) {
*slot = term;
} else {
*slot = pool.add(vec![*slot, term]);
}
}
match pool.get(expr) {
_ if is_free_of(expr, var, pool) => {
add_to_slot(&mut coeffs[0], expr, pool);
}
ExprData::Symbol { .. } if expr == var => {
while coeffs.len() < 2 {
coeffs.push(pool.integer(0_i32));
}
let one = pool.integer(1_i32);
add_to_slot(&mut coeffs[1], one, pool);
}
ExprData::Add(args) => {
for a in &args {
fill_coeffs(*a, var, coeffs, pool);
}
}
ExprData::Mul(args) => {
let (const_parts, var_parts): (Vec<ExprId>, Vec<ExprId>) =
args.iter().partition(|&&a| is_free_of(a, var, pool));
let c_factor = match const_parts.len() {
0 => pool.integer(1_i32),
1 => const_parts[0],
_ => pool.mul(const_parts),
};
if var_parts.len() == 1 {
let var_part = var_parts[0];
let mut sub_coeffs = vec![pool.integer(0_i32); coeffs.len()];
fill_coeffs(var_part, var, &mut sub_coeffs, pool);
while sub_coeffs.len() < coeffs.len() {
sub_coeffs.push(pool.integer(0_i32));
}
for (i, sc) in sub_coeffs.iter().enumerate() {
if i >= coeffs.len() {
coeffs.push(pool.integer(0_i32));
}
if !is_zero_expr(*sc, pool) {
let term = if is_one_expr(c_factor, pool) {
*sc
} else {
pool.mul(vec![c_factor, *sc])
};
add_to_slot(&mut coeffs[i], term, pool);
}
}
}
}
ExprData::Pow { base, exp } if base == var => {
if let Some(k) = as_integer(exp, pool).and_then(|k| u32::try_from(k).ok()) {
while coeffs.len() <= k as usize {
coeffs.push(pool.integer(0_i32));
}
let one = pool.integer(1_i32);
add_to_slot(&mut coeffs[k as usize], one, pool);
}
}
_ => {
if is_free_of(expr, var, pool) {
add_to_slot(&mut coeffs[0], expr, pool);
}
}
}
}
pub fn as_linear(expr: ExprId, var: ExprId, pool: &ExprPool) -> Option<(ExprId, ExprId)> {
use crate::poly::UniPoly;
let up = UniPoly::from_symbolic(expr, var, pool).ok()?;
let cs = up.coefficients();
let a_int = cs.get(1)?;
if *a_int == 0 {
return None;
}
let b_int = cs.first().cloned().unwrap_or_else(|| Integer::from(0));
let a = pool.integer(a_int.clone());
let b = pool.integer(b_int);
Some((a, b))
}
pub fn as_quadratic(
expr: ExprId,
var: ExprId,
pool: &ExprPool,
) -> Option<(ExprId, ExprId, ExprId)> {
use crate::poly::UniPoly;
let up = UniPoly::from_symbolic(expr, var, pool).ok()?;
let cs = up.coefficients();
let a_int = cs.get(2)?;
if *a_int == 0 {
return None;
}
let b_int = cs.get(1).cloned().unwrap_or_else(|| Integer::from(0));
let c_int = cs.first().cloned().unwrap_or_else(|| Integer::from(0));
let a = pool.integer(a_int.clone());
let b = pool.integer(b_int);
let c = pool.integer(c_int);
Some((a, b, c))
}
pub fn poly_int_coeffs(expr: ExprId, var: ExprId, pool: &ExprPool) -> Option<Vec<Integer>> {
use crate::poly::UniPoly;
let up = UniPoly::from_symbolic(expr, var, pool).ok()?;
Some(up.coefficients())
}