use crate::expr::TLExpr;
use crate::term::Term;
use crate::unification::{rename_vars, Substitution};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use super::literal::Literal;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Clause {
pub literals: Vec<Literal>,
}
impl Clause {
pub fn from_literals(literals: Vec<Literal>) -> Self {
let mut unique_lits: Vec<Literal> = literals.into_iter().collect();
unique_lits.sort_by(|a, b| {
let a_str = format!("{:?}", a);
let b_str = format!("{:?}", b);
a_str.cmp(&b_str)
});
unique_lits.dedup();
Clause {
literals: unique_lits,
}
}
pub fn empty() -> Self {
Clause { literals: vec![] }
}
pub fn unit(literal: Literal) -> Self {
Clause {
literals: vec![literal],
}
}
pub fn is_empty(&self) -> bool {
self.literals.is_empty()
}
pub fn is_unit(&self) -> bool {
self.literals.len() == 1
}
pub fn is_horn(&self) -> bool {
self.literals.iter().filter(|l| l.is_positive()).count() <= 1
}
pub fn len(&self) -> usize {
self.literals.len()
}
pub fn is_len_zero(&self) -> bool {
self.literals.is_empty()
}
pub fn free_vars(&self) -> HashSet<String> {
self.literals
.iter()
.flat_map(|lit| lit.free_vars())
.collect()
}
pub fn subsumes(&self, other: &Clause) -> bool {
if self.is_empty() {
return other.is_empty();
}
if self.literals.len() > other.literals.len() {
return false;
}
self.try_subsumption_matching(other).is_some()
}
fn try_subsumption_matching(&self, other: &Clause) -> Option<Substitution> {
static mut SUBSUME_COUNTER: usize = 0;
let counter = unsafe {
SUBSUME_COUNTER += 1;
SUBSUME_COUNTER
};
let renamed_self = self.rename_variables(&format!("_s{}", counter));
let renamed_vars: HashSet<String> = renamed_self.free_vars();
let mut subst = Substitution::empty();
for self_lit in &renamed_self.literals {
let mut found_match = false;
for other_lit in &other.literals {
if self_lit.polarity != other_lit.polarity {
continue;
}
if let Some(lit_mgu) = self_lit.try_one_way_match(&other_lit.atom, &renamed_vars) {
if let Ok(()) = subst.try_extend(&lit_mgu) {
found_match = true;
break;
}
}
}
if !found_match {
return None; }
}
Some(subst)
}
pub fn is_tautology(&self) -> bool {
for i in 0..self.literals.len() {
for j in (i + 1)..self.literals.len() {
if self.literals[i].is_complementary(&self.literals[j]) {
return true;
}
}
}
false
}
pub fn apply_substitution(&self, subst: &Substitution) -> Clause {
let new_literals = self
.literals
.iter()
.map(|lit| lit.apply_substitution(subst))
.collect();
Clause::from_literals(new_literals)
}
pub fn rename_variables(&self, suffix: &str) -> Clause {
let renamed_literals = self
.literals
.iter()
.map(|lit| self.rename_literal(lit, suffix))
.collect();
Clause::from_literals(renamed_literals)
}
fn rename_literal(&self, lit: &Literal, suffix: &str) -> Literal {
match &lit.atom {
TLExpr::Pred { name, args } => {
let renamed_args: Vec<Term> =
args.iter().map(|term| rename_vars(term, suffix)).collect();
Literal {
atom: TLExpr::Pred {
name: name.clone(),
args: renamed_args,
},
polarity: lit.polarity,
}
}
_ => lit.clone(),
}
}
}