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