use std::collections::VecDeque;
use smallvec::SmallVec;
use crate::ir::RoleId;
#[derive(Debug, Default, Clone)]
pub struct RoleHierarchyBuilder {
direct_super: Vec<SmallVec<[RoleId; 4]>>,
}
impl RoleHierarchyBuilder {
#[must_use]
pub fn with_roles(n: u32) -> Self {
Self {
direct_super: (0..n as usize).map(|_| SmallVec::new()).collect(),
}
}
pub fn add_sub_role(&mut self, sub: RoleId, sup: RoleId) {
let s = sub.index() as usize;
let supers = &mut self.direct_super[s];
if !supers.contains(&sup) {
supers.push(sup);
}
}
#[must_use]
pub fn num_roles(&self) -> usize {
self.direct_super.len()
}
#[must_use]
pub fn build(self) -> RoleHierarchy {
let n_u32: u32 =
u32::try_from(self.direct_super.len()).expect("RoleHierarchyBuilder: too many roles");
let n = self.direct_super.len();
let mut super_closure: Vec<Box<[RoleId]>> = Vec::with_capacity(n);
let mut sub_closure: Vec<Vec<RoleId>> = vec![Vec::new(); n];
for r in 0..n_u32 {
let mut visited = vec![false; n];
let mut queue: VecDeque<u32> = VecDeque::new();
queue.push_back(r);
let mut ups: Vec<RoleId> = Vec::new();
while let Some(curr) = queue.pop_front() {
let curr_idx = curr as usize;
if visited[curr_idx] {
continue;
}
visited[curr_idx] = true;
ups.push(RoleId::new(curr));
for &sup in &self.direct_super[curr_idx] {
queue.push_back(sup.index());
}
}
ups.sort_unstable();
for &sup in &ups {
sub_closure[sup.index() as usize].push(RoleId::new(r));
}
super_closure.push(ups.into_boxed_slice());
}
let sub_closure: Vec<Box<[RoleId]>> = sub_closure
.into_iter()
.map(|mut v| {
v.sort_unstable();
v.into_boxed_slice()
})
.collect();
RoleHierarchy {
super_closure,
sub_closure,
}
}
}
#[derive(Debug, Clone)]
pub struct RoleHierarchy {
super_closure: Vec<Box<[RoleId]>>,
sub_closure: Vec<Box<[RoleId]>>,
}
impl RoleHierarchy {
#[must_use]
pub fn super_roles(&self, r: RoleId) -> &[RoleId] {
&self.super_closure[r.index() as usize]
}
#[must_use]
pub fn sub_roles(&self, r: RoleId) -> &[RoleId] {
&self.sub_closure[r.index() as usize]
}
#[must_use]
pub fn is_sub_role(&self, sub: RoleId, sup: RoleId) -> bool {
self.super_closure[sub.index() as usize]
.binary_search(&sup)
.is_ok()
}
#[must_use]
pub fn num_roles(&self) -> usize {
self.super_closure.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn r(n: u32) -> RoleId {
RoleId::new(n)
}
#[test]
fn reflexive_only_when_no_axioms() {
let h = RoleHierarchyBuilder::with_roles(3).build();
for i in 0..3 {
assert_eq!(h.super_roles(r(i)), [r(i)].as_slice());
assert_eq!(h.sub_roles(r(i)), [r(i)].as_slice());
assert!(h.is_sub_role(r(i), r(i)));
}
}
#[test]
fn linear_chain() {
let mut b = RoleHierarchyBuilder::with_roles(3);
b.add_sub_role(r(0), r(1));
b.add_sub_role(r(1), r(2));
let h = b.build();
assert_eq!(h.super_roles(r(0)), [r(0), r(1), r(2)].as_slice());
assert_eq!(h.super_roles(r(1)), [r(1), r(2)].as_slice());
assert_eq!(h.super_roles(r(2)), [r(2)].as_slice());
assert_eq!(h.sub_roles(r(2)), [r(0), r(1), r(2)].as_slice());
assert!(h.is_sub_role(r(0), r(2)));
assert!(!h.is_sub_role(r(2), r(0)));
}
#[test]
fn diamond() {
let mut b = RoleHierarchyBuilder::with_roles(4);
b.add_sub_role(r(0), r(1));
b.add_sub_role(r(0), r(2));
b.add_sub_role(r(1), r(3));
b.add_sub_role(r(2), r(3));
let h = b.build();
assert!(h.is_sub_role(r(0), r(3)));
assert!(h.is_sub_role(r(0), r(1)));
assert!(h.is_sub_role(r(0), r(2)));
assert!(!h.is_sub_role(r(1), r(2)));
assert!(!h.is_sub_role(r(2), r(1)));
assert_eq!(h.super_roles(r(0)), [r(0), r(1), r(2), r(3)].as_slice());
}
#[test]
fn equivalence_cycle() {
let mut b = RoleHierarchyBuilder::with_roles(2);
b.add_sub_role(r(0), r(1));
b.add_sub_role(r(1), r(0));
let h = b.build();
assert_eq!(h.super_roles(r(0)), [r(0), r(1)].as_slice());
assert_eq!(h.super_roles(r(1)), [r(0), r(1)].as_slice());
assert!(h.is_sub_role(r(0), r(1)));
assert!(h.is_sub_role(r(1), r(0)));
}
#[test]
fn disconnected_components() {
let mut b = RoleHierarchyBuilder::with_roles(4);
b.add_sub_role(r(0), r(1));
b.add_sub_role(r(2), r(3));
let h = b.build();
assert!(h.is_sub_role(r(0), r(1)));
assert!(h.is_sub_role(r(2), r(3)));
assert!(!h.is_sub_role(r(0), r(2)));
assert!(!h.is_sub_role(r(0), r(3)));
assert!(!h.is_sub_role(r(1), r(2)));
}
#[test]
fn duplicate_axioms_are_idempotent() {
let mut b = RoleHierarchyBuilder::with_roles(2);
b.add_sub_role(r(0), r(1));
b.add_sub_role(r(0), r(1));
b.add_sub_role(r(0), r(1));
let h = b.build();
assert_eq!(h.super_roles(r(0)), [r(0), r(1)].as_slice());
}
}