All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jenetics.ext.util.Tree Maven / Gradle / Ivy

/*
 * Java Genetic Algorithm Library (jenetics-8.1.0).
 * Copyright (c) 2007-2024 Franz Wilhelmstötter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 *    Franz Wilhelmstötter ([email protected])
 */
package io.jenetics.ext.util;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.Spliterators.spliteratorUnknownSize;
import static io.jenetics.internal.util.SerialIO.readIntArray;
import static io.jenetics.internal.util.SerialIO.writeIntArray;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import io.jenetics.util.ISeq;
import io.jenetics.util.Self;

/**
 * General purpose tree structure. The interface only contains tree read methods.
 * For a mutable tree implementation have a look at the {@link TreeNode} class.
 *
 * @see TreeNode
 *
 * @author Franz Wilhelmstötter
 * @version 7.0
 * @since 3.9
 */
public interface Tree> extends Self, Iterable {

	/* *************************************************************************
	 * Basic (abstract) operations. All other tree operations can be derived
	 * from these methods.
	 **************************************************************************/

	/**
	 * Return the value of the current {@code Tree} node. The value may be
	 * {@code null}.
	 *
	 * @return the value of the current {@code Tree} node
	 */
	V value();

	/**
	 * Return the parent node of this tree node.
	 *
	 * @return the parent node, or {@code Optional.empty()} if this node is the
	 *         root of the tree
	 */
	Optional parent();

	/**
	 * Return the child node with the given index.
	 *
	 * @param index the child index
	 * @return the child node with the given index
	 * @throws IndexOutOfBoundsException  if the {@code index} is out of
	 *         bounds ({@code [0, childCount())})
	 */
	T childAt(final int index);

	/**
	 * Return the number of children this tree node consists of.
	 *
	 * @return the number of children this tree node consists of
	 */
	int childCount();


	/* *************************************************************************
	 * Derived operations
	 **************************************************************************/

	/**
	 * Return an iterator of the children of this {@code Tree} node.
	 *
	 * @return an iterator of the children of this {@code Tree} node.
	 */
	default Iterator childIterator() {
		return new TreeChildIterator(self());
	}

	/**
	 * Return a forward-order stream of this node's children.
	 *
	 * @return a stream of children of {@code this} node
	 */
	default Stream childStream() {
		return StreamSupport.stream(
			Spliterators.spliterator(
				childIterator(),
				childCount(),
				Spliterator.SIZED | Spliterator.ORDERED
			),
			false
		);
	}

	/**
	 * Returns {@code true} if this node is the root of the tree.
	 *
	 * @return {@code true} if this node is the root of its tree, {@code false}
	 *         otherwise
	 */
	default boolean isRoot() {
		return parent().isEmpty();
	}

	/**
	 * Returns the depth of the tree rooted at this node. The depth of a
	 * tree is the longest distance from {@code this} node to a leaf. If
	 * {@code this} node has no children, 0 is returned. This operation is much
	 * more expensive than {@link #level()} because it must effectively traverse
	 * the entire tree rooted at {@code this} node.
	 *
	 * @return the depth of the tree whose root is this node
	 */
	default int depth() {
		final Iterator it = breadthFirstIterator();

		T last = null;
		while (it.hasNext()) {
			last = it.next();
		}

		assert last != null;
		return last.level() - level();
	}

	/**
	 * Returns the number of levels above this node. The level of a tree
	 * is the distance from the root to {@code this} node. If {@code this} node
	 * is the root, returns 0.
	 *
	 * @return the number of levels above this node
	 */
	default int level() {
		Optional ancestor = Optional.of(self());
		int levels = 0;
		while ((ancestor = ancestor.flatMap(Tree::parent)).isPresent()) {
			++levels;
		}

		return levels;
	}

	/**
	 * Returns the index of the specified child in this node's child array, or
	 * {@code -1} if {@code this} node doesn't contain the given {@code child}.
	 * This method performs a linear search and is O(n) where {@code n} is the
	 * number of children.
	 *
	 * @param child  the TreeNode to search for among this node's children
	 * @throws NullPointerException if the given {@code child} is {@code null}
	 * @return the index of the node in this node's child array, or {@code -1}
	 *         if the node could not be found
	 */
	default int indexOf(final Tree child) {
		int index = -1;
		for (int i = 0, n = childCount(); i < n && index == -1; ++i) {
			if (childAt(i).identical(child)) {
				index = i;
			}
		}

		return index;
	}

