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