001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.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 */ 020package io.jenetics.ext; 021 022import static java.lang.String.format; 023import static java.util.Objects.requireNonNull; 024import static io.jenetics.internal.util.Hashes.hash; 025 026import java.io.Serial; 027import java.io.Serializable; 028import java.util.Objects; 029import java.util.Optional; 030 031import io.jenetics.util.BaseSeq; 032import 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 */ 041public 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}