CharacterGene.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-4.0.0).
003  * Copyright (c) 2007-2017 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 
024 import java.io.Serializable;
025 import java.util.Objects;
026 import java.util.Random;
027 
028 import io.jenetics.internal.math.random;
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  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
047  @since 1.0
048  @version 4.0
049  */
050 public final class CharacterGene
051     implements
052         Gene<Character, CharacterGene>,
053         Comparable<CharacterGene>,
054         Serializable
055 {
056     private static final long serialVersionUID = 2L;
057 
058     /**
059      * The default character set used by this gene.
060      */
061     public static final CharSeq DEFAULT_CHARACTERS = new CharSeq(
062         "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +
063         " !\"$%&/()=?`{[]}\\+~*#';.:,-_<>|@^'"
064     );
065 
066     private final Character _character;
067     private final CharSeq _validCharacters;
068 
069     private CharacterGene(final CharSeq chars, final int index) {
070         _character = chars.get(index);
071         _validCharacters = chars;
072     }
073 
074     /**
075      * Create a new character gene from the given {@code character} and the
076      * given set of valid characters.
077      *
078      @param character the char this gene represents
079      @param validChars the set of valid characters.
080      @throws NullPointerException if one of the arguments is {@code null}.
081      */
082     CharacterGene(final Character character, final CharSeq validChars) {
083         _character = requireNonNull(character);
084         _validCharacters = requireNonNull(validChars);
085     }
086 
087     @Override
088     public boolean isValid() {
089         return _validCharacters.contains(_character);
090     }
091 
092     @Override
093     public Character getAllele() {
094         return _character;
095     }
096 
097     /**
098      * Return the {@code char} value of this character gene.
099      *
100      @return the {@code char} value.
101      */
102     public char charValue() {
103         return _character;
104     }
105 
106     /**
107      * Test, if the given character is valid.
108      *
109      @param character The character to test.
110      @return true if the character is valid, false otherwise.
111      */
112     public boolean isValidCharacter(final Character character) {
113         return _validCharacters.contains(character);
114     }
115 
116     /**
117      * Return a (unmodifiable) set of valid characters.
118      *
119      @return the {@link CharSeq} of valid characters.
120      */
121     public CharSeq getValidCharacters() {
122         return _validCharacters;
123     }
124 
125     /**
126      @see java.lang.Character#compareTo(java.lang.Character)
127      @param that The other gene to compare.
128      @return the value 0 if the argument Character is equal to this Character;
129      *         a value less than 0 if this Character is numerically less than
130      *         the Character argument; and a value greater than 0 if this
131      *         Character is numerically greater than the Character argument
132      *         (unsigned comparison). Note that this is strictly a numerical
133      *         comparison; it is not local-dependent.
134      */
135     @Override
136     public int compareTo(final CharacterGene that) {
137         return getAllele().compareTo(that.getAllele());
138     }
139 
140     @Override
141     public int hashCode() {
142         int hash = 17;
143         hash += 31*_character.hashCode() 37;
144         hash += 31*_validCharacters.hashCode() 37;
145         return hash;
146     }
147 
148     @Override
149     public boolean equals(final Object obj) {
150         return obj instanceof CharacterGene &&
151             Objects.equals(((CharacterGene)obj)._character, _character&&
152             Objects.equals(((CharacterGene)obj)._validCharacters, _validCharacters);
153     }
154 
155     @Override
156     public String toString() {
157         return _character.toString();
158     }
159 
160 
161     /* *************************************************************************
162      *  Factory methods
163      * ************************************************************************/
164 
165     @Override
166     public CharacterGene newInstance() {
167         return of(_validCharacters);
168     }
169 
170     /**
171      * Create a new character gene from the given character. If the character
172      * is not within the {@link #getValidCharacters()}, an invalid gene will be
173      * created.
174      *
175      @param character the character value of the created gene.
176      @return a new character gene.
177      @throws NullPointerException if the given {@code character} is
178      *         {@code null}.
179      */
180     public CharacterGene newInstance(final Character character) {
181         return of(character, _validCharacters);
182     }
183 
184 
185     /* *************************************************************************
186      *  Static object creation methods
187      * ************************************************************************/
188 
189     /**
190      * Create a new CharacterGene with a randomly chosen character from the
191      * set of valid characters.
192      *
193      @param validCharacters the valid characters for this gene.
194      @return a new valid, <em>random</em> gene,
195      @throws NullPointerException if the {@code validCharacters} are
196      *         {@code null}.
197      */
198     public static CharacterGene of(final CharSeq validCharacters) {
199         return new CharacterGene(
200             validCharacters,
201             RandomRegistry.getRandom().nextInt(validCharacters.length())
202         );
203     }
204 
205     /**
206      * Create a new character gene from the given character. If the character
207      * is not within the {@link #DEFAULT_CHARACTERS}, an invalid gene will be
208      * created.
209      *
210      @param character the character value of the created gene.
211      @return a new character gene.
212      @throws NullPointerException if the given {@code character} is
213      *         {@code null}.
214      */
215     public static CharacterGene of(final Character character) {
216         return new CharacterGene(character, 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.getRandom().nextInt(DEFAULT_CHARACTERS.length())
229         );
230     }
231 
232     /**
233      * Create a new CharacterGene from the give character.
234      *
235      @param character 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 character,
243         final CharSeq validCharacters
244     ) {
245         return new CharacterGene(character, validCharacters);
246     }
247 
248     static ISeq<CharacterGene> seq(
249         final CharSeq chars,
250         final IntRange lengthRange
251     ) {
252         final Random r = RandomRegistry.getRandom();
253 
254         return MSeq.<CharacterGene>ofLength(random.nextInt(lengthRange, r))
255             .fill(() -> new CharacterGene(chars, r.nextInt(chars.length())))
256             .toISeq();
257     }
258 
259 }