	/**
	 * Return the number of nodes of {@code this} node (subtree).
	 *
	 * @return the number of nodes of {@code this} node (subtree)
	 */
	default int size() {
		return Trees.countChildren(this) + 1;
	}

	/**
	 * A tree is considered empty if it's {@link #value()} is
	 * {@code null} and has no children and parent. A newly created tree node
	 * with no value is empty.
	 *
	 * {@snippet lang="java":
	 * final Tree tree = TreeNode.of();
	 * assert tree.isEmpty();
	 * }
	 *
	 * @since 7.0
	 *
	 * @return {@code true} if {@code this} tree is empty, {@code false}
	 *          otherwise
	 */
	default boolean isEmpty() {
		return value() == null && childCount() == 0 && parent().isEmpty();
	}


	/* *************************************************************************
	 * Query operations
	 **************************************************************************/

	/**
	 * Return the child node at the given {@code path}. A path consists of the
	 * child index at a give level, starting with level 1. (The root note has
	 * level zero.) {@code tree.childAtPath(Path.of(2))} will return the third
	 * child node of {@code this} node, if it exists and
	 * {@code tree.childAtPath(Path.of(2, 0))} will return the first child of
	 * the third child of {@code this node}.
	 *
	 * @since 4.4
	 *
	 * @see #childAtPath(int...)
	 *
	 * @param path the child path
	 * @return the child node at the given {@code path}
	 * @throws NullPointerException if the given {@code path} array is
	 *         {@code null}
	 */
	default Optional childAtPath(final Path path) {
		T node = self();
		for (int i = 0; i < path.length() && node != null; ++i) {
			node = path.get(i) < node.childCount()
				? node.childAt(path.get(i))
				: null;
		}

		return Optional.ofNullable(node);
	}

	/**
	 * Return the child node at the given {@code path}. A path consists of the
	 * child index at a give level, starting with level 1. (The root note has
	 * level zero.) {@code tree.childAtPath(2)} will return the third child node
	 * of {@code this} node, if it exists and {@code tree.childAtPath(2, 0)} will
	 * return the first child of the third child of {@code this node}.
	 *
	 * @since 4.3
	 *
	 * @see #childAtPath(Path)
	 *
	 * @param path the child path
	 * @return the child node at the given {@code path}
	 * @throws NullPointerException if the given {@code path} array is
	 *         {@code null}
	 * @throws IllegalArgumentException if one of the path elements is smaller
	 *         than zero
	 */
	default Optional childAtPath(final int... path) {
		return childAtPath(Path.of(path));
	}

	/**
	 * Return {@code true} if the given {@code node} is an ancestor of
	 * {@code this} node. This operation is at worst {@code O(h)} where {@code h}
	 * is the distance from the root to {@code this} node.
	 *
	 * @param node the node to test
	 * @return {@code true} if the given {@code node} is an ancestor of
	 *         {@code this} node, {@code false} otherwise
	 * @throws NullPointerException if the given {@code node} is {@code null}
	 */
	default boolean isAncestor(final Tree node) {
		requireNonNull(node);

		Optional ancestor = Optional.of(self());
		boolean result;
		do {
			result = ancestor.filter(a -> a.identical(node)).isPresent();
		} while (!result &&
				(ancestor = ancestor.flatMap(Tree::parent)).isPresent());

		return result;
	}

	/**
	 * Return {@code true} if the given {@code node} is a descendant of
	 * {@code this} node. If the given {@code node} is {@code null},
	 * {@code false} is returned. This operation is at worst {@code O(h)} where
	 * {@code h} is the distance from the root to {@code this} node.
	 *
	 * @param node the node to test as descendant of this node
	 * @return {@code true} if this node is an ancestor of the given {@code node}
	 * @throws NullPointerException if the given {@code node} is {@code null}
	 */
	default boolean isDescendant(final Tree node) {
		return requireNonNull(node).isAncestor(this);
	}

