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