use std::borrow::Borrow;
use std::ffi::CStr;
use crate::CHOCO_BACKEND;
use crate::CHOCO_LIB;
use crate::Sealed;
use crate::SolverError;
use crate::model::Model;
use crate::utils::{Handle, HandleT, ModelObject, make_constraint_array};
use crate::variables::{BoolVar, IntVar};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EqualityOperator {
Eq,
Neq,
Lt,
Leq,
Gt,
Geq,
}
impl EqualityOperator {
#[must_use]
pub fn to_cstr(&self) -> &CStr {
match self {
EqualityOperator::Eq => c"=",
EqualityOperator::Neq => c"!=",
EqualityOperator::Lt => c"<",
EqualityOperator::Leq => c"<=",
EqualityOperator::Gt => c">",
EqualityOperator::Geq => c">=",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArithmeticOperator {
Sum,
Sub,
Mul,
Div,
}
impl ArithmeticOperator {
#[must_use]
pub fn to_cstr(&self) -> &CStr {
match self {
ArithmeticOperator::Sum => c"+",
ArithmeticOperator::Sub => c"-",
ArithmeticOperator::Mul => c"*",
ArithmeticOperator::Div => c"/",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstraintStatus {
FREE,
POSTED,
REIFIED,
}
impl TryFrom<i32> for ConstraintStatus {
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(ConstraintStatus::FREE),
1 => Ok(ConstraintStatus::POSTED),
2 => Ok(ConstraintStatus::REIFIED),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ESat {
True,
False,
Undefined,
}
pub trait ConstraintEquality<'model, Rhs> {
fn eq(self, other: Rhs) -> Constraint<'model>;
fn ne(self, other: Rhs) -> Constraint<'model>;
}
pub trait ArithmConstraint<'model, MOD, RES> {
fn modulo(&'model self, modulo: MOD, res: RES) -> Constraint<'model>;
}
pub struct Constraint<'model> {
handle: Handle,
model: &'model Model,
}
impl HandleT for Constraint<'_> {
fn get_raw_handle(&self) -> *mut std::os::raw::c_void {
self.handle.get_raw_handle()
}
}
impl<'model> ModelObject<'model> for Constraint<'model> {
fn get_model(&self) -> &'model Model {
self.model
}
}
impl<'model> Constraint<'model> {
pub(crate) unsafe fn new(handle: *mut std::os::raw::c_void, model: &'model Model) -> Self {
Constraint {
handle: Handle::new(handle),
model,
}
}
pub fn post(&self) -> Result<(), SolverError> {
if self.status() != ConstraintStatus::FREE {
Err(SolverError::NotFreeConstraint)
} else {
CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ConstraintApi_post(
backend.thread,
self.get_raw_handle(),
)
});
Ok(())
}
}
#[must_use]
pub fn reify(&self) -> BoolVar<'model> {
CHOCO_BACKEND.with(|backend|
unsafe {
let var_handle = CHOCO_LIB.Java_org_chocosolver_capi_ConstraintApi_reify(
backend.thread,
self.get_raw_handle(),
);
assert!(!var_handle.is_null(), "Failed to reify constraint");
BoolVar::from_raw_handle(var_handle, self.model)})
}
pub fn reify_with(&self, boolvar: &BoolVar<'model>) {
CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ConstraintApi_reify_with(
backend.thread,
self.get_raw_handle(),
boolvar.get_raw_handle(),
)
});
}
pub fn implies(&self, boolvar: &BoolVar<'model>) {
CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ConstraintApi_implies(
backend.thread,
self.get_raw_handle(),
boolvar.get_raw_handle(),
)
});
}
pub fn implied_by(&self, boolvar: &BoolVar<'model>) {
CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ConstraintApi_implied_by(
backend.thread,
self.get_raw_handle(),
boolvar.get_raw_handle(),
)
});
}
#[must_use]
pub fn is_satisfied(&self) -> ESat {
let state = CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ConstraintApi_is_satisfied(
backend.thread,
self.get_raw_handle(),
)
});
match state {
0 => ESat::False,
1 => ESat::True,
_ => ESat::Undefined,
}
}
#[must_use]
pub fn status(&self) -> ConstraintStatus {
let status_code = CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ConstraintApi_getStatus(
backend.thread,
self.get_raw_handle(),
)
});
match ConstraintStatus::try_from(status_code) {
Ok(status) => status,
Err(_) => panic!("Unknown constraint status code: {}", status_code),
}
}
pub fn if_then_else(
&self,
then_constraint: &Constraint<'model>,
else_constraint: &Constraint<'model>,
) {
CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ReificationApi_if_then_else(
backend.thread,
self.model.get_raw_handle(),
self.get_raw_handle(),
then_constraint.get_raw_handle(),
else_constraint.get_raw_handle(),
)
});
}
pub fn if_then(&self, then_constraint: &Constraint<'model>) {
CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ReificationApi_if_then(
backend.thread,
self.model.get_raw_handle(),
self.get_raw_handle(),
then_constraint.get_raw_handle(),
)
});
}
pub fn if_only_if(&self, constraint: &Constraint<'model>) {
CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_ReificationApi_if_only_if(
backend.thread,
self.model.get_raw_handle(),
self.get_raw_handle(),
constraint.get_raw_handle(),
)
});
}
}
unsafe trait IntoRawIntVarHandleT: Copy {
fn into_raw_ptr(self, model: &Model) -> *mut std::os::raw::c_void;
}
unsafe impl<'model, T: Borrow<IntVar<'model>>> IntoRawIntVarHandleT for &T {
fn into_raw_ptr(self, _model: &Model) -> *mut std::os::raw::c_void {
self.borrow().get_raw_handle()
}
}
unsafe impl IntoRawIntVarHandleT for i32 {
fn into_raw_ptr(self, model: &Model) -> *mut std::os::raw::c_void {
CHOCO_BACKEND.with(|backend| unsafe {
CHOCO_LIB.Java_org_chocosolver_capi_IntVarApi_intVar_i(
backend.thread,
model.get_raw_handle(),
self,
)
})
}
}
pub(crate) trait ConstraintArithmT<'model> {
fn create(&self) -> Constraint<'model>;
}
impl<'model, X: Borrow<IntVar<'model>>> ConstraintArithmT<'model> for (X, EqualityOperator, i32) {
fn create(&self) -> Constraint<'model> {
let int_var = self.0.borrow();
CHOCO_BACKEND.with(|backend|
unsafe {
let constraint_handle = CHOCO_LIB
.Java_org_chocosolver_capi_ConstraintApi_arithm_iv_cst(
backend.thread,
int_var.get_model().get_raw_handle(),
int_var.get_raw_handle(),
self.1.to_cstr().as_ptr().cast_mut(),
self.2,
);
assert!(
!constraint_handle.is_null(),
"Invalid parameters combination for arithm constraint. Please refer to the doc"
);
Constraint::new(constraint_handle, int_var.get_model())})
}
}
impl<'model, X: Borrow<IntVar<'model>>, Y: Borrow<IntVar<'model>>> ConstraintArithmT<'model>
for (X, EqualityOperator, Y)
{
fn create(&self) -> Constraint<'model> {
let x_var = self.0.borrow();
let y_var = self.2.borrow();
CHOCO_BACKEND.with(|backend|
unsafe {
let constraint_handle = CHOCO_LIB
.Java_org_chocosolver_capi_ConstraintApi_arithm_iv_iv(
backend.thread,
x_var.get_model().get_raw_handle(),
x_var.get_raw_handle(),
self.1.to_cstr().as_ptr().cast_mut(),
y_var.get_raw_handle(),
);
assert!(
!constraint_handle.is_null(),
"Invalid parameters combination for arithm constraint. Please refer to the doc"
);
Constraint::new(constraint_handle, x_var.get_model())})
}
}
impl<'model, X: Borrow<IntVar<'model>>, Y: IntoRawIntVarHandleT> ConstraintArithmT<'model>
for (X, EqualityOperator, Y, ArithmeticOperator, &IntVar<'model>)
{
fn create(&self) -> Constraint<'model> {
let x_var = self.0.borrow();
let yy = self.2.into_raw_ptr(x_var.get_model());
assert!(
!yy.is_null(),
"Failed to convert parameter to raw pointer for arithm constraint"
);
CHOCO_BACKEND.with(|backend|
unsafe {
let constraint_handle = CHOCO_LIB
.Java_org_chocosolver_capi_ConstraintApi_arithm_iv_iv_iv(
backend.thread,
x_var.get_model().get_raw_handle(),
x_var.get_raw_handle(),
self.1.to_cstr().as_ptr().cast_mut(),
yy,
self.3.to_cstr().as_ptr().cast_mut(),
self.4.get_raw_handle(),
);
assert!(
!constraint_handle.is_null(),
"Invalid parameters combination for arithm constraint. Please refer to the doc"
);
Constraint::new(constraint_handle, x_var.get_model())})
}
}
impl<'model, X: Borrow<IntVar<'model>>, Y: IntoRawIntVarHandleT> ConstraintArithmT<'model>
for (X, EqualityOperator, Y, ArithmeticOperator, i32)
{
fn create(&self) -> Constraint<'model> {
let x_var = self.0.borrow();
let yy = self.2.into_raw_ptr(x_var.get_model());
CHOCO_BACKEND.with(|backend|
unsafe {
let constraint_handle = CHOCO_LIB
.Java_org_chocosolver_capi_ConstraintApi_arithm_iv_iv_cst(
backend.thread,
x_var.get_model().get_raw_handle(),
x_var.get_raw_handle(),
self.1.to_cstr().as_ptr().cast_mut(),
yy,
self.3.to_cstr().as_ptr().cast_mut(),
self.4,
);
assert!(
!constraint_handle.is_null(),
"Invalid parameters combination for arithm constraint. Please refer to the doc");
Constraint::new(constraint_handle, x_var.get_model())
})
}
}
impl<'model, X: Borrow<IntVar<'model>>, Y: IntoRawIntVarHandleT> ConstraintArithmT<'model>
for (X, ArithmeticOperator, Y, EqualityOperator, &IntVar<'model>)
{
fn create(&self) -> Constraint<'model> {
let x_var = self.0.borrow();
let yy = self.2.into_raw_ptr(x_var.get_model());
assert!(
!yy.is_null(),
"Failed to convert parameter to raw pointer for arithm constraint"
);
CHOCO_BACKEND.with(|backend|
unsafe {
let constraint_handle =CHOCO_LIB
.Java_org_chocosolver_capi_ConstraintApi_arithm_iv_iv_iv(
backend.thread,
x_var.get_model().get_raw_handle(),
x_var.get_raw_handle(),
self.1.to_cstr().as_ptr().cast_mut(),
yy,
self.3.to_cstr().as_ptr().cast_mut(),
self.4.get_raw_handle(),
);
assert!(
!constraint_handle.is_null(),
"Invalid parameters combination for arithm constraint. Please refer to the doc");
Constraint::new(constraint_handle, x_var.get_model())
})
}
}
impl<'model, X: Borrow<IntVar<'model>>, Y: IntoRawIntVarHandleT> ConstraintArithmT<'model>
for (X, ArithmeticOperator, Y, EqualityOperator, i32)
{
fn create(&self) -> Constraint<'model> {
let x_var = self.0.borrow();
let yy = self.2.into_raw_ptr(x_var.get_model());
CHOCO_BACKEND.with(|backend|
unsafe {
let constraint_handle = CHOCO_LIB
.Java_org_chocosolver_capi_ConstraintApi_arithm_iv_iv_cst(
backend.thread,
x_var.get_model().get_raw_handle(),
x_var.get_raw_handle(),
self.1.to_cstr().as_ptr().cast_mut(),
yy,
self.3.to_cstr().as_ptr().cast_mut(),
self.4,
);
assert!(
!constraint_handle.is_null(),
"Invalid parameters combination for arithm constraint. Please refer to the doc");
Constraint::new(constraint_handle, x_var.get_model())
})
}
}
#[allow(private_bounds)]
pub trait ConstraintArrayLogicOps<'model>: Sealed {
fn and(self) -> Constraint<'model>;
fn or(self) -> Constraint<'model>;
}
impl<'model, Q: Borrow<Constraint<'model>> + Sealed> ConstraintArrayLogicOps<'model> for &[Q] {
fn and(self) -> Constraint<'model> {
let array_handle = make_constraint_array(self);
let model = self
.first()
.expect("Slice shall contains at least one element")
.borrow()
.get_model();
CHOCO_BACKEND.with(|backend|
unsafe {
let constraint_handle =CHOCO_LIB
.Java_org_chocosolver_capi_ConstraintApi_and_cs_cs(
backend.thread,
model.get_raw_handle(),
array_handle,
);
assert!(
!constraint_handle.is_null(),
"Failed to create AND constraint"
);
Constraint::new(constraint_handle, model)
})
}
fn or(self) -> Constraint<'model> {
let array_handle = make_constraint_array(self);
let model = self
.first()
.expect("Slice shall contains at least one element")
.borrow()
.get_model();
CHOCO_BACKEND.with(|backend|
unsafe {
let constraint_handle = CHOCO_LIB
.Java_org_chocosolver_capi_ConstraintApi_or_cs_cs(
backend.thread,
model.get_raw_handle(),
array_handle,
);
assert!(!constraint_handle.is_null(),
"Failed to create OR constraint"
);
Constraint::new(constraint_handle, model)
})
}
}
#[cfg(test)]
mod tests;