	/**
	 * Returns the nearest common ancestor to this node and the given {@code node}.
	 * A node is considered an ancestor of itself.
	 *
	 * @param node {@code node} to find common ancestor with
	 * @return nearest ancestor common to this node and the given {@code node},
	 *         or {@link Optional#empty()} if no common ancestor exists.
	 * @throws NullPointerException if the given {@code node} is {@code null}
	 */
	default Optional sharedAncestor(final T node) {
		requireNonNull(node);

		T ancestor = null;
		if (node.identical(this)) {
			ancestor = self();
		} else {
			final int level1 = level();
			final int level2 = node.level();

			T node1;
			T node2;
			int diff;
			if (level2 > level1) {
				diff = level2 - level1;
				node1 = node;
				node2 = self();
			} else {
				diff = level1 - level2;
				node1 = self();
				node2 = node;
			}

			while (diff > 0 && node1 != null) {
				node1 = node1.parent().orElse(null);
				--diff;
			}

			do {
				if (node1 != null && node1.identical(node2)) {
					ancestor = node1;
				}
				node1 = node1 != null
					? node1.parent().orElse(null)
					: null;
				node2 = node2.parent().orElse(null);
			} while (node1 != null && node2 != null && ancestor == null);
		}

		return Optional.ofNullable(ancestor);
	}

	/**
	 * Returns true if and only if the given {@code node} is in the same tree as
	 * {@code this} node.
	 *
	 * @param node the other node to check
	 * @return true if the given {@code node} is in the same tree as {@code this}
	 *         node, {@code false} otherwise.
	 * @throws NullPointerException if the given {@code node} is {@code null}
	 */
	default boolean isRelated(final Tree node) {
		requireNonNull(node);
		return node.root().identical(root());
	}

	/**
	 * Returns the path from the root, to get to this node. The last element in
	 * the path is this node.
	 *
	 * @since 5.1
	 *
	 * @return an array of TreeNode objects giving the path, where the
	 *         first element in the path is the root and the last
	 *         element is this node.
	 */
	default ISeq pathElements() {
		return Trees.pathElementsFromRoot(self(), 0).toISeq();
	}

	/**
	 * Return the {@link Path} of {@code this} tree, such that
	 * {@snippet lang="java":
	 * final Tree tree = null; // @replace substring='null' replacement="..."
	 * final Tree.Path path = tree.path();
	 * assert tree == tree.getRoot()
	 *     .childAtPath(path)
	 *     .orElse(null);
	 * }
	 *
	 * @since 5.1
	 *
	 * @return the path from the root element to {@code this} node.
	 */
	default Path path() {
		final int[] p = Trees.pathFromRoot(self(), 0);
		return Path.of(p);
	}

	/**
	 * Returns the root of the tree that contains this node. The root is the
	 * ancestor with no parent.
	 *
	 * @return the root of the tree that contains this node
	 */
	default T root() {
		T anc = self();
		T prev;

		do {
			prev = anc;
			anc = anc.parent().orElse(null);
		} while (anc != null);

		return prev;
	}

	/* *************************************************************************
	 * Child query operations
	 **************************************************************************/

	/**
	 * Return {@code true} if the given {@code node} is a child of {@code this}
	 * node.
	 *
	 * @param node the other node to check
	 * @return {@code true} if {@code node}is a child, {@code false} otherwise
	 * @throws NullPointerException if the given {@code node} is {@code null}
	 */
	default boolean isChild(final Tree node) {
		requireNonNull(node);
		return childCount() != 0 &&
			node.parent().equals(Optional.of(self()));
	}

	/**
	 * Return the first child of {@code this} node, or {@code Optional.empty()}
	 * if {@code this} node has no children.
	 *
	 * @return the first child of this node
	 */
	default Optional firstChild() {
		return childCount() > 0
			? Optional.of(childAt(0))
			: Optional.empty();
	}

	/**
	 * Return the last child of {@code this} node, or {@code Optional.empty()}
	 * if {@code this} node has no children.
	 *
	 * @return the last child of this node
	 */
	default Optional lastChild() {
		return childCount() > 0
			? Optional.of(childAt(childCount() - 1))
			: Optional.empty();
	}

