use crate::error::Error;
use crate::framework::ValueBasedFramework;
use crate::types::Audience;
use std::collections::HashSet;
use std::hash::Hash;
pub struct MultiAudience<'a> {
audiences: &'a [Audience],
}
impl<'a> MultiAudience<'a> {
pub fn new(audiences: &'a [Audience]) -> Self {
Self { audiences }
}
pub fn audiences(&self) -> &[Audience] {
self.audiences
}
pub fn common_credulous<A>(
&self,
vaf: &ValueBasedFramework<A>,
) -> Result<HashSet<A>, Error>
where
A: Clone + Eq + Hash + Ord + std::fmt::Debug,
{
if self.audiences.is_empty() {
return Ok(vaf.base().arguments().cloned().collect());
}
let per_audience: Vec<HashSet<A>> = self
.audiences
.iter()
.map(|aud| -> Result<HashSet<A>, Error> {
let defeat = vaf.defeat_graph(aud)?;
let extensions = defeat.preferred_extensions().map_err(Error::from)?;
let mut credulous = HashSet::new();
for ext in extensions {
for arg in ext {
credulous.insert(arg);
}
}
Ok(credulous)
})
.collect::<Result<_, _>>()?;
let mut iter = per_audience.into_iter();
let Some(mut acc) = iter.next() else {
return Ok(HashSet::new());
};
for next in iter {
acc.retain(|a| next.contains(a));
}
Ok(acc)
}
pub fn common_grounded<A>(
&self,
vaf: &ValueBasedFramework<A>,
) -> Result<HashSet<A>, Error>
where
A: Clone + Eq + Hash + Ord + std::fmt::Debug,
{
if self.audiences.is_empty() {
return Ok(vaf.base().arguments().cloned().collect());
}
let per_audience: Vec<HashSet<A>> = self
.audiences
.iter()
.map(|aud| vaf.grounded_for(aud))
.collect::<Result<_, _>>()?;
let mut iter = per_audience.into_iter();
let Some(mut acc) = iter.next() else {
return Ok(HashSet::new());
};
for next in iter {
acc.retain(|a| next.contains(a));
}
Ok(acc)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Value, ValueAssignment};
use argumentation::ArgumentationFramework;
fn hal_carla() -> ValueBasedFramework<&'static str> {
let mut base = ArgumentationFramework::new();
for arg in ["h1", "c1", "h2", "c2"] {
base.add_argument(arg);
}
base.add_attack(&"h1", &"c1").unwrap();
base.add_attack(&"c1", &"h1").unwrap();
base.add_attack(&"c2", &"h2").unwrap();
base.add_attack(&"h2", &"c1").unwrap();
let mut values = ValueAssignment::new();
values.promote("h1", Value::new("life"));
values.promote("c1", Value::new("property"));
values.promote("h2", Value::new("fairness"));
values.promote("c2", Value::new("life"));
ValueBasedFramework::new(base, values)
}
#[test]
fn common_grounded_across_opposing_audiences_yields_only_unanimous_winners() {
let vaf = hal_carla();
let life_first = Audience::total([Value::new("life"), Value::new("property")]);
let property_first = Audience::total([Value::new("property"), Value::new("life")]);
let auds = [life_first, property_first];
let multi = MultiAudience::new(&auds);
let common = multi.common_grounded(&vaf).unwrap();
assert!(common.contains("c2"));
assert!(!common.contains("h1"));
assert!(!common.contains("c1"));
}
#[test]
fn empty_audience_set_returns_all_arguments() {
let vaf = hal_carla();
let multi = MultiAudience::new(&[]);
let common = multi.common_grounded(&vaf).unwrap();
assert_eq!(common.len(), 4);
}
#[test]
fn common_credulous_is_superset_of_common_grounded() {
let vaf = hal_carla();
let life_first = Audience::total([Value::new("life"), Value::new("property")]);
let property_first = Audience::total([Value::new("property"), Value::new("life")]);
let auds = [life_first, property_first];
let multi = MultiAudience::new(&auds);
let credulous = multi.common_credulous(&vaf).unwrap();
let grounded = multi.common_grounded(&vaf).unwrap();
for arg in &grounded {
assert!(credulous.contains(arg), "common_grounded must subset common_credulous");
}
}
}