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