001/*
002 * Java Genetic Algorithm Library (jenetics-8.1.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.engine;
021
022import static java.lang.String.format;
023import static java.util.Map.entry;
024import static java.util.Objects.checkIndex;
025import static java.util.Objects.requireNonNull;
026import static java.util.function.Function.identity;
027
028import java.util.Arrays;
029import java.util.HashMap;
030import java.util.Map;
031import java.util.Map.Entry;
032import java.util.Objects;
033import java.util.function.Predicate;
034import java.util.function.Supplier;
035import java.util.stream.Collectors;
036import java.util.stream.DoubleStream;
037import java.util.stream.IntStream;
038import java.util.stream.LongStream;
039import java.util.stream.Stream;
040
041import io.jenetics.AnyChromosome;
042import io.jenetics.AnyGene;
043import io.jenetics.BitChromosome;
044import io.jenetics.BitGene;
045import io.jenetics.DoubleChromosome;
046import io.jenetics.DoubleGene;
047import io.jenetics.EnumGene;
048import io.jenetics.Gene;
049import io.jenetics.Genotype;
050import io.jenetics.IntegerChromosome;
051import io.jenetics.IntegerGene;
052import io.jenetics.LongChromosome;
053import io.jenetics.LongGene;
054import io.jenetics.PermutationChromosome;
055import io.jenetics.internal.util.Bits;
056import io.jenetics.internal.util.Predicates;
057import io.jenetics.internal.util.Requires;
058import io.jenetics.util.DoubleRange;
059import io.jenetics.util.ISeq;
060import io.jenetics.util.IntRange;
061import io.jenetics.util.LongRange;
062
063/**
064 * This class contains factory methods for creating common problem encodings.
065 *
066 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
067 * @since 3.2
068 * @version 5.2
069 */
070public final class Codecs {
071
072        private Codecs() {}
073
074        /**
075         * Return a scalar {@link InvertibleCodec} for the given range.
076         *
077         * @param domain the domain of the returned {@code Codec}
078         * @return a new scalar {@code Codec} with the given domain.
079         * @throws NullPointerException if the given {@code domain} is {@code null}
080         */
081        public static InvertibleCodec<Integer, IntegerGene>
082        ofScalar(final IntRange domain) {
083                requireNonNull(domain);
084
085                return InvertibleCodec.of(
086                        Genotype.of(IntegerChromosome.of(domain)),
087                        gt -> gt.chromosome().gene().allele(),
088                        val -> Genotype.of(IntegerChromosome.of(IntegerGene.of(val, domain)))
089                );
090        }
091
092        /**
093         * Return a scalar {@link InvertibleCodec} for the given range.
094         *
095         * @param domain the domain of the returned {@code Codec}
096         * @return a new scalar {@code Codec} with the given domain.
097         * @throws NullPointerException if the given {@code domain} is {@code null}
098         */
099        public static InvertibleCodec<Long, LongGene>
100        ofScalar(final LongRange domain) {
101                requireNonNull(domain);
102
103                return InvertibleCodec.of(
104                        Genotype.of(LongChromosome.of(domain)),
105                        gt -> gt.gene().allele(),
106                        val -> Genotype.of(LongChromosome.of(LongGene.of(val, domain)))
107                );
108        }
109
110        /**
111         * Return a scalar {@link InvertibleCodec} for the given range.
112         *
113         * @param domain the domain of the returned {@code Codec}
114         * @return a new scalar {@code Codec} with the given domain.
115         * @throws NullPointerException if the given {@code domain} is {@code null}
116         */
117        public static InvertibleCodec<Double, DoubleGene>
118        ofScalar(final DoubleRange domain) {
119                requireNonNull(domain);
120
121                return InvertibleCodec.of(
122                        Genotype.of(DoubleChromosome.of(domain)),
123                        gt -> gt.gene().allele(),
124                        val -> Genotype.of(DoubleChromosome.of(DoubleGene.of(val, domain)))
125                );
126        }
127
128        /**
129         * Return a scala {@link Codec} with the given allele {@link Supplier} and
130         * allele {@code validator}. The {@code supplier} is responsible for
131         * creating new random alleles, and the {@code validator} can verify it.
132         * <p>
133         * The following example shows a codec which creates and verifies
134         * {@code BigInteger} objects.
135         * {@snippet lang="java":
136         * final Codec<BigInteger, AnyGene<BigInteger>> codec = Codecs.of(
137         *     // Create new random 'BigInteger' object.
138         *     () -> {
139         *         final byte[] data = new byte[100];
140         *         RandomRegistry.getRandom().nextBytes(data);
141         *         return new BigInteger(data);
142         *     },
143         *     // Verify that bit 7 is set. (For illustration purpose.)
144         *     bi -> bi.testBit(7)
145         * );
146         * }
147         *
148         * @see AnyGene#of(Supplier, Predicate)
149         * @see AnyChromosome#of(Supplier, Predicate)
150         *
151         * @param <A> the allele type
152         * @param supplier the allele-supplier which is used for creating new,
153         *        random alleles
154         * @param validator the validator used for validating the created gene. This
155         *        predicate is used in the {@link AnyGene#isValid()} method.
156         * @return a new {@code Codec} with the given parameters
157         * @throws NullPointerException if one of the parameters is {@code null}
158         */
159        public static <A> Codec<A, AnyGene<A>> ofScalar(
160                final Supplier<? extends A> supplier,
161                final Predicate<? super A> validator
162        ) {
163                return Codec.of(
164                        Genotype.of(AnyChromosome.of(supplier, validator)),
165                        gt -> gt.gene().allele()
166                );
167        }
168
169        /**
170         * Return a scala {@link Codec} with the given allele {@link Supplier} and
171         * allele {@code validator}. The {@code supplier} is responsible for
172         * creating new random alleles.
173         *
174         * @see #ofScalar(Supplier, Predicate)
175         * @see AnyGene#of(Supplier)
176         * @see AnyChromosome#of(Supplier)
177         *
178         * @param <A> the allele type
179         * @param supplier the allele-supplier which is used for creating new,
180         *        random alleles
181         * @return a new {@code Codec} with the given parameters
182         * @throws NullPointerException if the parameter is {@code null}
183         */
184        public static <A> Codec<A, AnyGene<A>> ofScalar(
185                final Supplier<? extends A> supplier
186        ) {
187                return Codec.of(
188                        Genotype.of(AnyChromosome.of(supplier)),
189                        gt -> gt.gene().allele()
190                );
191        }
192
193        /**
194         * Return a vector {@link InvertibleCodec} for the given range. All vector
195         * values are restricted by the same domain.
196         *
197         * @param domain the domain of the vector values
198         * @param length the vector length
199         * @return a new vector {@code Codec}
200         * @throws NullPointerException if the given {@code domain} is {@code null}
201         * @throws IllegalArgumentException if the {@code length} is smaller than
202         *         one.
203         */
204        public static InvertibleCodec<int[], IntegerGene> ofVector(
205                final IntRange domain,
206                final int length
207        ) {
208                return ofVector(domain, IntRange.of(length));
209        }
210
211        /**
212         * Return a vector {@link InvertibleCodec} for the given range. All vector
213         * values are restricted by the same domain.
214         *
215         * @since 7.0
216         *
217         * @param domain the domain of the vector values
218         * @param length the vector length range
219         * @return a new vector {@code Codec}
220         * @throws NullPointerException if the given {@code domain} is {@code null}
221         * @throws IllegalArgumentException if the {@code length} is smaller than
222         *         one.
223         */
224        public static InvertibleCodec<int[], IntegerGene> ofVector(
225                final IntRange domain,
226                final IntRange length
227        ) {
228                requireNonNull(domain);
229                Requires.positive(length.min());
230
231                return InvertibleCodec.of(
232                        Genotype.of(IntegerChromosome.of(domain, length)),
233                        gt -> gt.chromosome().as(IntegerChromosome.class).toArray(),
234                        val -> Genotype.of(
235                                IntegerChromosome.of(
236                                        IntStream.of(val)
237                                                .mapToObj(i -> IntegerGene.of(i, domain))
238                                                .collect(ISeq.toISeq())
239                                )
240                        )
241                );
242        }
243
244        /**
245         * Return a vector {@link InvertibleCodec} for the given range. All vector
246         * values are restricted by the same domain.
247         *
248         * @param domain the domain of the vector values
249         * @param length the vector length
250         * @return a new vector {@code Codec}
251         * @throws NullPointerException if the given {@code domain} is {@code null}
252         * @throws IllegalArgumentException if the {@code length} is smaller than
253         *         one.
254         */
255        public static InvertibleCodec<long[], LongGene> ofVector(
256                final LongRange domain,
257                final int length
258        ) {
259                return ofVector(domain, IntRange.of(length));
260        }
261
262        /**
263         * Return a vector {@link InvertibleCodec} for the given range. All vector
264         * values are restricted by the same domain.
265         *
266         * @since 7.0
267         *
268         * @param domain the domain of the vector values
269         * @param length the vector length range
270         * @return a new vector {@code Codec}
271         * @throws NullPointerException if the given {@code domain} is {@code null}
272         * @throws IllegalArgumentException if the {@code length} is smaller than
273         *         one.
274         */
275        public static InvertibleCodec<long[], LongGene> ofVector(
276                final LongRange domain,
277                final IntRange length
278        ) {
279                requireNonNull(domain);
280                Requires.positive(length.min());
281
282                return InvertibleCodec.of(
283                        Genotype.of(LongChromosome.of(domain, length)),
284                        gt -> gt.chromosome().as(LongChromosome.class).toArray(),
285                        val -> Genotype.of(
286                                LongChromosome.of(
287                                        LongStream.of(val)
288                                                .mapToObj(l -> LongGene.of(l, domain))
289                                                .collect(ISeq.toISeq())
290                                )
291                        )
292                );
293        }
294
295        /**
296         * Return a vector {@link InvertibleCodec} for the given range. All vector
297         * values are restricted by the same domain. Use the following code if you
298         * want to create {@code int[]} arrays and still using {@link DoubleGene}.
299         * The {@code int[]} array elements are created by casting the {@code double}
300         * values to {@code int} values.
301         * {@snippet lang=java:
302         * final Codec<int[], DoubleGene> codec = Codecs
303         *     .ofVector(DoubleRange.of(0, 100), 100)
304         *     .map(ArrayConversions::doubleToIntArray);
305         * }
306         * If you want round the double values, you can use the following code.
307         * {@snippet lang=java:
308         * final Codec<int[], DoubleGene> codec = Codecs
309         *     .ofVector(DoubleRange.of(0, 100), 100)
310         *     .map(ArrayConversions.doubleToIntArray(v -> (int)Math.round(v)));
311         * }
312         *
313         * @param domain the domain of the vector values
314         * @param length the vector length
315         * @return a new vector {@code Codec}
316         * @throws NullPointerException if the given {@code domain} is {@code null}
317         * @throws IllegalArgumentException if the {@code length} is smaller than
318         *         one.
319         */
320        public static InvertibleCodec<double[], DoubleGene> ofVector(
321                final DoubleRange domain,
322                final int length
323        ) {
324                return ofVector(domain, IntRange.of(length));
325        }
326
327        /**
328         * Return a vector {@link InvertibleCodec} for the given range. All vector
329         * values are restricted by the same domain.
330         *
331         * @since 7.0
332         *
333         * @param domain the domain of the vector values
334         * @param length the vector length range
335         * @return a new vector {@code Codec}
336         * @throws NullPointerException if the given {@code domain} is {@code null}
337         * @throws IllegalArgumentException if the {@code length} is smaller than
338         *         one.
339         */
340        public static InvertibleCodec<double[], DoubleGene> ofVector(
341                final DoubleRange domain,
342                final IntRange length
343        ) {
344                requireNonNull(domain);
345                Requires.positive(length.min());
346
347                return InvertibleCodec.of(
348                        Genotype.of(DoubleChromosome.of(domain, length)),
349                        gt -> gt.chromosome().as(DoubleChromosome.class).toArray(),
350                        val -> Genotype.of(
351                                DoubleChromosome.of(
352                                        DoubleStream.of(val)
353                                                .mapToObj(d -> DoubleGene.of(d, domain))
354                                                .collect(ISeq.toISeq())
355                                )
356                        )
357                );
358        }
359
360        /**
361         * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
362         * element might have a different domain. The vector length is equal to the
363         * number of domains.
364         *
365         * @param domains the domain ranges
366         * @return a new vector {@code Codec}
367         * @throws NullPointerException if one of the arguments is {@code null}
368         * @throws IllegalArgumentException if the {@code domains} array is empty
369         */
370        public static InvertibleCodec<int[], IntegerGene>
371        ofVector(final IntRange... domains) {
372                if (domains.length == 0) {
373                        throw new IllegalArgumentException("Domains must not be empty.");
374                }
375
376                final ISeq<IntegerChromosome> chromosomes = Stream.of(domains)
377                        .peek(Objects::requireNonNull)
378                        .map(IntegerGene::of)
379                        .map(IntegerChromosome::of)
380                        .collect(ISeq.toISeq());
381
382                return InvertibleCodec.of(
383                        Genotype.of(chromosomes),
384                        gt -> {
385                                final int[] args = new int[gt.length()];
386                                for (int i = gt.length(); --i >= 0;) {
387                                        args[i] = gt.get(i).gene().intValue();
388                                }
389                                return args;
390                        },
391                        val -> Genotype.of(
392                                IntStream.range(0, val.length)
393                                        .mapToObj(i ->
394                                                IntegerChromosome.of(IntegerGene.of(val[i], domains[i])))
395                                        .collect(ISeq.toISeq())
396                        )
397                );
398        }
399
400        /**
401         * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
402         * element might have a different domain. The vector length is equal to the
403         * number of domains.
404         *
405         * @param domains the domain ranges
406         * @return a new vector {@code Codec}
407         * @throws NullPointerException if one of the arguments is {@code null}
408         * @throws IllegalArgumentException if the {@code domains} array is empty
409         */
410        public static InvertibleCodec<long[], LongGene>
411        ofVector(final LongRange... domains) {
412                if (domains.length == 0) {
413                        throw new IllegalArgumentException("Domains must not be empty.");
414                }
415
416                final ISeq<LongChromosome> chromosomes = Stream.of(domains)
417                        .peek(Objects::requireNonNull)
418                        .map(LongGene::of)
419                        .map(LongChromosome::of)
420                        .collect(ISeq.toISeq());
421
422                return InvertibleCodec.of(
423                        Genotype.of(chromosomes),
424                        gt -> {
425                                final long[] args = new long[gt.length()];
426                                for (int i = gt.length(); --i >= 0;) {
427                                        args[i] = gt.get(i).gene().longValue();
428                                }
429                                return args;
430                        },
431                        val -> Genotype.of(
432                                IntStream.range(0, val.length)
433                                        .mapToObj(i ->
434                                                LongChromosome.of(LongGene.of(val[i], domains[i])))
435                                        .collect(ISeq.toISeq())
436                        )
437                );
438        }
439
440        /**
441         * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
442         * element might have a different domain. The vector length is equal to the
443         * number of domains.
444         *
445         * @param domains the domain ranges
446         * @return a new vector {@code Codec}
447         * @throws NullPointerException if one of the arguments is {@code null}
448         * @throws IllegalArgumentException if the {@code domains} array is empty
449         */
450        public static InvertibleCodec<double[], DoubleGene>
451        ofVector(final DoubleRange... domains) {
452                if (domains.length == 0) {
453                        throw new IllegalArgumentException("Domains must not be empty.");
454                }
455
456                final ISeq<DoubleChromosome> chromosomes = Stream.of(domains)
457                        .peek(Objects::requireNonNull)
458                        .map(DoubleGene::of)
459                        .map(DoubleChromosome::of)
460                        .collect(ISeq.toISeq());
461
462                return InvertibleCodec.of(
463                        Genotype.of(chromosomes),
464                        gt -> {
465                                final double[] args = new double[gt.length()];
466                                for (int i = gt.length(); --i >= 0;) {
467                                        args[i] = gt.get(i).gene().doubleValue();
468                                }
469                                return args;
470                        },
471                        val -> Genotype.of(
472                                IntStream.range(0, val.length)
473                                        .mapToObj(i ->
474                                                DoubleChromosome.of(DoubleGene.of(val[i], domains[i])))
475                                        .collect(ISeq.toISeq())
476                        )
477                );
478        }
479
480        /**
481         * Return a scala {@link Codec} with the given allele {@link Supplier},
482         * allele {@code validator} and {@code Chromosome} length. The
483         * {@code supplier} is responsible for creating new random alleles, and the
484         * {@code validator} can verify it.
485         * <p>
486         * The following example shows a codec which creates and verifies
487         * {@code BigInteger} object arrays.
488         * {@snippet lang="java":
489         * final Codec<BigInteger[], AnyGene<BigInteger>> codec = Codecs.of(
490         *     // Create new random 'BigInteger' object.
491         *     () -> {
492         *         final byte[] data = new byte[100];
493         *         RandomRegistry.getRandom().nextBytes(data);
494         *         return new BigInteger(data);
495         *     },
496         *     // Verify that bit 7 is set. (For illustration purpose.)
497         *     bi -> bi.testBit(7),
498         *     // The 'Chromosome' length.
499         *     123
500         * );
501         * }
502         *
503         * @see AnyChromosome#of(Supplier, Predicate, Predicate, int)
504         *
505         * @param <A> the allele type
506         * @param supplier the allele-supplier which is used for creating new,
507         *        random alleles
508         * @param alleleValidator the validator used for validating the created gene.
509         *        This predicate is used in the {@link AnyGene#isValid()} method.
510         * @param alleleSeqValidator the validator used for validating the created
511         *        chromosome. This predicate is used in the
512         *        {@link AnyChromosome#isValid()} method.
513         * @param length the vector length
514         * @return a new {@code Codec} with the given parameters
515         * @throws NullPointerException if one of the parameters is {@code null}
516         * @throws IllegalArgumentException if the length of the vector is smaller
517         *         than one.
518         */
519        public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
520                final Supplier<? extends A> supplier,
521                final Predicate<? super A> alleleValidator,
522                final Predicate<? super ISeq<A>> alleleSeqValidator,
523                final int length
524        ) {
525                requireNonNull(supplier);
526                requireNonNull(alleleSeqValidator);
527                requireNonNull(alleleSeqValidator);
528                Requires.positive(length);
529
530                return Codec.of(
531                        Genotype.of(AnyChromosome
532                                .of(supplier, alleleValidator, alleleSeqValidator, length)),
533                        gt -> gt.chromosome().stream()
534                                .map(Gene::allele)
535                                .collect(ISeq.toISeq())
536                );
537        }
538
539        /**
540         * Return a scala {@link Codec} with the given allele {@link Supplier},
541         * allele {@code validator} and {@code Chromosome} length. The
542         * {@code supplier} is responsible for creating new random alleles, and the
543         * {@code validator} can verify it.
544         *
545         * @param <A> the allele type
546         * @param supplier the allele-supplier which is used for creating new,
547         *        random alleles
548         * @param validator the validator used for validating the created gene. This
549         *        predicate is used in the {@link AnyGene#isValid()} method.
550         * @param length the vector length
551         * @return a new {@code Codec} with the given parameters
552         * @throws NullPointerException if one of the parameters is {@code null}
553         * @throws IllegalArgumentException if the length of the vector is smaller
554         *         than one.
555         */
556        public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
557                final Supplier<? extends A> supplier,
558                final Predicate<? super A> validator,
559                final int length
560        ) {
561                return ofVector(
562                        supplier,
563                        validator,
564                        Predicates.True(),
565                        length
566                );
567        }
568
569        /**
570         * Return a scala {@link Codec} with the given allele {@link Supplier} and
571         * {@code Chromosome} length. The {@code supplier} is responsible for
572         * creating new random alleles.
573         *
574         * @param <A> the allele type
575         * @param supplier the allele-supplier which is used for creating new,
576         *        random alleles
577         * @param length the vector length
578         * @return a new {@code Codec} with the given parameters
579         * @throws NullPointerException if one of the parameters is {@code null}
580         * @throws IllegalArgumentException if the length of the vector is smaller
581         *         than one.
582         */
583        public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
584                final Supplier<? extends A> supplier,
585                final int length
586        ) {
587                return ofVector(supplier, Predicates.TRUE, length);
588        }
589
590        /**
591         * Create a vector ({@link ISeq}) of domain objects, which are created with
592         * the given {@code codec}.
593         * {@snippet lang=java:
594         * // Domain model of solution space.
595         * record Path(WayPoint[] stops) {}
596         *
597         * // Codec fora single GPS point (latitude, longitude).
598         * final Codec<WayPoint, DoubleGene> wpc = Codec.combine(
599         *     Codecs.ofScalar(DoubleRange.of(30, 50)), // latitude
600         *     Codecs.ofScalar(DoubleRange.of(69, 72)), // longitude
601         *     WayPoint::of
602         * );
603         *
604         * // Codec for the path object.
605         * final Codec<Path, DoubleGene> pc = Codecs.ofVector(wpc, 10)
606         *     .map(points -> points.toArray(WayPoint[]::new))
607         *     .map(Path::new);
608         *
609         * final Path path = pc.decode(pc.encoding().newInstance());
610         * }
611         *
612         * @since 8.1
613         *
614         * @param codec the codec for the <em>domain</em> object
615         * @param length the length of the vector.
616         * @return a codec for a sequence of domain objects
617         * @param <S> the type of the domain object
618         * @param <G> the encoding gene type
619         * @throws NullPointerException if the given {@code codec} is {@code null}
620         * @throws IllegalArgumentException if the length is smaller than 1
621         */
622        @SuppressWarnings("unchecked")
623        public static <S, G extends Gene<?, G>> Codec<ISeq<S>, G>
624        ofVector(final Codec<? extends S, G> codec, final int length) {
625                requireNonNull(codec);
626                if (length <= 0) {
627                        throw new IllegalArgumentException("Length must be positive: " + length);
628                }
629
630                return Codec.combine(
631                        IntStream.range(0, length)
632                                .mapToObj(__ -> codec)
633                                .collect(ISeq.toISeq()),
634                        objects -> ISeq.of((S[])objects)
635                );
636        }
637
638        /**
639         * Create a permutation {@link InvertibleCodec} of integer in the range
640         * {@code [0, length)}.
641         *
642         * @param length the number of permutation elements
643         * @return a permutation {@code Codec} of integers
644         * @throws IllegalArgumentException if the {@code length} is smaller than
645         *         one.
646         */
647        public static InvertibleCodec<int[], EnumGene<Integer>>
648        ofPermutation(final int length) {
649                Requires.positive(length);
650
651                final PermutationChromosome<Integer> chromosome =
652                        PermutationChromosome.ofInteger(length);
653
654                final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
655                        .collect(Collectors.toMap(EnumGene::allele, identity()));
656
657                return InvertibleCodec.of(
658                        Genotype.of(chromosome),
659                        gt -> gt.chromosome().stream()
660                                .mapToInt(EnumGene::allele)
661                                .toArray(),
662                        val -> Genotype.of(
663                                new PermutationChromosome<>(
664                                        IntStream.of(val)
665                                                .mapToObj(genes::get)
666                                                .collect(ISeq.toISeq())
667                                )
668                        )
669                );
670        }
671
672        /**
673         * Create a permutation {@link InvertibleCodec} with the given alleles.
674         *
675         * @param alleles the alleles of the permutation
676         * @param <T> the allele type
677         * @return a new permutation {@code Codec}
678         * @throws IllegalArgumentException if the given allele array is empty
679         * @throws NullPointerException if one of the alleles is {@code null}
680         */
681        public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>>
682        ofPermutation(final ISeq<? extends T> alleles) {
683                if (alleles.isEmpty()) {
684                        throw new IllegalArgumentException(
685                                "Empty allele array is not allowed."
686                        );
687                }
688
689                final Map<T, EnumGene<T>> genes =
690                        IntStream.range(0, alleles.length())
691                                .mapToObj(i -> EnumGene.<T>of(i, alleles))
692                                .collect(Collectors.toMap(EnumGene::allele, identity()));
693
694                return InvertibleCodec.of(
695                        Genotype.of(new PermutationChromosome<>(ISeq.of(genes.values()))),
696                        gt -> gt.chromosome().stream()
697                                .map(EnumGene::allele)
698                                .collect(ISeq.toISeq()),
699                        val -> Genotype.of(
700                                new PermutationChromosome<>(
701                                        val.stream()
702                                                .map(genes::get)
703                                                .collect(ISeq.toISeq())
704                                )
705                        )
706                );
707        }
708
709        /**
710         * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
711         * All matrix values are restricted by the same domain. The dimension of the
712         * returned matrix is {@code int[rows][cols]}.
713         *
714         * @since 4.4
715         *
716         * @param domain the domain of the matrix values
717         * @param rows the number of rows of the matrix
718         * @param cols the number of columns of the matrix
719         * @return a new matrix {@code Codec}
720         * @throws NullPointerException if the given {@code domain} is {@code null}
721         * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
722         *         smaller than one.
723         */
724        public static InvertibleCodec<int[][], IntegerGene> ofMatrix(
725                final IntRange domain,
726                final int rows,
727                final int cols
728        ) {
729                requireNonNull(domain);
730                Requires.positive(rows);
731                Requires.positive(cols);
732
733                return InvertibleCodec.of(
734                        Genotype.of(
735                                IntegerChromosome.of(domain, cols).instances()
736                                        .limit(rows)
737                                        .collect(ISeq.toISeq())
738                        ),
739                        gt -> gt.stream()
740                                .map(ch -> ch.as(IntegerChromosome.class).toArray())
741                                .toArray(int[][]::new),
742                        matrix -> Genotype.of(
743                                Stream.of(matrix)
744                                        .map(row ->
745                                                IntegerChromosome.of(
746                                                        IntStream.of(row)
747                                                                .mapToObj(v -> IntegerGene.of(v, domain))
748                                                                .collect(ISeq.toISeq())
749                                                ))
750                                        .collect(ISeq.toISeq())
751                        )
752                );
753        }
754
755        /**
756         * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
757         * All matrix values are restricted by the same domain. The dimension of the
758         * returned matrix is {@code long[rows][cols]}.
759         *
760         * @since 4.4
761         *
762         * @param domain the domain of the matrix values
763         * @param rows the number of rows of the matrix
764         * @param cols the number of columns of the matrix
765         * @return a new matrix {@code Codec}
766         * @throws NullPointerException if the given {@code domain} is {@code null}
767         * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
768         *         smaller than one.
769         */
770        public static InvertibleCodec<long[][], LongGene> ofMatrix(
771                final LongRange domain,
772                final int rows,
773                final int cols
774        ) {
775                requireNonNull(domain);
776                Requires.positive(rows);
777                Requires.positive(cols);
778
779                return InvertibleCodec.of(
780                        Genotype.of(
781                                LongChromosome.of(domain, cols).instances()
782                                        .limit(rows)
783                                        .collect(ISeq.toISeq())
784                        ),
785                        gt -> gt.stream()
786                                .map(ch -> ch.as(LongChromosome.class).toArray())
787                                .toArray(long[][]::new),
788                        matrix -> Genotype.of(
789                                Stream.of(matrix)
790                                        .map(row ->
791                                                LongChromosome.of(
792                                                        LongStream.of(row)
793                                                                .mapToObj(v -> LongGene.of(v, domain))
794                                                                .collect(ISeq.toISeq())
795                                                ))
796                                        .collect(ISeq.toISeq())
797                        )
798                );
799        }
800
801        /**
802         * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
803         * All matrix values are restricted by the same domain. The dimension of the
804         * returned matrix is {@code double[rows][cols]}.
805         *
806         * @since 4.4
807         *
808         * @param domain the domain of the matrix values
809         * @param rows the number of rows of the matrix
810         * @param cols the number of columns of the matrix
811         * @return a new matrix {@code Codec}
812         * @throws NullPointerException if the given {@code domain} is {@code null}
813         * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
814         *         smaller than one.
815         */
816        public static InvertibleCodec<double[][], DoubleGene> ofMatrix(
817                final DoubleRange domain,
818                final int rows,
819                final int cols
820        ) {
821                requireNonNull(domain);
822                Requires.positive(rows);
823                Requires.positive(cols);
824
825                return InvertibleCodec.of(
826                        Genotype.of(
827                                DoubleChromosome.of(domain, cols).instances()
828                                        .limit(rows)
829                                        .collect(ISeq.toISeq())
830                        ),
831                        gt -> gt.stream()
832                                .map(ch -> ch.as(DoubleChromosome.class).toArray())
833                                .toArray(double[][]::new),
834                        matrix -> Genotype.of(
835                                Stream.of(matrix)
836                                        .map(row ->
837                                                DoubleChromosome.of(
838                                                        DoubleStream.of(row)
839                                                                .mapToObj(v -> DoubleGene.of(v, domain))
840                                                                .collect(ISeq.toISeq())
841                                                ))
842                                        .collect(ISeq.toISeq())
843                        )
844                );
845        }
846
847        /**
848         * Create a codec, which creates a mapping from the elements given in the
849         * {@code source} sequence to the elements given in the {@code target}
850         * sequence. The returned mapping can be seen as a function which maps every
851         * element of the {@code target} set to an element of the {@code source} set.
852         * {@snippet lang="java":
853         * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
854         * final ISeq<String> strings = ISeq.of("1", "2", "3");
855         *
856         * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
857         *     Codecs.ofMapping(numbers, strings, HashMap::new);
858         * }
859         *
860         * If {@code source.size() > target.size()}, the created mapping is
861         * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
862         * if {@code source.size() < target.size()}, the mapping is
863         * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
864         * and if both sets have the same size, the returned mapping is
865         * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
866         *
867         * @since 4.3
868         *
869         * @param source the source elements. Will be the <em>keys</em> of the
870         *        encoded {@code Map}.
871         * @param target the target elements. Will be the <em>values</em> of the
872         *            encoded {@code Map}.
873         * @param mapSupplier a function which returns a new, empty Map into which
874         *        the mapping will be inserted
875         * @param <A> the type of the source elements
876         * @param <B> the type of the target elements
877         * @param <M> the type of the encoded Map
878         * @return a new mapping codec
879         * @throws IllegalArgumentException if the {@code target} sequences are empty
880         * @throws NullPointerException if one of the arguments is {@code null}
881         */
882        public static <A, B, M extends Map<A, B>> InvertibleCodec<M, EnumGene<Integer>>
883        ofMapping(
884                final ISeq<? extends A> source,
885                final ISeq<? extends B> target,
886                final Supplier<M> mapSupplier
887        ) {
888                requireNonNull(source);
889                requireNonNull(target);
890                requireNonNull(mapSupplier);
891
892                final Map<A, Integer> smap = IntStream.range(0, source.length())
893                        .mapToObj(i -> entry(source.get(i), i))
894                        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
895
896                final Map<B, Integer> tmap = IntStream.range(0, target.length())
897                        .mapToObj(i -> entry(target.get(i), i))
898                        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
899
900                final PermutationChromosome<Integer> chromosome =
901                        PermutationChromosome.ofInteger(target.size());
902
903                final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
904                        .collect(Collectors.toMap(EnumGene::allele, identity()));
905
906                final Codec<int[], EnumGene<Integer>> codec = Codec.of(
907                        Genotype.of(chromosome),
908                        gt -> gt.chromosome().stream()
909                                .mapToInt(EnumGene::allele)
910                                .toArray()
911                );
912
913                return codec
914                        .map(permutation -> toMapping(permutation, source, target, mapSupplier))
915                        .toInvertibleCodec(mapping -> toEncoding(mapping, smap,tmap, genes));
916        }
917
918        private static <A, B, M extends Map<A, B>> M toMapping(
919                final int[] perm,
920                final ISeq<? extends A> source,
921                final ISeq<? extends B> target,
922                final Supplier<M> mapSupplier
923        ) {
924                return IntStream.range(0, source.size())
925                        .mapToObj(i -> entry(source.get(i), target.get(perm[i%perm.length])))
926                        .collect(Collectors.toMap(
927                                Entry::getKey, Entry::getValue,
928                                (u,v) -> {throw new IllegalStateException("Duplicate key " + u);},
929                                mapSupplier));
930        }
931
932        private static <A, B> Genotype<EnumGene<Integer>> toEncoding(
933                final Map<A, B> mapping,
934                final Map<A, Integer> source,
935                final Map<B, Integer> target,
936                final Map<Integer, EnumGene<Integer>> genes
937        ) {
938                final int[] perm = new int[target.size()];
939                source.forEach((key, value) -> {
940                        final int i = value;
941                        final int j = target.get(mapping.get(key));
942                        perm[i%perm.length] = j;
943                });
944
945                // If the target size is greater the source size, only the first
946                // elements (source size) are filled. The rest of the 'perm' array
947                // has to be filled with unique elements.
948                if (target.size() > source.size()) {
949                        final int[] indexes = new int[target.size()];
950
951                        // Initialize the index set with all target indexes: O(|t|)
952                        for (int i = 0; i < target.size(); ++i) {
953                                indexes[i] = i;
954                        }
955
956                        // Mark existing permutations in the index array: O(|s|*log(|t|))
957                        for (int i = 0; i < source.size(); ++i) {
958                                final int si = Arrays.binarySearch(indexes, perm[i]);
959                                if (si >= 0) {
960                                        indexes[si] = -1;
961                                }
962                        }
963
964                        // Fill the 'perm' array with the remaining, non-duplicate indexes:
965                        // O(|t|)
966                        int j = 0;
967                        int next;
968                        for (int i = source.size(); i < target.size(); ++i) {
969                                while ((next = indexes[j++]) == -1);
970                                perm[i] = next;
971                        }
972                }
973
974                return  Genotype.of(
975                        new PermutationChromosome<>(
976                                IntStream.of(perm)
977                                        .mapToObj(genes::get)
978                                        .collect(ISeq.toISeq())
979                        )
980                );
981        }
982
983        /**
984         * Create a codec, which creates a mapping from the elements given in the
985         * {@code source} sequence to the elements given in the {@code target}
986         * sequence. The returned mapping can be seen as a function which maps every
987         * element of the {@code target} set to an element of the {@code source} set.
988         * {@snippet lang="java":
989         * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
990         * final ISeq<String> strings = ISeq.of("1", "2", "3");
991         *
992         * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
993         *     Codecs.ofMapping(numbers, strings);
994         * }
995         *
996         * If {@code source.size() > target.size()}, the created mapping is
997         * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
998         * if {@code source.size() < target.size()}, the mapping is
999         * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
1000         * and if both sets have the same size, the returned mapping is
1001         * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
1002         *
1003         * @since 4.3
1004         *
1005         * @param source the source elements. Will be the <em>keys</em> of the
1006         *        encoded {@code Map}.
1007         * @param target the target elements. Will be the <em>values</em> of the
1008         *            encoded {@code Map}.
1009         * @param <A> the type of the source elements
1010         * @param <B> the type of the target elements
1011         * @return a new mapping codec
1012         * @throws IllegalArgumentException if the {@code target} sequences are empty
1013         * @throws NullPointerException if one of the arguments is {@code null}
1014         */
1015        public static <A, B> InvertibleCodec<Map<A, B>, EnumGene<Integer>>
1016        ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
1017                return ofMapping(source, target, HashMap::new);
1018        }
1019
1020        /**
1021         * The subset {@link InvertibleCodec} can be used for problems where it is
1022         * required to find the best <b>variable-sized</b> subset from a given basic
1023         * set. A typical usage example of the returned {@code Codec} is the
1024         * Knapsack problem.
1025         * <p>
1026         * The following code snippet shows a simplified variation of the Knapsack
1027         * problem.
1028         * {@snippet lang="java":
1029         * public final class Main {
1030         *      // The basic set from where to choose an 'optimal' subset.
1031         *      private final static ISeq<Integer> SET =
1032         *          ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
1033         *
1034         *      // Fitness function directly takes an 'int' value.
1035         *      private static int fitness(final ISeq<Integer> subset) {
1036         *          assert(subset.size() <= SET.size());
1037         *          final int size = subset.stream()
1038         *             .mapToInt(Integer::intValue)
1039         *             .sum();
1040         *          return size <= 20 ? size : 0;
1041         *      }
1042         *
1043         *      public static void main(final String[] args) {
1044         *          final Engine<BitGene, Double> engine = Engine
1045         *              .builder(Main::fitness, codec.ofSubSet(SET))
1046         *              .build();
1047         *          // ...
1048         *      }
1049         *  }
1050         * }
1051         *
1052         * @param <T> the element type of the basic set
1053         * @param basicSet the basic set, from where to choose the <i>optimal</i>
1054         *        subset.
1055         * @return a new codec which can be used for modelling <i>subset</i>
1056         *         problems.
1057         * @throws NullPointerException if the given {@code basicSet} is
1058         *         {@code null}; {@code null} elements are allowed.
1059         * @throws IllegalArgumentException if the {@code basicSet} size is smaller
1060         *         than one.
1061         */
1062        public static <T> InvertibleCodec<ISeq<T>, BitGene>
1063        ofSubSet(final ISeq<? extends T> basicSet) {
1064                requireNonNull(basicSet);
1065                Requires.positive(basicSet.length());
1066
1067                return InvertibleCodec.of(
1068                        Genotype.of(BitChromosome.of(basicSet.length())),
1069                        gt -> gt.chromosome()
1070                                .as(BitChromosome.class).ones()
1071                                .<T>mapToObj(basicSet)
1072                                .collect(ISeq.toISeq()),
1073                        values -> {
1074                                final byte[] bits = Bits.newArray(basicSet.size());
1075
1076                                int i = 0;
1077                                for (T v : values) {
1078                                        while (i < basicSet.size() && !Objects.equals(basicSet.get(i), v)) {
1079                                                ++i;
1080                                        }
1081                                        if (i >= basicSet.size()) {
1082                                                throw new IllegalArgumentException(
1083                                                        "Invalid subset; input values were not created by " +
1084                                                                "'decode' method."
1085                                                );
1086                                        }
1087
1088                                        Bits.set(bits, i);
1089                                }
1090
1091                                return Genotype.of(new BitChromosome(bits, 0, basicSet.size()));
1092                        }
1093                );
1094        }
1095
1096        /**
1097         * The subset {@link InvertibleCodec} can be used for problems where it is
1098         * required to find the best <b>fixed-size</b> subset from a given basic set.
1099         *
1100         * @since 3.4
1101         *
1102         * @see PermutationChromosome
1103         * @see PermutationChromosome#of(ISeq, int)
1104         *
1105         * @param <T> the element type of the basic set
1106         * @param basicSet the basic set, from where to choose the <i>optimal</i>
1107         *        subset.
1108         * @param size the length of the desired subsets
1109         * @return a new codec which can be used for modelling <i>subset</i>
1110         *         problems.
1111         * @throws NullPointerException if the given {@code basicSet} is
1112         *         {@code null}; {@code null} elements are allowed.
1113         * @throws IllegalArgumentException if {@code basicSet.size() < size},
1114         *         {@code size <= 0} or {@code basicSet.size()*size} will cause an
1115         *         integer overflow.
1116         */
1117        public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> ofSubSet(
1118                final ISeq<? extends T> basicSet,
1119                final int size
1120        ) {
1121                requireNonNull(basicSet);
1122
1123                final Map<T, EnumGene<T>> genes =
1124                        IntStream.range(0, basicSet.length())
1125                                .mapToObj(i -> EnumGene.<T>of(i, basicSet))
1126                                .collect(Collectors.toMap(EnumGene::allele, identity()));
1127
1128                return InvertibleCodec.of(
1129                        Genotype.of(PermutationChromosome.of(basicSet, size)),
1130                        gt -> gt.chromosome().stream()
1131                                .map(EnumGene::allele)
1132                                .collect(ISeq.toISeq()),
1133                        values -> {
1134                                if (values.size() != size) {
1135                                        throw new IllegalArgumentException(format(
1136                                                "Expected sub-set size of %d, but got %d,",
1137                                                size, values.size()
1138                                        ));
1139                                }
1140
1141                                return Genotype.of(
1142                                        new PermutationChromosome<>(
1143                                                values.stream()
1144                                                        .map(genes::get)
1145                                                        .collect(ISeq.toISeq())
1146                                        )
1147                                );
1148                        }
1149                );
1150        }
1151
1152//      /**
1153//       * Creates a codec for a 2-dimensional affine transformation. The composed
1154//       * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
1155//       *
1156//       * @param sx the scale factor range in x direction
1157//       * @param sy the scale factor range in y direction
1158//       * @param tx the translation range in x direction
1159//       * @param ty the translation range in y direction
1160//       * @param th the rotation range (in radians)
1161//       * @param kx the shear range in x direction
1162//       * @param ky the shear range in x direction
1163//       * @return the affine transformation codec
1164//       * @throws NullPointerException if one of the arguments is {@code null}
1165//       */
1166//      static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1167//              final DoubleRange sx, final DoubleRange sy,
1168//              final DoubleRange tx, final DoubleRange ty,
1169//              final DoubleRange th,
1170//              final DoubleRange kx, final DoubleRange ky
1171//      ) {
1172//              return Codec.of(
1173//                      Genotype.of(
1174//                              // Scale
1175//                              DoubleChromosome.of(sx), DoubleChromosome.of(sy),
1176//                              // Translation
1177//                              DoubleChromosome.of(tx), DoubleChromosome.of(ty),
1178//                              // Rotation
1179//                              DoubleChromosome.of(th),
1180//                              // Shear
1181//                              DoubleChromosome.of(kx), DoubleChromosome.of(ky)
1182//                      ),
1183//                      gt -> {
1184//                              final AffineTransform at = new AffineTransform();
1185//
1186//                              at.translate(
1187//                                      gt.getChromosome(2).getGene().doubleValue(),
1188//                                      gt.getChromosome(3).getGene().doubleValue()
1189//                              );
1190//                              at.shear(
1191//                                      gt.getChromosome(5).getGene().doubleValue(),
1192//                                      gt.getChromosome(6).getGene().doubleValue()
1193//                              );
1194//                              at.scale(
1195//                                      gt.getChromosome(0).getGene().doubleValue(),
1196//                                      gt.getChromosome(1).getGene().doubleValue()
1197//                              );
1198//                              at.rotate(gt.getChromosome(4).getGene().doubleValue());
1199//
1200//                              return at;
1201//                      }
1202//              );
1203//      }
1204//
1205//      /**
1206//       * Creates a codec for a 2-dimensional affine transformation. The composed
1207//       * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
1208//       *
1209//       * @param s the scale factor range in x and y direction
1210//       * @param t the translation range in x and y direction
1211//       * @param th the rotation angle range
1212//       * @param k the shear range in x and y direction
1213//       * @return the affine transformation codec
1214//       * @throws NullPointerException if one of the arguments is {@code null}
1215//       */
1216//      static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1217//              final DoubleRange s,
1218//              final DoubleRange t,
1219//              final DoubleRange th,
1220//              final DoubleRange k
1221//      ) {
1222//              return ofAffineTransform(s, s, t, t, th, k, k);
1223//      }
1224
1225}