use std::hash::BuildHasher;
use foldhash::HashSet;
use foldhash::fast::FixedState;
use mago_codex::assertion::Assertion;
pub type Disjunction<T> = Vec<T>;
pub type Conjunction<T> = Vec<T>;
pub type AssertionSet = Conjunction<Disjunction<Assertion>>;
#[inline]
pub fn add_or_assertion(possibilities: &mut AssertionSet, assertion: Assertion) {
if possibilities.is_empty() {
possibilities.push(vec![assertion]);
return;
}
for clause in possibilities {
clause.push(assertion.clone());
}
}
#[inline]
pub fn add_and_assertion(possibilities: &mut AssertionSet, assertion: Assertion) {
possibilities.push(vec![assertion]);
}
#[inline]
pub fn add_and_clause(assertion_set: &mut AssertionSet, or_assertions: &[Assertion]) {
if or_assertions.is_empty() {
*assertion_set = vec![vec![]];
return;
}
assertion_set.push(or_assertions.to_vec());
}
#[inline]
#[must_use]
pub fn negate_assertion_set(assertion_set: AssertionSet) -> AssertionSet {
let dnf: AssertionSet = assertion_set
.into_iter()
.map(|or_clause| or_clause.into_iter().map(|a| a.get_negation()).collect::<Vec<_>>())
.filter(|and_clause| !and_clause.is_empty())
.collect();
if dnf.is_empty() {
return vec![vec![]];
}
let mut result_cnf: AssertionSet = dnf[0].iter().map(|literal| vec![literal.clone()]).collect();
for and_clause in dnf.iter().skip(1) {
let mut next_result_cnf = AssertionSet::new();
for literal in and_clause {
for cnf_clause in &result_cnf {
let mut new_clause = cnf_clause.clone();
new_clause.push(literal.clone());
next_result_cnf.push(new_clause);
}
}
result_cnf = next_result_cnf;
}
result_cnf
}
#[inline]
pub fn and_assertion_sets(set_a: AssertionSet, set_b: AssertionSet) -> AssertionSet {
if (set_a.len() == 1 && set_a[0].is_empty()) || (set_b.len() == 1 && set_b[0].is_empty()) {
return vec![vec![]];
}
let mut result: AssertionSet = set_a;
let mut existing_clause_hashes: HashSet<u64> = result.iter().map(hash_disjunction).collect();
for disjunction in set_b {
let disjunction_hash = hash_disjunction(&disjunction);
if existing_clause_hashes.insert(disjunction_hash) {
result.push(disjunction);
}
}
result
}
fn hash_disjunction(disjunction: &Disjunction<Assertion>) -> u64 {
let mut assertion_hashes: Vec<_> = disjunction.iter().map(mago_codex::assertion::Assertion::to_hash).collect();
assertion_hashes.sort_unstable();
FixedState::default().hash_one(&assertion_hashes)
}
#[cfg(test)]
mod tests {
use mago_codex::ttype::atomic::TAtomic;
use mago_codex::ttype::atomic::scalar::TScalar;
use super::*;
fn assert_sets_equal(a: AssertionSet, b: AssertionSet) {
let mut sorted_a: Vec<_> = a
.into_iter()
.map(|mut v| {
v.sort();
v
})
.collect();
sorted_a.sort();
let mut sorted_b: Vec<_> = b
.into_iter()
.map(|mut v| {
v.sort();
v
})
.collect();
sorted_b.sort();
assert_eq!(sorted_a, sorted_b);
}
#[test]
fn test_add_or_assertion() {
let mut set = vec![vec![Assertion::Truthy], vec![Assertion::Falsy]];
add_or_assertion(&mut set, Assertion::IsType(TAtomic::Scalar(TScalar::string())));
let expected = vec![
vec![Assertion::Truthy, Assertion::IsType(TAtomic::Scalar(TScalar::string()))],
vec![Assertion::Falsy, Assertion::IsType(TAtomic::Scalar(TScalar::string()))],
];
assert_sets_equal(expected, set);
}
#[test]
fn test_add_or_assertion_to_empty() {
let mut set = vec![];
add_or_assertion(&mut set, Assertion::IsType(TAtomic::Scalar(TScalar::string())));
let expected = vec![vec![Assertion::IsType(TAtomic::Scalar(TScalar::string()))]];
assert_sets_equal(expected, set);
}
#[test]
fn test_add_and_assertion() {
let mut set = vec![vec![Assertion::Truthy, Assertion::Falsy]];
add_and_assertion(&mut set, Assertion::IsType(TAtomic::Scalar(TScalar::string())));
let expected = vec![
vec![Assertion::Truthy, Assertion::Falsy],
vec![Assertion::IsType(TAtomic::Scalar(TScalar::string()))],
];
assert_sets_equal(expected, set);
}
#[test]
fn test_add_and_clause() {
let mut set = vec![vec![Assertion::Truthy]];
let or_clause = vec![
Assertion::IsType(TAtomic::Scalar(TScalar::string())),
Assertion::IsType(TAtomic::Scalar(TScalar::int())),
];
add_and_clause(&mut set, &or_clause);
let expected = vec![
vec![Assertion::Truthy],
vec![
Assertion::IsType(TAtomic::Scalar(TScalar::string())),
Assertion::IsType(TAtomic::Scalar(TScalar::int())),
],
];
assert_sets_equal(expected, set);
}
#[test]
fn test_add_and_empty_clause() {
let mut set = vec![vec![Assertion::Truthy]];
add_and_clause(&mut set, &[]);
let expected = vec![vec![]];
assert_sets_equal(expected, set);
}
#[test]
fn test_negate_simple_or_clause() {
let initial_cnf = vec![vec![
Assertion::IsType(TAtomic::Scalar(TScalar::string())),
Assertion::IsType(TAtomic::Scalar(TScalar::int())),
]];
let expected_cnf = vec![
vec![Assertion::IsNotType(TAtomic::Scalar(TScalar::string()))],
vec![Assertion::IsNotType(TAtomic::Scalar(TScalar::int()))],
];
assert_sets_equal(expected_cnf, negate_assertion_set(initial_cnf));
}
#[test]
fn test_negate_simple_and_clause() {
let initial_cnf = vec![vec![Assertion::Truthy], vec![Assertion::Falsy]];
let expected_cnf = vec![vec![Assertion::Falsy, Assertion::Truthy]];
assert_sets_equal(expected_cnf, negate_assertion_set(initial_cnf));
}
}