	/**
	 * Return the child which comes immediately after {@code this} node. This
	 * method performs a linear search of this node's children for {@code child}
	 * and is {@code O(n)} where n is the number of children.
	 *
	 * @param child the child node
	 * @return  the child of this node that immediately follows the {@code child},
	 *          or {@code Optional.empty()} if the given {@code node} is the
	 *          first node.
	 * @throws NullPointerException if the given {@code child} is {@code null}
	 */
	default Optional childAfter(final Tree child) {
		requireNonNull(child);

		final int index = indexOf(child);
		if (index == -1) {
			throw new IllegalArgumentException("The given node is not a child.");
		}

		return index < childCount() - 1
			? Optional.of(childAt(index + 1))
			: Optional.empty();
	}

	/**
	 * Return the child which comes immediately before {@code this} node. This
	 * method performs a linear search of this node's children for {@code child}
	 * and is {@code O(n)} where n is the number of children.
	 *
	 * @param child the child node
	 * @return  the child of this node that immediately precedes the {@code child},
	 *          or {@code null} if the given {@code node} is the first node.
	 * @throws NullPointerException if the given {@code child} is {@code null}
	 */
	default Optional childBefore(final Tree child) {
		requireNonNull(child);

		final int index = indexOf(child);
		if (index == -1) {
			throw new IllegalArgumentException("The given node is not a child.");
		}

		return index > 0
			? Optional.of(childAt(index - 1))
			: Optional.empty();
	}

	/**
	 * Return the node that follows {@code this} node in a pre-order traversal
	 * of {@code this} tree node. Return {@code Optional.empty()} if this node
	 * is the last node of the traversal. This is an inefficient way to traverse
	 * the entire tree use an iterator instead.
	 *
	 * @see #preorderIterator
	 * @return the node that follows this node in a pre-order traversal, or
	 *        {@code Optional.empty()} if this node is last
	 */
	default Optional nextNode() {
		Optional next = Optional.empty();

		if (childCount() == 0) {
			T node = self();
			while (node != null && (next = node.nextSibling()).isEmpty()) {
				node = node.parent().orElse(null);
			}
		} else {
			next = Optional.of(childAt(0));
		}

		return next;
	}

	/**
	 * Return the node that precedes this node in a pre-order traversal of
	 * {@code this} tree node. Returns {@code Optional.empty()} if this node is
	 * the first node of the traversal, the root of the tree. This is an
	 * inefficient way to traverse the entire tree; use an iterator instead.
	 *
	 * @see #preorderIterator
	 * @return the node that precedes this node in a pre-order traversal, or
	 *         {@code Optional.empty()} if this node is the first
	 */
	default Optional previousNode() {
		Optional node = Optional.empty();

		if (parent().isPresent()) {
			final Optional prev = previousSibling();
			if (prev.isPresent()) {
				node = prev.get().childCount() == 0
					? prev
					: prev.map(Tree::lastLeaf);
			} else {
				node = parent();
			}
		}

		return node;
	}

	/* *************************************************************************
	 * Sibling query operations
	 **************************************************************************/

	/**
	 * Test if the given {@code node} is a sibling of {@code this} node.
	 *
	 * @param node node to test as sibling of this node
	 * @return {@code true} if the {@code node} is a sibling of {@code this}
	 *         node
	 * @throws NullPointerException if the given {@code node} is {@code null}
	 */
	default boolean isSibling(final Tree node) {
		return identical(requireNonNull(node)) ||
			parent().equals(node.parent());
	}

	/**
	 * Return the number of siblings of {@code this} node. A node is its own
	 * sibling (if it has no parent or no siblings, this method returns
	 * {@code 1}).
	 *
	 * @return the number of siblings of {@code this} node
	 */
	default int siblingCount() {
		return parent().map(Tree::childCount).orElse(1);
	}

