use crate::clause::ClauseId;
use crate::literal::Lit;
#[allow(unused_imports)]
use crate::prelude::*;
use smallvec::SmallVec;
#[derive(Debug, Clone, Default)]
pub struct OccurrenceStats {
pub total_occurrences: usize,
pub active_literals: usize,
pub max_occurrences: usize,
pub avg_occurrences: f64,
}
impl OccurrenceStats {
pub fn display(&self) {
println!("Occurrence List Statistics:");
println!(" Total occurrences: {}", self.total_occurrences);
println!(" Active literals: {}", self.active_literals);
println!(" Max occurrences: {}", self.max_occurrences);
println!(" Avg occurrences: {:.1}", self.avg_occurrences);
}
}
#[derive(Debug)]
pub struct OccurrenceList {
occurrences: Vec<SmallVec<[ClauseId; 4]>>,
stats: OccurrenceStats,
}
impl Default for OccurrenceList {
fn default() -> Self {
Self::new()
}
}
impl OccurrenceList {
#[must_use]
pub fn new() -> Self {
Self {
occurrences: Vec::new(),
stats: OccurrenceStats::default(),
}
}
pub fn resize(&mut self, num_vars: usize) {
self.occurrences.resize(num_vars * 2, SmallVec::new());
}
pub fn add(&mut self, lit: Lit, clause_id: ClauseId) {
let idx = lit.code() as usize;
if idx >= self.occurrences.len() {
self.occurrences.resize(idx + 1, SmallVec::new());
}
if !self.occurrences[idx].contains(&clause_id) {
self.occurrences[idx].push(clause_id);
}
}
pub fn remove(&mut self, lit: Lit, clause_id: ClauseId) {
let idx = lit.code() as usize;
if idx < self.occurrences.len()
&& let Some(pos) = self.occurrences[idx].iter().position(|&id| id == clause_id)
{
self.occurrences[idx].swap_remove(pos);
}
}
#[must_use]
pub fn get(&self, lit: Lit) -> &[ClauseId] {
let idx = lit.code() as usize;
if idx < self.occurrences.len() {
&self.occurrences[idx]
} else {
&[]
}
}
#[must_use]
pub fn count(&self, lit: Lit) -> usize {
self.get(lit).len()
}
#[must_use]
pub fn has_occurrences(&self, lit: Lit) -> bool {
!self.get(lit).is_empty()
}
#[must_use]
pub fn var_occurrence_count(&self, var_idx: usize) -> usize {
if var_idx * 2 + 1 < self.occurrences.len() {
self.occurrences[var_idx * 2].len() + self.occurrences[var_idx * 2 + 1].len()
} else {
0
}
}
pub fn clear_literal(&mut self, lit: Lit) {
let idx = lit.code() as usize;
if idx < self.occurrences.len() {
self.occurrences[idx].clear();
}
}
pub fn clear(&mut self) {
for list in &mut self.occurrences {
list.clear();
}
self.stats = OccurrenceStats::default();
}
pub fn update_stats(&mut self) {
self.stats.total_occurrences = 0;
self.stats.active_literals = 0;
self.stats.max_occurrences = 0;
for list in &self.occurrences {
if !list.is_empty() {
self.stats.active_literals += 1;
self.stats.total_occurrences += list.len();
self.stats.max_occurrences = self.stats.max_occurrences.max(list.len());
}
}
if self.stats.active_literals > 0 {
self.stats.avg_occurrences =
self.stats.total_occurrences as f64 / self.stats.active_literals as f64;
} else {
self.stats.avg_occurrences = 0.0;
}
}
#[must_use]
pub fn stats(&self) -> &OccurrenceStats {
&self.stats
}
#[must_use]
pub fn find_common_clauses(&self, lits: &[Lit]) -> Vec<ClauseId> {
if lits.is_empty() {
return Vec::new();
}
let mut common: Vec<ClauseId> = self.get(lits[0]).to_vec();
for &lit in &lits[1..] {
let occurrences = self.get(lit);
common.retain(|id| occurrences.contains(id));
if common.is_empty() {
break;
}
}
common
}
#[must_use]
pub fn find_pure_literals(&self) -> Vec<Lit> {
let mut pure = Vec::new();
for var_idx in 0..self.occurrences.len() / 2 {
let pos_idx = var_idx * 2;
let neg_idx = var_idx * 2 + 1;
let pos_count = self.occurrences[pos_idx].len();
let neg_count = self.occurrences[neg_idx].len();
if pos_count > 0 && neg_count == 0 {
pure.push(Lit::from_code(pos_idx as u32));
} else if neg_count > 0 && pos_count == 0 {
pure.push(Lit::from_code(neg_idx as u32));
}
}
pure
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::literal::Var;
#[test]
fn test_occurrence_list_creation() {
let list = OccurrenceList::new();
assert_eq!(list.occurrences.len(), 0);
}
#[test]
fn test_add_and_get() {
let mut list = OccurrenceList::new();
list.resize(10);
let lit = Lit::pos(Var::new(0));
list.add(lit, ClauseId(1));
list.add(lit, ClauseId(2));
let occurrences = list.get(lit);
assert_eq!(occurrences.len(), 2);
assert!(occurrences.contains(&ClauseId(1)));
assert!(occurrences.contains(&ClauseId(2)));
}
#[test]
fn test_duplicate_add() {
let mut list = OccurrenceList::new();
list.resize(10);
let lit = Lit::pos(Var::new(0));
list.add(lit, ClauseId(1));
list.add(lit, ClauseId(1));
assert_eq!(list.count(lit), 1);
}
#[test]
fn test_remove() {
let mut list = OccurrenceList::new();
list.resize(10);
let lit = Lit::pos(Var::new(0));
list.add(lit, ClauseId(1));
list.add(lit, ClauseId(2));
list.remove(lit, ClauseId(1));
let occurrences = list.get(lit);
assert_eq!(occurrences.len(), 1);
assert!(occurrences.contains(&ClauseId(2)));
}
#[test]
fn test_count() {
let mut list = OccurrenceList::new();
list.resize(10);
let lit = Lit::pos(Var::new(0));
list.add(lit, ClauseId(1));
list.add(lit, ClauseId(2));
list.add(lit, ClauseId(3));
assert_eq!(list.count(lit), 3);
}
#[test]
fn test_has_occurrences() {
let mut list = OccurrenceList::new();
list.resize(10);
let lit = Lit::pos(Var::new(0));
assert!(!list.has_occurrences(lit));
list.add(lit, ClauseId(1));
assert!(list.has_occurrences(lit));
}
#[test]
fn test_var_occurrence_count() {
let mut list = OccurrenceList::new();
list.resize(10);
let pos_lit = Lit::pos(Var::new(0));
let neg_lit = Lit::neg(Var::new(0));
list.add(pos_lit, ClauseId(1));
list.add(pos_lit, ClauseId(2));
list.add(neg_lit, ClauseId(3));
assert_eq!(list.var_occurrence_count(0), 3);
}
#[test]
fn test_clear_literal() {
let mut list = OccurrenceList::new();
list.resize(10);
let lit = Lit::pos(Var::new(0));
list.add(lit, ClauseId(1));
list.add(lit, ClauseId(2));
list.clear_literal(lit);
assert_eq!(list.count(lit), 0);
}
#[test]
fn test_clear() {
let mut list = OccurrenceList::new();
list.resize(10);
list.add(Lit::pos(Var::new(0)), ClauseId(1));
list.add(Lit::neg(Var::new(1)), ClauseId(2));
list.clear();
assert_eq!(list.count(Lit::pos(Var::new(0))), 0);
assert_eq!(list.count(Lit::neg(Var::new(1))), 0);
}
#[test]
fn test_update_stats() {
let mut list = OccurrenceList::new();
list.resize(10);
list.add(Lit::pos(Var::new(0)), ClauseId(1));
list.add(Lit::pos(Var::new(0)), ClauseId(2));
list.add(Lit::neg(Var::new(1)), ClauseId(3));
list.update_stats();
let stats = list.stats();
assert_eq!(stats.total_occurrences, 3);
assert_eq!(stats.active_literals, 2);
assert_eq!(stats.max_occurrences, 2);
}
#[test]
fn test_find_common_clauses() {
let mut list = OccurrenceList::new();
list.resize(10);
let lit1 = Lit::pos(Var::new(0));
let lit2 = Lit::pos(Var::new(1));
list.add(lit1, ClauseId(1));
list.add(lit1, ClauseId(2));
list.add(lit2, ClauseId(2));
list.add(lit2, ClauseId(3));
let common = list.find_common_clauses(&[lit1, lit2]);
assert_eq!(common.len(), 1);
assert!(common.contains(&ClauseId(2)));
}
#[test]
fn test_find_pure_literals() {
let mut list = OccurrenceList::new();
list.resize(10);
list.add(Lit::pos(Var::new(0)), ClauseId(1));
list.add(Lit::pos(Var::new(0)), ClauseId(2));
list.add(Lit::pos(Var::new(1)), ClauseId(3));
list.add(Lit::neg(Var::new(1)), ClauseId(4));
list.add(Lit::neg(Var::new(2)), ClauseId(5));
let pure = list.find_pure_literals();
assert_eq!(pure.len(), 2);
assert!(pure.contains(&Lit::pos(Var::new(0))));
assert!(pure.contains(&Lit::neg(Var::new(2))));
}
#[test]
fn test_resize() {
let mut list = OccurrenceList::new();
list.resize(5);
assert!(list.occurrences.len() >= 10);
list.resize(10);
assert!(list.occurrences.len() >= 20); }
}