use super::super::risch::poly_rde::{degree, poly_deriv, trim, QPoly};
use super::super::risch::rational_rde::poly_gcd;
use super::residues::{residue_sum, Residue};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FindOrder {
Principal { order: u32 },
NonElementary,
NotDecided,
}
pub fn genus(n: usize, a: &QPoly) -> Option<usize> {
let a = trim(a.clone());
let m = degree(&a);
if m < 1 || n < 2 {
return None;
}
if degree(&poly_gcd(&a, &poly_deriv(&a))) > 0 {
return None;
}
let (m, n) = (m as i64, n as i64);
let g2 = m * n - m - n + 2 - gcd_i64(n, m);
if g2 < 0 || g2 % 2 != 0 {
return None;
}
Some((g2 / 2) as usize)
}
pub fn find_order(n: usize, a: &QPoly, divisor: &[Residue]) -> FindOrder {
if residue_sum(divisor) != 0 {
return FindOrder::NotDecided;
}
if divisor.iter().all(|r| r.value == 0) {
return FindOrder::Principal { order: 1 };
}
match genus(n, a) {
Some(0) => FindOrder::Principal { order: 1 },
Some(_) => FindOrder::NotDecided,
None => FindOrder::NotDecided,
}
}
fn gcd_i64(mut a: i64, mut b: i64) -> i64 {
a = a.abs();
b = b.abs();
while b != 0 {
let t = b;
b = a % b;
a = t;
}
a
}
#[cfg(test)]
mod tests {
use super::super::super::risch::alg_field::RatFn;
use super::super::residues::residue_divisor;
use super::*;
use rug::Rational;
fn qp(cs: &[i64]) -> QPoly {
cs.iter().map(|&c| Rational::from(c)).collect()
}
#[test]
fn genus_values() {
assert_eq!(genus(2, &qp(&[0, 1])), Some(0)); assert_eq!(genus(2, &qp(&[1, 0, 0, 1])), Some(1)); assert_eq!(genus(2, &qp(&[0, 1, 0, 0, 1])), Some(1)); assert_eq!(genus(2, &qp(&[1, 0, 0, 0, 0, 1])), Some(2)); assert_eq!(genus(3, &qp(&[0, 1])), Some(0)); assert_eq!(genus(3, &qp(&[1, 0, 1])), Some(1)); assert_eq!(genus(2, &qp(&[0, 0, 1])), None); }
#[test]
fn genus0_principal_order_one() {
let a = qp(&[0, 1]);
let h = vec![RatFn::int(0), RatFn::new(qp(&[1]), qp(&[0, -1, 1]))]; let div = residue_divisor(2, &a, &h);
assert_eq!(find_order(2, &a, &div), FindOrder::Principal { order: 1 });
}
#[test]
fn empty_divisor_principal() {
let a = qp(&[0, 1]);
let h = vec![RatFn::int(0), RatFn::new(qp(&[1]), qp(&[0, 1]))]; let div = residue_divisor(2, &a, &h);
assert!(matches!(
find_order(2, &a, &div),
FindOrder::Principal { order: 1 }
));
}
#[test]
fn genus1_not_decided() {
let a = qp(&[1, 0, 0, 1]); let h = vec![RatFn::int(0), RatFn::new(qp(&[1]), qp(&[-2, 1]))];
let div = residue_divisor(2, &a, &h);
assert_eq!(find_order(2, &a, &div), FindOrder::NotDecided);
}
}