001 /*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 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;
021
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 import static io.jenetics.internal.util.Hashes.hash;
025
026 import java.io.Serial;
027 import java.io.Serializable;
028 import java.util.Objects;
029 import java.util.Optional;
030
031 import io.jenetics.util.BaseSeq;
032 import io.jenetics.util.ISeq;
033
034 /**
035 * Abstract implementation of the {@link TreeGene} interface..
036 *
037 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
038 * @version 6.0
039 * @since 3.9
040 */
041 public abstract class AbstractTreeGene<A, G extends AbstractTreeGene<A, G>>
042 implements TreeGene<A, G>, Serializable
043 {
044
045 @Serial
046 private static final long serialVersionUID = 1L;
047
048 /**
049 * The allele of the tree-gene.
050 */
051 private final A _allele;
052 private final int _childOffset;
053 private final int _childCount;
054
055 private BaseSeq<G> _genes;
056
057 /**
058 * Creates a new tree-gene from the given data.
059 *
060 * @param allele the actual value (allele) of the tree-gene
061 * @param childOffset the offset index of the child in the containing
062 * chromosome. If this node has no child, the value should be set
063 * to zero.
064 * @param childCount the number of children of this gene
065 * @throws IllegalArgumentException if the {@code childCount} is smaller
066 * than zero
067 */
068 protected AbstractTreeGene(
069 final A allele,
070 final int childOffset,
071 final int childCount
072 ) {
073 if (childCount < 0) {
074 throw new IllegalArgumentException(format(
075 "Child count smaller than zero: %s", childCount
076 ));
077 }
078
079 _allele = allele;
080 _childOffset = childOffset;
081 _childCount = childCount;
082 }
083
084 /**
085 * Return the whole flattened tree values in breadth-first order. This method
086 * will always return the same {@code ISeq} instance.
087 *
088 * @return the whole flattened tree values
089 */
090 @Override
091 public ISeq<G> flattenedNodes() {
092 return ISeq.of(_genes);
093 }
094
095 @Override
096 public G root() {
097 return _genes.get(0);
098 }
099
100 @Override
101 public boolean isRoot() {
102 return root() == this;
103 }
104
105 @Override
106 public int size() {
107 return isRoot() ? _genes.length() : TreeGene.super.size();
108 }
109
110 protected void checkTreeState() {
111 if (_genes == null) {
112 throw new IllegalStateException(
113 "Gene is not attached to a chromosome."
114 );
115 }
116 }
117
118 /**
119 * This method is used by the {@code AbstractTreeChromosome} to attach
120 * itself to this gene.
121 *
122 * @param genes the genes of the attached chromosome
123 */
124 protected void bind(final BaseSeq<G> genes) {
125 _genes = requireNonNull(genes);
126 }
127
128 @Override
129 public int childOffset() {
130 return _childOffset;
131 }
132
133 @Override
134 public A allele() {
135 return _allele;
136 }
137
138 /**
139 * Return the <em>parent</em> node of this tree node.
140 *
141 * @return the parent node, or {@code Optional.empty()} if this node is the
142 * root of the tree
143 * @throws IllegalStateException if this gene is not part of a chromosome
144 */
145 @Override
146 public Optional<G> parent() {
147 checkTreeState();
148
149 return _genes.stream()
150 .filter(g -> g.childStream().anyMatch(this::identical))
151 .findFirst();
152 }
153
154 /**
155 * Return the child gene with the given index.
156 *
157 * @param index the child index
158 * @return the child node with the given index
159 * @throws IndexOutOfBoundsException if the {@code index} is out of
160 * bounds ({@code [0, childCount())})
161 * @throws IllegalStateException if this gene is not part of a chromosome
162 */
163 @Override
164 public G childAt(final int index) {
165 checkTreeState();
166 if (index < 0 || index >= childCount()) {
167 throw new IndexOutOfBoundsException(format(
168 "Child index out of bounds: %s", index
169 ));
170 }
171
172 assert _genes != null;
173 return _genes.get(_childOffset + index);
174 }
175
176 @Override
177 public int childCount() {
178 return _childCount;
179 }
180
181 @Override
182 public boolean isValid() {
183 return _genes != null;
184 }
185
186 @Override
187 public int hashCode() {
188 return hash(_allele, hash(_childOffset, hash(_childCount)));
189 }
190
191 @Override
192 public boolean equals(final Object obj) {
193 return obj == this ||
194 obj instanceof AbstractTreeGene<?, ?> other &&
195 Objects.equals(other._allele, _allele) &&
196 other._childOffset == _childOffset &&
197 other._childCount == _childCount;
198 }
199
200 @Override
201 public String toString() {
202 return Objects.toString(_allele);
203 }
204
205 }
|