use super::poly_utils::{
as_integer, as_linear, as_quadratic, is_free_of, poly_degree_in, poly_int_coeffs,
};
use crate::deriv::log::{DerivationLog, RewriteStep};
use crate::integrate::engine::IntegrationError;
use crate::integrate::risch::alg_field::{AlgElem, RatFn};
use crate::integrate::risch::number_field::KElem;
use crate::integrate::risch::poly_rde::{
degree, expr_to_qpoly, poly_add, poly_deriv, poly_mul, poly_scale, qpoly_to_expr, trim, QPoly,
};
use crate::integrate::risch::rational_rde::{
expr_to_qrational, poly_divrem, poly_gcd, solve_rational_rde_generalized,
};
use crate::kernel::{ExprData, ExprId, ExprPool};
use rug::{Integer, Rational};
use super::find_order::{find_order_placed, FindOrder};
use super::jacobian_torsion::AlgPlace;
use super::residues::{
finite_residues_algebraic, residue_divisor_placed, residue_sum_complete, AlgResidue,
};
use super::trager_log::trager_log_criterion_alg;
pub fn integrate_with_sqrt(
b: ExprId,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
let p_deg = poly_degree_in(p, var, pool).unwrap_or(u32::MAX);
match p_deg {
0 => integrate_b_sqrt_const(b, p, sqrt_id, var, pool, log),
1 => integrate_b_sqrt_linear(b, p, sqrt_id, var, pool, log),
2 => integrate_b_sqrt_quadratic(b, p, sqrt_id, var, pool, log),
_ => {
let actual_deg = poly_int_coeffs(p, var, pool)
.map(|cs| cs.len().saturating_sub(1))
.unwrap_or(3);
if actual_deg <= 2 {
match actual_deg {
0 => integrate_b_sqrt_const(b, p, sqrt_id, var, pool, log),
1 => integrate_b_sqrt_linear(b, p, sqrt_id, var, pool, log),
2 => integrate_b_sqrt_quadratic(b, p, sqrt_id, var, pool, log),
_ => unreachable!(),
}
} else {
integrate_b_sqrt_high_degree(b, p, sqrt_id, var, pool, log)
}
}
}
}
fn integrate_b_sqrt_const(
b: ExprId,
_p: ExprId,
sqrt_id: ExprId,
var: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
let int_b = crate::integrate::engine::integrate_raw(b, var, pool, log)?;
let result = pool.mul(vec![sqrt_id, int_b]);
log.push(RewriteStep::simple("alg_sqrt_const", b, result));
Ok(result)
}
fn integrate_b_sqrt_linear(
b_expr: ExprId,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
let (a, c_const) = as_linear(p, var, pool).ok_or_else(|| {
IntegrationError::NotImplemented("linear radicand extraction failed".to_string())
})?;
match try_poly_b_linear(b_expr, p, sqrt_id, var, a, c_const, pool, log) {
Ok(result) => return Ok(result),
Err(IntegrationError::NotImplemented(_)) => {} Err(e) => return Err(e),
}
match try_rational_b_linear(b_expr, p, sqrt_id, var, a, c_const, pool, log) {
Ok(result) => return Ok(result),
Err(IntegrationError::NotImplemented(_)) => {} Err(e) => return Err(e),
}
Err(IntegrationError::NotImplemented(format!(
"∫ B(x)·sqrt(P(x)) with P linear: B = {} not handled",
pool.display(b_expr)
)))
}
#[allow(clippy::too_many_arguments)]
fn try_poly_b_linear(
b_expr: ExprId,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
a: ExprId, _c_const: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
let _deg = poly_degree_in(b_expr, var, pool).ok_or_else(|| {
IntegrationError::NotImplemented("B is not a polynomial in var".to_string())
})?;
let b_coeffs_int = poly_int_coeffs(b_expr, var, pool).ok_or_else(|| {
IntegrationError::NotImplemented("B coefficients not extractable as integers".to_string())
})?;
let a_int = as_integer(a, pool).ok_or_else(|| {
IntegrationError::NotImplemented("linear coefficient a is not an integer".to_string())
})?;
let p_coeffs_int = poly_int_coeffs(p, var, pool).ok_or_else(|| {
IntegrationError::NotImplemented("P coefficients not extractable".to_string())
})?;
let c_int = p_coeffs_int
.first()
.cloned()
.unwrap_or_else(|| rug::Integer::from(0));
if a_int == 0 {
return Err(IntegrationError::NotImplemented(
"degenerate linear P: a=0".to_string(),
));
}
use rug::Rational;
let mut terms: Vec<ExprId> = Vec::new();
for (k, b_k) in b_coeffs_int.iter().enumerate() {
if *b_k == 0 {
continue;
}
let k = k as i64;
let a_pow = a_int.pow(k as u32 + 1);
for j in 0..=(k as usize) {
let j = j as i64;
let binom = binomial_coeff(k as u64, j as u64);
let neg_c_pow = neg_c_power(&c_int, k - j);
let denom = a_pow * rug::Integer::from(2 * j + 3);
let numer = b_k.clone() * binom * neg_c_pow * 2;
if numer == 0 {
continue;
}
let coeff = Rational::from((numer, denom));
if coeff == 0 {
continue;
}
let p_pow_expr = if j + 1 == 1 {
p
} else {
pool.pow(p, pool.integer(j + 1))
};
let coeff_expr = pool.rational(coeff.numer().clone(), coeff.denom().clone());
let term = pool.mul(vec![coeff_expr, p_pow_expr]);
terms.push(term);
}
}
let q_expr = match terms.len() {
0 => pool.integer(0_i32),
1 => terms[0],
_ => pool.add(terms),
};
let result = pool.mul(vec![q_expr, sqrt_id]);
log.push(RewriteStep::simple("alg_poly_linear", b_expr, result));
Ok(result)
}
#[allow(clippy::too_many_arguments)]
fn try_rational_b_linear(
b_expr: ExprId,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
a: ExprId,
_c_const: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
if let ExprData::Pow { base, exp } = pool.get(b_expr) {
if base == p {
if let Some(n) = as_integer(exp, pool) {
let two_n3 = 2 * n + 3;
if two_n3 == 0 {
return Err(IntegrationError::NotImplemented(
"pole in algebraic integration (n = -3/2)".to_string(),
));
}
let denom = pool.mul(vec![a, pool.integer(two_n3)]);
let denom_inv = pool.pow(denom, pool.integer(-1_i32));
let p_n1 = p_integer_power(p, n + 1, pool);
let result = pool.mul(vec![pool.integer(2_i32), denom_inv, p_n1, sqrt_id]);
log.push(RewriteStep::simple("alg_p_power_linear", b_expr, result));
return Ok(result);
}
}
}
if let ExprData::Mul(args) = pool.get(b_expr) {
let (const_parts, p_parts): (Vec<ExprId>, Vec<ExprId>) =
args.iter().partition(|&&id| is_free_of(id, var, pool));
if p_parts.len() == 1 {
if let ExprData::Pow { base, exp } = pool.get(p_parts[0]) {
if base == p {
if let Some(n) = as_integer(exp, pool) {
let two_n3 = 2 * n + 3;
if two_n3 == 0 {
return Err(IntegrationError::NotImplemented(
"pole in algebraic integration (n = -3/2)".to_string(),
));
}
let const_factor = match const_parts.len() {
0 => pool.integer(1_i32),
1 => const_parts[0],
_ => pool.mul(const_parts),
};
let denom = pool.mul(vec![a, pool.integer(two_n3)]);
let denom_inv = pool.pow(denom, pool.integer(-1_i32));
let p_n1 = p_integer_power(p, n + 1, pool);
let result = pool.mul(vec![
pool.integer(2_i32),
const_factor,
denom_inv,
p_n1,
sqrt_id,
]);
log.push(RewriteStep::simple("alg_rational_linear", b_expr, result));
return Ok(result);
}
}
}
}
}
Err(IntegrationError::NotImplemented(
"rational B with linear P: unsupported form".to_string(),
))
}
fn p_integer_power(p: ExprId, k: i64, pool: &ExprPool) -> ExprId {
match k {
0 => pool.integer(1_i32),
1 => p,
_ => pool.pow(p, pool.integer(k)),
}
}
fn integrate_b_sqrt_quadratic(
b_expr: ExprId,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
let (a, b_coeff, c) = as_quadratic(p, var, pool).ok_or_else(|| {
IntegrationError::NotImplemented("quadratic radicand extraction failed".to_string())
})?;
if let Ok(result) = try_poly_b_quadratic(b_expr, p, sqrt_id, var, a, b_coeff, c, pool, log) {
return Ok(result);
}
if let Ok(result) = try_rational_b_quadratic(b_expr, p, sqrt_id, var, a, b_coeff, c, pool, log)
{
return Ok(result);
}
Err(IntegrationError::NotImplemented(format!(
"∫ B(x)·sqrt(quadratic): B = {} not handled",
pool.display(b_expr)
)))
}
fn j0_quadratic(
_p: ExprId,
sqrt_id: ExprId, var: ExprId,
a: ExprId,
b_coeff: ExprId,
pool: &ExprPool,
) -> ExprId {
let sqrt_a = pool.func("sqrt", vec![a]);
let two = pool.integer(2_i32);
let two_sqrt_a_sqrt_p = pool.mul(vec![two, sqrt_a, sqrt_id]);
let two_ax = pool.mul(vec![two, a, var]);
let inner = pool.add(vec![two_sqrt_a_sqrt_p, two_ax, b_coeff]);
let log_inner = pool.func("log", vec![inner]);
let sqrt_a_inv = pool.pow(sqrt_a, pool.integer(-1_i32));
pool.mul(vec![sqrt_a_inv, log_inner])
}
#[allow(clippy::too_many_arguments)]
fn try_poly_b_quadratic(
b_expr: ExprId,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
a: ExprId,
b_coeff: ExprId,
c: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
let deg = poly_degree_in(b_expr, var, pool).ok_or_else(|| {
IntegrationError::NotImplemented("B is not polynomial for quadratic P".to_string())
})?;
if deg == 0 {
let b_const = b_expr; let result = integrate_sqrt_quadratic_base(p, sqrt_id, var, a, b_coeff, c, pool);
let scaled = pool.mul(vec![b_const, result]);
log.push(RewriteStep::simple("alg_const_sqrt_quad", b_expr, scaled));
return Ok(scaled);
}
if deg == 1 {
let b_coeffs = poly_int_coeffs(b_expr, var, pool).ok_or_else(|| {
IntegrationError::NotImplemented("degree-1 B coefficients not extractable".to_string())
})?;
let e_int = b_coeffs
.first()
.cloned()
.unwrap_or_else(|| rug::Integer::from(0));
let d_int = b_coeffs
.get(1)
.cloned()
.unwrap_or_else(|| rug::Integer::from(0));
let e_expr = pool.integer(e_int);
let d_expr = pool.integer(d_int);
let int_sqrt_p = integrate_sqrt_quadratic_base(p, sqrt_id, var, a, b_coeff, c, pool);
let three_a = pool.mul(vec![pool.integer(3_i32), a]);
let three_a_inv = pool.pow(three_a, pool.integer(-1_i32));
let p_sqrt_p = pool.mul(vec![p, sqrt_id]);
let term1 = pool.mul(vec![three_a_inv, p_sqrt_p]);
let six_a = pool.mul(vec![pool.integer(6_i32), a]);
let six_a_inv = pool.pow(six_a, pool.integer(-1_i32));
let term2 = pool.mul(vec![pool.integer(-1_i32), b_coeff, six_a_inv, int_sqrt_p]);
let int_x_sqrt_p = pool.add(vec![term1, term2]);
let part_d = pool.mul(vec![d_expr, int_x_sqrt_p]);
let part_e = pool.mul(vec![e_expr, int_sqrt_p]);
let result = pool.add(vec![part_d, part_e]);
log.push(RewriteStep::simple("alg_linear_sqrt_quad", b_expr, result));
return Ok(result);
}
Err(IntegrationError::NotImplemented(format!(
"∫ polynomial(deg {deg}) · sqrt(quadratic): not yet implemented for deg > 1"
)))
}
fn integrate_sqrt_quadratic_base(
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
a: ExprId,
b_coeff: ExprId,
c: ExprId,
pool: &ExprPool,
) -> ExprId {
let two = pool.integer(2_i32);
let four = pool.integer(4_i32);
let eight = pool.integer(8_i32);
let two_ax = pool.mul(vec![two, a, var]);
let two_ax_plus_b = pool.add(vec![two_ax, b_coeff]);
let four_a = pool.mul(vec![four, a]);
let four_a_inv = pool.pow(four_a, pool.integer(-1_i32));
let term1 = pool.mul(vec![four_a_inv, two_ax_plus_b, sqrt_id]);
let four_ac = pool.mul(vec![four, a, c]);
let b2 = pool.pow(b_coeff, pool.integer(2_i32));
let neg_b2 = pool.mul(vec![pool.integer(-1_i32), b2]);
let discriminant = pool.add(vec![four_ac, neg_b2]);
let eight_a = pool.mul(vec![eight, a]);
let eight_a_inv = pool.pow(eight_a, pool.integer(-1_i32));
let j0 = j0_quadratic(p, sqrt_id, var, a, b_coeff, pool);
let term2 = pool.mul(vec![eight_a_inv, discriminant, j0]);
pool.add(vec![term1, term2])
}
#[allow(clippy::too_many_arguments)]
fn try_rational_b_quadratic(
b_expr: ExprId,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
a: ExprId,
b_coeff: ExprId,
c: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
if let ExprData::Pow { base, exp } = pool.get(b_expr) {
if base == p {
if let Some(n) = as_integer(exp, pool) {
return integrate_p_power_sqrt_quad(n, p, sqrt_id, var, a, b_coeff, c, pool, log);
}
}
}
if let ExprData::Mul(args) = pool.get(b_expr) {
let (const_parts, p_parts): (Vec<ExprId>, Vec<ExprId>) =
args.iter().partition(|&&id| is_free_of(id, var, pool));
if p_parts.len() == 1 {
if let ExprData::Pow { base, exp } = pool.get(p_parts[0]) {
if base == p {
if let Some(n) = as_integer(exp, pool) {
let const_factor = match const_parts.len() {
0 => pool.integer(1_i32),
1 => const_parts[0],
_ => pool.mul(const_parts),
};
let int_pn_sqrt = integrate_p_power_sqrt_quad(
n, p, sqrt_id, var, a, b_coeff, c, pool, log,
)?;
let result = pool.mul(vec![const_factor, int_pn_sqrt]);
return Ok(result);
}
}
}
}
}
Err(IntegrationError::NotImplemented(
"rational B with quadratic P: unsupported form".to_string(),
))
}
#[allow(clippy::too_many_arguments)]
fn integrate_p_power_sqrt_quad(
n: i64,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
a: ExprId,
b_coeff: ExprId,
c: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
match n {
-1 => {
let j0 = j0_quadratic(p, sqrt_id, var, a, b_coeff, pool);
log.push(RewriteStep::simple("alg_j0_quad", p, j0));
Ok(j0)
}
0 => {
let result = integrate_sqrt_quadratic_base(p, sqrt_id, var, a, b_coeff, c, pool);
log.push(RewriteStep::simple("alg_sqrt_quad_base", p, result));
Ok(result)
}
1 => {
let two = pool.integer(2_i32);
let two_ax = pool.mul(vec![two, a, var]);
let two_ax_b = pool.add(vec![two_ax, b_coeff]);
let eight_a = pool.mul(vec![pool.integer(8_i32), a]);
let eight_a_inv = pool.pow(eight_a, pool.integer(-1_i32));
let term1 = pool.mul(vec![eight_a_inv, two_ax_b, p, sqrt_id]);
let four_ac = pool.mul(vec![pool.integer(4_i32), a, c]);
let b2 = pool.pow(b_coeff, pool.integer(2_i32));
let neg_b2 = pool.mul(vec![pool.integer(-1_i32), b2]);
let d = pool.add(vec![four_ac, neg_b2]); let three_d = pool.mul(vec![pool.integer(3_i32), d]);
let sixteen_a = pool.mul(vec![pool.integer(16_i32), a]);
let sixteen_a_inv = pool.pow(sixteen_a, pool.integer(-1_i32));
let int_sqrt_p = integrate_sqrt_quadratic_base(p, sqrt_id, var, a, b_coeff, c, pool);
let term2 = pool.mul(vec![sixteen_a_inv, three_d, int_sqrt_p]);
let result = pool.add(vec![term1, term2]);
log.push(RewriteStep::simple("alg_p_3_2_quad", p, result));
Ok(result)
}
_ => Err(IntegrationError::NotImplemented(format!(
"∫ P^{n}·sqrt(P) with quadratic P: higher powers not implemented"
))),
}
}
fn binomial_coeff(n: u64, k: u64) -> rug::Integer {
if k > n {
return rug::Integer::from(0);
}
let k = k.min(n - k);
let mut result = rug::Integer::from(1u64);
for i in 0..k {
result *= rug::Integer::from(n - i);
result /= rug::Integer::from(i + 1);
}
result
}
fn integrate_b_sqrt_high_degree(
b: ExprId,
p: ExprId,
sqrt_id: ExprId,
var: ExprId,
pool: &ExprPool,
log: &mut DerivationLog,
) -> Result<ExprId, IntegrationError> {
let nonelem = |msg: &str| IntegrationError::NonElementary(msg.to_string());
let notimpl = |msg: &str| IntegrationError::NotImplemented(msg.to_string());
let p_poly = expr_to_qpoly(p, var, pool)
.ok_or_else(|| notimpl("radicand P is not a polynomial in the variable"))?;
let (b_num, b_den) = expr_to_qrational(b, var, pool)
.ok_or_else(|| notimpl("weight B is not a rational function"))?;
if degree(&p_poly) < 3 {
return Err(notimpl("expected deg P ≥ 3 here"));
}
let p_prime = poly_deriv(&p_poly);
let two_p = poly_scale(&p_poly, &Rational::from(2));
if let Some((q_num, q_den)) = solve_rational_rde_generalized(&p_prime, &two_p, &b_num, &b_den) {
let q_expr = qrational_to_expr(&q_num, &q_den, var, pool);
let result = pool.mul(vec![q_expr, sqrt_id]);
log.push(RewriteStep::simple("alg_integral_part_sqrt", b, result));
return Ok(result);
}
if degree(&poly_gcd(&p_poly, &p_prime)) > 0 {
return Err(notimpl("non-squarefree radicand at deg ≥ 3"));
}
let h: AlgElem = vec![RatFn::int(0), RatFn::new(b_num.clone(), b_den.clone())];
if residue_sum_complete(2, &p_poly, &h) != 0 {
return Err(notimpl(
"residue divisor incomplete (missing places not yet supported)",
));
}
let alg_res = finite_residues_algebraic(2, &p_poly, &h);
if alg_res.is_empty() {
let divisor = residue_divisor_placed(2, &p_poly, &h);
if divisor.is_empty() {
return Err(nonelem(
"∫ B·√P over a genus ≥ 1 curve with no logarithmic part and no \
algebraic primitive: non-elementary",
));
}
return match find_order_placed(2, &p_poly, &divisor) {
FindOrder::NonElementary => Err(nonelem(
"residue divisor is non-torsion (FIND-ORDER): no elementary log part",
)),
_ => Err(notimpl(
"genus ≥ 2 logarithmic part: torsion/undecided, log argument not \
yet constructible (Coates)",
)),
};
}
let verdict = if alg_res.iter().all(|r| r.conjugates == 1) {
try_genus2_alg_log(&p_poly, &h, &alg_res)
} else {
try_alg_base_log(&p_poly, &h, &alg_res)
};
match verdict {
Some(FindOrder::NonElementary) => Err(nonelem(
"Trager ℚ-basis criterion: a residue component is non-torsion ⇒ \
no elementary logarithmic part",
)),
_ => Err(notimpl(
"genus ≥ 2 logarithmic part with algebraic residues: not decided \
(torsion log not yet emittable, or out of the handled scope — \
non-Galois tower / base degree ≥ 3)",
)),
}
}
fn qmod_l(a: &QPoly, m: &QPoly) -> QPoly {
trim(poly_divrem(a, m).1)
}
fn try_alg_base_log(p: &QPoly, h: &AlgElem, alg_res: &[AlgResidue]) -> Option<FindOrder> {
if alg_res.len() != 1 || alg_res[0].conjugates != 2 || degree(&alg_res[0].minpoly) != 2 {
return None;
}
let ar = &alg_res[0];
let q_raw = trim(ar.minpoly.clone());
if degree(&q_raw) != 2 {
return None;
}
let lead = q_raw[2].clone();
let b = q_raw[1].clone() / &lead;
let c0 = q_raw[0].clone() / &lead;
let half_b = b / Rational::from(2);
let shift = -half_b.clone();
let m_val = half_b.clone() * &half_b - &c0; let q = vec![-m_val, Rational::from(0), Rational::from(1)]; let to_beta = |e: &QPoly| -> QPoly {
let e0 = e.first().cloned().unwrap_or_else(|| Rational::from(0));
let e1 = e.get(1).cloned().unwrap_or_else(|| Rational::from(0));
trim(vec![e0 + e1.clone() * &shift, e1])
};
let shift_x = |x: &QPoly| -> QPoly {
let mut v = x.clone();
if v.is_empty() {
v.push(shift.clone());
} else {
v[0] = v[0].clone() + &shift;
}
trim(v)
};
let c = to_beta(&trim(poly_divrem(p, &q_raw).1));
let r0_b = to_beta(&ar.r0);
let r1_b = to_beta(&ar.r1);
let r0 = &r0_b;
let r1 = &r1_b;
let (alg_places, alg_residues, dim) =
if let Some((m, a_in, w_in, autos)) = super::alg_tower::galois_quartic(&q, &c) {
let dim = (degree(&m).max(0) as usize).max(1); let rho = qmod_l(
&poly_add(
&super::alg_tower::compose_mod(r0, &a_in, &m),
&qmod_l(
&poly_mul(&super::alg_tower::compose_mod(r1, &a_in, &m), &w_in),
&m,
),
),
&m,
);
let mut places = Vec::new();
let mut residues = Vec::new();
for pi in &autos {
let mut rj = super::alg_tower::compose_mod(&rho, pi, &m);
rj.resize(dim, Rational::from(0));
places.push(AlgPlace {
minpoly: m.clone(),
x_coord: shift_x(&super::alg_tower::compose_mod(&a_in, pi, &m)),
y_coord: super::alg_tower::compose_mod(&w_in, pi, &m),
coeff: Integer::from(0),
orbit: false,
});
residues.push(rj);
}
(places, residues, dim)
} else {
let (ml, alpha, w, v) = super::alg_tower::quartic_closure(&q, &c)?;
let dim = (degree(&ml).max(0) as usize).max(1); let neg_alpha = poly_scale(&alpha, &Rational::from(-1));
let lin = |coef: &QPoly, a: &QPoly| {
qmod_l(
&poly_add(
&vec![coef.first().cloned().unwrap_or_else(|| Rational::from(0))],
&poly_scale(
a,
&coef.get(1).cloned().unwrap_or_else(|| Rational::from(0)),
),
),
&ml,
)
};
let r0a = lin(r0, &alpha);
let r1a = lin(r1, &alpha);
let r0n = lin(r0, &neg_alpha);
let r1n = lin(r1, &neg_alpha);
let mulm = |x: &QPoly, y: &QPoly| qmod_l(&poly_mul(x, y), &ml);
let sub = |a: &QPoly, b: &QPoly| poly_add(a, &poly_scale(b, &Rational::from(-1)));
let entries: [(QPoly, QPoly, QPoly); 4] = [
(alpha.clone(), w.clone(), poly_add(&r0a, &mulm(&r1a, &w))),
(
alpha.clone(),
poly_scale(&w, &Rational::from(-1)),
sub(&r0a, &mulm(&r1a, &w)),
),
(
neg_alpha.clone(),
v.clone(),
poly_add(&r0n, &mulm(&r1n, &v)),
),
(
neg_alpha.clone(),
poly_scale(&v, &Rational::from(-1)),
sub(&r0n, &mulm(&r1n, &v)),
),
];
let mut places = Vec::new();
let mut residues = Vec::new();
for (x, y, res) in entries {
places.push(AlgPlace {
minpoly: ml.clone(),
x_coord: shift_x(&x),
y_coord: y,
coeff: Integer::from(0),
orbit: false,
});
let mut rv = trim(res);
rv.resize(dim, Rational::from(0));
residues.push(rv);
}
(places, residues, dim)
};
let rat_div = residue_divisor_placed(2, p, h);
let rat_residues: Vec<KElem> = rat_div
.iter()
.map(|r| {
let mut v = vec![Rational::from(0); dim];
v[0] = r.residue.value.clone();
v
})
.collect();
Some(trager_log_criterion_alg(
2,
p,
&rat_div,
&rat_residues,
&alg_places,
&alg_residues,
dim,
))
}
fn try_genus2_alg_log(p: &QPoly, h: &AlgElem, alg_res: &[AlgResidue]) -> Option<FindOrder> {
let mut d_list: Vec<Integer> = Vec::new();
for ar in alg_res {
if ar.conjugates != 1 || degree(&ar.minpoly) != 1 {
return None; }
let alpha = -ar.minpoly[0].clone(); let a_at = eval_poly_q(p, &alpha);
if a_at == 0 {
return None; }
let d = squarefree_part_rat(&a_at);
if !d_list.contains(&d) {
d_list.push(d);
}
}
let dim = 1 + d_list.len();
let d_index = |d: &Integer| d_list.iter().position(|x| x == d).unwrap();
let rat_div = residue_divisor_placed(2, p, h);
let rat_residues: Vec<KElem> = rat_div
.iter()
.map(|r| {
let mut v = vec![Rational::from(0); dim];
v[0] = r.residue.value.clone();
v
})
.collect();
let mut alg_places: Vec<AlgPlace> = Vec::new();
let mut alg_residues: Vec<KElem> = Vec::new();
for ar in alg_res {
let alpha = -ar.minpoly[0].clone();
let a_at = eval_poly_q(p, &alpha);
let d = squarefree_part_rat(&a_at);
let d_rat = Rational::from(d.clone());
let theta_min = vec![-d_rat.clone(), Rational::from(0), Rational::from(1)]; let k = rat_sqrt(&(a_at / &d_rat))?; let r0 = ar.r0.first().cloned().unwrap_or_else(|| Rational::from(0));
let r1 = ar.r1.first().cloned().unwrap_or_else(|| Rational::from(0));
let idx = 1 + d_index(&d);
for sign in [Rational::from(1), Rational::from(-1)] {
alg_places.push(AlgPlace {
minpoly: theta_min.clone(),
x_coord: vec![alpha.clone()], y_coord: vec![Rational::from(0), sign.clone() * &k], coeff: Integer::from(0), orbit: false, });
let mut v = vec![Rational::from(0); dim];
v[0] = r0.clone();
v[idx] = sign.clone() * &r1 * &k;
alg_residues.push(v);
}
}
Some(trager_log_criterion_alg(
2,
p,
&rat_div,
&rat_residues,
&alg_places,
&alg_residues,
dim,
))
}
fn eval_poly_q(p: &QPoly, x: &Rational) -> Rational {
p.iter().rev().fold(Rational::from(0), |acc, c| acc * x + c)
}
fn squarefree_part_rat(r: &Rational) -> Integer {
let prod = r.numer().clone() * r.denom();
let sign = if prod < 0 {
Integer::from(-1)
} else {
Integer::from(1)
};
let mut m = prod.abs();
let mut sq = Integer::from(1);
let mut dd = Integer::from(2);
while Integer::from(&dd * &dd) <= m {
if m.is_divisible(&dd) {
let mut e = 0u32;
while m.is_divisible(&dd) {
m /= ⅆ
e += 1;
}
if e % 2 == 1 {
sq *= ⅆ
}
}
dd += 1;
}
sq *= &m; sq * sign
}
fn rat_sqrt(r: &Rational) -> Option<Rational> {
if *r < 0 {
return None;
}
let n = r.numer().clone();
let d = r.denom().clone();
let ns = n.clone().sqrt();
let ds = d.clone().sqrt();
if Integer::from(&ns * &ns) == n && Integer::from(&ds * &ds) == d {
Some(Rational::from((ns, ds)))
} else {
None
}
}
fn qrational_to_expr(num: &QPoly, den: &QPoly, var: ExprId, pool: &ExprPool) -> ExprId {
let n = qpoly_to_expr(num, var, pool);
if den.len() == 1 && den.first().map(|c| *c == 1).unwrap_or(false) {
return n;
}
let d = qpoly_to_expr(den, var, pool);
pool.mul(vec![n, pool.pow(d, pool.integer(-1_i32))])
}
fn neg_c_power(c: &rug::Integer, n: i64) -> rug::Integer {
if n == 0 {
return rug::Integer::from(1);
}
let base = rug::Integer::from(-1) * c;
if n > 0 {
let mut result = rug::Integer::from(1);
for _ in 0..n {
result *= &base;
}
result
} else {
rug::Integer::from(0)
}
}