001 /*
002 * Java Genetic Algorithm Library (jenetics-6.1.0).
003 * Copyright (c) 2007-2020 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;
021
022 import static java.util.Objects.requireNonNull;
023 import static io.jenetics.internal.util.Hashes.hash;
024
025 import java.io.Serializable;
026 import java.util.Objects;
027
028 import io.jenetics.internal.math.Randoms;
029 import io.jenetics.util.CharSeq;
030 import io.jenetics.util.ISeq;
031 import io.jenetics.util.IntRange;
032 import io.jenetics.util.MSeq;
033 import io.jenetics.util.RandomRegistry;
034
035 /**
036 * Character gene implementation.
037 *
038 * <p>This is a <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html">
039 * value-based</a> class; use of identity-sensitive operations (including
040 * reference equality ({@code ==}), identity hash code, or synchronization) on
041 * instances of {@code CharacterGene} may have unpredictable results and should
042 * be avoided.
043 *
044 * @see CharacterChromosome
045 *
046 * @implNote
047 * This class is immutable and thread-safe.
048 *
049 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
050 * @since 1.0
051 * @version 6.0
052 */
053 public final class CharacterGene
054 implements
055 Gene<Character, CharacterGene>,
056 Comparable<CharacterGene>,
057 Serializable
058 {
059 private static final long serialVersionUID = 3L;
060
061 /**
062 * The default character set used by this gene.
063 */
064 public static final CharSeq DEFAULT_CHARACTERS = new CharSeq(
065 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +
066 " !\"$%&/()=?`{[]}\\+~*#';.:,-_<>|@^'"
067 );
068
069 private final char _allele;
070 private final CharSeq _validCharacters;
071
072 private CharacterGene(final CharSeq chars, final int index) {
073 _allele = chars.get(index);
074 _validCharacters = chars;
075 }
076
077 /**
078 * Create a new character gene from the given {@code character} and the
079 * given set of valid characters.
080 *
081 * @param allele the char this gene represents
082 * @param validChars the set of valid characters.
083 * @throws NullPointerException if one of the arguments is {@code null}.
084 */
085 private CharacterGene(final char allele, final CharSeq validChars) {
086 _allele = allele;
087 _validCharacters = requireNonNull(validChars);
088 }
089
090 @Override
091 public boolean isValid() {
092 return _validCharacters.contains(_allele);
093 }
094
095 @Override
096 public Character allele() {
097 return _allele;
098 }
099
100 /**
101 * Return the {@code char} value of this character gene.
102 *
103 * @return the {@code char} value.
104 */
105 public char charValue() {
106 return _allele;
107 }
108
109 /**
110 * Test, if the given character is valid.
111 *
112 * @param allele The character to test.
113 * @return true if the character is valid, false otherwise.
114 */
115 public boolean isValidCharacter(final Character allele) {
116 return _validCharacters.contains(allele);
117 }
118
119 /**
120 * Return a (unmodifiable) set of valid characters.
121 *
122 * @return the {@link CharSeq} of valid characters.
123 */
124 public CharSeq validChars() {
125 return _validCharacters;
126 }
127
128 /**
129 * @see java.lang.Character#compareTo(java.lang.Character)
130 * @param that The other gene to compare.
131 * @return the value 0 if the argument Character is equal to this Character;
132 * a value less than 0 if this Character is numerically less than
133 * the Character argument; and a value greater than 0 if this
134 * Character is numerically greater than the Character argument
135 * (unsigned comparison). Note that this is strictly a numerical
136 * comparison; it is not local-dependent.
137 */
138 @Override
139 public int compareTo(final CharacterGene that) {
140 return Character.compare(_allele, that._allele);
141 }
142
143 @Override
144 public int hashCode() {
145 return hash(_allele, hash(_validCharacters));
146 }
147
148 @Override
149 public boolean equals(final Object obj) {
150 return obj == this ||
151 obj instanceof CharacterGene &&
152 ((CharacterGene)obj)._allele == _allele &&
153 Objects.equals(((CharacterGene)obj)._validCharacters, _validCharacters);
154 }
155
156 @Override
157 public String toString() {
158 return Character.toString(_allele);
159 }
160
161
162 /* *************************************************************************
163 * Factory methods
164 * ************************************************************************/
165
166 @Override
167 public CharacterGene newInstance() {
168 return of(_validCharacters);
169 }
170
171 /**
172 * Create a new character gene from the given character. If the character
173 * is not within the {@link #validChars()}, an invalid gene will be
174 * created.
175 *
176 * @param allele the character value of the created gene.
177 * @return a new character gene.
178 * @throws NullPointerException if the given {@code character} is
179 * {@code null}.
180 */
181 @Override
182 public CharacterGene newInstance(final Character allele) {
183 return of(allele, _validCharacters);
184 }
185
186
187 /* *************************************************************************
188 * Static object creation methods
189 * ************************************************************************/
190
191 /**
192 * Create a new CharacterGene with a randomly chosen character from the
193 * set of valid characters.
194 *
195 * @param validCharacters the valid characters for this gene.
196 * @return a new valid, <em>random</em> gene,
197 * @throws NullPointerException if the {@code validCharacters} are
198 * {@code null}.
199 */
200 public static CharacterGene of(final CharSeq validCharacters) {
201 return new CharacterGene(
202 validCharacters,
203 RandomRegistry.random().nextInt(validCharacters.length())
204 );
205 }
206
207 /**
208 * Create a new character gene from the given character. If the character
209 * is not within the {@link #DEFAULT_CHARACTERS}, an invalid gene will be
210 * created.
211 *
212 * @param allele the character value of the created gene.
213 * @return a new character gene.
214 */
215 public static CharacterGene of(final char allele) {
216 return new CharacterGene(allele, DEFAULT_CHARACTERS);
217 }
218
219 /**
220 * Create a new random character gene, chosen from the
221 * {@link #DEFAULT_CHARACTERS}.
222 *
223 * @return a new random character gene.
224 */
225 public static CharacterGene of() {
226 return new CharacterGene(
227 DEFAULT_CHARACTERS,
228 RandomRegistry.random().nextInt(DEFAULT_CHARACTERS.length())
229 );
230 }
231
232 /**
233 * Create a new CharacterGene from the give character.
234 *
235 * @param allele The allele.
236 * @param validCharacters the valid characters fo the new gene
237 * @return a new {@code CharacterGene} with the given parameter
238 * @throws NullPointerException if one of the arguments is {@code null}.
239 * @throws IllegalArgumentException if the {@code validCharacters} are empty.
240 */
241 public static CharacterGene of(
242 final char allele,
243 final CharSeq validCharacters
244 ) {
245 return new CharacterGene(allele, validCharacters);
246 }
247
248 static ISeq<CharacterGene> seq(
249 final CharSeq chars,
250 final IntRange lengthRange
251 ) {
252 final var r = RandomRegistry.random();
253
254 return MSeq.<CharacterGene>ofLength(Randoms.nextInt(lengthRange, r))
255 .fill(() -> new CharacterGene(chars, r.nextInt(chars.length())))
256 .toISeq();
257 }
258
259 }
|