001/*
002 * Java Genetic Algorithm Library (jenetics-8.2.0).
003 * Copyright (c) 2007-2025 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 instanceof IntegerGene other &&
202                        other._allele == _allele &&
203                        other._min == _min &&
204                        other._max == _max;
205        }
206
207        @Override
208        public String toString() {
209                return String.format("[%s]", _allele);
210        }
211
212        /* *************************************************************************
213         * Static factory methods.
214         * ************************************************************************/
215
216        /**
217         * Create a new random {@code IntegerGene} with the given value and the
218         * given range. If the {@code value} isn't within the interval
219         * {@code [min, max)}, no exception is thrown. In this case the method
220         * {@link IntegerGene#isValid()} returns {@code false}.
221         *
222         * @param allele the value of the gene.
223         * @param min the minimal valid value of this gene (inclusively).
224         * @param max the maximal valid value of this gene (exclusively).
225         * @return a new {@code IntegerGene} with the given {@code value}
226         */
227        public static IntegerGene of(final int allele, final int min, final int max) {
228                return new IntegerGene(allele, min, max);
229        }
230
231        /**
232         * Create a new random {@code IntegerGene} with the given value and the
233         * given range. If the {@code value} isn't within the interval [min, max],
234         * no exception is thrown. In this case the method
235         * {@link IntegerGene#isValid()} returns {@code false}.
236         *
237         * @since 3.2
238         *
239         * @param allele the value of the gene.
240         * @param range the integer range to use
241         * @return a new {@code IntegerGene} with the give {@code value}
242         * @throws NullPointerException if the given {@code range} is {@code null}.
243         */
244        public static IntegerGene of(final int allele, final IntRange range) {
245                return IntegerGene.of(allele, range.min(), range.max());
246        }
247
248        /**
249         * Create a new random {@code IntegerGene}. It is guaranteed that the value of
250         * the {@code IntegerGene} lies in the interval {@code [min, max)}.
251         *
252         * @param min the minimal valid value of this gene (inclusively).
253         * @param max the maximal valid value of this gene (exclusively).
254         * @return a new random {@code IntegerGene}
255         * @throws IllegalArgumentException if {@code max} is greater than
256         *         or equal to {@code min}
257         */
258        public static IntegerGene of(final int min, final int max) {
259                return of(random().nextInt(min, max), min, max);
260        }
261
262        /**
263         * Create a new random {@code IntegerGene}. It is guaranteed that the value of
264         * the {@code IntegerGene} lies in the interval {@code [min, max)}.
265         *
266         * @since 3.2
267         *
268         * @param range the integer range to use
269         * @return a new random {@code IntegerGene}
270         * @throws NullPointerException if the given {@code range} is {@code null}.
271         * @throws IllegalArgumentException if {@code max} is greater than
272         *         or equal to {@code min}
273         */
274        public static IntegerGene of(final IntRange range) {
275                return of(random().nextInt(range.min(), range.max()), range);
276        }
277
278        static ISeq<IntegerGene> seq(
279                final int min,
280                final int max,
281                final IntRange lengthRange
282        ) {
283                final var random = random();
284                final var length = random.nextInt(lengthRange.min(), lengthRange.max());
285
286                return MSeq.<IntegerGene>ofLength(length)
287                        .fill(() -> new IntegerGene(random.nextInt(min, max), min, max))
288                        .toISeq();
289        }
290
291
292        /* *************************************************************************
293         *  Java object serialization
294         * ************************************************************************/
295
296        @Serial
297        private Object writeReplace() {
298                return new SerialProxy(SerialProxy.INTEGER_GENE, this);
299        }
300
301        @Serial
302        private void readObject(final ObjectInputStream stream)
303                throws InvalidObjectException
304        {
305                throw new InvalidObjectException("Serialization proxy required.");
306        }
307
308        void write(final DataOutput out) throws IOException {
309                writeInt(_allele, out);
310                writeInt(_min, out);
311                writeInt(_max, out);
312        }
313
314        static IntegerGene read(final DataInput in) throws IOException {
315                return of(readInt(in), readInt(in), readInt(in));
316        }
317
318}