use std::collections::{hash_map::Entry, HashMap};
use std::fmt;
use std::sync::Arc;
use crate::node::{self, Disconnectable, Node};
#[derive(Clone, Debug)]
pub enum Dag<T> {
Nullary,
Unary(T),
Binary(T, T),
}
impl<T> Dag<T> {
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> Dag<U> {
match self {
Dag::Nullary => Dag::Nullary,
Dag::Unary(t) => Dag::Unary(f(t)),
Dag::Binary(tl, tr) => Dag::Binary(f(tl), f(tr)),
}
}
}
pub trait SharingTracker<D: DagLike> {
fn record(&mut self, object: &D, index: usize) -> Option<usize>;
fn seen_before(&self, object: &D) -> Option<usize>;
}
impl<D: DagLike> SharingTracker<D> for &mut dyn SharingTracker<D> {
fn record(&mut self, object: &D, index: usize) -> Option<usize> {
(**self).record(object, index)
}
fn seen_before(&self, object: &D) -> Option<usize> {
(**self).seen_before(object)
}
}
#[derive(Clone, Debug, Default)]
pub struct NoSharing;
impl<D: DagLike> SharingTracker<D> for NoSharing {
fn record(&mut self, _: &D, _: usize) -> Option<usize> {
None
}
fn seen_before(&self, _: &D) -> Option<usize> {
None
}
}
#[derive(Clone, Debug, Default)]
pub struct InternalSharing {
map: HashMap<PointerId, usize>,
}
impl<D: DagLike> SharingTracker<D> for InternalSharing {
fn record(&mut self, d: &D, index: usize) -> Option<usize> {
match self.map.entry(PointerId::from(d)) {
Entry::Occupied(occ) => Some(*occ.get()),
Entry::Vacant(vac) => {
vac.insert(index);
None
}
}
}
fn seen_before(&self, d: &D) -> Option<usize> {
self.map.get(&PointerId::from(d)).copied()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MaxSharing<N: node::Marker> {
map: HashMap<N::SharingId, usize>,
}
impl<N: node::Marker> Default for MaxSharing<N> {
fn default() -> Self {
MaxSharing {
map: HashMap::default(),
}
}
}
impl<N: node::Marker> SharingTracker<&Node<N>> for MaxSharing<N> {
fn record(&mut self, d: &&Node<N>, index: usize) -> Option<usize> {
let id = d.sharing_id()?;
match self.map.entry(id) {
Entry::Occupied(occ) => Some(*occ.get()),
Entry::Vacant(vac) => {
vac.insert(index);
None
}
}
}
fn seen_before(&self, d: &&Node<N>) -> Option<usize> {
d.sharing_id().and_then(|id| self.map.get(&id)).copied()
}
}
impl<N: node::Marker> SharingTracker<Arc<Node<N>>> for MaxSharing<N> {
fn record(&mut self, d: &Arc<Node<N>>, index: usize) -> Option<usize> {
let id = d.sharing_id()?;
match self.map.entry(id) {
Entry::Occupied(occ) => Some(*occ.get()),
Entry::Vacant(vac) => {
vac.insert(index);
None
}
}
}
fn seen_before(&self, d: &Arc<Node<N>>) -> Option<usize> {
d.sharing_id().and_then(|id| self.map.get(&id)).copied()
}
}
impl<N, D> SharingTracker<SwapChildren<D>> for MaxSharing<N>
where
D: DagLike,
MaxSharing<N>: SharingTracker<D>,
N: node::Marker,
{
fn record(&mut self, d: &SwapChildren<D>, index: usize) -> Option<usize> {
self.record(&d.0, index)
}
fn seen_before(&self, d: &SwapChildren<D>) -> Option<usize> {
self.seen_before(&d.0)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
struct PointerId(usize);
impl<D: DagLike> From<&D> for PointerId {
fn from(dag: &D) -> Self {
PointerId(dag.data() as *const _ as usize)
}
}
pub type RtlPostOrderIter<D, S> = std::iter::Map<
PostOrderIter<SwapChildren<D>, S>,
fn(PostOrderIterItem<SwapChildren<D>>) -> PostOrderIterItem<D>,
>;
pub trait DagLike: Sized {
type Node;
fn data(&self) -> &Self::Node;
fn as_dag_node(&self) -> Dag<Self>;
fn left_child(&self) -> Option<Self> {
match self.as_dag_node() {
Dag::Nullary => None,
Dag::Unary(sub) => Some(sub),
Dag::Binary(left, _) => Some(left),
}
}
fn right_child(&self) -> Option<Self> {
match self.as_dag_node() {
Dag::Nullary => None,
Dag::Unary(_) => None,
Dag::Binary(_, right) => Some(right),
}
}
fn n_children(&self) -> usize {
match self.as_dag_node() {
Dag::Nullary => 0,
Dag::Unary(..) => 1,
Dag::Binary(..) => 2,
}
}
fn rtl_post_order_iter<S: SharingTracker<SwapChildren<Self>> + Default>(
self,
) -> RtlPostOrderIter<Self, S> {
PostOrderIter {
index: 0,
stack: vec![IterStackItem::unprocessed(
SwapChildren(self),
Previous::Root,
)],
tracker: Default::default(),
}
.map(PostOrderIterItem::unswap)
}
fn pre_order_iter<S: SharingTracker<Self> + Default>(self) -> PreOrderIter<Self, S> {
PreOrderIter {
stack: vec![self],
tracker: Default::default(),
}
}
fn verbose_pre_order_iter<S: SharingTracker<Self> + Default>(
self,
max_depth: Option<usize>,
) -> VerbosePreOrderIter<Self, S>
where
Self: Clone,
{
VerbosePreOrderIter {
stack: vec![PreOrderIterItem::initial(self, 0, None)],
index: 0,
max_depth,
tracker: Default::default(),
}
}
fn post_order_iter<S: SharingTracker<Self> + Default>(self) -> PostOrderIter<Self, S> {
PostOrderIter {
index: 0,
stack: vec![IterStackItem::unprocessed(self, Previous::Root)],
tracker: Default::default(),
}
}
#[allow(clippy::wrong_self_convention)] fn is_shared_as<S: SharingTracker<Self> + Default>(self) -> bool
where
Self: Clone,
{
let iter_is = self.clone().post_order_iter::<InternalSharing>();
let iter_ought = self.post_order_iter::<S>();
for (data_is, data_ought) in iter_is.zip(iter_ought) {
if PointerId::from(&data_is.node) != PointerId::from(&data_ought.node) {
return false;
}
}
true
}
fn post_order_iter_with_tracker<S: SharingTracker<Self>>(
self,
tracker: S,
) -> PostOrderIter<Self, S> {
PostOrderIter {
index: 0,
stack: vec![IterStackItem::unprocessed(self, Previous::Root)],
tracker,
}
}
}
#[derive(Clone, Debug)]
pub struct SwapChildren<D>(D);
impl<D: DagLike> DagLike for SwapChildren<D> {
type Node = D::Node;
fn data(&self) -> &Self::Node {
self.0.data()
}
fn as_dag_node(&self) -> Dag<Self> {
match self.0.as_dag_node() {
Dag::Nullary => Dag::Nullary,
Dag::Unary(sub) => Dag::Unary(SwapChildren(sub)),
Dag::Binary(left, right) => Dag::Binary(SwapChildren(right), SwapChildren(left)),
}
}
}
impl<N: node::Marker> DagLike for &'_ Node<N> {
type Node = Node<N>;
fn data(&self) -> &Node<N> {
self
}
#[rustfmt::skip]
fn as_dag_node(&self) -> Dag<Self> {
match self.inner() {
node::Inner::Iden
| node::Inner::Unit
| node::Inner::Fail(..)
| node::Inner::Jet(..)
| node::Inner::Word(..) => Dag::Nullary,
node::Inner::InjL(ref sub)
| node::Inner::InjR(ref sub)
| node::Inner::Take(ref sub)
| node::Inner::Drop(ref sub)
| node::Inner::AssertL(ref sub, _)
| node::Inner::AssertR(_, ref sub) => Dag::Unary(sub),
node::Inner::Comp(ref left, ref right)
| node::Inner::Case(ref left, ref right)
| node::Inner::Pair(ref left, ref right) => Dag::Binary(left, right),
node::Inner::Disconnect(ref left, ref right) => right.disconnect_dag_ref(left),
node::Inner::Witness(..) => Dag::Nullary,
}
}
}
impl<N: node::Marker> DagLike for Arc<Node<N>> {
type Node = Node<N>;
fn data(&self) -> &Node<N> {
self
}
#[rustfmt::skip]
fn as_dag_node(&self) -> Dag<Self> {
match self.inner() {
node::Inner::Iden
| node::Inner::Unit
| node::Inner::Fail(..)
| node::Inner::Jet(..)
| node::Inner::Word(..) => Dag::Nullary,
node::Inner::InjL(ref sub)
| node::Inner::InjR(ref sub)
| node::Inner::Take(ref sub)
| node::Inner::Drop(ref sub)
| node::Inner::AssertL(ref sub, _)
| node::Inner::AssertR(_, ref sub) => Dag::Unary(Arc::clone(sub)),
node::Inner::Comp(ref left, ref right)
| node::Inner::Case(ref left, ref right)
| node::Inner::Pair(ref left, ref right) => Dag::Binary(Arc::clone(left), Arc::clone(right)),
node::Inner::Disconnect(ref left, ref right) => right.clone().disconnect_dag_arc(Arc::clone(left)),
node::Inner::Witness(..) => Dag::Nullary,
}
}
}
enum Child<D: DagLike> {
None,
Repeat { idx: usize },
New(D),
}
#[derive(Clone, Debug)]
enum Previous {
Root,
ParentLeft,
SiblingLeft,
ParentRight,
}
#[derive(Clone, Debug)]
struct IterStackItem<D> {
elem: D,
processed: bool,
left_idx: Option<usize>,
right_idx: Option<usize>,
previous: Previous,
}
impl<D: DagLike> IterStackItem<D> {
fn unprocessed(elem: D, previous: Previous) -> Self {
IterStackItem {
elem,
processed: false,
left_idx: None,
right_idx: None,
previous,
}
}
fn left_child<V: SharingTracker<D>>(&self, tracker: &V) -> Child<D> {
match self.elem.left_child() {
Some(child) => match tracker.seen_before(&child) {
Some(idx) => Child::Repeat { idx },
None => Child::New(child),
},
None => Child::None,
}
}
fn right_child<V: SharingTracker<D>>(&self, tracker: &V) -> Child<D> {
match self.elem.right_child() {
Some(child) => match tracker.seen_before(&child) {
Some(idx) => Child::Repeat { idx },
None => Child::New(child),
},
None => Child::None,
}
}
}
#[derive(Clone, Debug)]
pub struct PostOrderIter<D, S> {
index: usize,
stack: Vec<IterStackItem<D>>,
tracker: S,
}
#[derive(Clone, Debug)]
pub struct PostOrderIterItem<D> {
pub node: D,
pub index: usize,
pub left_index: Option<usize>,
pub right_index: Option<usize>,
}
impl<D: DagLike> PostOrderIterItem<SwapChildren<D>> {
pub fn unswap(mut self) -> PostOrderIterItem<D> {
if matches!(self.node.as_dag_node(), Dag::Binary(..)) {
std::mem::swap(&mut self.left_index, &mut self.right_index);
}
PostOrderIterItem {
node: self.node.0,
index: self.index,
left_index: self.left_index,
right_index: self.right_index,
}
}
}
impl<D: DagLike, S: SharingTracker<D>> Iterator for PostOrderIter<D, S> {
type Item = PostOrderIterItem<D>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(mut current) = self.stack.pop() {
if !current.processed {
current.processed = true;
match (
current.left_child(&self.tracker),
current.right_child(&self.tracker),
) {
(Child::None, _) => {
self.stack.push(current);
}
(Child::Repeat { idx }, Child::None) => {
current.left_idx = Some(idx);
self.stack.push(current);
}
(Child::New(child), Child::None) => {
self.stack.push(current);
self.stack
.push(IterStackItem::unprocessed(child, Previous::ParentLeft));
}
(Child::Repeat { idx: lidx }, Child::Repeat { idx: ridx }) => {
current.left_idx = Some(lidx);
current.right_idx = Some(ridx);
self.stack.push(current);
}
(Child::New(child), Child::Repeat { idx }) => {
current.right_idx = Some(idx);
self.stack.push(current);
self.stack
.push(IterStackItem::unprocessed(child, Previous::ParentLeft));
}
(Child::Repeat { idx }, Child::New(child)) => {
current.left_idx = Some(idx);
self.stack.push(current);
self.stack
.push(IterStackItem::unprocessed(child, Previous::ParentRight));
}
(Child::New(lchild), Child::New(rchild)) => {
self.stack.push(current);
self.stack
.push(IterStackItem::unprocessed(rchild, Previous::ParentRight));
self.stack
.push(IterStackItem::unprocessed(lchild, Previous::SiblingLeft));
}
}
} else {
let current_index = self.tracker.record(¤t.elem, self.index);
let already_yielded = current_index.is_some();
let current_index = current_index.unwrap_or(self.index);
let stack_len = self.stack.len();
match current.previous {
Previous::Root => {
assert_eq!(stack_len, 0);
}
Previous::ParentLeft => {
assert!(self.stack[stack_len - 1].processed);
self.stack[stack_len - 1].left_idx = Some(current_index);
}
Previous::ParentRight => {
assert!(self.stack[stack_len - 1].processed);
self.stack[stack_len - 1].right_idx = Some(current_index);
}
Previous::SiblingLeft => {
assert!(self.stack[stack_len - 2].processed);
self.stack[stack_len - 2].left_idx = Some(current_index);
}
}
if already_yielded {
continue;
}
self.index += 1;
return Some(PostOrderIterItem {
node: current.elem,
index: current_index,
left_index: current.left_idx,
right_index: current.right_idx,
});
}
} else {
return None;
}
}
}
}
impl<'a, N: node::Marker, S: SharingTracker<&'a Node<N>> + Clone> PostOrderIter<&'a Node<N>, S> {
pub fn into_witnesses(self) -> impl Iterator<Item = &'a N::Witness> + Clone {
self.filter_map(|data| {
if let node::Inner::Witness(value) = data.node.inner() {
Some(value)
} else {
None
}
})
}
}
impl<D: DagLike, S: SharingTracker<D>> PostOrderIter<D, S> {
pub fn into_display<F, G>(
self,
f: &mut fmt::Formatter<'_>,
mut display_body: F,
mut display_aux: G,
) -> fmt::Result
where
F: FnMut(&D, &mut fmt::Formatter<'_>) -> fmt::Result,
G: FnMut(&D, &mut fmt::Formatter<'_>) -> fmt::Result,
{
for data in self {
write!(f, "{}: ", data.index)?;
display_body(&data.node, f)?;
if let Some(i_abs) = data.left_index {
if let Some(j_abs) = data.right_index {
write!(f, "({}, {})", i_abs, j_abs)?;
} else {
write!(f, "({})", i_abs)?;
}
}
display_aux(&data.node, f)?;
f.write_str("\n")?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct PreOrderIter<D, S> {
stack: Vec<D>,
tracker: S,
}
impl<D: DagLike, S: SharingTracker<D>> Iterator for PreOrderIter<D, S> {
type Item = D;
fn next(&mut self) -> Option<Self::Item> {
while let Some(top) = self.stack.pop() {
if self.tracker.record(&top, 0).is_none() {
if let Some(child) = top.right_child() {
self.stack.push(child);
}
if let Some(child) = top.left_child() {
self.stack.push(child);
}
return Some(top);
}
}
None
}
}
#[derive(Clone, Debug)]
pub struct VerbosePreOrderIter<D, S> {
stack: Vec<PreOrderIterItem<D>>,
max_depth: Option<usize>,
index: usize,
tracker: S,
}
impl<D: DagLike + Clone, S: SharingTracker<D>> Iterator for VerbosePreOrderIter<D, S> {
type Item = PreOrderIterItem<D>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(mut top) = self.stack.pop() {
if top.n_children_yielded == 0 {
if self.tracker.record(&top.node, 0).is_some() {
continue;
}
top.index = self.index;
self.index += 1;
}
match (top.n_children_yielded, top.node.n_children()) {
(0, 0) => {}
(0, n) => {
self.stack.push(top.clone().increment(n == 1));
if top.depth < self.max_depth.unwrap_or(top.depth + 1) {
let child = top.node.left_child().unwrap();
self.stack.push(PreOrderIterItem::initial(
child,
top.depth + 1,
Some(top.node.clone()),
));
}
}
(1, 0) => unreachable!(),
(1, 1) => {}
(1, _) => {
self.stack.push(top.clone().increment(true));
if top.depth < self.max_depth.unwrap_or(top.depth + 1) {
let child = top.node.right_child().unwrap();
self.stack.push(PreOrderIterItem::initial(
child,
top.depth + 1,
Some(top.node.clone()),
));
}
}
(x, y) => {
debug_assert_eq!((x, y), (2, 2));
}
}
return Some(top);
}
None
}
}
#[derive(Clone, Debug)]
pub struct PreOrderIterItem<D> {
pub node: D,
pub parent: Option<D>,
pub index: usize,
pub depth: usize,
pub n_children_yielded: usize,
pub is_complete: bool,
}
impl<D: DagLike + Clone> PreOrderIterItem<D> {
fn initial(d: D, depth: usize, parent: Option<D>) -> Self {
PreOrderIterItem {
is_complete: matches!(d.as_dag_node(), Dag::Nullary),
node: d,
parent,
index: 0,
depth,
n_children_yielded: 0,
}
}
fn increment(self, is_complete: bool) -> Self {
PreOrderIterItem {
node: self.node,
index: self.index,
depth: self.depth,
parent: self.parent,
n_children_yielded: self.n_children_yielded + 1,
is_complete,
}
}
}