use super::goal::Goal;
use super::unification::Bindings;
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct Disjunction {
pub branches: Vec<Goal>,
pub pattern: String,
}
impl Disjunction {
pub fn new(branches: Vec<Goal>, pattern: String) -> Self {
assert!(
!branches.is_empty(),
"Disjunction must have at least one branch"
);
Self { branches, pattern }
}
pub fn from_pair(left: Goal, right: Goal) -> Self {
let pattern = format!("({} OR {})", left.pattern, right.pattern);
Self {
branches: vec![left, right],
pattern,
}
}
pub fn add_branch(&mut self, goal: Goal) {
self.branches.push(goal);
}
pub fn branch_count(&self) -> usize {
self.branches.len()
}
}
#[derive(Debug, Clone)]
pub struct DisjunctionResult {
pub solutions: Vec<Bindings>,
pub successful_branches: Vec<usize>,
pub success: bool,
}
impl DisjunctionResult {
pub fn new() -> Self {
Self {
solutions: Vec::new(),
successful_branches: Vec::new(),
success: false,
}
}
pub fn success(solutions: Vec<Bindings>, successful_branches: Vec<usize>) -> Self {
Self {
solutions,
successful_branches,
success: true,
}
}
pub fn failure() -> Self {
Self {
solutions: Vec::new(),
successful_branches: Vec::new(),
success: false,
}
}
pub fn add_branch_solutions(&mut self, branch_index: usize, solutions: Vec<Bindings>) {
if !solutions.is_empty() {
self.successful_branches.push(branch_index);
self.solutions.extend(solutions);
self.success = true;
}
}
pub fn deduplicate(&mut self) {
let mut seen = HashSet::new();
let mut unique_solutions = Vec::new();
for solution in &self.solutions {
let binding_map = solution.to_map();
let key = format!("{:?}", binding_map);
if seen.insert(key) {
unique_solutions.push(solution.clone());
}
}
self.solutions = unique_solutions;
}
pub fn solution_count(&self) -> usize {
self.solutions.len()
}
}
impl Default for DisjunctionResult {
fn default() -> Self {
Self::new()
}
}
pub struct DisjunctionParser;
impl DisjunctionParser {
pub fn parse(pattern: &str) -> Option<Disjunction> {
let pattern = pattern.trim();
if !pattern.starts_with('(') || !pattern.ends_with(')') {
return None;
}
let inner = &pattern[1..pattern.len() - 1];
if !inner.contains(" OR ") {
return None;
}
let branches: Vec<Goal> = inner
.split(" OR ")
.map(|s| Goal::new(s.trim().to_string()))
.collect();
if branches.len() < 2 {
return None;
}
Some(Disjunction::new(branches, pattern.to_string()))
}
pub fn contains_or(pattern: &str) -> bool {
pattern.contains(" OR ")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_disjunction_creation() {
let goal1 = Goal::new("manager(?person)".to_string());
let goal2 = Goal::new("senior(?person)".to_string());
let disj = Disjunction::from_pair(goal1, goal2);
assert_eq!(disj.branch_count(), 2);
assert!(disj.pattern.contains("OR"));
}
#[test]
fn test_disjunction_add_branch() {
let goal1 = Goal::new("manager(?person)".to_string());
let goal2 = Goal::new("senior(?person)".to_string());
let goal3 = Goal::new("director(?person)".to_string());
let mut disj = Disjunction::from_pair(goal1, goal2);
disj.add_branch(goal3);
assert_eq!(disj.branch_count(), 3);
}
#[test]
fn test_disjunction_result_success() {
let mut result = DisjunctionResult::new();
let bindings1 = Bindings::new();
let bindings2 = Bindings::new();
result.add_branch_solutions(0, vec![bindings1]);
result.add_branch_solutions(1, vec![bindings2]);
assert!(result.success);
assert_eq!(result.solution_count(), 2);
assert_eq!(result.successful_branches.len(), 2);
}
#[test]
fn test_disjunction_result_empty() {
let mut result = DisjunctionResult::new();
result.add_branch_solutions(0, vec![]);
result.add_branch_solutions(1, vec![]);
assert!(!result.success);
assert_eq!(result.solution_count(), 0);
}
#[test]
fn test_parser_simple_or() {
let pattern = "(manager(?person) OR senior(?person))";
let disj = DisjunctionParser::parse(pattern);
assert!(disj.is_some());
let disj = disj.unwrap();
assert_eq!(disj.branch_count(), 2);
}
#[test]
fn test_parser_triple_or() {
let pattern = "(A OR B OR C)";
let disj = DisjunctionParser::parse(pattern);
assert!(disj.is_some());
let disj = disj.unwrap();
assert_eq!(disj.branch_count(), 3);
}
#[test]
fn test_parser_no_or() {
let pattern = "manager(?person)";
let disj = DisjunctionParser::parse(pattern);
assert!(disj.is_none());
}
#[test]
fn test_parser_contains_or() {
assert!(DisjunctionParser::contains_or("(A OR B)"));
assert!(!DisjunctionParser::contains_or("A AND B"));
}
#[test]
fn test_deduplication() {
let mut result = DisjunctionResult::new();
let bindings = Bindings::new();
result.add_branch_solutions(0, vec![bindings.clone(), bindings.clone()]);
assert_eq!(result.solution_count(), 2);
result.deduplicate();
assert_eq!(result.solution_count(), 1);
}
}