use malachite_bigint::BigInt;
use num_traits::Zero;
use crate::{lit::Lit, pb_constraint::Int, pb_term::PBTerm};
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct GeneralPBTerm<C>
where
C: Int,
{
pub coeff: C,
pub lit: Lit,
}
impl<C> GeneralPBTerm<C>
where
C: Int,
{
#[inline]
pub fn new(coeff: C, lit: Lit) -> Self {
GeneralPBTerm { coeff, lit }
}
#[inline]
pub fn change_negation(&mut self) {
self.lit.negate();
self.coeff = -self.coeff.clone();
}
#[inline]
pub fn add_with(&mut self, term: GeneralPBTerm<C>) -> C {
match (self.lit.is_negated(), term.lit.is_negated()) {
(false, false) | (true, true) => {
self.coeff += term.coeff;
Zero::zero()
}
_ => {
if self.coeff >= term.coeff {
self.coeff -= term.coeff.clone();
term.coeff
} else {
self.lit.negate();
self.coeff = term.coeff.clone() - self.coeff.clone();
term.coeff - self.coeff.clone()
}
}
}
}
#[inline]
pub fn multiply_to_i128(&self, factor: i128) -> GeneralPBTerm<i128> {
GeneralPBTerm::new(
TryInto::<i128>::try_into(self.coeff.clone()).ok().unwrap() * factor,
self.lit,
)
}
#[inline]
pub fn multiply_to_bigint(&self, factor: &BigInt) -> GeneralPBTerm<BigInt> {
GeneralPBTerm::new(self.coeff.clone().into() * factor, self.lit)
}
}
impl<C> PBTerm for GeneralPBTerm<C>
where
C: Int,
{
type CoeffType = C;
#[inline]
fn negate(&mut self) {
self.lit.negate();
}
#[inline]
fn get_lit(&self) -> Lit {
self.lit
}
#[inline]
fn get_coeff(&self) -> &C {
&self.coeff
}
#[inline]
fn set_coeff(&mut self, coeff: Self::CoeffType) {
self.coeff = coeff;
}
#[inline]
fn divide_round_up(&mut self, divisor: &Self::CoeffType) {
self.coeff = self.coeff.div_ceil(divisor);
}
}
impl From<GeneralPBTerm<i64>> for GeneralPBTerm<i128> {
#[inline]
fn from(value: GeneralPBTerm<i64>) -> Self {
GeneralPBTerm::new(value.coeff.into(), value.lit)
}
}
impl From<GeneralPBTerm<i64>> for GeneralPBTerm<BigInt> {
#[inline]
fn from(value: GeneralPBTerm<i64>) -> Self {
GeneralPBTerm::new(value.coeff.into(), value.lit)
}
}
impl From<GeneralPBTerm<i128>> for GeneralPBTerm<BigInt> {
#[inline]
fn from(value: GeneralPBTerm<i128>) -> Self {
GeneralPBTerm::new(value.coeff.into(), value.lit)
}
}
impl<N: Int> PartialOrd for GeneralPBTerm<N> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<N: Int> Ord for GeneralPBTerm<N> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.lit.cmp(&other.lit)
}
}