	/**
	 * Return the next sibling of {@code this} node in the parent's children
	 * array, or {@code null} if {@code this} node has no parent, or it is the
	 * last child of the paren. This method performs a linear search that is
	 * {@code O(n)} where n is the number of children; to traverse the entire
	 * array, use the iterator of the parent instead.
	 *
	 * @see #childStream()
	 * @return the sibling of {@code this} node that immediately follows
	 *         {@code this} node
	 */
	default Optional nextSibling() {
		return parent().flatMap(p -> p.childAfter(self()));
	}

	/**
	 * Return the previous sibling of {@code this} node in the parent's children
	 * list, or {@code Optional.empty()} if this node has no parent or is the
	 * parent's first child. This method performs a linear search that is O(n)
	 * where n is the number of children.
	 *
	 * @return the sibling of {@code this} node that immediately precedes this
	 *         node
	 */
	default Optional previousSibling() {
		return parent().flatMap(p -> p.childBefore(self()));
	}


	/* *************************************************************************
	 * Leaf query operations
	 **************************************************************************/

	/**
	 * Return {@code true} if {@code this} node has no children.
	 *
	 * @return {@code true} if {@code this} node has no children, {@code false}
	 *         otherwise
	 */
	default boolean isLeaf() {
		return childCount() == 0;
	}

	/**
	 * Return the first leaf that is a descendant of {@code this} node; either
	 * this node or its first child's first leaf. {@code this} node is returned
	 * if it is a leaf.
	 *
	 * @see #isLeaf
	 * @see  #isDescendant
	 * @return the first leaf in the subtree rooted at this node
	 */
	default T firstLeaf() {
		T leaf = self();
		while (!leaf.isLeaf()) {
			leaf = leaf.firstChild().orElseThrow(AssertionError::new);
		}

		return leaf;
	}

	/**
	 * Return the last leaf that is a descendant of this node; either
	 * {@code this} node or its last child's last leaf. Returns {@code this}
	 * node if it is a leaf.
	 *
	 * @see #isLeaf
	 * @see #isDescendant
	 * @return the last leaf in this subtree
	 */
	default T lastLeaf() {
		T leaf = self();
		while (!leaf.isLeaf()) {
			leaf = leaf.lastChild().orElseThrow(AssertionError::new);
		}

		return leaf;
	}

