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