pub(super) mod decompose;
pub(super) mod genus_zero;
pub(super) mod poly_utils;
use crate::deriv::log::{DerivationLog, DerivedExpr, RewriteStep};
use crate::integrate::engine::IntegrationError;
use crate::kernel::{ExprData, ExprId, ExprPool};
use crate::simplify::engine::simplify;
use decompose::decompose_sqrt;
use genus_zero::integrate_with_sqrt;
use poly_utils::is_zero_expr;
pub fn contains_algebraic_subterm(expr: ExprId, pool: &ExprPool) -> bool {
match pool.get(expr) {
ExprData::Func { ref name, ref args } => {
if name == "sqrt" {
return true;
}
args.iter().any(|&a| contains_algebraic_subterm(a, pool))
}
ExprData::Pow { base, exp } => {
if matches!(pool.get(exp), ExprData::Rational(_)) {
return true;
}
contains_algebraic_subterm(base, pool)
}
ExprData::Add(args) | ExprData::Mul(args) => {
args.iter().any(|&a| contains_algebraic_subterm(a, pool))
}
_ => false,
}
}
fn collect_generators(expr: ExprId, pool: &ExprPool, out: &mut Vec<ExprId>) {
match pool.get(expr) {
ExprData::Func { ref name, ref args } => {
if name == "sqrt" && args.len() == 1 {
out.push(expr);
} else {
for &a in args.iter() {
collect_generators(a, pool, out);
}
}
}
ExprData::Pow { base, exp } => {
if matches!(pool.get(exp), ExprData::Rational(_)) {
out.push(expr);
} else {
collect_generators(base, pool, out);
}
}
ExprData::Add(args) | ExprData::Mul(args) => {
for &a in args.iter() {
collect_generators(a, pool, out);
}
}
_ => {}
}
}
fn get_radicand(expr: ExprId, pool: &ExprPool) -> Option<ExprId> {
match pool.get(expr) {
ExprData::Func { ref name, ref args } if name == "sqrt" && args.len() == 1 => Some(args[0]),
ExprData::Pow { base, exp } => {
match pool.get(exp) {
ExprData::Rational(r) if r.0 == rug::Rational::from((1u32, 2u32)) => Some(base),
_ => None,
}
}
_ => None,
}
}
fn find_generator(expr: ExprId, pool: &ExprPool) -> Option<(ExprId, ExprId)> {
let mut generators = Vec::new();
collect_generators(expr, pool, &mut generators);
generators.sort_unstable();
generators.dedup();
if generators.len() != 1 {
return None;
}
let sqrt_id = generators[0];
let radicand = get_radicand(sqrt_id, pool)?;
Some((sqrt_id, radicand))
}
pub fn integrate_algebraic(
expr: ExprId,
var: ExprId,
pool: &ExprPool,
) -> Result<DerivedExpr<ExprId>, IntegrationError> {
let mut log = DerivationLog::new();
let (sqrt_id, p_expr) = find_generator(expr, pool).ok_or_else(|| {
IntegrationError::NotImplemented(
"algebraic integrator requires exactly one sqrt(P(x)) generator; \
multiple or nested generators are not supported in v1.1"
.to_string(),
)
})?;
if let ExprData::Pow { exp, .. } = pool.get(sqrt_id) {
if let ExprData::Rational(r) = pool.get(exp) {
let q = r.0.denom().to_u32().unwrap_or(0);
if q != 2 {
return Err(IntegrationError::UnsupportedExtensionDegree(q));
}
}
}
let (a_raw, b_raw) = decompose_sqrt(expr, sqrt_id, p_expr, pool).ok_or_else(|| {
IntegrationError::NotImplemented(
"could not decompose integrand into A(x) + B(x)·sqrt(P(x)); \
expression structure is not supported"
.to_string(),
)
})?;
let a_part = simplify(a_raw, pool).value;
let b_part = simplify(b_raw, pool).value;
let zero = pool.integer(0_i32);
let int_a = if is_zero_expr(a_part, pool) {
zero
} else {
crate::integrate::engine::integrate_raw(a_part, var, pool, &mut log)?
};
let int_b = if is_zero_expr(b_part, pool) {
zero
} else {
integrate_with_sqrt(b_part, p_expr, sqrt_id, var, pool, &mut log)?
};
let raw = match (is_zero_expr(int_a, pool), is_zero_expr(int_b, pool)) {
(true, true) => zero,
(false, true) => int_a,
(true, false) => int_b,
(false, false) => pool.add(vec![int_a, int_b]),
};
let simplified = simplify(raw, pool);
log = log.merge(simplified.log);
log.push(RewriteStep::simple(
"algebraic_risch",
expr,
simplified.value,
));
Ok(DerivedExpr::with_log(simplified.value, log))
}