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]));
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]));
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> {
let nvars = self.ctx.nvars();
let len = self.length();
let mut result = BTreeMap::new();
for i in 0..len {
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;
}
let mut exp_u32: Vec<u32> = exp_u64.iter().map(|&e| e as u32).collect();
while exp_u32.last() == Some(&0) {
exp_u32.pop();
}
result.insert(exp_u32, coeff);
}
result
}
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 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();
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 {
debug_assert!(i < 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 exp_at(&mut self, i: usize) -> u32 {
debug_assert!(i < self.len());
unsafe {
super::ffi::fmpz_mpoly_factor_get_exp_si(
&mut self.inner,
i as super::ffi::slong,
self.ctx.as_ptr(),
) as u32
}
}
}
impl Drop for FlintMPolyFactor {
fn drop(&mut self) {
unsafe {
super::ffi::fmpz_mpoly_factor_clear(&mut self.inner, self.ctx.as_ptr());
}
}
}