AbstractTreeGene.java
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 < || 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 }