use std::ffi::{CStr, c_int, c_void};
use crate::diagnostics::diag;
mod ffi {
use std::ffi::{c_char, c_double, c_int, c_longlong, c_ulong, c_void};
#[repr(C)]
pub(super) struct Solver {
_private: [u8; 0],
}
pub(super) type TerminateCb = extern "C" fn(state: *mut c_void) -> bool;
pub(super) type ClauseCb =
extern "C" fn(state: *mut c_void, lits: *const c_int, len: c_int) -> bool;
unsafe extern "C" {
pub(super) fn cadical_shim_new() -> *mut Solver;
pub(super) fn cadical_shim_delete(s: *mut Solver);
pub(super) fn cadical_shim_add(s: *mut Solver, lit: c_int);
pub(super) fn cadical_shim_assume(s: *mut Solver, lit: c_int);
pub(super) fn cadical_shim_constrain(s: *mut Solver, lit: c_int);
pub(super) fn cadical_shim_solve(s: *mut Solver) -> c_int;
pub(super) fn cadical_shim_simplify(s: *mut Solver, rounds: c_int) -> c_int;
pub(super) fn cadical_shim_val(s: *mut Solver, lit: c_int) -> c_int;
pub(super) fn cadical_shim_fixed(s: *mut Solver, lit: c_int) -> c_int;
pub(super) fn cadical_shim_flippable(s: *mut Solver, lit: c_int) -> bool;
pub(super) fn cadical_shim_phase(s: *mut Solver, lit: c_int);
pub(super) fn cadical_shim_freeze(s: *mut Solver, lit: c_int);
pub(super) fn cadical_shim_reserve(s: *mut Solver, min_max_var: c_int);
pub(super) fn cadical_shim_limit(s: *mut Solver, name: *const c_char, val: c_int) -> bool;
pub(super) fn cadical_shim_traverse_clauses(
s: *mut Solver,
cb: ClauseCb,
state: *mut c_void,
) -> bool;
pub(super) fn cadical_shim_connect_terminator(
s: *mut Solver,
cb: TerminateCb,
state: *mut c_void,
);
pub(super) fn cadical_shim_disconnect_terminator(s: *mut Solver);
pub(super) fn cadical_shim_redundant(s: *mut Solver) -> c_longlong;
pub(super) fn cadical_shim_irredundant(s: *mut Solver) -> c_longlong;
pub(super) fn cadical_shim_score_of(s: *mut Solver, lit: c_int) -> c_double;
pub(super) fn cadical_shim_search_stats(s: *mut Solver, out: *mut c_longlong, n: c_ulong);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
Unknown,
Satisfiable,
Unsatisfiable,
}
impl Status {
fn from_raw(v: c_int) -> Self {
match v {
10 => Status::Satisfiable,
20 => Status::Unsatisfiable,
_ => Status::Unknown,
}
}
}
pub trait Terminator {
fn terminated(&mut self) -> bool;
}
pub trait ClauseIterator {
fn clause(&mut self, clause: &[i32]) -> bool;
}
extern "C" fn terminate_trampoline<T: Terminator>(state: *mut c_void) -> bool {
unsafe { (*(state as *mut T)).terminated() }
}
extern "C" fn clause_trampoline<I: ClauseIterator>(
state: *mut c_void,
lits: *const c_int,
len: c_int,
) -> bool {
let slice: &[c_int] = if len <= 0 || lits.is_null() {
&[]
} else {
unsafe { std::slice::from_raw_parts(lits, len as usize) }
};
unsafe { (*(state as *mut I)).clause(slice) }
}
pub struct CaDiCal {
handle: *mut ffi::Solver,
}
pub(super) fn note_solver_unavailable(stage: &str, consequence: &str) {
diag!("[{stage}] no CaDiCaL solver — {consequence}");
}
impl CaDiCal {
pub fn new() -> Option<Self> {
let handle = unsafe { ffi::cadical_shim_new() };
if handle.is_null() {
None
} else {
Some(Self { handle })
}
}
pub fn add(&mut self, lit: i32) {
unsafe { ffi::cadical_shim_add(self.handle, lit) }
}
pub fn assume(&mut self, lit: i32) {
unsafe { ffi::cadical_shim_assume(self.handle, lit) }
}
pub fn constrain(&mut self, lit: i32) {
unsafe { ffi::cadical_shim_constrain(self.handle, lit) }
}
pub fn solve(&mut self) -> Status {
Status::from_raw(unsafe { ffi::cadical_shim_solve(self.handle) })
}
pub fn simplify(&mut self, rounds: i32) -> Status {
Status::from_raw(unsafe { ffi::cadical_shim_simplify(self.handle, rounds) })
}
pub fn val(&mut self, lit: i32) -> i32 {
unsafe { ffi::cadical_shim_val(self.handle, lit) }
}
pub fn fixed(&mut self, lit: i32) -> i32 {
unsafe { ffi::cadical_shim_fixed(self.handle, lit) }
}
pub fn flippable(&mut self, lit: i32) -> bool {
unsafe { ffi::cadical_shim_flippable(self.handle, lit) }
}
pub fn phase(&mut self, lit: i32) {
unsafe { ffi::cadical_shim_phase(self.handle, lit) }
}
pub fn freeze(&mut self, lit: i32) {
unsafe { ffi::cadical_shim_freeze(self.handle, lit) }
}
pub fn reserve(&mut self, min_max_var: i32) {
unsafe { ffi::cadical_shim_reserve(self.handle, min_max_var) }
}
pub fn limit(&mut self, name: &CStr, val: i32) -> bool {
unsafe { ffi::cadical_shim_limit(self.handle, name.as_ptr(), val) }
}
pub fn traverse_clauses<I: ClauseIterator>(&mut self, it: &mut I) -> bool {
unsafe {
ffi::cadical_shim_traverse_clauses(
self.handle,
clause_trampoline::<I>,
it as *mut I as *mut c_void,
)
}
}
pub fn redundant(&self) -> i64 {
unsafe { ffi::cadical_shim_redundant(self.handle) }
}
pub fn irredundant(&self) -> i64 {
unsafe { ffi::cadical_shim_irredundant(self.handle) }
}
pub fn score_of(&self, lit: i32) -> f64 {
unsafe { ffi::cadical_shim_score_of(self.handle, lit) }
}
pub fn search_stats(&self) -> SearchStats {
let mut slots = [0 as std::ffi::c_longlong; SearchStats::SLOTS];
unsafe {
ffi::cadical_shim_search_stats(
self.handle,
slots.as_mut_ptr(),
SearchStats::SLOTS as std::ffi::c_ulong,
)
};
SearchStats::from_slots(slots)
}
unsafe fn connect_terminator<T: Terminator>(&mut self, t: &mut T) {
unsafe {
ffi::cadical_shim_connect_terminator(
self.handle,
terminate_trampoline::<T>,
t as *mut T as *mut c_void,
)
}
}
fn disconnect_terminator(&mut self) {
unsafe { ffi::cadical_shim_disconnect_terminator(self.handle) }
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct SearchStats {
pub conflicts: i64,
pub decisions: i64,
pub propagations: i64,
pub restarts: i64,
pub learned_clauses: i64,
pub searched: i64,
}
impl SearchStats {
pub(crate) const SLOTS: usize = 6;
pub(crate) fn from_slots(v: [std::ffi::c_longlong; Self::SLOTS]) -> Self {
SearchStats {
conflicts: v[0],
decisions: v[1],
propagations: v[2],
restarts: v[3],
learned_clauses: v[4],
searched: v[5],
}
}
pub fn since(self, earlier: Self) -> Self {
SearchStats {
conflicts: self.conflicts.saturating_sub(earlier.conflicts).max(0),
decisions: self.decisions.saturating_sub(earlier.decisions).max(0),
propagations: self
.propagations
.saturating_sub(earlier.propagations)
.max(0),
restarts: self.restarts.saturating_sub(earlier.restarts).max(0),
learned_clauses: self
.learned_clauses
.saturating_sub(earlier.learned_clauses)
.max(0),
searched: self.searched.saturating_sub(earlier.searched).max(0),
}
}
}
pub struct Bounded<'s, T: Terminator> {
solver: &'s mut CaDiCal,
_term: Box<T>,
}
impl<'s, T: Terminator> Bounded<'s, T> {
pub fn new(solver: &'s mut CaDiCal, term: T) -> Self {
let mut term = Box::new(term);
unsafe { solver.connect_terminator(&mut *term) };
Bounded {
solver,
_term: term,
}
}
}
impl<T: Terminator> Drop for Bounded<'_, T> {
fn drop(&mut self) {
self.solver.disconnect_terminator();
}
}
impl<T: Terminator> std::ops::Deref for Bounded<'_, T> {
type Target = CaDiCal;
fn deref(&self) -> &CaDiCal {
&*self.solver
}
}
impl<T: Terminator> std::ops::DerefMut for Bounded<'_, T> {
fn deref_mut(&mut self) -> &mut CaDiCal {
&mut *self.solver
}
}
impl Drop for CaDiCal {
fn drop(&mut self) {
unsafe { ffi::cadical_shim_delete(self.handle) }
}
}