use std::ffi::{c_int, c_void};
use rustsat::{
solvers::SolverResult,
types::{Assignment, Clause, Lit},
utils::from_raw_parts_maybe_null,
};
use crate::ffi;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct ClauseId(pub i64);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Conclusion {
Conflict,
Assumptions,
Constraint,
}
#[allow(unused_variables)]
pub trait TraceProof {
fn add_original_clause(
&mut self,
id: ClauseId,
redundant: bool,
clause: &CaDiCaLClause,
restored: bool,
) {
}
fn add_derived_clause(
&mut self,
id: ClauseId,
redundant: bool,
clause: &CaDiCaLClause,
antecedents: &[ClauseId],
) {
}
fn delete_clause(&mut self, id: ClauseId, redundant: bool, clause: &CaDiCaLClause) {}
fn weaken_minus(&mut self, id: ClauseId, clause: &CaDiCaLClause) {}
fn strengthen(&mut self, id: ClauseId) {}
fn report_status(&mut self, status: SolverResult, id: ClauseId) {}
fn finalize_clause(&mut self, id: ClauseId, clause: &CaDiCaLClause) {}
fn begin_proof(&mut self, id: ClauseId) {}
fn solve_query(&mut self) {}
fn add_assumption(&mut self, assumption: Lit) {}
fn add_constraint(&mut self, constraint: &CaDiCaLClause) {}
fn reset_assumptions(&mut self) {}
fn add_assumption_clause(
&mut self,
id: ClauseId,
clause: &CaDiCaLClause,
antecedents: &[ClauseId],
) {
}
fn conclude_unsat(&mut self, conclusion: Conclusion, failing: &[ClauseId]) {}
fn conclude_sat(&mut self, solution: &CaDiCaLAssignment) {}
fn conclude_unknown(&mut self, solution: &CaDiCaLAssignment) {}
fn notify_equivalence(&mut self, first: Lit, second: Lit) {}
}
#[derive(Debug)]
pub struct ProofTracerHandle<PT> {
c_class: *mut ffi::CCaDiCaLTracer,
tracer: *mut PT,
trait_ptr: *mut *mut dyn TraceProof,
}
impl<PT> Drop for ProofTracerHandle<PT> {
fn drop(&mut self) {
let trait_ptr = unsafe { Box::from_raw(self.trait_ptr) };
drop(trait_ptr);
let tracer = unsafe { Box::from_raw(self.tracer) };
drop(tracer);
}
}
#[derive(Clone, Copy, Debug, thiserror::Error)]
#[error("the provided proof tracer handle is not connected to the solver")]
pub struct ProofTracerNotConnected;
impl super::CaDiCaL<'_, '_> {
pub fn connect_proof_tracer<PT>(
&mut self,
tracer: PT,
antecedents: bool,
) -> ProofTracerHandle<PT>
where
PT: TraceProof + 'static,
{
let tracer = Box::new(tracer);
let tracer = Box::into_raw(tracer);
let trait_ptr = Box::new(tracer as *mut dyn TraceProof);
let trait_ptr = Box::into_raw(trait_ptr);
let ptr = unsafe {
ffi::ccadical_connect_proof_tracer(
self.handle,
trait_ptr.cast::<c_void>(),
ffi::prooftracer::DISPATCH_CALLBACKS,
antecedents,
)
};
ProofTracerHandle {
c_class: ptr,
tracer,
trait_ptr,
}
}
#[allow(clippy::needless_pass_by_value)]
pub fn disconnect_proof_tracer<PT>(
&mut self,
handle: ProofTracerHandle<PT>,
) -> Result<PT, ProofTracerNotConnected>
where
PT: TraceProof + 'static,
{
if !unsafe { ffi::ccadical_disconnect_proof_tracer(self.handle, handle.c_class) } {
return Err(ProofTracerNotConnected);
}
let trait_ptr = unsafe { Box::from_raw(handle.trait_ptr) };
drop(trait_ptr);
let tracer = unsafe { Box::from_raw(handle.tracer) };
let _ = std::mem::ManuallyDrop::new(handle);
Ok(*tracer)
}
#[allow(clippy::unused_self)]
#[allow(clippy::missing_panics_doc)]
pub fn proof_tracer_mut<PT>(&mut self, handle: &ProofTracerHandle<PT>) -> &mut PT
where
PT: TraceProof + 'static,
{
unsafe { handle.tracer.as_mut() }.expect("unexpected null ptr")
}
}
#[derive(Debug)]
pub struct CaDiCaLClause<'slf> {
lits: &'slf [c_int],
}
impl CaDiCaLClause<'_> {
pub(crate) fn new(cl_len: usize, cl_data: *const c_int) -> Self {
Self {
lits: unsafe { from_raw_parts_maybe_null(cl_data, cl_len) },
}
}
pub fn iter(&self) -> impl Iterator<Item = Lit> + '_ {
self.lits
.iter()
.map(|&lit| Lit::from_ipasir(lit).expect("got invalid literal from CaDiCaL"))
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.lits.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.lits.is_empty()
}
#[must_use]
pub fn to_owned(&self) -> Clause {
self.iter().collect()
}
}
#[cfg(feature = "pigeons")]
impl pigeons::ConstraintLike<rustsat::types::Var> for CaDiCaLClause<'_> {
fn rhs(&self) -> isize {
1
}
fn sum_iter(&self) -> impl Iterator<Item = (isize, pigeons::Axiom<rustsat::types::Var>)> {
self.iter().map(|l| (1, pigeons::Axiom::from(l)))
}
}
#[derive(Debug)]
pub struct CaDiCaLAssignment<'slf> {
lits: &'slf [c_int],
}
impl CaDiCaLAssignment<'_> {
pub(crate) fn new(assign_len: usize, assign_data: *const c_int) -> Self {
Self {
lits: unsafe { from_raw_parts_maybe_null(assign_data, assign_len) },
}
}
pub fn iter(&self) -> impl Iterator<Item = Lit> + '_ {
self.lits
.iter()
.map(|&lit| Lit::from_ipasir(lit).expect("got invalid literal from CaDiCaL"))
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.lits.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.lits.is_empty()
}
#[must_use]
pub fn to_owned(&self) -> Assignment {
self.iter().collect()
}
}
#[cfg(test)]
mod test {
use crate::CaDiCaL;
struct Tracer;
impl super::TraceProof for Tracer {}
#[test]
fn connect_tracer() {
let mut slv = CaDiCaL::default();
let handle = slv.connect_proof_tracer(Tracer, false);
let _ = slv.disconnect_proof_tracer(handle).unwrap();
}
}