use std::collections::HashSet;
use std::sync::Arc;
use crate::formulas::operation_cache::OperationCache;
use crate::formulas::{EncodedFormula, Formula, FormulaFactory};
pub fn sub_nodes(formula: EncodedFormula, f: &FormulaFactory) -> Arc<[EncodedFormula]> {
if f.config.caches.sub_nodes {
sub_nodes_cached(formula, f, &mut None)
} else {
sub_nodes_cached(formula, f, &mut Some(OperationCache::new()))
}
}
fn sub_nodes_cached(
formula: EncodedFormula,
f: &FormulaFactory,
local_cache: &mut Option<OperationCache<Arc<[EncodedFormula]>>>,
) -> Arc<[EncodedFormula]> {
if let Some(lc) = local_cache {
if let Some(v) = lc.get(formula) {
return v;
}
}
if let Some(v) = f.caches.sub_nodes.get(formula) {
return v;
}
let mut result = Vec::new();
let mut result_set = HashSet::new();
for &op in &*formula.operands(f) {
if !result_set.contains(&formula) {
for &sub in &*sub_nodes_cached(op, f, local_cache) {
if !result_set.contains(&sub) {
result.push(sub);
result_set.insert(sub);
}
}
}
}
result.push(formula);
let rc: Arc<[_]> = Arc::from(result);
if f.config.caches.sub_nodes {
f.caches.sub_nodes.insert(formula, Arc::clone(&rc));
} else {
local_cache.as_mut().unwrap().insert(formula, Arc::clone(&rc));
}
rc
}
pub fn number_of_atoms(formula: EncodedFormula, f: &FormulaFactory) -> u64 {
if formula.is_atomic() {
1
} else if let Some(result) = f.caches.number_of_atoms.get(formula) {
result
} else {
let result = formula.operands(f).iter().map(|op| number_of_atoms(*op, f)).sum();
if f.config.caches.number_of_atoms {
f.caches.number_of_atoms.insert(formula, result);
}
result
}
}
pub fn number_of_internal_nodes(formula: EncodedFormula, f: &FormulaFactory) -> u64 {
sub_nodes(formula, f).len() as u64
}
pub fn number_of_nodes(formula: EncodedFormula, f: &FormulaFactory) -> u64 {
1 + if formula.is_constant() || formula.is_literal() {
0
} else if let Some(result) = f.caches.number_of_nodes.get(formula) {
result
} else {
let result = if formula.is_cc() {
formula.as_cc(f).unwrap().variables.len() as u64
} else if formula.is_pbc() {
formula.as_pbc(f).unwrap().literals.len() as u64
} else {
formula.operands(f).iter().map(|op| number_of_nodes(*op, f)).sum()
};
if f.config.caches.number_of_nodes {
f.caches.number_of_nodes.insert(formula, result);
}
result
}
}
pub fn number_of_operands(formula: EncodedFormula, f: &FormulaFactory) -> usize {
use Formula::{And, Equiv, Impl, Not, Or};
match formula.unpack(f) {
Equiv(_) | Impl(_) => 2,
Not(_) => 1,
Or(ops) | And(ops) => ops.len(),
_ => 0,
}
}
#[cfg(test)]
mod tests {
use crate::formulas::{EncodedFormula, FormulaFactory, ToFormula};
use crate::operations::functions::sub_node_function::sub_nodes;
#[test]
fn test() {
let f = &FormulaFactory::new();
let f1 = "((a & ~b & c) | (d & (~e | c))) & (a => (~x | y) & (x | ~z))".to_formula(f);
let expected = [
"a".to_formula(f),
"~b".to_formula(f),
"c".to_formula(f),
"a & ~b & c".to_formula(f),
"d".to_formula(f),
"~e".to_formula(f),
"~e | c".to_formula(f),
"d & (~e | c)".to_formula(f),
"(a & ~b & c) | (d & (~e | c))".to_formula(f),
"~x".to_formula(f),
"y".to_formula(f),
"~x | y".to_formula(f),
"x".to_formula(f),
"~z".to_formula(f),
"x | ~z".to_formula(f),
"(~x | y) & (x | ~z)".to_formula(f),
"a => (~x | y) & (x | ~z)".to_formula(f),
"((a & ~b & c) | (d & (~e | c))) & (a => (~x | y) & (x | ~z))".to_formula(f),
]
.into_iter()
.collect::<Vec<EncodedFormula>>();
assert_eq!(*sub_nodes(f1, f), expected);
}
}