FlatTreeNode.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-5.2.0).
003  * Copyright (c) 2007-2020 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics.ext.util;
021 
022 import static java.util.Objects.requireNonNull;
023 import static io.jenetics.internal.util.SerialIO.readIntArray;
024 import static io.jenetics.internal.util.SerialIO.readObjectArray;
025 import static io.jenetics.internal.util.SerialIO.writeIntArray;
026 import static io.jenetics.internal.util.SerialIO.writeObjectArray;
027 
028 import java.io.IOException;
029 import java.io.InvalidObjectException;
030 import java.io.ObjectInput;
031 import java.io.ObjectInputStream;
032 import java.io.ObjectOutput;
033 import java.io.Serializable;
034 import java.util.Arrays;
035 import java.util.Optional;
036 import java.util.function.Function;
037 import java.util.stream.IntStream;
038 import java.util.stream.Stream;
039 
040 import io.jenetics.util.ISeq;
041 
042 /**
043  * Default implementation of the {@link FlatTree} interface. Beside the
044  * flattened and dense layout it is also an <em>immutable</em> implementation of
045  * the {@link Tree} interface. It can only be created from an existing tree.
046  *
047  <pre>{@code
048  * final Tree<String, ?> immutable = FlatTreeNode.of(TreeNode.parse(...));
049  * }</pre>
050  *
051  * @implNote
052  * This class is immutable and thread-safe.
053  *
054  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
055  @version 5.2
056  @since 3.9
057  */
058 public final class FlatTreeNode<T>
059     implements
060         FlatTree<T, FlatTreeNode<T>>,
061         Serializable
062 {
063     private static final long serialVersionUID = 3L;
064 
065     private final int _index;
066     private final Object[] _elements;
067     private final int[] _childOffsets;
068     private final int[] _childCounts;
069 
070     private FlatTreeNode(
071         final int index,
072         final Object[] elements,
073         final int[] childOffsets,
074         final int[] childCounts
075     ) {
076         _index = index;
077         _elements = requireNonNull(elements);
078         _childOffsets = requireNonNull(childOffsets);
079         _childCounts = requireNonNull(childCounts);
080     }
081 
082     /**
083      * Returns the root of the tree that contains this node. The root is the
084      * ancestor with no parent. This implementation have a runtime complexity
085      * of O(1).
086      *
087      @return the root of the tree that contains this node
088      */
089     @Override
090     public FlatTreeNode<T> root() {
091         return nodeAt(0);
092     }
093 
094     @Override
095     public boolean isRoot() {
096         return _index == 0;
097     }
098 
099     private FlatTreeNode<T> nodeAt(final int index) {
100         return new FlatTreeNode<>(
101             index,
102             _elements,
103             _childOffsets,
104             _childCounts
105         );
106     }
107 
108     @SuppressWarnings("unchecked")
109     @Override
110     @Deprecated
111     public T getValue() {
112         return (T)_elements[_index];
113     }
114 
115     @Deprecated
116     @Override
117     public Optional<FlatTreeNode<T>> getParent() {
118         int index = -1;
119         for (int i = _index; --i >= && index == -1;) {
120             if (isParent(i)) {
121                 index = i;
122             }
123         }
124 
125         return index != -1
126             ? Optional.of(nodeAt(index))
127             : Optional.empty();
128     }
129 
130     private boolean isParent(final int index) {
131         return _childCounts[index&&
132             _childOffsets[index<= _index &&
133             _childOffsets[index+ _childCounts[index> _index;
134     }
135 
136     @Override
137     public FlatTreeNode<T> childAt(final int index) {
138         if (index < || index >= childCount()) {
139             throw new IndexOutOfBoundsException(Integer.toString(index));
140         }
141 
142         return nodeAt(childOffset() + index);
143     }
144 
145     @Override
146     public int childCount() {
147         return _childCounts[_index];
148     }
149 
150     /**
151      * Return the index of the first child node in the underlying node array.
152      * {@code -1} is returned if {@code this} node is a leaf.
153      *
154      @return Return the index of the first child node in the underlying node
155      *         array, or {@code -1} if {@code this} node is a leaf
156      */
157     @Override
158     public int childOffset() {
159         return _childOffsets[_index];
160     }
161 
162     @Override
163     public ISeq<FlatTreeNode<T>> flattenedNodes() {
164         return stream().collect(ISeq.toISeq());
165     }
166 
167     /**
168      * Return a stream of all nodes of the whole underlying tree. This method
169      * call is equivalent to
170      <pre>{@code
171      * final Stream<FlatTreeNode<T>> nodes = getRoot().breadthFirstStream();
172      * }</pre>
173      *
174      @return a stream of all nodes of the whole underlying tree
175      */
176     public Stream<FlatTreeNode<T>> stream() {
177         return IntStream.range(0, _elements.length).mapToObj(this::nodeAt);
178     }
179 
180     /**
181      * Return a sequence of all <em>mapped</em> nodes of the whole underlying
182      * tree. This is a convenient method for
183      <pre>{@code
184      * final ISeq<B> seq = stream()
185      *     .map(mapper)
186      *     .collect(ISeq.toISeq())
187      * }</pre>
188      *
189      @param mapper the mapper function
190      @param <B> the mapped type
191      @return a sequence of all <em>mapped</em> nodes
192      */
193     public <B> ISeq<B> map(final Function<FlatTreeNode<T>, ? extends B> mapper) {
194         return stream()
195             .map(mapper)
196             .collect(ISeq.toISeq());
197     }
198 
199     @Override
200     public boolean identical(final Tree<?, ?> other) {
201         return other == this ||
202             other instanceof FlatTreeNode &&
203             ((FlatTreeNode)other)._index == _index &&
204             ((FlatTreeNode)other)._elements == _elements;
205     }
206 
207     @Override
208     public int hashCode() {
209         return Tree.hashCode(this);
210     }
211 
212     @Override
213     public boolean equals(final Object obj) {
214         return obj == this ||
215             obj instanceof FlatTreeNode &&
216             (equals((FlatTreeNode<?>)obj|| Tree.equals((Tree<?, ?>)obj, this));
217     }
218 
219     private boolean equals(final FlatTreeNode<?> tree) {
220         return tree._index == _index &&
221             Arrays.equals(tree._elements, _elements&&
222             Arrays.equals(tree._childCounts, _childCounts&&
223             Arrays.equals(tree._childOffsets, _childOffsets);
224     }
225 
226     @Override
227     public String toString() {
228         return toParenthesesString();
229     }
230 
231     @Override
232     public int size() {
233         return countChildren_index1;
234     }
235 
236     private int countChildren(final int index) {
237         int count = _childCounts[index];
238         for (int i = 0; i < _childCounts[index]; ++i) {
239             count += countChildren(_childOffsets[index+ i);
240         }
241         return count;
242     }
243 
244     /**
245      * Create a new, immutable {@code FlatTreeNode} from the given {@code tree}.
246      *
247      @param tree the source tree
248      @param <V> the tree value types
249      @return a new {@code FlatTreeNode} from the given {@code tree}
250      @throws NullPointerException if the given {@code tree} is {@code null}
251      */
252     public static <V> FlatTreeNode<V> of(final Tree<? extends V, ?> tree) {
253         requireNonNull(tree);
254 
255         final int size = tree.size();
256         assert size >= 1;
257 
258         final Object[] elements = new Object[size];
259         final int[] childOffsets = new int[size];
260         final int[] childCounts = new int[size];
261 
262         int childOffset = 1;
263         int index = 0;
264 
265         for (Tree<?, ?> node : tree) {
266             elements[index= node.value();
267             childCounts[index= node.childCount();
268             childOffsets[index= node.isLeaf() ? -: childOffset;
269 
270             childOffset += node.childCount();
271             ++index;
272         }
273 
274         return new FlatTreeNode<>(
275             0,
276             elements,
277             childOffsets,
278             childCounts
279         );
280     }
281 
282     /**
283      * Parses a (parentheses) tree string, created with
284      {@link Tree#toParenthesesString()}. The tree string might look like this:
285      <pre>
286      *  mul(div(cos(1.0),cos(π)),sin(mul(1.0,z)))
287      </pre>
288      *
289      @see Tree#toParenthesesString(Function)
290      @see Tree#toParenthesesString()
291      @see TreeNode#parse(String)
292      *
293      @since 5.0
294      *
295      @param tree the parentheses tree string
296      @return the parsed tree
297      @throws NullPointerException if the given {@code tree} string is
298      *         {@code null}
299      @throws IllegalArgumentException if the given tree string could not be
300      *         parsed
301      */
302     public static FlatTreeNode<String> parse(final String tree) {
303         return of(TreeParser.parse(tree, Function.identity()));
304     }
305 
306     /**
307      * Parses a (parentheses) tree string, created with
308      {@link Tree#toParenthesesString()}. The tree string might look like this
309      <pre>
310      *  0(1(4,5),2(6),3(7(10,11),8,9))
311      </pre>
312      * and can be parsed to an integer tree with the following code:
313      <pre>{@code
314      * final Tree<Integer, ?> tree = FlatTreeNode.parse(
315      *     "0(1(4,5),2(6),3(7(10,11),8,9))",
316      *     Integer::parseInt
317      * );
318      * }</pre>
319      *
320      @see Tree#toParenthesesString(Function)
321      @see Tree#toParenthesesString()
322      @see TreeNode#parse(String, Function)
323      *
324      @since 5.0
325      *
326      @param <B> the tree node value type
327      @param tree the parentheses tree string
328      @param mapper the mapper which converts the serialized string value to
329      *        the desired type
330      @return the parsed tree object
331      @throws NullPointerException if one of the arguments is {@code null}
332      @throws IllegalArgumentException if the given parentheses tree string
333      *         doesn't represent a valid tree
334      */
335     public static <B> FlatTreeNode<B> parse(
336         final String tree,
337         final Function<? super String, ? extends B> mapper
338     ) {
339         return of(TreeParser.parse(tree, mapper));
340     }
341 
342 
343     /* *************************************************************************
344      *  Java object serialization
345      * ************************************************************************/
346 
347     private Object writeReplace() {
348         return new Serial(Serial.FLAT_TREE_NODE, this);
349     }
350 
351     private void readObject(final ObjectInputStream stream)
352         throws InvalidObjectException
353     {
354         throw new InvalidObjectException("Serialization proxy required.");
355     }
356 
357 
358     void write(final ObjectOutput outthrows IOException {
359         final FlatTreeNode<T> node = _index == this : of(this);
360 
361         writeObjectArray(node._elements, out);
362         writeIntArray(node._childOffsets, out);
363         writeIntArray(node._childCounts, out);
364     }
365 
366     @SuppressWarnings("rawtypes")
367     static FlatTreeNode read(final ObjectInput in)
368         throws IOException, ClassNotFoundException
369     {
370         return new FlatTreeNode(
371             0,
372             readObjectArray(in),
373             readIntArray(in),
374             readIntArray(in)
375         );
376     }
377 
378 }