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