	/**
	 * Returns the leaf after {@code this} node or {@code Optional.empty()} if
	 * this node is the last leaf in the tree.
	 * 

* In order to determine the next node, this method first performs a linear * search in the parent's child-list in order to find the current node. *

* That implementation makes the operation suitable for short traversals * from a known position. But to traverse all the leaves in the tree, you * should use {@link #depthFirstIterator()} to iterator the nodes in the * tree and use {@link #isLeaf()} on each node to determine which are leaves. * * @see #depthFirstIterator * @see #isLeaf * @return return the next leaf past this node */ default Optional nextLeaf() { return nextSibling() .map(Tree::firstLeaf) .or(() -> parent().flatMap(Tree::nextLeaf)); } /** * Return the leaf before {@code this} node or {@code null} if {@code this} * node is the first leaf in the tree. *

* In order to determine the previous node, this method first performs a * linear search in the parent's child-list in order to find the current * node. *

* That implementation makes the operation suitable for short traversals * from a known position. But to traverse all the leaves in the tree, you * should use {@link #depthFirstIterator()} to iterate the nodes in the tree * and use {@link #isLeaf()} on each node to determine which are leaves. * * @see #depthFirstIterator * @see #isLeaf * @return returns the leaf before {@code this} node */ default Optional previousLeaf() { return previousSibling() .map(Tree::lastLeaf) .or(() -> parent().flatMap(Tree::previousLeaf)); } /** * Returns the total number of leaves that are descendants of this node. * If this node is a leaf, returns {@code 1}. This method is {@code O(n)}, * where n is the number of descendants of {@code this} node. * * @see #isLeaf() * @return the number of leaves beneath this node */ default int leafCount() { return (int)leaves().count(); } /** * Return a stream of leaves that are descendants of this node. * * @since 7.0 * * @return a stream of leaves that are descendants of this node */ default Stream leaves() { return breadthFirstStream().filter(Tree::isLeaf); } /* ************************************************************************* * Tree traversing. **************************************************************************/ /** * Return an iterator that traverses the subtree rooted at {@code this} * node in breadth-first order. The first node returned by the iterator is * {@code this} node. *

* Modifying the tree by inserting, removing, or moving a node invalidates * any iterator created before the modification. * * @see #depthFirstIterator * @return an iterator for traversing the tree in breadth-first order */ default Iterator breadthFirstIterator() { return new TreeNodeBreadthFirstIterator<>(self()); } /** * Return an iterator that traverses the subtree rooted at {@code this}. * The first node returned by the iterator is {@code this} node. *

* Modifying the tree by inserting, removing, or moving a node invalidates * any iterator created before the modification. * * @see #breadthFirstIterator * @return an iterator for traversing the tree in breadth-first order */ @Override default Iterator iterator() { return breadthFirstIterator(); } /** * Return a stream that traverses the subtree rooted at {@code this} node in * breadth-first order. The first node returned by the stream is * {@code this} node. * * @see #depthFirstIterator * @see #stream() * @return a stream for traversing the tree in breadth-first order */ default Stream breadthFirstStream() { return StreamSupport .stream(spliteratorUnknownSize(breadthFirstIterator(), 0), false); } /** * Return a stream that traverses the subtree rooted at {@code this} node in * breadth-first order. The first node returned by the stream is * {@code this} node. * * @see #breadthFirstStream * @return a stream for traversing the tree in breadth-first order */ default Stream stream() { return breadthFirstStream(); } /** * Return an iterator that traverses the subtree rooted at {@code this} node * in pre-order. The first node returned by the iterator is {@code this} * node. *

* Modifying the tree by inserting, removing, or moving a node invalidates * any iterator created before the modification. * * @see #postorderIterator * @return an iterator for traversing the tree in pre-order */ default Iterator preorderIterator() { return new TreeNodePreorderIterator<>(self()); } /** * Return a stream that traverses the subtree rooted at {@code this} node * in pre-order. The first node returned by the stream is {@code this} node. *

* Modifying the tree by inserting, removing, or moving a node invalidates * any iterator created before the modification. * * @see #preorderIterator * @return a stream for traversing the tree in pre-order */ default Stream preorderStream() { return StreamSupport .stream(spliteratorUnknownSize(preorderIterator(), 0), false); } /** * Return an iterator that traverses the subtree rooted at {@code this} * node in post-order. The first node returned by the iterator is the * leftmost leaf. This is the same as a depth-first traversal. * * @see #depthFirstIterator * @see #preorderIterator * @return an iterator for traversing the tree in post-order */ default Iterator postorderIterator() { return new TreeNodePostorderIterator<>(self()); } /** * Return a stream that traverses the subtree rooted at {@code this} node in * post-order. The first node returned by the iterator is the leftmost leaf. * This is the same as a depth-first traversal. * * @see #depthFirstIterator * @see #preorderIterator * @return a stream for traversing the tree in post-order */ default Stream postorderStream() { return StreamSupport .stream(spliteratorUnknownSize(postorderIterator(), 0), false); } /** * Return an iterator that traverses the subtree rooted at {@code this} node * in depth-first order. The first node returned by the iterator is the * leftmost leaf. This is the same as a postorder traversal. *

* Modifying the tree by inserting, removing, or moving a node invalidates * any iterator created before the modification. * * @see #breadthFirstIterator * @see #postorderIterator * @return an iterator for traversing the tree in depth-first order */ default Iterator depthFirstIterator() { return postorderIterator(); } /** * Return a stream that traverses the subtree rooted at {@code this} node in * depth-first. The first node returned by the iterator is the leftmost leaf. * This is the same as a post-order traversal. * * @see #depthFirstIterator * @see #preorderIterator * @return a stream for traversing the tree in post-order */ default Stream depthFirstStream() { return postorderStream(); } /** * Return an iterator that follows the path from {@code ancestor} to * {@code this} node. The iterator return {@code ancestor} as a first element, * The creation of the iterator is O(m), where m is the number of nodes * between {@code this} node and the {@code ancestor}, inclusive. *

* Modifying the tree by inserting, removing, or moving a node invalidates * any iterator created before the modification. * * @see #isAncestor * @see #isDescendant * @param ancestor the ancestor node * @return an iterator for following the path from an ancestor of {@code this} * node to this one * @throws IllegalArgumentException if the {@code ancestor} is not an * ancestor of this node * @throws NullPointerException if the given {@code ancestor} is {@code null} */ default Iterator pathFromAncestorIterator(final Tree ancestor) { return new TreeNodePathIterator<>(ancestor, self()); } /** * Return the path of {@code this} child node from the root node. You will * get {@code this} node, if you call {@link #childAtPath(Path)} on the * root node of {@code this} node. * {@snippet lang="java": * final Tree node = null; // @replace substring='null' replacement="..." * final Tree root = node.getRoot(); * final int[] path = node.childPath(); * assert node == root.childAtPath(path); * } * * @since 4.4 * * @see #childAtPath(Path) * * @return the path of {@code this} child node from the root node. */ default Path childPath() { final Iterator it = pathFromAncestorIterator(root()); final int[] path = new int[level()]; T tree = null; int index = 0; while (it.hasNext()) { final T child = it.next(); if (tree != null) { path[index++] = tree.indexOf(child); } tree = child; } assert index == path.length; return new Path(path); } /** * Tests whether {@code this} node is the same as the {@code other} node. * The default implementation returns the object identity, * {@code this == other}, of the two objects, but other implementations may * use different criteria for checking the identity. * * @param other the {@code other} node * @return {@code true} if the {@code other} node is the same as {@code this} * node. */ default boolean identical(final Tree other) { return this == other; } /** * Performs a reduction on the elements of {@code this} tree, using an * associative reduction function. This can be used for evaluating a given * expression tree in pre-order. * {@snippet lang="java": * final Tree formula = TreeNode.parse("add(sub(6,div(230,10)),mul(5,6))"); * final double result = formula.reduce(new Double[0], (op, args) -> * switch (op) { * case "add" -> args[0] + args[1]; * case "sub" -> args[0] - args[1]; * case "mul" -> args[0] * args[1]; * case "div" -> args[0] / args[1]; * default -> Double.parseDouble(op); * } * ); * assert result == 13.0; * } * * @since 7.1 * * @param neutral the neutral element of the reduction. In most cases this will * be {@code new U[0]}. * @param reducer the reduce function * @param the result type * @return the result of the reduction, or {@code null} if {@code this} tree * is empty ({@code isEmpty() == true}) */ default U reduce( final U[] neutral, final BiFunction reducer ) { requireNonNull(neutral); requireNonNull(reducer); @SuppressWarnings("unchecked") final class Reducing { private U reduce(final Tree node) { return node.isLeaf() ? reducer.apply(node.value(), neutral) : reducer.apply(node.value(), children(node)); } private U[] children(final Tree node) { final U[] values = (U[])Array.newInstance( neutral.getClass().getComponentType(), node.childCount() ); for (int i = 0; i < node.childCount(); ++i) { values[i] = reduce(node.childAt(i)); } return values; } } return isEmpty() ? null : new Reducing().reduce(this); } /* ************************************************************************* * 'toString' methods **************************************************************************/ /** * Return a compact string representation of the given tree. The tree *

	 *  mul
	 *  ├── div
	 *  │   ├── cos
	 *  │   │   └── 1.0
	 *  │   └── cos
	 *  │       └── π
	 *  └── sin
	 *      └── mul
	 *          ├── 1.0
	 *          └── z
	 *  
* is printed as *
	 *  mul(div(cos(1.0),cos(π)),sin(mul(1.0,z)))
	 * 
* * @since 4.3 * * @see #toParenthesesString() * @see TreeFormatter#PARENTHESES * * @param mapper the {@code mapper} which converts the tree value to a string * @return the string representation of the given tree */ default String toParenthesesString(final Function mapper) { return TreeFormatter.PARENTHESES.format(this, mapper); } /** * Return a compact string representation of the given tree. The tree *
	 *  mul
	 *  ├── div
	 *  │   ├── cos
	 *  │   │   └── 1.0
	 *  │   └── cos
	 *  │       └── π
	 *  └── sin
	 *      └── mul
	 *          ├── 1.0
	 *          └── z
	 *  
* is printed as *
	 *  mul(div(cos(1.0), cos(π)), sin(mul(1.0, z)))
	 * 
* * @since 4.3 * * @see #toParenthesesString(Function) * @see TreeFormatter#PARENTHESES * * @return the string representation of the given tree * @throws NullPointerException if the {@code mapper} is {@code null} */ default String toParenthesesString() { return toParenthesesString(Objects::toString); } /* ************************************************************************* * Static helper methods. **************************************************************************/ /** * Calculates the hash code of the given tree. * * @param tree the tree where the hash is calculated from * @return the hash code of the tree * @throws NullPointerException if the given {@code tree} is {@code null} */ static int hashCode(final Tree tree) { return tree != null ? tree.breadthFirstStream() .mapToInt(node -> 31*Objects.hashCode(node.value()) + 37) .sum() + 17 : 0; } /** * Checks if the two given trees has the same structure with the same values. * * @param a the first tree * @param b the second tree * @return {@code true} if the two given trees are structurally equals, * {@code false} otherwise */ static boolean equals(final Tree a, final Tree b) { return Trees.equals(a, b); } /** * Return a string representation of the given tree, like the following * example. * *
	 *  mul(div(cos(1.0), cos(π)), sin(mul(1.0, z)))
	 * 
* * This method is intended to be used when override the * {@link Object#toString()} method. * * @param tree the input tree * @return the string representation of the given tree */ static String toString(final Tree tree) { return tree.toParenthesesString(); } /* ************************************************************************* * Inner classes **************************************************************************/ /** * This class represents the path to child within a given tree. It allows * pointing (and fetch) a tree child. * * @see Tree#childAtPath(Path) * * @author Franz Wilhelmstötter * @version 7.2 * @since 4.4 */ final class Path implements Comparable, Serializable { @Serial private static final long serialVersionUID = 1L; private final int[] _path; private Path(final int[] path) { _path = requireNonNull(path); } /** * Return the path length, which is the level of the child {@code this} * path points to. * * @return the path length */ public int length() { return _path.length; } /** * Return the child index at the given index (child level). * * @param index the path index * @return the child index at the given child level * @throws IndexOutOfBoundsException if the index is not with the range * {@code [0, length())} */ public int get(final int index) { return _path[index]; } /** * Return the path as {@code int[]} array. * * @return the path as {@code int[]} array */ public int[] toArray() { return _path.clone(); } /** * Appends the given {@code path} to {@code this} one. * * @param path the path to append * @return a new {@code Path} with the given {@code path} appended * @throws NullPointerException if the given {@code path} is {@code null} */ public Path append(final Path path) { final int[] p = new int[length() + path.length()]; System.arraycopy(_path, 0, p, 0, length()); System.arraycopy(path._path, 0, p, length(), path.length()); return new Path(p); } @Override public int compareTo(final Path other) { for (int i = 0, n = Math.min(length(), other.length()); i < n; ++i) { final int cmp = Integer.compare(get(i), other.get(i)); if (cmp != 0) { return cmp; } } return Integer.compare(length(), other.length()); } @Override public int hashCode() { return Arrays.hashCode(_path); } @Override public boolean equals(final Object obj) { return obj == this || obj instanceof Path other && Arrays.equals(_path, other._path); } @Override public String toString() { return Arrays.toString(_path); } /** * Create a new path object from the given child indexes. * * @param path the child indexes * @return a new tree path * @throws IllegalArgumentException if one of the path elements is * smaller than zero */ public static Path of(final int... path) { for (int i = 0; i < path.length; ++i) { if (path[i] < 0) { throw new IllegalArgumentException(format( "Path element at position %d is smaller than zero: %d", i, path[i] )); } } return new Path(path.clone()); } /* ********************************************************************* * Java object serialization * ********************************************************************/ @Serial private Object writeReplace() { return new SerialProxy(SerialProxy.TREE_PATH, this); } @Serial private void readObject(final ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Serialization proxy required."); } void write(final DataOutput out) throws IOException { writeIntArray(_path, out); } static Object read(final DataInput in) throws IOException { return Path.of(readIntArray(in)); } } }