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 >= 0 && 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] > 0 &&
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 < 0 || 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( _index) + 1;
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() ? -1 : 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 out) throws IOException {
359 final FlatTreeNode<T> node = _index == 0 ? 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 }
|