pub(crate) mod constraint;
pub(crate) mod model;
pub(crate) mod solution;
pub(crate) mod solver;
pub(crate) mod utils;
pub(crate) mod variables;
pub use constraint::*;
pub use model::*;
pub use solution::*;
pub use solver::*;
pub use variables::*;
use choco_solver_sys::{graal_isolate_t, graal_isolatethread_t, libchoco_capi, library_filename};
use std::{
path::Path,
ptr,
sync::{LazyLock, Mutex, OnceLock},
};
use thiserror::Error;
#[cfg(target_os = "windows")]
const CHOCO_SOLVER_LIB_NAME: &str = "libchoco_capi";
#[cfg(not(target_os = "windows"))]
const CHOCO_SOLVER_LIB_NAME: &str = "choco_capi";
static DLL_FOLDER: OnceLock<Option<String>> = OnceLock::new();
static CHOCO_LIB: LazyLock<libchoco_capi> = LazyLock::new(|| {
let dll_folder = DLL_FOLDER.get_or_init(|| std::env::var("CHOCO_SOLVER_DLL_FOLDER").ok());
unsafe {
match dll_folder {
Some(folder) => {
libchoco_capi::new(Path::new(&folder).join(library_filename(CHOCO_SOLVER_LIB_NAME)))
.expect("Failed to load Choco C API library")
}
None => libchoco_capi::new(library_filename(CHOCO_SOLVER_LIB_NAME))
.expect("Failed to load Choco C API library"),
}
}
});
#[repr(transparent)]
struct IsolatePtr(Option<*mut graal_isolate_t>);
unsafe impl Send for IsolatePtr {}
static ISOLATE_PTR: Mutex<IsolatePtr> = Mutex::new(IsolatePtr(None));
thread_local! {
pub(crate) static CHOCO_BACKEND: ChocoBackend = {
unsafe {
let mut thread: *mut graal_isolatethread_t = ptr::null_mut();
let mut lock = ISOLATE_PTR
.lock()
.expect("Failed to acquire lock for creating GraalVM isolate");
match lock.0 {
None => {
let mut isolate: *mut graal_isolate_t = ptr::null_mut();
if CHOCO_LIB.graal_create_isolate(ptr::null_mut(), &mut isolate, &mut thread)
!= 0
{
panic!("graal_create_isolate error");
}
lock.0 = Some(isolate);
}
Some(isolate_ptr) => {
if CHOCO_LIB.graal_attach_thread(isolate_ptr, &mut thread) != 0 {
panic!("graal_create_isolate error");
}
}
}
ChocoBackend { thread }
}}}
#[derive(Error, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum SolverError {
#[error("Tried to post a not free constraint or reified constraint")]
NotFreeConstraint,
#[error("Found contradiction while propagating")]
FoundContradiction,
#[error("Cannot be converted to BoolVar: domain is not [0,1]")]
BoolVarConversionError,
#[error("unknown data store error")]
Unknown,
}
pub(crate) enum IntOrIntVar<'a, 'model> {
IntVar(&'a IntVar<'model>),
Int(i32),
}
impl<'a, 'model: 'a> From<&'a IntVar<'model>> for IntOrIntVar<'a, 'model> {
fn from(val: &'a IntVar<'model>) -> Self {
IntOrIntVar::IntVar(val)
}
}
impl From<i32> for IntOrIntVar<'_, '_> {
fn from(val: i32) -> Self {
IntOrIntVar::Int(val)
}
}
trait Sealed {}
impl Sealed for BoolVar<'_> {}
impl Sealed for IntVar<'_> {}
impl Sealed for Constraint<'_> {}
impl<Q> Sealed for &Q {}
impl<Q: Sealed> Sealed for &[Q] {}
pub struct ChocoBackend {
thread: *mut graal_isolatethread_t,
}
impl ChocoBackend {
pub fn set_dll_folder(dll_folder_path: String) -> Result<(), Option<String>> {
DLL_FOLDER.set(Some(dll_folder_path))
}
}
impl Drop for ChocoBackend {
fn drop(&mut self) {
unsafe {
CHOCO_LIB.graal_detach_thread(self.thread);
}
}
}
#[allow(private_bounds)]
pub trait ArrayEqualityConstraints<'model>: Sealed {
fn all_different(self) -> Constraint<'model>;
fn all_different_except_0(self) -> Constraint<'model>;
fn all_equal(self) -> Constraint<'model>;
fn not_all_equal(self) -> Constraint<'model>;
fn at_least_n_value<'a>(self, n_values: &'a IntVar<'model>, ac: bool) -> Constraint<'model>
where
'model: 'a;
fn at_most_n_value<'a>(self, n_values: &IntVar<'model>, strong: bool) -> Constraint<'model>
where
'model: 'a;
}