use super::ffi::{FmpzMPolyBuf, FmpzMPolyCtxBuf, FmpzMPolyFactorStruct};
use crate::flint::integer::FlintInteger;
use std::collections::BTreeMap;
use std::sync::Arc;
const ORD_LEX: std::ffi::c_int = 0;
pub struct FlintMPolyCtx {
buf: Box<FmpzMPolyCtxBuf>,
nvars: usize,
}
impl FlintMPolyCtx {
pub fn new(nvars: usize) -> Arc<Self> {
let mut buf = Box::new(FmpzMPolyCtxBuf([0u8; 608]));
crate::flint::note_thread_uses_flint();
unsafe {
super::ffi::fmpz_mpoly_ctx_init(buf.as_mut(), nvars as i64, ORD_LEX);
}
Arc::new(FlintMPolyCtx { buf, nvars })
}
pub fn nvars(&self) -> usize {
self.nvars
}
pub(super) fn as_ptr(&self) -> *const FmpzMPolyCtxBuf {
self.buf.as_ref() as *const _
}
}
impl Drop for FlintMPolyCtx {
fn drop(&mut self) {
unsafe {
super::ffi::fmpz_mpoly_ctx_clear(self.buf.as_mut());
}
}
}
pub struct FlintMPoly {
buf: Box<FmpzMPolyBuf>,
ctx: Arc<FlintMPolyCtx>,
}
unsafe impl Send for FlintMPoly {}
unsafe impl Sync for FlintMPoly {}
#[allow(dead_code)]
impl FlintMPoly {
pub fn new(ctx: Arc<FlintMPolyCtx>) -> Self {
let mut buf = Box::new(FmpzMPolyBuf([0u8; 40]));
crate::flint::note_thread_uses_flint();
unsafe {
super::ffi::fmpz_mpoly_init(buf.as_mut(), ctx.as_ptr());
}
FlintMPoly { buf, ctx }
}
pub(crate) fn as_ptr(&self) -> *const FmpzMPolyBuf {
self.buf.as_ref() as *const _
}
pub(crate) fn as_mut_ptr(&mut self) -> *mut FmpzMPolyBuf {
self.buf.as_mut() as *mut _
}
pub fn is_zero(&self) -> bool {
unsafe { super::ffi::fmpz_mpoly_is_zero(self.as_ptr(), self.ctx.as_ptr()) != 0 }
}
pub fn length(&self) -> usize {
unsafe { super::ffi::fmpz_mpoly_length(self.as_ptr(), self.ctx.as_ptr()) as usize }
}
pub fn push_term(&mut self, coeff: &rug::Integer, exp: &[u64]) {
let fz = FlintInteger::from_rug(coeff);
unsafe {
super::ffi::fmpz_mpoly_push_term_fmpz_ui(
self.as_mut_ptr(),
fz.inner_ptr(),
exp.as_ptr(),
self.ctx.as_ptr(),
);
}
}
pub fn finish(&mut self) {
unsafe {
super::ffi::fmpz_mpoly_sort_terms(self.as_mut_ptr(), self.ctx.as_ptr());
super::ffi::fmpz_mpoly_combine_like_terms(self.as_mut_ptr(), self.ctx.as_ptr());
}
}
pub fn gcd(&self, other: &FlintMPoly) -> Option<FlintMPoly> {
let mut g = FlintMPoly::new(Arc::clone(&self.ctx));
let ok = unsafe {
super::ffi::fmpz_mpoly_gcd(
g.as_mut_ptr(),
self.as_ptr(),
other.as_ptr(),
self.ctx.as_ptr(),
)
};
if ok != 0 {
Some(g)
} else {
None
}
}
pub fn resultant(&self, other: &FlintMPoly, var_idx: usize) -> Option<FlintMPoly> {
let mut r = FlintMPoly::new(Arc::clone(&self.ctx));
let ok = unsafe {
super::ffi::fmpz_mpoly_resultant(
r.as_mut_ptr(),
self.as_ptr(),
other.as_ptr(),
var_idx as super::ffi::slong,
self.ctx.as_ptr(),
)
};
if ok != 0 {
Some(r)
} else {
None
}
}
pub fn terms(&self) -> BTreeMap<Vec<u32>, rug::Integer> {
self.try_terms()
.unwrap_or_else(|| crate::poly::exponent::overflow_panic())
}
pub(crate) fn try_terms(&self) -> Option<BTreeMap<Vec<u32>, rug::Integer>> {
let wide = self.terms_u64()?;
let mut result = BTreeMap::new();
for (exp, coeff) in wide {
let exp: Option<Vec<u32>> = exp.into_iter().map(|e| u32::try_from(e).ok()).collect();
result.insert(exp?, coeff);
}
Some(result)
}
pub(crate) fn terms_u64(&self) -> Option<BTreeMap<Vec<u64>, rug::Integer>> {
let nvars = self.ctx.nvars();
let len = self.length();
let mut result = BTreeMap::new();
for i in 0..len {
let fits = unsafe {
super::ffi::fmpz_mpoly_term_exp_fits_ui(self.as_ptr(), i as i64, self.ctx.as_ptr())
};
if fits == 0 {
return None;
}
let mut coeff_fz = FlintInteger::new();
let mut exp_u64 = vec![0u64; nvars];
unsafe {
super::ffi::fmpz_mpoly_get_term_coeff_fmpz(
coeff_fz.inner_mut_ptr(),
self.as_ptr(),
i as i64,
self.ctx.as_ptr(),
);
super::ffi::fmpz_mpoly_get_term_exp_ui(
exp_u64.as_mut_ptr(),
self.as_ptr(),
i as i64,
self.ctx.as_ptr(),
);
}
let coeff = coeff_fz.to_rug();
if coeff == 0 {
continue;
}
while exp_u64.last() == Some(&0) {
exp_u64.pop();
}
result.insert(exp_u64, coeff);
}
Some(result)
}
pub(crate) fn mul(&self, other: &FlintMPoly) -> FlintMPoly {
let mut r = FlintMPoly::new(Arc::clone(&self.ctx));
unsafe {
super::ffi::fmpz_mpoly_mul(
r.as_mut_ptr(),
self.as_ptr(),
other.as_ptr(),
self.ctx.as_ptr(),
);
}
r
}
pub(crate) fn add(&self, other: &FlintMPoly) -> FlintMPoly {
let mut r = FlintMPoly::new(Arc::clone(&self.ctx));
unsafe {
super::ffi::fmpz_mpoly_add(
r.as_mut_ptr(),
self.as_ptr(),
other.as_ptr(),
self.ctx.as_ptr(),
);
}
r
}
pub(crate) fn sub(&self, other: &FlintMPoly) -> FlintMPoly {
let mut r = FlintMPoly::new(Arc::clone(&self.ctx));
unsafe {
super::ffi::fmpz_mpoly_sub(
r.as_mut_ptr(),
self.as_ptr(),
other.as_ptr(),
self.ctx.as_ptr(),
);
}
r
}
pub(crate) fn pow_ui(&self, k: u64) -> Option<FlintMPoly> {
let mut r = FlintMPoly::new(Arc::clone(&self.ctx));
let ok = unsafe {
super::ffi::fmpz_mpoly_pow_ui(r.as_mut_ptr(), self.as_ptr(), k, self.ctx.as_ptr())
};
if ok != 0 {
Some(r)
} else {
None
}
}
pub fn divides(&self, divisor: &FlintMPoly) -> Option<FlintMPoly> {
let mut q = FlintMPoly::new(Arc::clone(&self.ctx));
let ok = unsafe {
super::ffi::fmpz_mpoly_divides(
q.as_mut_ptr(),
self.as_ptr(),
divisor.as_ptr(),
self.ctx.as_ptr(),
)
};
if ok != 0 {
Some(q)
} else {
None
}
}
pub fn ctx(&self) -> &Arc<FlintMPolyCtx> {
&self.ctx
}
}
impl Clone for FlintMPoly {
fn clone(&self) -> Self {
let mut r = FlintMPoly::new(Arc::clone(&self.ctx));
unsafe {
super::ffi::fmpz_mpoly_set(r.as_mut_ptr(), self.as_ptr(), self.ctx.as_ptr());
}
r
}
}
impl Drop for FlintMPoly {
fn drop(&mut self) {
unsafe {
super::ffi::fmpz_mpoly_clear(self.buf.as_mut(), self.ctx.as_ptr());
}
}
}
pub struct FlintMPolyFactor {
inner: FmpzMPolyFactorStruct,
ctx: Arc<FlintMPolyCtx>,
}
unsafe impl Send for FlintMPolyFactor {}
unsafe impl Sync for FlintMPolyFactor {}
impl FlintMPolyFactor {
pub fn new(ctx: Arc<FlintMPolyCtx>) -> Self {
let mut inner = std::mem::MaybeUninit::<FmpzMPolyFactorStruct>::uninit();
crate::flint::note_thread_uses_flint();
unsafe { super::ffi::fmpz_mpoly_factor_init(inner.as_mut_ptr(), ctx.as_ptr()) };
Self {
inner: unsafe { inner.assume_init() },
ctx,
}
}
pub fn factor(&mut self, poly: &FlintMPoly) -> bool {
unsafe {
super::ffi::fmpz_mpoly_factor(&mut self.inner, poly.as_ptr(), self.ctx.as_ptr()) != 0
}
}
pub fn constant_den_is_one(&self) -> bool {
unsafe { super::ffi::fmpz_cmp_ui(std::ptr::addr_of!(self.inner.constant_den), 1) == 0 }
}
pub fn unit(&self) -> FlintInteger {
let mut u = FlintInteger::new();
unsafe {
super::ffi::fmpz_mpoly_factor_get_constant_fmpz(
u.inner_mut_ptr(),
&self.inner,
self.ctx.as_ptr(),
);
}
u
}
pub fn len(&self) -> usize {
unsafe {
super::ffi::fmpz_mpoly_factor_length(&self.inner, self.ctx.as_ptr()).max(0) as usize
}
}
pub fn base_at(&self, i: usize) -> FlintMPoly {
assert!(
i < self.len(),
"factor index {i} out of range (len {})",
self.len()
);
let mut base = FlintMPoly::new(Arc::clone(&self.ctx));
unsafe {
super::ffi::fmpz_mpoly_factor_get_base(
base.as_mut_ptr(),
&self.inner,
i as super::ffi::slong,
self.ctx.as_ptr(),
);
}
base
}
pub fn canonical_order(&mut self) -> Vec<usize> {
let n = self.len();
let mut keyed = Vec::with_capacity(n);
for i in 0..n {
let Some(terms) = self.base_at(i).terms_u64() else {
return (0..n).collect();
};
keyed.push((i, terms, self.exp_at(i)));
}
keyed.sort_by(|a, b| {
crate::poly::factor::cmp_multivariate_terms(&a.1, &b.1).then(a.2.cmp(&b.2))
});
keyed.into_iter().map(|(i, _, _)| i).collect()
}
pub fn exp_at(&mut self, i: usize) -> u32 {
assert!(
i < self.len(),
"factor index {i} out of range (len {})",
self.len()
);
let e = unsafe {
super::ffi::fmpz_mpoly_factor_get_exp_si(
&mut self.inner,
i as super::ffi::slong,
self.ctx.as_ptr(),
)
};
u32::try_from(e).unwrap_or_else(|_| crate::poly::exponent::overflow_panic())
}
}
impl Drop for FlintMPolyFactor {
fn drop(&mut self) {
unsafe {
super::ffi::fmpz_mpoly_factor_clear(&mut self.inner, self.ctx.as_ptr());
}
}
}