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 java.util.Objects.requireNonNull;
023import static io.jenetics.internal.util.SerialIO.readInt;
024import static io.jenetics.internal.util.SerialIO.readLong;
025import static io.jenetics.internal.util.SerialIO.writeInt;
026import static io.jenetics.internal.util.SerialIO.writeLong;
027
028import java.io.DataInput;
029import java.io.DataOutput;
030import java.io.IOException;
031import java.io.InvalidObjectException;
032import java.io.ObjectInputStream;
033import java.io.Serial;
034import java.io.Serializable;
035import java.util.function.Function;
036import java.util.stream.IntStream;
037import java.util.stream.LongStream;
038import java.util.stream.Stream;
039
040import io.jenetics.util.ISeq;
041import io.jenetics.util.IntRange;
042import io.jenetics.util.LongRange;
043import io.jenetics.util.MSeq;
044
045/**
046 * Numeric chromosome implementation which holds 64-bit integer numbers.
047 *
048 * @see LongGene
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.1
056 */
057public class LongChromosome
058        extends AbstractBoundedChromosome<Long, LongGene>
059        implements
060                NumericChromosome<Long, LongGene>,
061                Serializable
062{
063        @Serial
064        private static final long serialVersionUID = 3L;
065
066        /**
067         * Create a new chromosome from the given {@code genes} and the allowed
068         * length range of the chromosome.
069         *
070         * @since 4.0
071         *
072         * @param genes the genes that form the chromosome.
073         * @param lengthRange the allowed length range of the chromosome
074         * @throws NullPointerException if one of the arguments is {@code null}.
075         * @throws IllegalArgumentException if the length of the gene sequence is
076         *         empty, doesn't match with the allowed length range, the minimum
077         *         or maximum of the range is smaller or equal zero, or the given
078         *         range size is zero.
079         */
080        protected LongChromosome(
081                final ISeq<LongGene> genes,
082                final IntRange lengthRange
083        ) {
084                super(genes, lengthRange);
085        }
086
087        @Override
088        public LongChromosome newInstance(final ISeq<LongGene> genes) {
089                return new LongChromosome(genes, lengthRange());
090        }
091
092        @Override
093        public LongChromosome newInstance() {
094                return of(_min, _max, lengthRange());
095        }
096
097        /**
098         * Maps the gene alleles of this chromosome, given as {@code long[]} array,
099         * by applying the given mapper function {@code f}. The mapped gene values
100         * are then wrapped into a newly created chromosome.
101         *
102         * {@snippet lang="java":
103         * final LongChromosome chromosome = null; // @replace substring='null' replacement="..."
104         * final LongChromosome halved = chromosome.map(Main::half);
105         *
106         * static long[] half(final long[] values) {
107         *     for (int i = 0; i < values.length; ++i) {
108         *         values[i] /= 2;
109         *     }
110         *     return values;
111         * }
112         * }
113         *
114         * @since 6.1
115         *
116         * @param f the mapper function
117         * @return a newly created chromosome with the mapped gene values
118         * @throws NullPointerException if the mapper function is {@code null}.
119         * @throws IllegalArgumentException if the length of the mapped
120         *         {@code long[]} array is empty or doesn't match with the allowed
121         *         length range
122         */
123        public LongChromosome map(final Function<? super long[], long[]> f) {
124                requireNonNull(f);
125
126                final var range = LongRange.of(_min, _max);
127                final var genes = LongStream.of(f.apply(toArray()))
128                        .mapToObj(v -> LongGene.of(v, range))
129                        .collect(ISeq.toISeq());
130
131                return newInstance(genes);
132        }
133
134        /**
135         * Returns a sequential stream of the alleles with this chromosome as its
136         * source.
137         *
138         * @since 4.3
139         *
140         * @return a sequential stream of alleles
141         */
142        public LongStream longStream() {
143                return IntStream.range(0, length()).mapToLong(this::longValue);
144        }
145
146        /**
147         * Returns a long array containing all the elements in this chromosome
148         * in a proper sequence.  If the chromosome fits in the specified array, it is
149         * returned therein. Otherwise, a new array is allocated with the length of
150         * this chromosome.
151         *
152         * @since 3.0
153         *
154         * @param array the array into which the elements of these chromosomes are to
155         *        be stored, if it is big enough; otherwise, a new array is
156         *        allocated for this purpose.
157         * @return an array containing the elements of this chromosome
158         * @throws NullPointerException if the given {@code array} is {@code null}
159         */
160        public long[] toArray(final long[] array) {
161                final long[] a = array.length >= length() ? array : new long[length()];
162                for (int i = length(); --i >= 0;) {
163                        a[i] = longValue(i);
164                }
165
166                return a;
167        }
168
169        /**
170         * Returns a long array containing all the elements in this chromosome
171         * in a proper sequence.
172         *
173         * @since 3.0
174         *
175         * @return an array containing the elements of this chromosome
176         */
177        public long[] toArray() {
178                return toArray(new long[length()]);
179        }
180
181
182        /* *************************************************************************
183         * Static factory methods.
184         * ************************************************************************/
185
186        /**
187         * Create a new {@code LongChromosome} with the given genes.
188         *
189         * @param genes the genes of the chromosome.
190         * @return a new chromosome with the given genes.
191         * @throws NullPointerException if the given {@code genes} are {@code null}
192         * @throws IllegalArgumentException if the length of the genes array is
193         *         empty or the given {@code genes} doesn't have the same range.
194         */
195        public static LongChromosome of(final LongGene... genes) {
196                checkGeneRange(Stream.of(genes).map(LongGene::range));
197                return new LongChromosome(ISeq.of(genes), IntRange.of(genes.length));
198        }
199
200        /**
201         * Create a new {@code LongChromosome} with the given genes.
202         *
203         * @since 4.3
204         *
205         * @param genes the genes of the chromosome.
206         * @return a new chromosome with the given genes.
207         * @throws NullPointerException if the given {@code genes} are {@code null}
208         * @throws IllegalArgumentException if the of the genes iterable is empty or
209         *         the given {@code genes} doesn't have the same range.
210         */
211        public static LongChromosome of(final Iterable<LongGene> genes) {
212                final ISeq<LongGene> values = ISeq.of(genes);
213                checkGeneRange(values.stream().map(LongGene::range));
214                return new LongChromosome(values, IntRange.of(values.length()));
215        }
216
217        /**
218         * Create a new random chromosome.
219         *
220         * @since 4.0
221         *
222         * @param min the min value of the {@link LongGene}s (inclusively).
223         * @param max the max value of the {@link LongGene}s (exclusively).
224         * @param lengthRange the allowed length range of the chromosome.
225         * @return a new {@code IntegerChromosome} with the given parameter
226         * @throws IllegalArgumentException if the length of the gene sequence is
227         *         empty, doesn't match with the allowed length range, the minimum
228         *         or maximum of the range is smaller or equal zero, or the given
229         *         range size is zero.
230         * @throws IllegalArgumentException if {@code max} is greater than
231         *         or equal to {@code min}
232         * @throws NullPointerException if the given {@code lengthRange} is
233         *         {@code null}
234         */
235        public static LongChromosome of(
236                final long min,
237                final long max,
238                final IntRange lengthRange
239        ) {
240                final ISeq<LongGene> values = LongGene.seq(min, max, lengthRange);
241                return new LongChromosome(values, lengthRange);
242        }
243
244        /**
245         * Create a new random {@code LongChromosome}.
246         *
247         * @param min the min value of the {@link LongGene}s (inclusively).
248         * @param max the max value of the {@link LongGene}s (exclusively).
249         * @param length the length of the chromosome.
250         * @return a new {@code LongChromosome} with the given gene parameters.
251         * @throws IllegalArgumentException if the {@code length} is smaller than
252         *         one.
253         * @throws IllegalArgumentException if {@code max} is greater than
254         *         or equal to {@code min}
255         */
256        public static LongChromosome of(
257                final long min,
258                final long max,
259                final int length
260        ) {
261                return of(min, max, IntRange.of(length));
262        }
263
264        /**
265         * Create a new random chromosome.
266         *
267         * @since 4.0
268         *
269         * @param range the integer range of the chromosome.
270         * @param lengthRange the allowed length range of the chromosome.
271         * @return a new {@code LongChromosome} with the given parameter
272         * @throws IllegalArgumentException if the length of the gene sequence is
273         *         empty, doesn't match with the allowed length range, the minimum
274         *         or maximum of the range is smaller or equal zero, or the given
275         *         range size is zero.
276         * @throws IllegalArgumentException if {@code max} is greater than
277         *         or equal to {@code min}
278         * @throws NullPointerException if the given {@code lengthRange} is
279         *         {@code null}
280         */
281        public static LongChromosome of(
282                final LongRange range,
283                final IntRange lengthRange
284        ) {
285                return of(range.min(), range.max(), lengthRange);
286        }
287
288        /**
289         * Create a new random {@code LongChromosome}.
290         *
291         * @since 3.2
292         *
293         * @param range the long range of the chromosome.
294         * @param length the length of the chromosome.
295         * @return a new random {@code LongChromosome}
296         * @throws NullPointerException if the given {@code range} is {@code null}
297         * @throws IllegalArgumentException if the {@code length} is smaller than
298         *         one.
299         * @throws IllegalArgumentException if {@code max} is greater than
300         *         or equal to {@code min}
301         */
302        public static LongChromosome of(final LongRange range, final int length) {
303                return of(range.min(), range.max(), length);
304        }
305
306        /**
307         * Create a new random {@code LongChromosome} of length one.
308         *
309         * @param min the minimal value of this chromosome (inclusively).
310         * @param max the maximal value of this chromosome (exclusively).
311         * @return a new {@code LongChromosome} with the given gene parameters.
312         * @throws IllegalArgumentException if {@code max} is greater than
313         *         or equal to {@code min}
314         */
315        public static LongChromosome of(final long min, final long max) {
316                return of(min, max, 1);
317        }
318
319        /**
320         * Create a new random {@code LongChromosome} of length one.
321         *
322         * @since 3.2
323         *
324         * @param range the long range of the chromosome.
325         * @return a new random {@code LongChromosome} of length one
326         * @throws NullPointerException if the given {@code range} is {@code null}
327         * @throws IllegalArgumentException if {@code max} is greater than
328         *         or equal to {@code min}
329         */
330        public static LongChromosome of(final LongRange range) {
331                return of(range.min(), range.max());
332        }
333
334
335
336        /* *************************************************************************
337         *  Java object serialization
338         * ************************************************************************/
339
340        @Serial
341        private Object writeReplace() {
342                return new SerialProxy(SerialProxy.LONG_CHROMOSOME, this);
343        }
344
345        @Serial
346        private void readObject(final ObjectInputStream stream)
347                throws InvalidObjectException
348        {
349                throw new InvalidObjectException("Serialization proxy required.");
350        }
351
352        void write(final DataOutput out) throws IOException {
353                writeInt(length(), out);
354                writeInt(lengthRange().min(), out);
355                writeInt(lengthRange().max(), out);
356                writeLong(_min, out);
357                writeLong(_max, out);
358
359                for (int i = 0, n = length(); i < n; ++i) {
360                        writeLong(longValue(i), out);
361                }
362        }
363
364        static LongChromosome read(final DataInput in) throws IOException {
365                final var length = readInt(in);
366                final var lengthRange = IntRange.of(readInt(in), readInt(in));
367                final var min = readLong(in);
368                final var max = readLong(in);
369
370                final MSeq<LongGene> values = MSeq.ofLength(length);
371                for (int i = 0; i < length; ++i) {
372                        values.set(i, LongGene.of(readLong(in), min, max));
373                }
374
375                return new LongChromosome(values.toISeq(), lengthRange);
376        }
377
378}