package fj.data; import fj.*; import fj.function.Booleans; import java.util.Iterator; import static fj.Equal.p3Equal; import static fj.Equal.p4Equal; import static fj.Equal.streamEqual; import static fj.Equal.treeEqual; import static fj.Function.compose; import static fj.Function.curry; import static fj.Function.flip; import static fj.Function.uncurryF2; import static fj.Show.p3Show; import static fj.Show.p4Show; import static fj.Show.streamShow; import static fj.Show.treeShow; import static fj.data.Option.none; import static fj.data.Option.some; import static fj.data.Stream.nil; import static fj.data.Stream.unfold; import static fj.data.Tree.node; import static fj.data.Tree.unfoldTree; /** * Provides a zipper structure for rose trees, which is a Tree supplied with a location within that tree. * Provides navigation, insertion, deletion, and memorization of visited locations within a tree. */ public final class TreeZipper<A> implements Iterable<TreeZipper<A>> { /** * Returns an iterator of all the positions of this TreeZipper. Exists for use with the foreach syntax. * * @return An iterator of all the positions of this TreeZipper. */ public Iterator<TreeZipper<A>> iterator() { return positions().toTree().iterator(); } private final Tree<A> tree; private final Stream<Tree<A>> lefts; private final Stream<Tree<A>> rights; private final Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>> parents; private TreeZipper(final Tree<A> tree, final Stream<Tree<A>> lefts, final Stream<Tree<A>> rights, final Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>> parents) { this.tree = tree; this.lefts = lefts; this.rights = rights; this.parents = parents; } /** * Creates a new tree zipper given a currently selected tree, a forest on the left, a forest on the right, * and a stream of parent contexts. * * @param tree The currently selected tree. * @param lefts The selected tree's left siblings, closest first. * @param rights The selected tree's right siblings, closest first. * @param parents The parent of the selected tree, and the parent's siblings. * @return A new zipper with the given tree selected, and the given forests on the left and right. */ public static <A> TreeZipper<A> treeZipper(final Tree<A> tree, final Stream<Tree<A>> lefts, final Stream<Tree<A>> rights, final Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>> parents) { return new TreeZipper<>(tree, lefts, rights, parents); } /** * First-class constructor for tree zippers. * * @return A function that returns a new tree zipper, given a selected tree, left and right siblings, * and a parent context. */ public static <A> F<Tree<A>, F<Stream<Tree<A>>, F<Stream<Tree<A>>, F<Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>>, TreeZipper<A>>>>> treeZipper() { return curry( TreeZipper::treeZipper); } /** * Returns the product-4 representation of this zipper. * * @return the product-4 representation of this zipper. */ public P4<Tree<A>, Stream<Tree<A>>, Stream<Tree<A>>, Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>>> p() { return P.p(tree, lefts, rights, parents); } /** * A first-class function that returns the product-4 representation of a given zipper. * * @return a function that converts a given zipper to its product-4 representation. */ public static <A> F<TreeZipper<A>, P4<Tree<A>, Stream<Tree<A>>, Stream<Tree<A>>, Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>>>> p_() { return TreeZipper::p; } /** * An Equal instance for tree zippers. * * @param e An Equal instance for tree elements. * @return An Equal instance for tree zippers. */ public static <A> Equal<TreeZipper<A>> eq(final Equal<A> e) { return p4Equal( treeEqual(e), streamEqual(treeEqual(e)), streamEqual(treeEqual(e)), streamEqual(p3Equal(streamEqual(treeEqual(e)), e, streamEqual(treeEqual(e))))).contramap(TreeZipper.p_()); } /** * A Show instance for tree zippers. * * @param s A Show instance for tree elements. * @return A Show instance for tree zippers. */ public static <A> Show<TreeZipper<A>> show(final Show<A> s) { return p4Show( treeShow(s), streamShow(treeShow(s)), streamShow(treeShow(s)), streamShow(p3Show(streamShow(treeShow(s)), s, streamShow(treeShow(s))))).contramap(TreeZipper.p_()); } private static <A> Stream<Tree<A>> combChildren(final Stream<Tree<A>> ls, final Tree<A> t, final Stream<Tree<A>> rs) { return ls.foldLeft(compose(flip(Stream.cons()), P.p1()), Stream.cons(t, P.p(rs))); } /** * Navigates to the parent of the current location. * * @return A new tree zipper focused on the parent node of the current node, * or none if the current node is the root node. */ public Option<TreeZipper<A>> parent() { if (parents.isEmpty()) return none(); else { final P3<Stream<Tree<A>>, A, Stream<Tree<A>>> p = parents.head(); return some(treeZipper(node(p._2(), combChildren(lefts, tree, rights)), p._1(), p._3(), parents.tail()._1())); } } /** * Navigates to the top-most parent of the current location. * * @return A new tree zipper focused on the top-most parent of the current node. */ public TreeZipper<A> root() { return parent().option(this, TreeZipper.root_()); } /** * A first-class version of the root function. * * @return A function that returns a new tree-zipper focused on the root of the given tree zipper's tree. */ public static <A> F<TreeZipper<A>, TreeZipper<A>> root_() { return TreeZipper::root; } /** * Navigates to the left sibling of the current location. * * @return A new tree zipper focused on the left sibling of the current node, * or none if there are no siblings on the left. */ public Option<TreeZipper<A>> left() { return lefts.isEmpty() ? Option.none() : some(treeZipper(lefts.head(), lefts.tail()._1(), rights.cons(tree), parents)); } /** * Navigates to the right sibling of the current location. * * @return A new tree zipper focused on the right sibling of the current node, * or none if there are no siblings on the right. */ public Option<TreeZipper<A>> right() { return rights.isEmpty() ? Option.none() : some(treeZipper(rights.head(), lefts.cons(tree), rights.tail()._1(), parents)); } /** * Navigtes to the first child of the current location. * * @return A new tree zipper focused on the first child of the current node, or none if the node has no children. */ public Option<TreeZipper<A>> firstChild() { final Stream<Tree<A>> ts = tree.subForest()._1(); return ts.isEmpty() ? Option.none() : some(treeZipper(ts.head(), Stream.nil(), ts.tail()._1(), downParents())); } /** * Navigtes to the last child of the current location. * * @return A new tree zipper focused on the last child of the current node, or none if the node has no children. */ public Option<TreeZipper<A>> lastChild() { final Stream<Tree<A>> ts = tree.subForest()._1().reverse(); return ts.isEmpty() ? Option.none() : some(treeZipper(ts.head(), ts.tail()._1(), Stream.nil(), downParents())); } /** * Navigates to the given child of the current location, starting at index 0. * * @param n The index of the child to which to navigate. * @return An optional tree zipper focused on the child node at the given index, or none if there is no such child. */ public Option<TreeZipper<A>> getChild(final int n) { Option<TreeZipper<A>> r = none(); for (final P2<Stream<Tree<A>>, Stream<Tree<A>>> lr : splitChildren(Stream.nil(), tree.subForest()._1(), n)) { r = some(treeZipper(lr._1().head(), lr._1().tail()._1(), lr._2(), downParents())); } return r; } /** * Navigates to the first child of the current location, that satisfies the given predicate. * * @param p A predicate to be satisfied by the child node. * @return An optional tree zipper focused on the first child node that satisfies the given predicate, * or none if there is no such child. */ public Option<TreeZipper<A>> findChild(final F<Tree<A>, Boolean> p) { Option<TreeZipper<A>> r = none(); final F2<Stream<Tree<A>>, Stream<Tree<A>>, Option<P3<Stream<Tree<A>>, Tree<A>, Stream<Tree<A>>>>> split = new F2<Stream<Tree<A>>, Stream<Tree<A>>, Option<P3<Stream<Tree<A>>, Tree<A>, Stream<Tree<A>>>>>() { public Option<P3<Stream<Tree<A>>, Tree<A>, Stream<Tree<A>>>> f(final Stream<Tree<A>> acc, final Stream<Tree<A>> xs) { return xs.isNotEmpty() ? p.f(xs.head()) ? some(P.p(acc, xs.head(), xs.tail()._1())) : f(acc.cons(xs.head()), xs.tail()._1()) : Option.none(); } }; Stream<Tree<A>> subforest = tree.subForest()._1(); if (subforest.isNotEmpty()) { for (final P3<Stream<Tree<A>>, Tree<A>, Stream<Tree<A>>> ltr : split.f(Stream.nil(), subforest)) { r = some(treeZipper(ltr._2(), ltr._1(), ltr._3(), downParents())); } } return r; } private Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>> downParents() { return parents.cons(P.p(lefts, tree.root(), rights)); } private static <A> Option<P2<Stream<A>, Stream<A>>> splitChildren(final Stream<A> acc, final Stream<A> xs, final int n) { return n == 0 ? some(P.p(acc, xs)) : xs.isNotEmpty() ? splitChildren(acc.cons(xs.head()), xs.tail()._1(), n - 1) : Option.none(); } private static <A> Stream<P3<Stream<Tree<A>>, A, Stream<Tree<A>>>> lp3nil() { return nil(); } /** * Creates a new tree zipper focused on the root of the given tree. * * @param t A tree over which to create a new zipper. * @return a new tree zipper focused on the root of the given tree. */ public static <A> TreeZipper<A> fromTree(final Tree<A> t) { return treeZipper(t, Stream.nil(), Stream.nil(), TreeZipper.lp3nil()); } /** * Creates a new tree zipper focused on the first element of the given forest. * * @param ts A forest over which to create a new zipper. * @return a new tree zipper focused on the first element of the given forest. */ public static <A> Option<TreeZipper<A>> fromForest(final Stream<Tree<A>> ts) { return ts.isNotEmpty() ? some(treeZipper(ts.head(), Stream.nil(), ts.tail()._1(), TreeZipper.lp3nil())) : Option.none(); } /** * Returns the tree containing this location. * * @return the tree containing this location. */ public Tree<A> toTree() { return root().tree; } /** * Returns the forest containing this location. * * @return the forest containing this location. */ public Stream<Tree<A>> toForest() { final TreeZipper<A> r = root(); return combChildren(r.lefts, r.tree, r.rights); } /** * Returns the tree at the currently focused node. * * @return the tree at the currently focused node. */ public Tree<A> focus() { return tree; } /** * Returns the left siblings of the currently focused node. * * @return the left siblings of the currently focused node. */ public Stream<Tree<A>> lefts() { return lefts; } /** * Returns the right siblings of the currently focused node. * * @return the right siblings of the currently focused node. */ public Stream<Tree<A>> rights() { return rights; } /** * Indicates whether the current node is at the top of the tree. * * @return true if the current node is the root of the tree, otherwise false. */ public boolean isRoot() { return parents.isEmpty(); } /** * Indicates whether the current node is the leftmost tree in the current forest. * * @return true if the current node has no left siblings, otherwise false. */ public boolean isFirst() { return lefts.isEmpty(); } /** * Indicates whether the current node is the rightmost tree in the current forest. * * @return true if the current node has no siblings on its right, otherwise false. */ public boolean isLast() { return rights.isEmpty(); } /** * Indicates whether the current node is at the bottom of the tree. * * @return true if the current node has no child nodes, otherwise false. */ public boolean isLeaf() { return tree.subForest()._1().isEmpty(); } /** * Indicates whether the current node is a child node of another node. * * @return true if the current node has a parent node, otherwise false. */ public boolean isChild() { return !isRoot(); } /** * Indicates whether the current node has any child nodes. * * @return true if the current node has child nodes, otherwise false. */ public boolean hasChildren() { return !isLeaf(); } /** * Replaces the current node with the given tree. * * @param t A tree with which to replace the current node. * @return A new tree zipper in which the focused node is replaced with the given tree. */ public TreeZipper<A> setTree(final Tree<A> t) { return treeZipper(t, lefts, rights, parents); } /** * Modifies the current node with the given function. * * @param f A function with which to modify the current tree. * @return A new tree zipper in which the focused node has been transformed by the given function. */ public TreeZipper<A> modifyTree(final F<Tree<A>, Tree<A>> f) { return setTree(f.f(tree)); } /** * Modifies the label at the current node with the given function. * * @param f A function with which to transform the current node's label. * @return A new tree zipper with the focused node's label transformed by the given function. */ public TreeZipper<A> modifyLabel(final F<A, A> f) { return setLabel(f.f(getLabel())); } /** * Replaces the label of the current node with the given value. * * @param v The new value for the node's label. * @return A new tree zipper with the focused node's label replaced by the given value. */ public TreeZipper<A> setLabel(final A v) { return modifyTree(t -> Tree.node(v, t.subForest())); } /** * Returns the label at the current node. * * @return the label at the current node. */ public A getLabel() { return tree.root(); } /** * Inserts a tree to the left of the current position. The inserted tree becomes the current tree. * * @param t A tree to insert to the left of the current position. * @return A new tree zipper with the given tree in focus and the current tree on the right. */ public TreeZipper<A> insertLeft(final Tree<A> t) { return treeZipper(t, lefts, rights.cons(tree), parents); } /** * Inserts a tree to the right of the current position. The inserted tree becomes the current tree. * * @param t A tree to insert to the right of the current position. * @return A new tree zipper with the given tree in focus and the current tree on the left. */ public TreeZipper<A> insertRight(final Tree<A> t) { return treeZipper(t, lefts.cons(tree), rights, parents); } /** * Inserts a tree as the first child of the current node. The inserted tree becomes the current tree. * * @param t A tree to insert. * @return A new tree zipper with the given tree in focus, as the first child of the current node. */ public TreeZipper<A> insertDownFirst(final Tree<A> t) { return treeZipper(t, Stream.nil(), tree.subForest()._1(), downParents()); } /** * Inserts a tree as the last child of the current node. The inserted tree becomes the current tree. * * @param t A tree to insert. * @return A new tree zipper with the given tree in focus, as the last child of the current node. */ public TreeZipper<A> insertDownLast(final Tree<A> t) { return treeZipper(t, tree.subForest()._1().reverse(), Stream.nil(), downParents()); } /** * Inserts a tree at the specified location in the current node's stream of children. The inserted tree * becomes the current node. * * @param n The index at which to insert the given tree, starting at 0. * @param t A tree to insert. * @return A new tree zipper with the given tree in focus, at the specified index in the current node's stream * of children, or None if the current node has fewer than <code>n</code> children. */ public Option<TreeZipper<A>> insertDownAt(final int n, final Tree<A> t) { Option<TreeZipper<A>> r = none(); for (final P2<Stream<Tree<A>>, Stream<Tree<A>>> lr : splitChildren(Stream.nil(), tree.subForest()._1(), n)) { r = some(treeZipper(t, lr._1(), lr._2(), downParents())); } return r; } /** * Removes the current node from the tree. The new position becomes the right sibling, or the left sibling * if the current node has no right siblings, or the parent node if the current node has no siblings. * * @return A new tree zipper with the current node removed. */ public Option<TreeZipper<A>> delete() { Option<TreeZipper<A>> r = none(); if (rights.isNotEmpty()) r = some(treeZipper(rights.head(), lefts, rights.tail()._1(), parents)); else if (lefts.isNotEmpty()) r = some(treeZipper(lefts.head(), lefts.tail()._1(), rights, parents)); else for (final TreeZipper<A> loc : parent()) r = some(loc.modifyTree(t -> node(t.root(), Stream.nil()))); return r; } /** * Zips the nodes in this zipper with a boolean that indicates whether that node has focus. * All of the booleans will be false, except for the focused node. * * @return A new zipper of pairs, with each node of this zipper paired with a boolean that is true if that * node has focus, and false otherwise. */ public TreeZipper<P2<A, Boolean>> zipWithFocus() { final F<A, P2<A, Boolean>> f = flip(P.<A, Boolean>p2()).f(false); return map(f).modifyLabel(P2.map2_(Booleans.not)); } /** * Maps the given function across this zipper (covariant functor pattern). * * @param f A function to map across this zipper. * @return A new zipper with the given function applied to the label of every node. */ public <B> TreeZipper<B> map(final F<A, B> f) { final F<Tree<A>, Tree<B>> g = Tree.<A, B>fmap_().f(f); final F<Stream<Tree<A>>, Stream<Tree<B>>> h = Stream.<Tree<A>, Tree<B>>map_().f(g); return treeZipper(tree.fmap(f), lefts.map(g), rights.map(g), parents.map( p -> p.map1(h).map2(f).map3(h))); } /** * First-class conversion of a Tree to the corresponding tree zipper. * * @return A function that takes a tree to its tree zipper representation. */ public static <A> F<Tree<A>, TreeZipper<A>> fromTree() { return TreeZipper::fromTree; } /** * A first-class version of the left() function. * * @return A function that focuses the given tree zipper on its left sibling. */ public static <A> F<TreeZipper<A>, Option<TreeZipper<A>>> left_() { return TreeZipper::left; } /** * A first-class version of the right() function. * * @return A function that focuses the given tree zipper on its right sibling. */ public static <A> F<TreeZipper<A>, Option<TreeZipper<A>>> right_() { return TreeZipper::right; } /** * Returns a zipper over the tree of all possible permutations of this tree zipper (comonad pattern). * This tree zipper becomes the focused node of the new zipper. * * @return A tree zipper over the tree of all possible permutations of this tree zipper. */ public TreeZipper<TreeZipper<A>> positions() { final Tree<TreeZipper<A>> t = unfoldTree(TreeZipper.<A>dwn()).f(this); final Stream<Tree<TreeZipper<A>>> l = uf(TreeZipper.left_()); final Stream<Tree<TreeZipper<A>>> r = uf(TreeZipper.right_()); final Stream<P3<Stream<Tree<TreeZipper<A>>>, TreeZipper<A>, Stream<Tree<TreeZipper<A>>>>> p = unfold( o -> { Option<P2< P3<Stream<Tree<TreeZipper<A>>>, TreeZipper<A>, Stream<Tree<TreeZipper<A>>>>, Option<TreeZipper<A>>>> r1 = none(); for (final TreeZipper<A> z : o) { r1 = some(P.p(P.p(z.uf(TreeZipper.left_()), z, z.uf(TreeZipper.right_())), z.parent())); } return r1; }, parent()); return treeZipper(t, l, r, p); } private Stream<Tree<TreeZipper<A>>> uf(final F<TreeZipper<A>, Option<TreeZipper<A>>> f) { return unfold( o -> { Option<P2<Tree<TreeZipper<A>>, Option<TreeZipper<A>>>> r = none(); for (final TreeZipper<A> c : o) { r = some(P.p(unfoldTree(TreeZipper.<A>dwn()).f(c), f.f(c))); } return r; }, f.f(this)); } private static <A> F<TreeZipper<A>, P2<TreeZipper<A>, P1<Stream<TreeZipper<A>>>>> dwn() { return tz -> P.p(tz, new P1<Stream<TreeZipper<A>>>() { private F<Option<TreeZipper<A>>, Option<P2<TreeZipper<A>, Option<TreeZipper<A>>>>> fwd() { return o -> { Option<P2<TreeZipper<A>, Option<TreeZipper<A>>>> r = none(); for (final TreeZipper<A> c : o) { r = some(P.p(c, c.right())); } return r; }; } public Stream<TreeZipper<A>> _1() { return unfold(fwd(), tz.firstChild()); } }); } /** * Maps the given function over the tree of all positions for this zipper (comonad pattern). Returns a zipper * over the tree of results of the function application. * * @param f A function to map over the tree of all positions for this zipper. * @return A zipper over the tree of results of the function application. */ public <B> TreeZipper<B> cobind(final F<TreeZipper<A>, B> f) { return positions().map(f); } /** * A first-class version of the findChild function. * * @return a function that finds the first child, of a given tree zipper, that matches a given predicate. */ public static <A> F2<F<Tree<A>, Boolean>, TreeZipper<A>, Option<TreeZipper<A>>> findChild() { return (f, az) -> az.findChild(f); } /** * Zips this TreeZipper with another, applying the given function lock-step over both zippers in all directions. * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. * * @param bs A TreeZipper to zip this one with. * @param f A function with which to zip together the two TreeZippers. * @return The result of applying the given function over this TreeZipper and the given TreeZipper, location-wise. */ public <B, C> TreeZipper<C> zipWith(final TreeZipper<B> bs, final F2<A, B, C> f) { return F2Functions.zipTreeZipperM(f).f(this, bs); } /** * Zips this TreeZipper with another, applying the given function lock-step over both zippers in all directions. * The structure of the resulting TreeZipper is the structural intersection of the two TreeZippers. * * @param bs A TreeZipper to zip this one with. * @param f A function with which to zip together the two TreeZippers. * @return The result of applying the given function over this TreeZipper and the given TreeZipper, location-wise. */ public <B, C> TreeZipper<C> zipWith(final TreeZipper<B> bs, final F<A, F<B, C>> f) { return zipWith(bs, uncurryF2(f)); } }