AbstractTreeGene.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-4.2.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 
025 import java.io.Serializable;
026 import java.util.Objects;
027 import java.util.Optional;
028 
029 import io.jenetics.util.ISeq;
030 
031 import io.jenetics.ext.util.Tree;
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 4.1
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 ISeq<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 _genes;
091     }
092 
093     @Override
094     public G getRoot() {
095         return _genes.get(0);
096     }
097 
098     @Override
099     public boolean isRoot() {
100         return getRoot() == this;
101     }
102 
103     @Override
104     public int size() {
105         return isRoot() ? _genes.size() : 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 ISeq<G> genes) {
123         _genes = requireNonNull(genes);
124     }
125 
126     @Override
127     public int childOffset() {
128         return _childOffset;
129     }
130 
131     @Override
132     public A getAllele() {
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> getParent() {
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 getChild(final int index) {
163         checkTreeState();
164         if (index < || 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 boolean identical(final Tree<?, ?> other) {
186         return other instanceof AbstractTreeGene &&
187             Objects.equals(((AbstractTreeGene)other)._allele, _allele&&
188             ((AbstractTreeGene)other)._genes == _genes &&
189             ((AbstractTreeGene)other)._childOffset == _childOffset &&
190             ((AbstractTreeGene)other)._childCount == _childCount;
191     }
192 
193     @Override
194     public int hashCode() {
195         int hash = 31;
196         hash += 31*Objects.hashCode(_allele17;
197         hash += 31*_childOffset + 17;
198         hash += 32*_childCount + 17;
199         return hash;
200     }
201 
202     @Override
203     public boolean equals(final Object obj) {
204         return obj == this ||
205             obj instanceof AbstractTreeGene &&
206             Objects.equals(((AbstractTreeGene)obj)._allele, _allele&&
207             ((AbstractTreeGene)obj)._childOffset == _childOffset &&
208             ((AbstractTreeGene)obj)._childCount == _childCount;
209     }
210 
211     @Override
212     public String toString() {
213         return Objects.toString(_allele);
214     }
215 
216 }