use std::fmt::Debug; /// Implement a FILO stack that lives on the regular call stack as a linked list. /// Mainly useful to detect loops in recursive algorithms where the recursion isn't /// deep enough to warrant a heap-allocated set #[derive(Clone, Copy)] pub struct Stackframe<'a, T> { pub item: T, pub prev: Option<&'a Stackframe<'a, T>>, pub len: usize } impl<'a, T: 'a> Stackframe<'a, T> { pub fn new(item: T) -> Self { Self { item, prev: None, len: 1 } } /// Get the item owned by this listlike, very fast O(1) pub fn item(&self) -> &T { &self.item } /// Get the next link in the list, very fast O(1) pub fn prev(&self) -> Option<&'a Stackframe> { self.prev } /// Construct an iterator over the listlike, very fast O(1) pub fn iter(&self) -> StackframeIterator { StackframeIterator { curr: Some(self) } } pub fn push(&self, item: T) -> Stackframe<'_, T> { Stackframe { item, prev: Some(self), len: self.len + 1 } } pub fn opush(prev: Option<&Self>, item: T) -> Self { Self { item, prev, len: prev.map_or(1, |s| s.len) } } pub fn len(&self) -> usize { self.len } pub fn pop(&self, count: usize) -> Option<&Self> { if count == 0 {Some(self)} else {self.prev.expect("Index out of range").pop(count - 1)} } pub fn opop(cur: Option<&Self>, count: usize) -> Option<&Self> { if count == 0 {cur} else {Self::opop(cur.expect("Index out of range").prev, count - 1)} } } impl<'a, T> Debug for Stackframe<'a, T> where T: Debug { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Substack")?; f.debug_list().entries(self.iter()).finish() } } pub struct StackframeIterator<'a, T> { curr: Option<&'a Stackframe<'a, T>> } impl<'a, T> Iterator for StackframeIterator<'a, T> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { let curr = self.curr?; let item = curr.item(); let prev = curr.prev(); self.curr = prev; Some(item) } }