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