1pub use crate::{
4    assign::AssignReason,
5    cdb::{Clause, ClauseDB, ClauseIF, ClauseId, ClauseIdIF},
6    config::Config,
7    primitive::{ema::*, luby::*},
8    solver::SolverEvent,
9};
10
11use std::{
12    cmp::Ordering,
13    fmt,
14    fs::File,
15    io::{BufRead, BufReader},
16    num::NonZeroU32,
17    ops::{Index, IndexMut, Not},
18    path::Path,
19};
20
21pub trait PropertyReference<I, O> {
26    fn refer(&self, key: I) -> &O;
27}
28
29pub trait PropertyDereference<I, O: Sized> {
30    fn derefer(&self, key: I) -> O;
31}
32
33pub trait LitIF {
35    fn as_bool(&self) -> bool;
37    fn vi(self) -> VarId;
39}
40
41pub trait ActivityIF<Ix> {
43    fn activity(&self, ix: Ix) -> f64;
45    fn set_activity(&mut self, ix: Ix, val: f64);
47    fn reward_at_analysis(&mut self, _ix: Ix) {
49        #[cfg(debug)]
50        todo!()
51    }
52    fn reward_at_assign(&mut self, _ix: Ix) {
54        #[cfg(debug)]
55        todo!()
56    }
57    fn reward_at_propagation(&mut self, _ix: Ix) {
59        #[cfg(debug)]
60        todo!()
61    }
62    fn reward_at_unassign(&mut self, _ix: Ix) {
64        #[cfg(debug)]
65        todo!()
66    }
67    fn update_activity_decay(&mut self, _decay: f64);
69    fn update_activity_tick(&mut self);
71}
72
73pub trait Instantiate {
85    fn instantiate(conf: &Config, cnf: &CNFDescription) -> Self;
87    fn handle(&mut self, _e: SolverEvent) {}
89}
90
91pub trait Delete<T> {
93    fn delete_unstable<F>(&mut self, filter: F)
95    where
96        F: FnMut(&T) -> bool;
97}
98
99pub type VarId = usize;
103
104pub type DecisionLevel = u32;
106
107#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
126pub struct Lit {
127    ordinal: NonZeroU32,
129}
130
131impl fmt::Display for Lit {
132    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133        write!(f, "{}L", i32::from(self))
134    }
135}
136
137impl fmt::Debug for Lit {
138    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139        write!(f, "{}L", i32::from(self))
140    }
141}
142
143pub fn i32s(v: &[Lit]) -> Vec<i32> {
145    v.iter().map(|l| i32::from(*l)).collect::<Vec<_>>()
146}
147
148impl From<(VarId, bool)> for Lit {
149    #[inline]
150    fn from((vi, b): (VarId, bool)) -> Self {
151        Lit {
152            ordinal: unsafe { NonZeroU32::new_unchecked(((vi as u32) << 1) + (b as u32)) },
153        }
154    }
155}
156
157impl From<usize> for Lit {
158    #[inline]
159    fn from(l: usize) -> Self {
160        Lit {
161            ordinal: unsafe { NonZeroU32::new_unchecked(l as u32) },
162        }
163    }
164}
165
166impl From<u32> for Lit {
167    #[inline]
168    fn from(l: u32) -> Self {
169        Lit {
170            ordinal: unsafe { NonZeroU32::new_unchecked(l) },
171        }
172    }
173}
174
175impl From<i32> for Lit {
176    #[inline]
177    fn from(x: i32) -> Self {
178        Lit {
179            ordinal: unsafe {
180                NonZeroU32::new_unchecked((if x < 0 { -2 * x } else { 2 * x + 1 }) as u32)
181            },
182        }
183    }
184}
185
186impl From<ClauseId> for Lit {
187    #[inline]
188    fn from(cid: ClauseId) -> Self {
189        Lit {
190            ordinal: unsafe {
191                NonZeroU32::new_unchecked(NonZeroU32::get(cid.ordinal) & 0x7FFF_FFFF)
192            },
193        }
194    }
195}
196
197impl From<Lit> for bool {
198    #[inline]
201    fn from(l: Lit) -> bool {
202        (NonZeroU32::get(l.ordinal) & 1) != 0
203    }
204}
205
206impl From<Lit> for ClauseId {
207    #[inline]
208    fn from(l: Lit) -> ClauseId {
209        ClauseId {
210            ordinal: unsafe { NonZeroU32::new_unchecked(NonZeroU32::get(l.ordinal) | 0x8000_0000) },
211        }
212    }
213}
214
215impl From<Lit> for usize {
216    #[inline]
217    fn from(l: Lit) -> usize {
218        NonZeroU32::get(l.ordinal) as usize
219    }
220}
221
222impl From<Lit> for i32 {
223    #[inline]
224    fn from(l: Lit) -> i32 {
225        if NonZeroU32::get(l.ordinal) % 2 == 0 {
226            -((NonZeroU32::get(l.ordinal) >> 1) as i32)
227        } else {
228            (NonZeroU32::get(l.ordinal) >> 1) as i32
229        }
230    }
231}
232
233impl From<&Lit> for i32 {
234    #[inline]
235    fn from(l: &Lit) -> i32 {
236        if NonZeroU32::get(l.ordinal) % 2 == 0 {
237            -((NonZeroU32::get(l.ordinal) >> 1) as i32)
238        } else {
239            (NonZeroU32::get(l.ordinal) >> 1) as i32
240        }
241    }
242}
243
244impl Not for Lit {
245    type Output = Lit;
246    #[inline]
247    fn not(self) -> Self {
248        Lit {
249            ordinal: unsafe { NonZeroU32::new_unchecked(NonZeroU32::get(self.ordinal) ^ 1) },
250        }
251    }
252}
253
254impl Index<Lit> for [bool] {
255    type Output = bool;
256    #[inline]
257    fn index(&self, l: Lit) -> &Self::Output {
258        #[cfg(feature = "unsafe_access")]
259        unsafe {
260            self.get_unchecked(usize::from(l))
261        }
262        #[cfg(not(feature = "unsafe_access"))]
263        &self[usize::from(l)]
264    }
265}
266
267impl IndexMut<Lit> for [bool] {
268    #[inline]
269    fn index_mut(&mut self, l: Lit) -> &mut Self::Output {
270        #[cfg(feature = "unsafe_access")]
271        unsafe {
272            self.get_unchecked_mut(usize::from(l))
273        }
274        #[cfg(not(feature = "unsafe_access"))]
275        &mut self[usize::from(l)]
276    }
277}
278
279impl Index<Lit> for Vec<bool> {
280    type Output = bool;
281    #[inline]
282    fn index(&self, l: Lit) -> &Self::Output {
283        #[cfg(feature = "unsafe_access")]
284        unsafe {
285            self.get_unchecked(usize::from(l))
286        }
287        #[cfg(not(feature = "unsafe_access"))]
288        &self[usize::from(l)]
289    }
290}
291
292impl IndexMut<Lit> for Vec<bool> {
293    #[inline]
294    fn index_mut(&mut self, l: Lit) -> &mut Self::Output {
295        #[cfg(feature = "unsafe_access")]
296        unsafe {
297            self.get_unchecked_mut(usize::from(l))
298        }
299        #[cfg(not(feature = "unsafe_access"))]
300        &mut self[usize::from(l)]
301    }
302}
303
304impl LitIF for Lit {
320    #[inline]
321    fn as_bool(&self) -> bool {
322        NonZeroU32::get(self.ordinal) & 1 == 1
323    }
324    #[inline]
325    fn vi(self) -> VarId {
326        (NonZeroU32::get(self.ordinal) >> 1) as VarId
327    }
328}
329
330pub type ConflictContext = (Lit, AssignReason);
332
333pub type PropagationResult = Result<(), ConflictContext>;
335
336#[derive(Clone, Debug, Eq, PartialEq)]
340pub enum RefClause {
341    Clause(ClauseId),
342    Dead,
343    EmptyClause,
344    RegisteredClause(ClauseId),
345    UnitClause(Lit),
346}
347
348impl RefClause {
349    pub fn as_cid(&self) -> ClauseId {
350        match self {
351            RefClause::Clause(cid) => *cid,
352            RefClause::RegisteredClause(cid) => *cid,
353            _ => panic!("invalid reference to clause"),
354        }
355    }
356    pub fn is_new(&self) -> Option<ClauseId> {
357        match self {
358            RefClause::Clause(cid) => Some(*cid),
359            RefClause::RegisteredClause(_) => None,
360            RefClause::EmptyClause => None,
361            RefClause::Dead => None,
362            RefClause::UnitClause(_) => None,
363        }
364    }
365}
366
367#[derive(Debug, Eq, PartialEq)]
370pub enum SolverError {
371    EmptyClause,
375    InvalidLiteral,
378    IOError,
380    Inconsistent,
382    OutOfMemory,
383    RootLevelConflict(ConflictContext),
385    TimeOut,
386    SolverBug,
387    UndescribedError,
389}
390
391impl fmt::Display for SolverError {
392    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
393        write!(f, "{self:?}")
394    }
395}
396
397pub type MaybeInconsistent = Result<(), SolverError>;
399
400#[derive(Clone, Debug, Default)]
402pub enum CNFIndicator {
403    #[default]
405    Void,
406    File(String),
408    LitVec(usize),
410}
411
412impl fmt::Display for CNFIndicator {
413    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
414        match self {
415            CNFIndicator::Void => write!(f, "No CNF specified)"),
416            CNFIndicator::File(file) => write!(f, "CNF file({file})"),
417            CNFIndicator::LitVec(n) => write!(f, "A vec({n} clauses)"),
418        }
419    }
420}
421
422#[derive(Clone, Debug)]
434pub struct CNFDescription {
435    pub num_of_variables: usize,
436    pub num_of_clauses: usize,
437    pub pathname: CNFIndicator,
438}
439
440impl Default for CNFDescription {
441    fn default() -> CNFDescription {
442        CNFDescription {
443            num_of_variables: 0,
444            num_of_clauses: 0,
445            pathname: CNFIndicator::Void,
446        }
447    }
448}
449
450impl fmt::Display for CNFDescription {
451    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
452        let CNFDescription {
453            num_of_variables: nv,
454            num_of_clauses: nc,
455            pathname: path,
456        } = &self;
457        write!(f, "CNF({nv}, {nc}, {path})")
458    }
459}
460
461impl<V: AsRef<[i32]>> From<&[V]> for CNFDescription {
462    fn from(vec: &[V]) -> Self {
463        let num_of_variables = vec
464            .iter()
465            .map(|clause| clause.as_ref().iter().map(|l| l.abs()).max().unwrap_or(0))
466            .max()
467            .unwrap_or(0) as usize;
468        CNFDescription {
469            num_of_variables,
470            num_of_clauses: vec.len(),
471            pathname: CNFIndicator::LitVec(vec.len()),
472        }
473    }
474}
475
476#[derive(Debug)]
481pub struct CNFReader {
482    pub cnf: CNFDescription,
483    pub reader: BufReader<File>,
484}
485
486impl TryFrom<&Path> for CNFReader {
487    type Error = SolverError;
488    fn try_from(path: &Path) -> Result<Self, Self::Error> {
489        let pathname = if path.to_string_lossy().is_empty() {
490            "--".to_string()
491        } else {
492            Path::new(&path.to_string_lossy().into_owned())
493                .file_name()
494                .map_or("aStrangeNamed".to_string(), |f| {
495                    f.to_string_lossy().into_owned()
496                })
497        };
498        let fs = File::open(path).map_or(Err(SolverError::IOError), Ok)?;
499        let mut reader = BufReader::new(fs);
500        let mut buf = String::new();
501        let mut nv: usize = 0;
502        let mut nc: usize = 0;
503        let mut found_valid_header = false;
504        loop {
505            buf.clear();
506            match reader.read_line(&mut buf) {
507                Ok(0) => break,
508                Ok(_k) => {
509                    let mut iter = buf.split_whitespace();
510                    if iter.next() == Some("p") && iter.next() == Some("cnf") {
511                        if let Some(v) = iter.next().map(|s| s.parse::<usize>().ok().unwrap()) {
512                            if let Some(c) = iter.next().map(|s| s.parse::<usize>().ok().unwrap()) {
513                                nv = v;
514                                nc = c;
515                                found_valid_header = true;
516                                break;
517                            }
518                        }
519                    }
520                    continue;
521                }
522                Err(e) => {
523                    println!("{e}");
524                    return Err(SolverError::IOError);
525                }
526            }
527        }
528        if !found_valid_header {
529            return Err(SolverError::IOError);
530        }
531        let cnf = CNFDescription {
532            num_of_variables: nv,
533            num_of_clauses: nc,
534            pathname: CNFIndicator::File(pathname),
535        };
536        Ok(CNFReader { cnf, reader })
537    }
538}
539
540impl<T> Delete<T> for Vec<T> {
541    fn delete_unstable<F>(&mut self, filter: F)
542    where
543        F: FnMut(&T) -> bool,
544    {
545        if let Some(i) = self.iter().position(filter) {
546            self.swap_remove(i);
547        }
548    }
549}
550
551pub trait FlagIF {
553    type FlagType;
554    fn is(&self, flag: Self::FlagType) -> bool;
556    fn set(&mut self, f: Self::FlagType, b: bool);
558    fn toggle(&mut self, flag: Self::FlagType);
560    fn turn_off(&mut self, flag: Self::FlagType);
562    fn turn_on(&mut self, flag: Self::FlagType);
564}
565
566bitflags! {
567    #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
569    pub struct FlagClause: u8 {
570        const LEARNT       = 0b0000_0001;
572        const USED         = 0b0000_0010;
574        const ENQUEUED     = 0b0000_0100;
576        const OCCUR_LINKED = 0b0000_1000;
578        const DERIVE20     = 0b0001_0000;
580    }
581}
582
583bitflags! {
584    #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
586    pub struct FlagVar: u8 {
587        const PHASE        = 0b0000_0001;
589        const USED         = 0b0000_0010;
591        const ELIMINATED   = 0b0000_0100;
593        const ENQUEUED     = 0b0000_1000;
595        const CA_SEEN      = 0b0001_0000;
597
598        #[cfg(feature = "debug_propagation")]
599        const PROPAGATED   = 0b0010_0000;
601    }
602}
603
604#[derive(Debug, Default)]
605pub struct Logger {
606    dest: Option<File>,
607}
608
609impl fmt::Display for Logger {
610    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
611        write!(f, "Dump({:?})", self.dest)
612    }
613}
614
615impl Logger {
616    pub fn new<T: AsRef<str>>(fname: T) -> Self {
617        Logger {
618            dest: File::create(fname.as_ref()).ok(),
619        }
620    }
621    pub fn dump(&mut self, mes: String) {
622        use std::io::Write;
623        if let Some(f) = &mut self.dest {
624            f.write_all(&mes.into_bytes())
625                .unwrap_or_else(|_| panic!("fail to dump {f:?}"));
626        } else {
627            println!("{mes}");
628        }
629    }
630}
631
632#[derive(Clone, Debug)]
633pub struct OrderedProxy<T: Clone + Default + Sized + Ord> {
634    index: f64,
635    body: T,
636}
637
638impl<T: Clone + Default + Sized + Ord> Default for OrderedProxy<T> {
639    fn default() -> Self {
640        OrderedProxy {
641            index: 0.0,
642            body: T::default(),
643        }
644    }
645}
646
647impl<T: Clone + Default + Sized + Ord> PartialEq for OrderedProxy<T> {
648    fn eq(&self, other: &OrderedProxy<T>) -> bool {
649        self.index == other.index && self.body == other.body
650    }
651}
652
653impl<T: Clone + Default + Sized + Ord> Eq for OrderedProxy<T> {}
654
655impl<T: Clone + Default + PartialEq + Ord> PartialOrd for OrderedProxy<T> {
656    fn partial_cmp(&self, other: &OrderedProxy<T>) -> Option<Ordering> {
657        Some(self.cmp(other))
658    }
659}
660
661impl<T: Clone + Default + PartialEq + Ord> Ord for OrderedProxy<T> {
662    fn cmp(&self, other: &OrderedProxy<T>) -> Ordering {
663        if (self.index - other.index).abs() < f64::EPSILON {
664            self.body.cmp(&other.body)
665        } else if self.index < other.index {
666            Ordering::Less
667        } else {
668            Ordering::Greater
669        }
670    }
671}
672
673impl<T: Clone + Default + Sized + Ord> OrderedProxy<T> {
674    pub fn new(body: T, index: f64) -> Self {
675        OrderedProxy { index, body }
676    }
677    pub fn new_invert(body: T, rindex: f64) -> Self {
678        OrderedProxy {
679            index: -rindex,
680            body,
681        }
682    }
683    pub fn to(&self) -> T {
684        self.body.clone()
685    }
686    pub fn value(&self) -> f64 {
687        self.index
688    }
689}
690
691#[cfg(feature = "boundary_check")]
692#[derive(Clone, Debug, PartialEq, PartialOrd)]
693pub enum Propagate {
694    None,
695    CacheSatisfied(usize),
696    FindNewWatch(usize, Lit, Lit),
697    BecameUnit(usize, Lit),
698    EmitConflict(usize, Lit),
699    SandboxCacheSatisfied(usize),
700    SandboxFindNewWatch(usize, Lit, Lit),
701    SandboxBecameUnit(usize),
702    SandboxEmitConflict(usize, Lit),
703}
704
705#[cfg(feature = "boundary_check")]
706#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
707pub enum VarState {
708    AssertedSandbox(usize),
709    Assigned(usize),
710    AssignedSandbox(usize),
711    Propagated(usize),
712    Unassigned(usize),
713}
714
715#[cfg(test)]
716mod tests {
717    use super::*;
718    use std::path::Path;
719
720    #[test]
721    fn test_cnf() {
722        if let Ok(reader) = CNFReader::try_from(Path::new("cnfs/sample.cnf")) {
723            assert_eq!(reader.cnf.num_of_variables, 250);
724            assert_eq!(reader.cnf.num_of_clauses, 1065);
725        } else {
726            panic!("failed to load cnfs/sample.cnf");
727        }
728    }
729}