use crate::core::{IgraphResult, VertexId};
pub(crate) struct MarkedQueue {
queue: Vec<i64>,
member: Vec<bool>,
size: usize,
}
impl MarkedQueue {
pub(crate) fn new(n: usize) -> Self {
Self {
queue: Vec::new(),
member: vec![false; n],
size: 0,
}
}
pub(crate) fn iselement(&self, e: VertexId) -> bool {
self.member[e as usize]
}
pub(crate) fn size(&self) -> usize {
self.size
}
pub(crate) fn push(&mut self, e: VertexId) {
if !self.member[e as usize] {
self.queue.push(i64::from(e));
self.member[e as usize] = true;
self.size += 1;
}
}
pub(crate) fn start_batch(&mut self) {
self.queue.push(-1);
}
pub(crate) fn pop_back_batch(&mut self) {
while let Some(top) = self.queue.pop() {
if top == -1 {
break;
}
if let Ok(idx) = usize::try_from(top) {
self.member[idx] = false;
self.size -= 1;
}
}
}
pub(crate) fn as_vector(&self) -> Vec<VertexId> {
self.queue
.iter()
.filter_map(|&e| VertexId::try_from(e).ok())
.collect()
}
}
pub(crate) struct EStack {
stack: Vec<VertexId>,
isin: Vec<bool>,
}
impl EStack {
pub(crate) fn new(n: usize) -> Self {
Self {
stack: Vec::new(),
isin: vec![false; n],
}
}
pub(crate) fn iselement(&self, e: VertexId) -> bool {
self.isin[e as usize]
}
pub(crate) fn push(&mut self, e: VertexId) {
if !self.isin[e as usize] {
self.stack.push(e);
self.isin[e as usize] = true;
}
}
pub(crate) fn pop(&mut self) {
if let Some(e) = self.stack.pop() {
self.isin[e as usize] = false;
}
}
}
pub(crate) trait Pivot {
fn pivot(
&mut self,
s: &MarkedQueue,
t: &EStack,
source: VertexId,
target: VertexId,
) -> IgraphResult<(VertexId, Vec<VertexId>)>;
}
pub(crate) fn provan_shier_list<P: Pivot>(
n: usize,
source: VertexId,
target: VertexId,
pivot: &mut P,
) -> IgraphResult<Vec<Vec<VertexId>>> {
let mut s = MarkedQueue::new(n);
let mut t = EStack::new(n);
let mut result: Vec<Vec<VertexId>> = Vec::new();
recurse(n, &mut s, &mut t, source, target, &mut result, pivot)?;
result.reverse();
Ok(result)
}
fn recurse<P: Pivot>(
n: usize,
s: &mut MarkedQueue,
t: &mut EStack,
source: VertexId,
target: VertexId,
result: &mut Vec<Vec<VertexId>>,
pivot: &mut P,
) -> IgraphResult<()> {
let (v, isv) = pivot.pivot(s, t, source, target)?;
if isv.is_empty() {
let sz = s.size();
if sz != 0 && sz != n {
result.push(s.as_vector());
}
return Ok(());
}
s.start_batch();
for &x in &isv {
s.push(x);
}
recurse(n, s, t, source, target, result, pivot)?;
s.pop_back_batch();
t.push(v);
recurse(n, s, t, source, target, result, pivot)?;
t.pop();
Ok(())
}