use super::functions::*;
#[allow(dead_code)]
pub struct TruthTableChecker {
pub num_vars: usize,
}
#[allow(dead_code)]
impl TruthTableChecker {
pub fn new(n: usize) -> Self {
Self { num_vars: n }
}
pub fn all_assignments(&self) -> Vec<Vec<bool>> {
let n = 1usize << self.num_vars;
(0..n)
.map(|mask| (0..self.num_vars).map(|i| (mask >> i) & 1 == 1).collect())
.collect()
}
pub fn is_tautology(&self, formula: &NnfFormula) -> bool {
self.all_assignments().iter().all(|a| formula.eval(a))
}
pub fn is_satisfiable(&self, formula: &NnfFormula) -> bool {
self.all_assignments().iter().any(|a| formula.eval(a))
}
pub fn is_contradiction(&self, formula: &NnfFormula) -> bool {
!self.is_satisfiable(formula)
}
pub fn find_satisfying_assignment(&self, formula: &NnfFormula) -> Option<Vec<bool>> {
self.all_assignments().into_iter().find(|a| formula.eval(a))
}
pub fn count_satisfying(&self, formula: &NnfFormula) -> usize {
self.all_assignments()
.iter()
.filter(|a| formula.eval(a))
.count()
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NnfFormula {
Atom(usize),
NegAtom(usize),
And(Box<NnfFormula>, Box<NnfFormula>),
Or(Box<NnfFormula>, Box<NnfFormula>),
Top,
Bot,
}
#[allow(dead_code)]
impl NnfFormula {
pub fn is_literal(&self) -> bool {
matches!(self, NnfFormula::Atom(_) | NnfFormula::NegAtom(_))
}
pub fn atom_count(&self) -> usize {
match self {
NnfFormula::Atom(_) | NnfFormula::NegAtom(_) => 1,
NnfFormula::And(a, b) | NnfFormula::Or(a, b) => a.atom_count() + b.atom_count(),
NnfFormula::Top | NnfFormula::Bot => 0,
}
}
pub fn connective_count(&self) -> usize {
match self {
NnfFormula::Atom(_) | NnfFormula::NegAtom(_) | NnfFormula::Top | NnfFormula::Bot => 0,
NnfFormula::And(a, b) | NnfFormula::Or(a, b) => {
1 + a.connective_count() + b.connective_count()
}
}
}
pub fn variables(&self) -> Vec<usize> {
let mut vars = Vec::new();
self.collect_vars(&mut vars);
vars.sort();
vars.dedup();
vars
}
fn collect_vars(&self, acc: &mut Vec<usize>) {
match self {
NnfFormula::Atom(i) | NnfFormula::NegAtom(i) => acc.push(*i),
NnfFormula::And(a, b) | NnfFormula::Or(a, b) => {
a.collect_vars(acc);
b.collect_vars(acc);
}
_ => {}
}
}
pub fn eval(&self, assignment: &[bool]) -> bool {
match self {
NnfFormula::Atom(i) => assignment.get(*i).copied().unwrap_or(false),
NnfFormula::NegAtom(i) => !assignment.get(*i).copied().unwrap_or(false),
NnfFormula::And(a, b) => a.eval(assignment) && b.eval(assignment),
NnfFormula::Or(a, b) => a.eval(assignment) || b.eval(assignment),
NnfFormula::Top => true,
NnfFormula::Bot => false,
}
}
pub fn simplify(self) -> NnfFormula {
match self {
NnfFormula::And(a, b) => {
let a = a.simplify();
let b = b.simplify();
match (&a, &b) {
(NnfFormula::Bot, _) | (_, NnfFormula::Bot) => NnfFormula::Bot,
(NnfFormula::Top, _) => b,
(_, NnfFormula::Top) => a,
_ => NnfFormula::And(Box::new(a), Box::new(b)),
}
}
NnfFormula::Or(a, b) => {
let a = a.simplify();
let b = b.simplify();
match (&a, &b) {
(NnfFormula::Top, _) | (_, NnfFormula::Top) => NnfFormula::Top,
(NnfFormula::Bot, _) => b,
(_, NnfFormula::Bot) => a,
_ => NnfFormula::Or(Box::new(a), Box::new(b)),
}
}
other => other,
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct Sequent {
pub antecedent: Vec<NnfFormula>,
pub succedent: Vec<NnfFormula>,
}
#[allow(dead_code)]
impl Sequent {
pub fn empty() -> Self {
Self {
antecedent: vec![],
succedent: vec![],
}
}
pub fn new(antecedent: Vec<NnfFormula>, succedent: Vec<NnfFormula>) -> Self {
Self {
antecedent,
succedent,
}
}
pub fn axiom(p: NnfFormula) -> Self {
Self {
antecedent: vec![p.clone()],
succedent: vec![p],
}
}
pub fn is_initial(&self) -> bool {
for a in &self.antecedent {
if self.succedent.contains(a) {
return true;
}
}
false
}
pub fn is_classically_valid(&self, num_vars: usize) -> bool {
let checker = TruthTableChecker::new(num_vars);
checker.all_assignments().iter().all(|assign| {
let ant_false = self.antecedent.iter().any(|f| !f.eval(assign));
let suc_true = self.succedent.iter().any(|f| f.eval(assign));
ant_false || suc_true
})
}
pub fn size(&self) -> usize {
self.antecedent.len() + self.succedent.len()
}
pub fn with_assumption(mut self, f: NnfFormula) -> Self {
self.antecedent.push(f);
self
}
pub fn with_conclusion(mut self, f: NnfFormula) -> Self {
self.succedent.push(f);
self
}
pub fn has_false_in_antecedent(&self) -> bool {
self.antecedent.contains(&NnfFormula::Bot)
}
pub fn has_true_in_succedent(&self) -> bool {
self.succedent.contains(&NnfFormula::Top)
}
}
#[allow(dead_code)]
pub struct KripkeModel {
pub num_worlds: usize,
pub num_vars: usize,
pub access: Vec<Vec<bool>>,
pub val: Vec<Vec<bool>>,
}
#[allow(dead_code)]
impl KripkeModel {
pub fn reflexive(num_worlds: usize, num_vars: usize) -> Self {
let mut access = vec![vec![false; num_worlds]; num_worlds];
for i in 0..num_worlds {
access[i][i] = true;
}
let val = vec![vec![false; num_vars]; num_worlds];
Self {
num_worlds,
num_vars,
access,
val,
}
}
pub fn set_val(&mut self, world: usize, var: usize, truth: bool) {
if world < self.num_worlds && var < self.num_vars {
self.val[world][var] = truth;
}
}
pub fn set_access(&mut self, from: usize, to: usize) {
if from < self.num_worlds && to < self.num_worlds {
self.access[from][to] = true;
}
}
pub fn satisfies_at(&self, formula: &NnfFormula, world: usize) -> bool {
formula.eval(&self.val[world])
}
pub fn is_reflexive(&self) -> bool {
(0..self.num_worlds).all(|i| self.access[i][i])
}
pub fn is_transitive(&self) -> bool {
for i in 0..self.num_worlds {
for j in 0..self.num_worlds {
for k in 0..self.num_worlds {
if self.access[i][j] && self.access[j][k] && !self.access[i][k] {
return false;
}
}
}
}
true
}
pub fn is_symmetric(&self) -> bool {
for i in 0..self.num_worlds {
for j in 0..self.num_worlds {
if self.access[i][j] && !self.access[j][i] {
return false;
}
}
}
true
}
pub fn is_euclidean(&self) -> bool {
for i in 0..self.num_worlds {
for j in 0..self.num_worlds {
for k in 0..self.num_worlds {
if self.access[i][j] && self.access[i][k] && !self.access[j][k] {
return false;
}
}
}
}
true
}
pub fn accessible_count(&self, world: usize) -> usize {
if world < self.num_worlds {
self.access[world].iter().filter(|&&b| b).count()
} else {
0
}
}
}