1 | package net.bmahe.genetics4j.core.chromosomes; | |
2 | ||
3 | import java.util.ArrayList; | |
4 | import java.util.Collection; | |
5 | import java.util.Comparator; | |
6 | import java.util.List; | |
7 | import java.util.stream.Collectors; | |
8 | ||
9 | import org.apache.commons.lang3.Validate; | |
10 | ||
11 | /** | |
12 | * Represents a node in a tree structure used for genetic programming and tree-based chromosomes. | |
13 | * | |
14 | * <p>TreeNode provides a flexible, generic tree data structure that forms the foundation for | |
15 | * tree-based genetic programming operations. Each node contains data of a specified type and | |
16 | * can have zero or more child nodes, enabling the representation of complex hierarchical | |
17 | * structures such as mathematical expressions, decision trees, or program syntax trees. | |
18 | * | |
19 | * <p>Key features include: | |
20 | * <ul> | |
21 | * <li><strong>Generic data storage</strong>: Can hold any type of data (functions, terminals, values)</li> | |
22 | * <li><strong>Dynamic structure</strong>: Supports adding, removing, and modifying children</li> | |
23 | * <li><strong>Tree metrics</strong>: Provides size and depth calculations for analysis</li> | |
24 | * <li><strong>Hierarchical operations</strong>: Enables tree traversal and manipulation</li> | |
25 | * </ul> | |
26 | * | |
27 | * <p>Common uses in genetic programming: | |
28 | * <ul> | |
29 | * <li><strong>Expression trees</strong>: Mathematical or logical expressions with operators and operands</li> | |
30 | * <li><strong>Program trees</strong>: Structured representation of executable code or algorithms</li> | |
31 | * <li><strong>Decision trees</strong>: Conditional logic structures for classification or control</li> | |
32 | * <li><strong>Grammar trees</strong>: Parse trees representing valid grammatical constructs</li> | |
33 | * </ul> | |
34 | * | |
35 | * <p>Tree structure characteristics: | |
36 | * <ul> | |
37 | * <li><strong>Mutable structure</strong>: Children can be added, removed, or replaced during evolution</li> | |
38 | * <li><strong>Type safety</strong>: Generic parameterization ensures consistent data types</li> | |
39 | * <li><strong>Recursive operations</strong>: Size and depth calculations traverse the entire subtree</li> | |
40 | * <li><strong>Equality semantics</strong>: Two trees are equal if their structure and data match</li> | |
41 | * </ul> | |
42 | * | |
43 | * <p>Example usage in genetic programming: | |
44 | * <pre>{@code | |
45 | * // Creating a simple mathematical expression: (x + 2) * 3 | |
46 | * TreeNode<String> multiply = new TreeNode<>("*"); | |
47 | * TreeNode<String> add = new TreeNode<>("+"); | |
48 | * TreeNode<String> x = new TreeNode<>("x"); | |
49 | * TreeNode<String> two = new TreeNode<>("2"); | |
50 | * TreeNode<String> three = new TreeNode<>("3"); | |
51 | * | |
52 | * add.addChild(x); | |
53 | * add.addChild(two); | |
54 | * multiply.addChild(add); | |
55 | * multiply.addChild(three); | |
56 | * | |
57 | * // Tree metrics | |
58 | * int treeSize = multiply.getSize(); // Total nodes in tree | |
59 | * int treeDepth = multiply.getDepth(); // Maximum depth from root | |
60 | * | |
61 | * // Factory method for creating nodes with children | |
62 | * TreeNode<String> expression = TreeNode.of("*", List.of( | |
63 | * TreeNode.of("+", List.of( | |
64 | * new TreeNode<>("x"), | |
65 | * new TreeNode<>("2") | |
66 | * )), | |
67 | * new TreeNode<>("3") | |
68 | * )); | |
69 | * }</pre> | |
70 | * | |
71 | * <p>Integration with genetic operations: | |
72 | * <ul> | |
73 | * <li><strong>Crossover</strong>: Subtree exchange between parent trees</li> | |
74 | * <li><strong>Mutation</strong>: Replacement or modification of individual nodes or subtrees</li> | |
75 | * <li><strong>Size constraints</strong>: Enforcement of maximum tree size or depth limits</li> | |
76 | * <li><strong>Evaluation</strong>: Tree traversal for fitness computation</li> | |
77 | * </ul> | |
78 | * | |
79 | * <p>Performance considerations: | |
80 | * <ul> | |
81 | * <li><strong>Memory usage</strong>: Each node maintains a list of children references</li> | |
82 | * <li><strong>Tree traversal</strong>: Size and depth calculations have O(n) complexity</li> | |
83 | * <li><strong>Structural sharing</strong>: Nodes can be shared between trees with care</li> | |
84 | * <li><strong>Deep trees</strong>: Very deep trees may cause stack overflow in recursive operations</li> | |
85 | * </ul> | |
86 | * | |
87 | * @param <T> the type of data stored in each node | |
88 | * @see TreeChromosome | |
89 | * @see TreeChromosome | |
90 | */ | |
91 | public class TreeNode<T> { | |
92 | ||
93 | private final T data; | |
94 | ||
95 | private ArrayList<TreeNode<T>> children; | |
96 | ||
97 | /** | |
98 | * Constructs a new tree node with the specified data and no children. | |
99 | * | |
100 | * <p>Creates a leaf node that can later have children added to it. The data | |
101 | * provided becomes the payload for this node and cannot be changed after construction. | |
102 | * | |
103 | * @param _data the data to store in this node | |
104 | * @throws IllegalArgumentException if data is null | |
105 | */ | |
106 | public TreeNode(final T _data) { | |
107 | Validate.notNull(_data); | |
108 | ||
109 |
1
1. <init> : Removed assignment to member variable data → KILLED |
this.data = _data; |
110 |
2
1. <init> : Removed assignment to member variable children → KILLED 2. <init> : removed call to java/util/ArrayList::<init> → KILLED |
this.children = new ArrayList<>(); |
111 | } | |
112 | ||
113 | /** | |
114 | * Returns the data stored in this node. | |
115 | * | |
116 | * @return the data payload of this node | |
117 | */ | |
118 | public T getData() { | |
119 |
1
1. getData : replaced return value with null for net/bmahe/genetics4j/core/chromosomes/TreeNode::getData → KILLED |
return data; |
120 | } | |
121 | ||
122 | /** | |
123 | * Returns the list of direct children of this node. | |
124 | * | |
125 | * <p>The returned list is the actual internal list used by this node. | |
126 | * Modifications to the returned list will affect this node's structure. | |
127 | * | |
128 | * @return the mutable list of child nodes | |
129 | */ | |
130 | public List<TreeNode<T>> getChildren() { | |
131 |
1
1. getChildren : replaced return value with Collections.emptyList for net/bmahe/genetics4j/core/chromosomes/TreeNode::getChildren → KILLED |
return children; |
132 | } | |
133 | ||
134 | /** | |
135 | * Returns the child node at the specified index. | |
136 | * | |
137 | * @param childIndex the index of the child to retrieve (0-based) | |
138 | * @return the child node at the specified index | |
139 | * @throws IllegalArgumentException if childIndex is negative | |
140 | * @throws IndexOutOfBoundsException if childIndex is >= number of children | |
141 | */ | |
142 | public TreeNode<T> getChild(final int childIndex) { | |
143 | Validate.isTrue(childIndex >= 0); | |
144 | ||
145 |
2
1. getChild : replaced return value with null for net/bmahe/genetics4j/core/chromosomes/TreeNode::getChild → KILLED 2. getChild : removed call to java/util/ArrayList::get → KILLED |
return children.get(childIndex); |
146 | } | |
147 | ||
148 | /** | |
149 | * Replaces the child node at the specified index with a new node. | |
150 | * | |
151 | * <p>This operation modifies the tree structure by replacing an existing | |
152 | * child with a new subtree rooted at the provided node. | |
153 | * | |
154 | * @param childIndex the index of the child to replace (0-based) | |
155 | * @param childData the new child node to set at the specified index | |
156 | * @throws IllegalArgumentException if childIndex is negative | |
157 | * @throws IndexOutOfBoundsException if childIndex is >= number of children | |
158 | */ | |
159 | public void setChild(final int childIndex, final TreeNode<T> childData) { | |
160 | Validate.isTrue(childIndex >= 0); | |
161 | ||
162 |
2
1. setChild : replaced call to java/util/ArrayList::set with argument → NO_COVERAGE 2. setChild : removed call to java/util/ArrayList::set → NO_COVERAGE |
children.set(childIndex, childData); |
163 | } | |
164 | ||
165 | /** | |
166 | * Adds a new child node to this node. | |
167 | * | |
168 | * <p>The new child is appended to the end of the children list. | |
169 | * This operation increases the arity of this node by one. | |
170 | * | |
171 | * @param childData the child node to add | |
172 | * @throws IllegalArgumentException if childData is null | |
173 | */ | |
174 | public void addChild(final TreeNode<T> childData) { | |
175 | Validate.notNull(childData); | |
176 | ||
177 |
1
1. addChild : removed call to java/util/ArrayList::add → KILLED |
children.add(childData); |
178 | } | |
179 | ||
180 | /** | |
181 | * Adds multiple child nodes to this node. | |
182 | * | |
183 | * <p>All nodes in the provided collection are appended to the children list | |
184 | * in the order they appear in the collection. | |
185 | * | |
186 | * @param childrenNodes the collection of child nodes to add | |
187 | * @throws IllegalArgumentException if childrenNodes is null or empty | |
188 | */ | |
189 | public void addChildren(final Collection<TreeNode<T>> childrenNodes) { | |
190 | Validate.notNull(childrenNodes); | |
191 | Validate.isTrue(childrenNodes.isEmpty() == false); | |
192 | ||
193 |
1
1. addChildren : removed call to java/util/ArrayList::addAll → NO_COVERAGE |
children.addAll(childrenNodes); |
194 | } | |
195 | ||
196 | /** | |
197 | * Returns the total number of nodes in the subtree rooted at this node. | |
198 | * | |
199 | * <p>This method recursively counts all nodes in the subtree, including | |
200 | * this node and all its descendants. The size is useful for analyzing | |
201 | * tree complexity and implementing size-based genetic operations. | |
202 | * | |
203 | * @return the total number of nodes in this subtree (always >= 1) | |
204 | */ | |
205 | public int getSize() { | |
206 |
11
1. getSize : removed call to java/lang/Integer::intValue → KILLED 2. getSize : replaced int return with 0 for net/bmahe/genetics4j/core/chromosomes/TreeNode::getSize → KILLED 3. getSize : removed call to java/util/ArrayList::stream → KILLED 4. getSize : removed call to java/util/stream/Stream::collect → KILLED 5. getSize : replaced call to java/util/stream/Stream::map with receiver → KILLED 6. getSize : Replaced integer addition with subtraction → KILLED 7. getSize : removed call to java/util/stream/Collectors::summingInt → KILLED 8. lambda$getSize$0 : replaced int return with 0 for net/bmahe/genetics4j/core/chromosomes/TreeNode::lambda$getSize$0 → KILLED 9. lambda$getSize$0 : removed call to java/lang/Integer::intValue → KILLED 10. getSize : removed call to java/util/stream/Stream::map → KILLED 11. getSize : Substituted 1 with 0 → KILLED |
return 1 + children.stream().map(TreeNode::getSize).collect(Collectors.summingInt(x -> x)); |
207 | } | |
208 | ||
209 | /** | |
210 | * Returns the maximum depth of the subtree rooted at this node. | |
211 | * | |
212 | * <p>The depth is defined as the length of the longest path from this node | |
213 | * to any leaf node in the subtree. A leaf node has depth 1. | |
214 | * | |
215 | * @return the maximum depth of this subtree (always >= 1) | |
216 | */ | |
217 | public int getDepth() { | |
218 |
13
1. getDepth : removed call to java/util/stream/Stream::map → KILLED 2. getDepth : removed call to java/util/Comparator::naturalOrder → KILLED 3. getDepth : replaced call to java/util/stream/Stream::map with receiver → KILLED 4. getDepth : removed call to java/util/ArrayList::stream → KILLED 5. getDepth : Substituted 1 with 0 → KILLED 6. getDepth : Substituted 0 with 1 → KILLED 7. getDepth : removed call to java/lang/Integer::intValue → KILLED 8. getDepth : Replaced integer addition with subtraction → KILLED 9. getDepth : removed call to java/lang/Integer::valueOf → KILLED 10. getDepth : removed call to java/util/Optional::orElse → KILLED 11. getDepth : replaced call to java/util/Optional::orElse with argument → KILLED 12. getDepth : removed call to java/util/stream/Stream::max → KILLED 13. getDepth : replaced int return with 0 for net/bmahe/genetics4j/core/chromosomes/TreeNode::getDepth → KILLED |
return 1 + children.stream().map(TreeNode::getDepth).max(Comparator.naturalOrder()).orElse(0); |
219 | } | |
220 | ||
221 | @Override | |
222 | public int hashCode() { | |
223 |
1
1. hashCode : Substituted 31 with 32 → NO_COVERAGE |
final int prime = 31; |
224 |
1
1. hashCode : Substituted 1 with 0 → NO_COVERAGE |
int result = 1; |
225 |
8
1. hashCode : removed call to java/util/ArrayList::hashCode → NO_COVERAGE 2. hashCode : removed conditional - replaced equality check with false → NO_COVERAGE 3. hashCode : Replaced integer multiplication with division → NO_COVERAGE 4. hashCode : Substituted 0 with 1 → NO_COVERAGE 5. hashCode : negated conditional → NO_COVERAGE 6. hashCode : Replaced integer addition with subtraction → NO_COVERAGE 7. hashCode : Substituted 31 with 32 → NO_COVERAGE 8. hashCode : removed conditional - replaced equality check with true → NO_COVERAGE |
result = prime * result + ((children == null) ? 0 : children.hashCode()); |
226 |
8
1. hashCode : negated conditional → NO_COVERAGE 2. hashCode : removed conditional - replaced equality check with true → NO_COVERAGE 3. hashCode : Replaced integer multiplication with division → NO_COVERAGE 4. hashCode : Substituted 0 with 1 → NO_COVERAGE 5. hashCode : Substituted 31 with 32 → NO_COVERAGE 6. hashCode : Replaced integer addition with subtraction → NO_COVERAGE 7. hashCode : removed conditional - replaced equality check with false → NO_COVERAGE 8. hashCode : removed call to java/lang/Object::hashCode → NO_COVERAGE |
result = prime * result + ((data == null) ? 0 : data.hashCode()); |
227 |
1
1. hashCode : replaced int return with 0 for net/bmahe/genetics4j/core/chromosomes/TreeNode::hashCode → NO_COVERAGE |
return result; |
228 | } | |
229 | ||
230 | @Override | |
231 | public boolean equals(Object obj) { | |
232 |
2
1. equals : removed conditional - replaced equality check with true → SURVIVED 2. equals : negated conditional → SURVIVED |
if (this == obj) |
233 |
2
1. equals : replaced boolean return with false for net/bmahe/genetics4j/core/chromosomes/TreeNode::equals → KILLED 2. equals : Substituted 1 with 0 → KILLED |
return true; |
234 |
3
1. equals : removed conditional - replaced equality check with false → NO_COVERAGE 2. equals : negated conditional → NO_COVERAGE 3. equals : removed conditional - replaced equality check with true → NO_COVERAGE |
if (obj == null) |
235 |
2
1. equals : replaced boolean return with true for net/bmahe/genetics4j/core/chromosomes/TreeNode::equals → NO_COVERAGE 2. equals : Substituted 0 with 1 → NO_COVERAGE |
return false; |
236 |
5
1. equals : removed conditional - replaced equality check with false → NO_COVERAGE 2. equals : negated conditional → NO_COVERAGE 3. equals : removed conditional - replaced equality check with true → NO_COVERAGE 4. equals : removed call to java/lang/Object::getClass → NO_COVERAGE 5. equals : removed call to java/lang/Object::getClass → NO_COVERAGE |
if (getClass() != obj.getClass()) |
237 |
2
1. equals : Substituted 0 with 1 → NO_COVERAGE 2. equals : replaced boolean return with true for net/bmahe/genetics4j/core/chromosomes/TreeNode::equals → NO_COVERAGE |
return false; |
238 | TreeNode other = (TreeNode) obj; | |
239 |
3
1. equals : removed conditional - replaced equality check with false → NO_COVERAGE 2. equals : removed conditional - replaced equality check with true → NO_COVERAGE 3. equals : negated conditional → NO_COVERAGE |
if (data == null) { |
240 |
3
1. equals : negated conditional → NO_COVERAGE 2. equals : removed conditional - replaced equality check with true → NO_COVERAGE 3. equals : removed conditional - replaced equality check with false → NO_COVERAGE |
if (other.data != null) |
241 |
2
1. equals : replaced boolean return with true for net/bmahe/genetics4j/core/chromosomes/TreeNode::equals → NO_COVERAGE 2. equals : Substituted 0 with 1 → NO_COVERAGE |
return false; |
242 |
4
1. equals : removed conditional - replaced equality check with true → NO_COVERAGE 2. equals : negated conditional → NO_COVERAGE 3. equals : removed call to java/lang/Object::equals → NO_COVERAGE 4. equals : removed conditional - replaced equality check with false → NO_COVERAGE |
} else if (!data.equals(other.data)) |
243 |
2
1. equals : Substituted 0 with 1 → NO_COVERAGE 2. equals : replaced boolean return with true for net/bmahe/genetics4j/core/chromosomes/TreeNode::equals → NO_COVERAGE |
return false; |
244 |
3
1. equals : removed conditional - replaced equality check with false → NO_COVERAGE 2. equals : negated conditional → NO_COVERAGE 3. equals : removed conditional - replaced equality check with true → NO_COVERAGE |
if (children == null) { |
245 |
3
1. equals : removed conditional - replaced equality check with false → NO_COVERAGE 2. equals : removed conditional - replaced equality check with true → NO_COVERAGE 3. equals : negated conditional → NO_COVERAGE |
if (other.children != null) |
246 |
2
1. equals : Substituted 0 with 1 → NO_COVERAGE 2. equals : replaced boolean return with true for net/bmahe/genetics4j/core/chromosomes/TreeNode::equals → NO_COVERAGE |
return false; |
247 |
4
1. equals : negated conditional → NO_COVERAGE 2. equals : removed conditional - replaced equality check with true → NO_COVERAGE 3. equals : removed call to java/util/ArrayList::equals → NO_COVERAGE 4. equals : removed conditional - replaced equality check with false → NO_COVERAGE |
} else if (!children.equals(other.children)) |
248 |
2
1. equals : replaced boolean return with true for net/bmahe/genetics4j/core/chromosomes/TreeNode::equals → NO_COVERAGE 2. equals : Substituted 0 with 1 → NO_COVERAGE |
return false; |
249 | ||
250 |
2
1. equals : replaced boolean return with false for net/bmahe/genetics4j/core/chromosomes/TreeNode::equals → NO_COVERAGE 2. equals : Substituted 1 with 0 → NO_COVERAGE |
return true; |
251 | } | |
252 | ||
253 | @Override | |
254 | public String toString() { | |
255 |
3
1. toString : replaced return value with "" for net/bmahe/genetics4j/core/chromosomes/TreeNode::toString → NO_COVERAGE 2. toString : removed call to java/lang/String::valueOf → NO_COVERAGE 3. toString : removed call to java/lang/String::valueOf → NO_COVERAGE |
return "TreeNode [data=" + data + ", children=" + children + "]"; |
256 | } | |
257 | ||
258 | /** | |
259 | * Creates a new tree node with the specified data and children. | |
260 | * | |
261 | * <p>This factory method provides a convenient way to construct trees | |
262 | * with a specific structure in a single operation. All provided children | |
263 | * are added to the new node. | |
264 | * | |
265 | * @param <U> the type of data stored in the nodes | |
266 | * @param data the data to store in the root node | |
267 | * @param children the collection of child nodes to add | |
268 | * @return a new tree node with the specified data and children | |
269 | * @throws IllegalArgumentException if data is null, children is null, or children is empty | |
270 | */ | |
271 | public static <U> TreeNode<U> of(final U data, final Collection<TreeNode<U>> children) { | |
272 | Validate.notNull(data); | |
273 | Validate.notNull(children); | |
274 | Validate.isTrue(children.size() > 0); | |
275 | ||
276 |
1
1. of : removed call to net/bmahe/genetics4j/core/chromosomes/TreeNode::<init> → KILLED |
final TreeNode<U> rootNode = new TreeNode<>(data); |
277 | for (TreeNode<U> childNode : children) { | |
278 |
1
1. of : removed call to net/bmahe/genetics4j/core/chromosomes/TreeNode::addChild → KILLED |
rootNode.addChild(childNode); |
279 | } | |
280 | ||
281 |
1
1. of : replaced return value with null for net/bmahe/genetics4j/core/chromosomes/TreeNode::of → KILLED |
return rootNode; |
282 | } | |
283 | } | |
Mutations | ||
109 |
1.1 |
|
110 |
1.1 2.2 |
|
119 |
1.1 |
|
131 |
1.1 |
|
145 |
1.1 2.2 |
|
162 |
1.1 2.2 |
|
177 |
1.1 |
|
193 |
1.1 |
|
206 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 10.10 11.11 |
|
218 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 10.10 11.11 12.12 13.13 |
|
223 |
1.1 |
|
224 |
1.1 |
|
225 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
226 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
227 |
1.1 |
|
232 |
1.1 2.2 |
|
233 |
1.1 2.2 |
|
234 |
1.1 2.2 3.3 |
|
235 |
1.1 2.2 |
|
236 |
1.1 2.2 3.3 4.4 5.5 |
|
237 |
1.1 2.2 |
|
239 |
1.1 2.2 3.3 |
|
240 |
1.1 2.2 3.3 |
|
241 |
1.1 2.2 |
|
242 |
1.1 2.2 3.3 4.4 |
|
243 |
1.1 2.2 |
|
244 |
1.1 2.2 3.3 |
|
245 |
1.1 2.2 3.3 |
|
246 |
1.1 2.2 |
|
247 |
1.1 2.2 3.3 4.4 |
|
248 |
1.1 2.2 |
|
250 |
1.1 2.2 |
|
255 |
1.1 2.2 3.3 |
|
276 |
1.1 |
|
278 |
1.1 |
|
281 |
1.1 |