use super::ffi::{FmpzMPolyBuf, FmpzMPolyCtxBuf};
use crate::flint::ffi::{fmpz, fmpz_clear, fmpz_init, fmpz_set_str};
use std::collections::BTreeMap;
use std::ffi::CString;
const ORD_LEX: std::ffi::c_int = 0;
pub struct FlintMPolyCtx {
buf: Box<FmpzMPolyCtxBuf>,
}
impl FlintMPolyCtx {
pub fn new(nvars: usize) -> Self {
let mut buf = Box::new(FmpzMPolyCtxBuf([0u8; 608]));
unsafe {
super::ffi::fmpz_mpoly_ctx_init(buf.as_mut(), nvars as i64, ORD_LEX);
}
FlintMPolyCtx { buf }
}
pub fn as_ptr(&self) -> *const FmpzMPolyCtxBuf {
self.buf.as_ref() as *const _
}
#[allow(dead_code)]
pub fn as_mut_ptr(&mut self) -> *mut FmpzMPolyCtxBuf {
self.buf.as_mut() as *mut _
}
}
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>,
}
impl FlintMPoly {
pub fn new(ctx: &FlintMPolyCtx) -> Self {
let mut buf = Box::new(FmpzMPolyBuf([0u8; 40]));
unsafe {
super::ffi::fmpz_mpoly_init(buf.as_mut(), ctx.as_ptr());
}
FlintMPoly { buf }
}
pub fn as_ptr(&self) -> *const FmpzMPolyBuf {
self.buf.as_ref() as *const _
}
pub fn as_mut_ptr(&mut self) -> *mut FmpzMPolyBuf {
self.buf.as_mut() as *mut _
}
#[allow(dead_code)]
pub fn is_zero(&self, ctx: &FlintMPolyCtx) -> bool {
unsafe { super::ffi::fmpz_mpoly_is_zero(self.as_ptr(), ctx.as_ptr()) != 0 }
}
pub fn length(&self, ctx: &FlintMPolyCtx) -> usize {
unsafe { super::ffi::fmpz_mpoly_length(self.as_ptr(), ctx.as_ptr()) as usize }
}
pub fn push_term(&mut self, coeff: &rug::Integer, exp: &[u64], ctx: &FlintMPolyCtx) {
let s = coeff.to_string_radix(10);
let cstr = CString::new(s).unwrap();
let mut fz: fmpz = 0;
unsafe {
fmpz_init(&mut fz);
fmpz_set_str(&mut fz, cstr.as_ptr(), 10);
super::ffi::fmpz_mpoly_push_term_fmpz_ui(
self.as_mut_ptr(),
&fz,
exp.as_ptr(),
ctx.as_ptr(),
);
fmpz_clear(&mut fz);
}
}
pub fn finish(&mut self, ctx: &FlintMPolyCtx) {
unsafe {
super::ffi::fmpz_mpoly_sort_terms(self.as_mut_ptr(), ctx.as_ptr());
super::ffi::fmpz_mpoly_combine_like_terms(self.as_mut_ptr(), ctx.as_ptr());
}
}
pub unsafe fn clear_with_ctx(&mut self, ctx: &FlintMPolyCtx) {
super::ffi::fmpz_mpoly_clear(self.as_mut_ptr(), ctx.as_ptr());
self.buf.0.fill(0);
}
pub fn resultant(
&self,
other: &FlintMPoly,
var_idx: usize,
ctx: &FlintMPolyCtx,
) -> Option<FlintMPoly> {
let mut r = FlintMPoly::new(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,
ctx.as_ptr(),
)
};
if ok != 0 {
Some(r)
} else {
None
}
}
pub fn gcd(&self, other: &FlintMPoly, ctx: &FlintMPolyCtx) -> Option<FlintMPoly> {
let mut g = FlintMPoly::new(ctx);
let ok = unsafe {
super::ffi::fmpz_mpoly_gcd(g.as_mut_ptr(), self.as_ptr(), other.as_ptr(), ctx.as_ptr())
};
if ok != 0 {
Some(g)
} else {
None
}
}
pub fn terms(&self, nvars: usize, ctx: &FlintMPolyCtx) -> BTreeMap<Vec<u32>, rug::Integer> {
let len = self.length(ctx);
let mut result = BTreeMap::new();
for i in 0..len {
let mut exp_u64 = vec![0u64; nvars];
let mut fz: fmpz = 0;
unsafe {
fmpz_init(&mut fz);
super::ffi::fmpz_mpoly_get_term_coeff_fmpz(
&mut fz,
self.as_ptr(),
i as i64,
ctx.as_ptr(),
);
super::ffi::fmpz_mpoly_get_term_exp_ui(
exp_u64.as_mut_ptr(),
self.as_ptr(),
i as i64,
ctx.as_ptr(),
);
}
let coeff = fmpz_to_rug(fz);
unsafe {
fmpz_clear(&mut fz);
}
let mut exp_u32: Vec<u32> = exp_u64.iter().map(|&e| e as u32).collect();
while exp_u32.last() == Some(&0) {
exp_u32.pop();
}
if coeff != 0 {
result.insert(exp_u32, coeff);
}
}
result
}
}
impl Drop for FlintMPoly {
fn drop(&mut self) {
self.buf.0.iter_mut().for_each(|b| *b = 0);
}
}
fn fmpz_to_rug(fz: fmpz) -> rug::Integer {
let s = fmpz_to_string(fz);
s.parse().unwrap_or(rug::Integer::from(0))
}
fn fmpz_to_string(fz: fmpz) -> String {
use std::ffi::CStr;
unsafe {
let ptr = super::ffi::fmpz_get_str(std::ptr::null_mut(), 10, &fz);
if ptr.is_null() {
return "0".to_string();
}
let s = CStr::from_ptr(ptr).to_string_lossy().into_owned();
super::ffi::flint_free(ptr as *mut _);
s
}
}