use crate::error::Error;
use crate::types::{Audience, ValueAssignment};
use argumentation::ArgumentationFramework;
use std::collections::HashSet;
use std::hash::Hash;
#[derive(Debug, Clone)]
pub struct ValueBasedFramework<A: Clone + Eq + Hash> {
base: ArgumentationFramework<A>,
values: ValueAssignment<A>,
}
impl<A: Clone + Eq + Hash + Ord + std::fmt::Debug> ValueBasedFramework<A> {
pub fn new(base: ArgumentationFramework<A>, values: ValueAssignment<A>) -> Self {
Self { base, values }
}
pub fn base(&self) -> &ArgumentationFramework<A> {
&self.base
}
pub fn value_assignment(&self) -> &ValueAssignment<A> {
&self.values
}
pub fn defeat_graph(&self, audience: &Audience) -> Result<ArgumentationFramework<A>, Error> {
let mut result = ArgumentationFramework::new();
let args: Vec<A> = self.base.arguments().cloned().collect();
for arg in &args {
result.add_argument(arg.clone());
}
for target in &args {
let attackers: Vec<A> = self.base.attackers(target).into_iter().cloned().collect();
for attacker in &attackers {
if self.defeats(attacker, target, audience) {
result.add_attack(attacker, target)?;
}
}
}
Ok(result)
}
pub fn defeats(&self, attacker: &A, target: &A, audience: &Audience) -> bool {
let attacker_values = self.values.values(attacker);
let target_values = self.values.values(target);
if attacker_values.is_empty() || target_values.is_empty() {
return true;
}
target_values.iter().all(|tv| {
attacker_values
.iter()
.any(|av| !audience.prefers(tv, av))
})
}
pub fn accepted_for(&self, audience: &Audience, arg: &A) -> Result<bool, Error> {
let defeat = self.defeat_graph(audience)?;
let extensions = defeat.preferred_extensions().map_err(Error::from)?;
Ok(extensions.iter().any(|ext| ext.contains(arg)))
}
pub fn grounded_for(&self, audience: &Audience) -> Result<HashSet<A>, Error> {
let defeat = self.defeat_graph(audience)?;
Ok(defeat.grounded_extension())
}
pub fn subjectively_accepted(&self, arg: &A) -> Result<bool, Error> {
crate::acceptance::subjectively_accepted(self, arg)
}
pub fn objectively_accepted(&self, arg: &A) -> Result<bool, Error> {
crate::acceptance::objectively_accepted(self, arg)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::Value;
fn framework_two_args_mutual_attack() -> ValueBasedFramework<&'static str> {
let mut base = ArgumentationFramework::new();
base.add_argument("h1");
base.add_argument("c1");
base.add_attack(&"h1", &"c1").unwrap();
base.add_attack(&"c1", &"h1").unwrap();
let mut values = ValueAssignment::new();
values.promote("h1", Value::new("life"));
values.promote("c1", Value::new("property"));
ValueBasedFramework::new(base, values)
}
#[test]
fn life_audience_defeats_property_attack() {
let vaf = framework_two_args_mutual_attack();
let audience = Audience::total([Value::new("life"), Value::new("property")]);
assert!(vaf.defeats(&"h1", &"c1", &audience));
assert!(!vaf.defeats(&"c1", &"h1", &audience));
}
#[test]
fn property_audience_inverts_defeats() {
let vaf = framework_two_args_mutual_attack();
let audience = Audience::total([Value::new("property"), Value::new("life")]);
assert!(!vaf.defeats(&"h1", &"c1", &audience));
assert!(vaf.defeats(&"c1", &"h1", &audience));
}
#[test]
fn null_promotion_attacker_always_defeats() {
let mut base = ArgumentationFramework::new();
base.add_argument("a");
base.add_argument("b");
base.add_attack(&"a", &"b").unwrap();
let mut values = ValueAssignment::new();
values.promote("b", Value::new("life"));
let vaf = ValueBasedFramework::new(base, values);
let audience = Audience::total([Value::new("life")]);
assert!(vaf.defeats(&"a", &"b", &audience));
}
#[test]
fn empty_audience_preserves_all_attacks() {
let vaf = framework_two_args_mutual_attack();
let audience = Audience::new();
assert!(vaf.defeats(&"h1", &"c1", &audience));
assert!(vaf.defeats(&"c1", &"h1", &audience));
}
#[test]
fn defeat_graph_filters_attacks() {
let vaf = framework_two_args_mutual_attack();
let audience = Audience::total([Value::new("life"), Value::new("property")]);
let defeat = vaf.defeat_graph(&audience).unwrap();
assert_eq!(defeat.attackers(&"h1").len(), 0);
assert_eq!(defeat.attackers(&"c1").len(), 1);
}
}