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;
021
022import static io.jenetics.internal.util.Hashes.hash;
023import static io.jenetics.internal.util.SerialIO.readInt;
024import static io.jenetics.internal.util.SerialIO.writeInt;
025import static io.jenetics.util.RandomRegistry.random;
026
027import java.io.DataInput;
028import java.io.DataOutput;
029import java.io.IOException;
030import java.io.InvalidObjectException;
031import java.io.ObjectInputStream;
032import java.io.Serial;
033import java.io.Serializable;
034
035import io.jenetics.util.ISeq;
036import io.jenetics.util.IntRange;
037import io.jenetics.util.MSeq;
038import io.jenetics.util.Mean;
039
040/**
041 * NumericGene implementation which holds a 32-bit integer number.
042 *
043 * <p>This is a <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html">
044 * value-based</a> class; use of identity-sensitive operations (including
045 * reference equality ({@code ==}), identity hash code, or synchronization) on
046 * instances of {@code IntegerGene} may have unpredictable results and should
047 * be avoided.
048 *
049 * @see IntegerChromosome
050 *
051 * @implNote
052 * This class is immutable and thread-safe.
053 *
054 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
055 * @since 2.0
056 * @version 7.0
057 */
058public final class IntegerGene
059        implements
060                NumericGene<Integer, IntegerGene>,
061                Mean<IntegerGene>,
062                Comparable<IntegerGene>,
063                Serializable
064{
065
066        @Serial
067        private static final long serialVersionUID = 2L;
068
069        private final int _allele;
070        private final int _min;
071        private final int _max;
072
073        /**
074         * Create a new random {@code IntegerGene} with the given value and the
075         * given range. If the {@code value} isn't within the interval
076         * {@code [min, max)}, no exception is thrown. In this case the method
077         * {@link IntegerGene#isValid()} returns {@code false}.
078         *
079         * @param allele the value of the gene.
080         * @param min the minimal valid value of this gene (inclusively).
081         * @param max the maximal valid value of this gene (exclusively).
082         */
083        private IntegerGene(final int allele, final int min, final int max) {
084                _allele = allele;
085                _min = min;
086                _max = max;
087        }
088
089        @Override
090        public Integer allele() {
091                return _allele;
092        }
093
094        @Override
095        public Integer min() {
096                return _min;
097        }
098
099        @Override
100        public Integer max() {
101                return _max;
102        }
103
104        /**
105         * Return the range of {@code this} gene.
106         *
107         * @since 4.4
108         *
109         * @return the range of {@code this} gene
110         */
111        public IntRange range() {
112                return IntRange.of(_min, _max);
113        }
114
115        @Override
116        public byte byteValue() {
117                return (byte) _allele;
118        }
119
120        @Override
121        public short shortValue() {
122                return (short) _allele;
123        }
124
125        @Override
126        public int intValue() {
127                return _allele;
128        }
129
130        @Override
131        public long longValue() {
132                return _allele;
133        }
134
135        @Override
136        public float floatValue() {
137                return (float) _allele;
138        }
139
140        @Override
141        public double doubleValue() {
142                return _allele;
143        }
144
145        @Override
146        public boolean isValid() {
147                return _allele >= _min && _allele < _max;
148        }
149
150        @Override
151        public int compareTo(final IntegerGene other) {
152                return Integer.compare(_allele, other._allele);
153        }
154
155        @Override
156        public IntegerGene mean(final IntegerGene that) {
157                final int x = that._allele;
158                final int y = _allele;
159
160                // http://aggregate.org/MAGIC/#Average%20of%20Integers
161                return IntegerGene.of((x&y) + ((x^y) >> 1), _min, _max);
162        }
163
164        /**
165         * Create a new gene from the given {@code value} and the gene context.
166         *
167         * @since 5.0
168         * @param allele the value of the new gene.
169         * @return a new gene with the given value.
170         */
171        public IntegerGene newInstance(final int allele) {
172                return IntegerGene.of(allele, _min, _max);
173        }
174
175        @Override
176        public IntegerGene newInstance(final Integer allele) {
177                return IntegerGene.of(allele, _min, _max);
178        }
179
180        @Override
181        public IntegerGene newInstance(final Number allele) {
182                final int value = allele instanceof Double || allele instanceof Float
183                        ? (int)Math.round(allele.doubleValue())
184                        : allele.intValue();
185
186                return IntegerGene.of(value, _min, _max);
187        }
188
189        @Override
190        public IntegerGene newInstance() {
191                return IntegerGene.of(random().nextInt(_min, _max), _min, _max);
192        }
193
194        @Override
195        public int hashCode() {
196                return hash(_allele, hash(_min, hash(_max)));
197        }
198
199        @Override
200        public boolean equals(final Object obj) {
201                return obj == this ||
202                        obj instanceof IntegerGene other &&
203                        other._allele == _allele &&
204                        other._min == _min &&
205                        other._max == _max;
206        }
207
208        @Override
209        public String toString() {
210                return String.format("[%s]", _allele);
211        }
212
213        /* *************************************************************************
214         * Static factory methods.
215         * ************************************************************************/
216
217        /**
218         * Create a new random {@code IntegerGene} with the given value and the
219         * given range. If the {@code value} isn't within the interval
220         * {@code [min, max)}, no exception is thrown. In this case the method
221         * {@link IntegerGene#isValid()} returns {@code false}.
222         *
223         * @param allele the value of the gene.
224         * @param min the minimal valid value of this gene (inclusively).
225         * @param max the maximal valid value of this gene (exclusively).
226         * @return a new {@code IntegerGene} with the given {@code value}
227         */
228        public static IntegerGene of(final int allele, final int min, final int max) {
229                return new IntegerGene(allele, min, max);
230        }
231
232        /**
233         * Create a new random {@code IntegerGene} with the given value and the
234         * given range. If the {@code value} isn't within the interval [min, max],
235         * no exception is thrown. In this case the method
236         * {@link IntegerGene#isValid()} returns {@code false}.
237         *
238         * @since 3.2
239         *
240         * @param allele the value of the gene.
241         * @param range the integer range to use
242         * @return a new {@code IntegerGene} with the give {@code value}
243         * @throws NullPointerException if the given {@code range} is {@code null}.
244         */
245        public static IntegerGene of(final int allele, final IntRange range) {
246                return IntegerGene.of(allele, range.min(), range.max());
247        }
248
249        /**
250         * Create a new random {@code IntegerGene}. It is guaranteed that the value of
251         * the {@code IntegerGene} lies in the interval {@code [min, max)}.
252         *
253         * @param min the minimal valid value of this gene (inclusively).
254         * @param max the maximal valid value of this gene (exclusively).
255         * @return a new random {@code IntegerGene}
256         * @throws IllegalArgumentException if {@code max} is greater than
257         *         or equal to {@code min}
258         */
259        public static IntegerGene of(final int min, final int max) {
260                return of(random().nextInt(min, max), min, max);
261        }
262
263        /**
264         * Create a new random {@code IntegerGene}. It is guaranteed that the value of
265         * the {@code IntegerGene} lies in the interval {@code [min, max)}.
266         *
267         * @since 3.2
268         *
269         * @param range the integer range to use
270         * @return a new random {@code IntegerGene}
271         * @throws NullPointerException if the given {@code range} is {@code null}.
272         * @throws IllegalArgumentException if {@code max} is greater than
273         *         or equal to {@code min}
274         */
275        public static IntegerGene of(final IntRange range) {
276                return of(random().nextInt(range.min(), range.max()), range);
277        }
278
279        static ISeq<IntegerGene> seq(
280                final int min,
281                final int max,
282                final IntRange lengthRange
283        ) {
284                final var random = random();
285                final var length = random.nextInt(lengthRange.min(), lengthRange.max());
286
287                return MSeq.<IntegerGene>ofLength(length)
288                        .fill(() -> new IntegerGene(random.nextInt(min, max), min, max))
289                        .toISeq();
290        }
291
292
293        /* *************************************************************************
294         *  Java object serialization
295         * ************************************************************************/
296
297        @Serial
298        private Object writeReplace() {
299                return new SerialProxy(SerialProxy.INTEGER_GENE, this);
300        }
301
302        @Serial
303        private void readObject(final ObjectInputStream stream)
304                throws InvalidObjectException
305        {
306                throw new InvalidObjectException("Serialization proxy required.");
307        }
308
309        void write(final DataOutput out) throws IOException {
310                writeInt(_allele, out);
311                writeInt(_min, out);
312                writeInt(_max, out);
313        }
314
315        static IntegerGene read(final DataInput in) throws IOException {
316                return of(readInt(in), readInt(in), readInt(in));
317        }
318
319}