001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.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, IntRange.of(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, IntRange.of(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.
297         *
298         * @param domain the domain of the vector values
299         * @param length the vector length
300         * @return a new vector {@code Codec}
301         * @throws NullPointerException if the given {@code domain} is {@code null}
302         * @throws IllegalArgumentException if the {@code length} is smaller than
303         *         one.
304         */
305        public static InvertibleCodec<double[], DoubleGene> ofVector(
306                final DoubleRange domain,
307                final int length
308        ) {
309                return ofVector(domain, IntRange.of(length));
310        }
311
312        /**
313         * Return a vector {@link InvertibleCodec} for the given range. All vector
314         * values are restricted by the same domain.
315         *
316         * @since 7.0
317         *
318         * @param domain the domain of the vector values
319         * @param length the vector length range
320         * @return a new vector {@code Codec}
321         * @throws NullPointerException if the given {@code domain} is {@code null}
322         * @throws IllegalArgumentException if the {@code length} is smaller than
323         *         one.
324         */
325        public static InvertibleCodec<double[], DoubleGene> ofVector(
326                final DoubleRange domain,
327                final IntRange length
328        ) {
329                requireNonNull(domain);
330                Requires.positive(length.min());
331
332                return InvertibleCodec.of(
333                        Genotype.of(DoubleChromosome.of(domain, length)),
334                        gt -> gt.chromosome().as(DoubleChromosome.class).toArray(),
335                        val -> Genotype.of(
336                                DoubleChromosome.of(
337                                        DoubleStream.of(val)
338                                                .mapToObj(d -> DoubleGene.of(d, domain))
339                                                .collect(ISeq.toISeq())
340                                )
341                        )
342                );
343        }
344
345        /**
346         * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
347         * element might have a different domain. The vector length is equal to the
348         * number of domains.
349         *
350         * @param domains the domain ranges
351         * @return a new vector {@code Codec}
352         * @throws NullPointerException if one of the arguments is {@code null}
353         * @throws IllegalArgumentException if the {@code domains} array is empty
354         */
355        public static InvertibleCodec<int[], IntegerGene>
356        ofVector(final IntRange... domains) {
357                if (domains.length == 0) {
358                        throw new IllegalArgumentException("Domains must not be empty.");
359                }
360
361                final ISeq<IntegerChromosome> chromosomes = Stream.of(domains)
362                        .peek(Objects::requireNonNull)
363                        .map(IntegerGene::of)
364                        .map(IntegerChromosome::of)
365                        .collect(ISeq.toISeq());
366
367                return InvertibleCodec.of(
368                        Genotype.of(chromosomes),
369                        gt -> {
370                                final int[] args = new int[gt.length()];
371                                for (int i = gt.length(); --i >= 0;) {
372                                        args[i] = gt.get(i).gene().intValue();
373                                }
374                                return args;
375                        },
376                        val -> Genotype.of(
377                                IntStream.range(0, val.length)
378                                        .mapToObj(i ->
379                                                IntegerChromosome.of(IntegerGene.of(val[i], domains[i])))
380                                        .collect(ISeq.toISeq())
381                        )
382                );
383        }
384
385        /**
386         * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
387         * element might have a different domain. The vector length is equal to the
388         * number of domains.
389         *
390         * @param domains the domain ranges
391         * @return a new vector {@code Codec}
392         * @throws NullPointerException if one of the arguments is {@code null}
393         * @throws IllegalArgumentException if the {@code domains} array is empty
394         */
395        public static InvertibleCodec<long[], LongGene>
396        ofVector(final LongRange... domains) {
397                if (domains.length == 0) {
398                        throw new IllegalArgumentException("Domains must not be empty.");
399                }
400
401                final ISeq<LongChromosome> chromosomes = Stream.of(domains)
402                        .peek(Objects::requireNonNull)
403                        .map(LongGene::of)
404                        .map(LongChromosome::of)
405                        .collect(ISeq.toISeq());
406
407                return InvertibleCodec.of(
408                        Genotype.of(chromosomes),
409                        gt -> {
410                                final long[] args = new long[gt.length()];
411                                for (int i = gt.length(); --i >= 0;) {
412                                        args[i] = gt.get(i).gene().longValue();
413                                }
414                                return args;
415                        },
416                        val -> Genotype.of(
417                                IntStream.range(0, val.length)
418                                        .mapToObj(i ->
419                                                LongChromosome.of(LongGene.of(val[i], domains[i])))
420                                        .collect(ISeq.toISeq())
421                        )
422                );
423        }
424
425        /**
426         * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
427         * element might have a different domain. The vector length is equal to the
428         * number of domains.
429         *
430         * @param domains the domain ranges
431         * @return a new vector {@code Codec}
432         * @throws NullPointerException if one of the arguments is {@code null}
433         * @throws IllegalArgumentException if the {@code domains} array is empty
434         */
435        public static InvertibleCodec<double[], DoubleGene>
436        ofVector(final DoubleRange... domains) {
437                if (domains.length == 0) {
438                        throw new IllegalArgumentException("Domains must not be empty.");
439                }
440
441                final ISeq<DoubleChromosome> chromosomes = Stream.of(domains)
442                        .peek(Objects::requireNonNull)
443                        .map(DoubleGene::of)
444                        .map(DoubleChromosome::of)
445                        .collect(ISeq.toISeq());
446
447                return InvertibleCodec.of(
448                        Genotype.of(chromosomes),
449                        gt -> {
450                                final double[] args = new double[gt.length()];
451                                for (int i = gt.length(); --i >= 0;) {
452                                        args[i] = gt.get(i).gene().doubleValue();
453                                }
454                                return args;
455                        },
456                        val -> Genotype.of(
457                                IntStream.range(0, val.length)
458                                        .mapToObj(i ->
459                                                DoubleChromosome.of(DoubleGene.of(val[i], domains[i])))
460                                        .collect(ISeq.toISeq())
461                        )
462                );
463        }
464
465        /**
466         * Return a scala {@link Codec} with the given allele {@link Supplier},
467         * allele {@code validator} and {@code Chromosome} length. The
468         * {@code supplier} is responsible for creating new random alleles, and the
469         * {@code validator} can verify it.
470         * <p>
471         * The following example shows a codec which creates and verifies
472         * {@code BigInteger} object arrays.
473         * {@snippet lang="java":
474         * final Codec<BigInteger[], AnyGene<BigInteger>> codec = Codecs.of(
475         *     // Create new random 'BigInteger' object.
476         *     () -> {
477         *         final byte[] data = new byte[100];
478         *         RandomRegistry.getRandom().nextBytes(data);
479         *         return new BigInteger(data);
480         *     },
481         *     // Verify that bit 7 is set. (For illustration purpose.)
482         *     bi -> bi.testBit(7),
483         *     // The 'Chromosome' length.
484         *     123
485         * );
486         * }
487         *
488         * @see AnyChromosome#of(Supplier, Predicate, Predicate, int)
489         *
490         * @param <A> the allele type
491         * @param supplier the allele-supplier which is used for creating new,
492         *        random alleles
493         * @param alleleValidator the validator used for validating the created gene.
494         *        This predicate is used in the {@link AnyGene#isValid()} method.
495         * @param alleleSeqValidator the validator used for validating the created
496         *        chromosome. This predicate is used in the
497         *        {@link AnyChromosome#isValid()} method.
498         * @param length the vector length
499         * @return a new {@code Codec} with the given parameters
500         * @throws NullPointerException if one of the parameters is {@code null}
501         * @throws IllegalArgumentException if the length of the vector is smaller
502         *         than one.
503         */
504        public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
505                final Supplier<? extends A> supplier,
506                final Predicate<? super A> alleleValidator,
507                final Predicate<? super ISeq<A>> alleleSeqValidator,
508                final int length
509        ) {
510                requireNonNull(supplier);
511                requireNonNull(alleleSeqValidator);
512                requireNonNull(alleleSeqValidator);
513                Requires.positive(length);
514
515                return Codec.of(
516                        Genotype.of(AnyChromosome
517                                .of(supplier, alleleValidator, alleleSeqValidator, length)),
518                        gt -> gt.chromosome().stream()
519                                .map(Gene::allele)
520                                .collect(ISeq.toISeq())
521                );
522        }
523
524        /**
525         * Return a scala {@link Codec} with the given allele {@link Supplier},
526         * allele {@code validator} and {@code Chromosome} length. The
527         * {@code supplier} is responsible for creating new random alleles, and the
528         * {@code validator} can verify it.
529         *
530         * @param <A> the allele type
531         * @param supplier the allele-supplier which is used for creating new,
532         *        random alleles
533         * @param validator the validator used for validating the created gene. This
534         *        predicate is used in the {@link AnyGene#isValid()} method.
535         * @param length the vector length
536         * @return a new {@code Codec} with the given parameters
537         * @throws NullPointerException if one of the parameters is {@code null}
538         * @throws IllegalArgumentException if the length of the vector is smaller
539         *         than one.
540         */
541        public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
542                final Supplier<? extends A> supplier,
543                final Predicate<? super A> validator,
544                final int length
545        ) {
546                return ofVector(
547                        supplier,
548                        validator,
549                        Predicates.True(),
550                        length
551                );
552        }
553
554        /**
555         * Return a scala {@link Codec} with the given allele {@link Supplier} and
556         * {@code Chromosome} length. The {@code supplier} is responsible for
557         * creating new random alleles.
558         *
559         * @param <A> the allele type
560         * @param supplier the allele-supplier which is used for creating new,
561         *        random alleles
562         * @param length the vector length
563         * @return a new {@code Codec} with the given parameters
564         * @throws NullPointerException if one of the parameters is {@code null}
565         * @throws IllegalArgumentException if the length of the vector is smaller
566         *         than one.
567         */
568        public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
569                final Supplier<? extends A> supplier,
570                final int length
571        ) {
572                return ofVector(supplier, Predicates.TRUE, length);
573        }
574
575        /**
576         * Create a permutation {@link InvertibleCodec} of integer in the range
577         * {@code [0, length)}.
578         *
579         * @param length the number of permutation elements
580         * @return a permutation {@code Codec} of integers
581         * @throws IllegalArgumentException if the {@code length} is smaller than
582         *         one.
583         */
584        public static InvertibleCodec<int[], EnumGene<Integer>>
585        ofPermutation(final int length) {
586                Requires.positive(length);
587
588                final PermutationChromosome<Integer> chromosome =
589                        PermutationChromosome.ofInteger(length);
590
591                final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
592                        .collect(Collectors.toMap(EnumGene::allele, identity()));
593
594                return InvertibleCodec.of(
595                        Genotype.of(chromosome),
596                        gt -> gt.chromosome().stream()
597                                .mapToInt(EnumGene::allele)
598                                .toArray(),
599                        val -> Genotype.of(
600                                new PermutationChromosome<>(
601                                        IntStream.of(val)
602                                                .mapToObj(genes::get)
603                                                .collect(ISeq.toISeq())
604                                )
605                        )
606                );
607        }
608
609        /**
610         * Create a permutation {@link InvertibleCodec} with the given alleles.
611         *
612         * @param alleles the alleles of the permutation
613         * @param <T> the allele type
614         * @return a new permutation {@code Codec}
615         * @throws IllegalArgumentException if the given allele array is empty
616         * @throws NullPointerException if one of the alleles is {@code null}
617         */
618        public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>>
619        ofPermutation(final ISeq<? extends T> alleles) {
620                if (alleles.isEmpty()) {
621                        throw new IllegalArgumentException(
622                                "Empty allele array is not allowed."
623                        );
624                }
625
626                final Map<T, EnumGene<T>> genes =
627                        IntStream.range(0, alleles.length())
628                                .mapToObj(i -> EnumGene.<T>of(i, alleles))
629                                .collect(Collectors.toMap(EnumGene::allele, identity()));
630
631                return InvertibleCodec.of(
632                        Genotype.of(new PermutationChromosome<>(ISeq.of(genes.values()))),
633                        gt -> gt.chromosome().stream()
634                                .map(EnumGene::allele)
635                                .collect(ISeq.toISeq()),
636                        val -> Genotype.of(
637                                new PermutationChromosome<>(
638                                        val.stream()
639                                                .map(genes::get)
640                                                .collect(ISeq.toISeq())
641                                )
642                        )
643                );
644        }
645
646        /**
647         * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
648         * All matrix values are restricted by the same domain. The dimension of the
649         * returned matrix is {@code int[rows][cols]}.
650         *
651         * @since 4.4
652         *
653         * @param domain the domain of the matrix values
654         * @param rows the number of rows of the matrix
655         * @param cols the number of columns of the matrix
656         * @return a new matrix {@code Codec}
657         * @throws NullPointerException if the given {@code domain} is {@code null}
658         * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
659         *         smaller than one.
660         */
661        public static InvertibleCodec<int[][], IntegerGene> ofMatrix(
662                final IntRange domain,
663                final int rows,
664                final int cols
665        ) {
666                requireNonNull(domain);
667                Requires.positive(rows);
668                Requires.positive(cols);
669
670                return InvertibleCodec.of(
671                        Genotype.of(
672                                IntegerChromosome.of(domain, cols).instances()
673                                        .limit(rows)
674                                        .collect(ISeq.toISeq())
675                        ),
676                        gt -> gt.stream()
677                                .map(ch -> ch.as(IntegerChromosome.class).toArray())
678                                .toArray(int[][]::new),
679                        matrix -> Genotype.of(
680                                Stream.of(matrix)
681                                        .map(row ->
682                                                IntegerChromosome.of(
683                                                        IntStream.of(row)
684                                                                .mapToObj(v -> IntegerGene.of(v, domain))
685                                                                .collect(ISeq.toISeq())
686                                                ))
687                                        .collect(ISeq.toISeq())
688                        )
689                );
690        }
691
692        /**
693         * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
694         * All matrix values are restricted by the same domain. The dimension of the
695         * returned matrix is {@code long[rows][cols]}.
696         *
697         * @since 4.4
698         *
699         * @param domain the domain of the matrix values
700         * @param rows the number of rows of the matrix
701         * @param cols the number of columns of the matrix
702         * @return a new matrix {@code Codec}
703         * @throws NullPointerException if the given {@code domain} is {@code null}
704         * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
705         *         smaller than one.
706         */
707        public static InvertibleCodec<long[][], LongGene> ofMatrix(
708                final LongRange domain,
709                final int rows,
710                final int cols
711        ) {
712                requireNonNull(domain);
713                Requires.positive(rows);
714                Requires.positive(cols);
715
716                return InvertibleCodec.of(
717                        Genotype.of(
718                                LongChromosome.of(domain, cols).instances()
719                                        .limit(rows)
720                                        .collect(ISeq.toISeq())
721                        ),
722                        gt -> gt.stream()
723                                .map(ch -> ch.as(LongChromosome.class).toArray())
724                                .toArray(long[][]::new),
725                        matrix -> Genotype.of(
726                                Stream.of(matrix)
727                                        .map(row ->
728                                                LongChromosome.of(
729                                                        LongStream.of(row)
730                                                                .mapToObj(v -> LongGene.of(v, domain))
731                                                                .collect(ISeq.toISeq())
732                                                ))
733                                        .collect(ISeq.toISeq())
734                        )
735                );
736        }
737
738        /**
739         * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
740         * All matrix values are restricted by the same domain. The dimension of the
741         * returned matrix is {@code double[rows][cols]}.
742         *
743         * @since 4.4
744         *
745         * @param domain the domain of the matrix values
746         * @param rows the number of rows of the matrix
747         * @param cols the number of columns of the matrix
748         * @return a new matrix {@code Codec}
749         * @throws NullPointerException if the given {@code domain} is {@code null}
750         * @throws IllegalArgumentException if the {@code rows} or {@code cols} are
751         *         smaller than one.
752         */
753        public static InvertibleCodec<double[][], DoubleGene> ofMatrix(
754                final DoubleRange domain,
755                final int rows,
756                final int cols
757        ) {
758                requireNonNull(domain);
759                Requires.positive(rows);
760                Requires.positive(cols);
761
762                return InvertibleCodec.of(
763                        Genotype.of(
764                                DoubleChromosome.of(domain, cols).instances()
765                                        .limit(rows)
766                                        .collect(ISeq.toISeq())
767                        ),
768                        gt -> gt.stream()
769                                .map(ch -> ch.as(DoubleChromosome.class).toArray())
770                                .toArray(double[][]::new),
771                        matrix -> Genotype.of(
772                                Stream.of(matrix)
773                                        .map(row ->
774                                                DoubleChromosome.of(
775                                                        DoubleStream.of(row)
776                                                                .mapToObj(v -> DoubleGene.of(v, domain))
777                                                                .collect(ISeq.toISeq())
778                                                ))
779                                        .collect(ISeq.toISeq())
780                        )
781                );
782        }
783
784        /**
785         * Create a codec, which creates a mapping from the elements given in the
786         * {@code source} sequence to the elements given in the {@code target}
787         * sequence. The returned mapping can be seen as a function which maps every
788         * element of the {@code target} set to an element of the {@code source} set.
789         *
790         * {@snippet lang="java":
791         * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
792         * final ISeq<String> strings = ISeq.of("1", "2", "3");
793         *
794         * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
795         *     Codecs.ofMapping(numbers, strings, HashMap::new);
796         * }
797         *
798         * If {@code source.size() > target.size()}, the created mapping is
799         * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
800         * if {@code source.size() < target.size()}, the mapping is
801         * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
802         * and if both sets have the same size, the returned mapping is
803         * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
804         *
805         * @since 4.3
806         *
807         * @param source the source elements. Will be the <em>keys</em> of the
808         *        encoded {@code Map}.
809         * @param target the target elements. Will be the <em>values</em> of the
810         *            encoded {@code Map}.
811         * @param mapSupplier a function which returns a new, empty Map into which
812         *        the mapping will be inserted
813         * @param <A> the type of the source elements
814         * @param <B> the type of the target elements
815         * @param <M> the type of the encoded Map
816         * @return a new mapping codec
817         * @throws IllegalArgumentException if the {@code target} sequences are empty
818         * @throws NullPointerException if one of the arguments is {@code null}
819         */
820        public static <A, B, M extends Map<A, B>> InvertibleCodec<M, EnumGene<Integer>>
821        ofMapping(
822                final ISeq<? extends A> source,
823                final ISeq<? extends B> target,
824                final Supplier<M> mapSupplier
825        ) {
826                requireNonNull(source);
827                requireNonNull(target);
828                requireNonNull(mapSupplier);
829
830                final Map<A, Integer> smap = IntStream.range(0, source.length())
831                        .mapToObj(i -> entry(source.get(i), i))
832                        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
833
834                final Map<B, Integer> tmap = IntStream.range(0, target.length())
835                        .mapToObj(i -> entry(target.get(i), i))
836                        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
837
838                final PermutationChromosome<Integer> chromosome =
839                        PermutationChromosome.ofInteger(target.size());
840
841                final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
842                        .collect(Collectors.toMap(EnumGene::allele, identity()));
843
844                final Codec<int[], EnumGene<Integer>> codec = Codec.of(
845                        Genotype.of(chromosome),
846                        gt -> gt.chromosome().stream()
847                                .mapToInt(EnumGene::allele)
848                                .toArray()
849                );
850
851                return codec
852                        .map(permutation -> toMapping(permutation, source, target, mapSupplier))
853                        .toInvertibleCodec(mapping -> toEncoding(mapping, smap,tmap, genes));
854        }
855
856        private static <A, B, M extends Map<A, B>> M toMapping(
857                final int[] perm,
858                final ISeq<? extends A> source,
859                final ISeq<? extends B> target,
860                final Supplier<M> mapSupplier
861        ) {
862                return IntStream.range(0, source.size())
863                        .mapToObj(i -> entry(source.get(i), target.get(perm[i%perm.length])))
864                        .collect(Collectors.toMap(
865                                Entry::getKey, Entry::getValue,
866                                (u,v) -> {throw new IllegalStateException("Duplicate key " + u);},
867                                mapSupplier));
868        }
869
870        private static <A, B> Genotype<EnumGene<Integer>> toEncoding(
871                final Map<A, B> mapping,
872                final Map<A, Integer> source,
873                final Map<B, Integer> target,
874                final Map<Integer, EnumGene<Integer>> genes
875        ) {
876                final int[] perm = new int[target.size()];
877                source.forEach((key, value) -> {
878                        final int i = value;
879                        final int j = target.get(mapping.get(key));
880                        perm[i%perm.length] = j;
881                });
882
883                // If the target size is greater the source size, only the first
884                // elements (source size) are filled. The rest of the 'perm' array
885                // has to be filled with unique elements.
886                if (target.size() > source.size()) {
887                        final int[] indexes = new int[target.size()];
888
889                        // Initialize the index set with all target indexes: O(|t|)
890                        for (int i = 0; i < target.size(); ++i) {
891                                indexes[i] = i;
892                        }
893
894                        // Mark existing permutations in the index array: O(|s|*log(|t|))
895                        for (int i = 0; i < source.size(); ++i) {
896                                final int si = Arrays.binarySearch(indexes, perm[i]);
897                                if (si >= 0) {
898                                        indexes[si] = -1;
899                                }
900                        }
901
902                        // Fill the 'perm' array with the remaining, non-duplicate indexes:
903                        // O(|t|)
904                        int j = 0;
905                        int next;
906                        for (int i = source.size(); i < target.size(); ++i) {
907                                while ((next = indexes[j++]) == -1);
908                                perm[i] = next;
909                        }
910                }
911
912                return  Genotype.of(
913                        new PermutationChromosome<>(
914                                IntStream.of(perm)
915                                        .mapToObj(genes::get)
916                                        .collect(ISeq.toISeq())
917                        )
918                );
919        }
920
921        /**
922         * Create a codec, which creates a mapping from the elements given in the
923         * {@code source} sequence to the elements given in the {@code target}
924         * sequence. The returned mapping can be seen as a function which maps every
925         * element of the {@code target} set to an element of the {@code source} set.
926         *
927         * {@snippet lang="java":
928         * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
929         * final ISeq<String> strings = ISeq.of("1", "2", "3");
930         *
931         * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
932         *     Codecs.ofMapping(numbers, strings);
933         * }
934         *
935         * If {@code source.size() > target.size()}, the created mapping is
936         * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
937         * if {@code source.size() < target.size()}, the mapping is
938         * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
939         * and if both sets have the same size, the returned mapping is
940         * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
941         *
942         * @since 4.3
943         *
944         * @param source the source elements. Will be the <em>keys</em> of the
945         *        encoded {@code Map}.
946         * @param target the target elements. Will be the <em>values</em> of the
947         *            encoded {@code Map}.
948         * @param <A> the type of the source elements
949         * @param <B> the type of the target elements
950         * @return a new mapping codec
951         * @throws IllegalArgumentException if the {@code target} sequences are empty
952         * @throws NullPointerException if one of the arguments is {@code null}
953         */
954        public static <A, B> InvertibleCodec<Map<A, B>, EnumGene<Integer>>
955        ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
956                return ofMapping(source, target, HashMap::new);
957        }
958
959        /**
960         * The subset {@link InvertibleCodec} can be used for problems where it is
961         * required to find the best <b>variable-sized</b> subset from a given basic
962         * set. A typical usage example of the returned {@code Codec} is the
963         * Knapsack problem.
964         * <p>
965         * The following code snippet shows a simplified variation of the Knapsack
966         * problem.
967         * {@snippet lang="java":
968         * public final class Main {
969         *      // The basic set from where to choose an 'optimal' subset.
970         *      private final static ISeq<Integer> SET =
971         *          ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
972         *
973         *      // Fitness function directly takes an 'int' value.
974         *      private static int fitness(final ISeq<Integer> subset) {
975         *          assert(subset.size() <= SET.size());
976         *          final int size = subset.stream()
977         *             .mapToInt(Integer::intValue)
978         *             .sum();
979         *          return size <= 20 ? size : 0;
980         *      }
981         *
982         *      public static void main(final String[] args) {
983         *          final Engine<BitGene, Double> engine = Engine
984         *              .builder(Main::fitness, codec.ofSubSet(SET))
985         *              .build();
986         *          // ...
987         *      }
988         *  }
989         * }
990         *
991         * @param <T> the element type of the basic set
992         * @param basicSet the basic set, from where to choose the <i>optimal</i>
993         *        subset.
994         * @return a new codec which can be used for modelling <i>subset</i>
995         *         problems.
996         * @throws NullPointerException if the given {@code basicSet} is
997         *         {@code null}; {@code null} elements are allowed.
998         * @throws IllegalArgumentException if the {@code basicSet} size is smaller
999         *         than one.
1000         */
1001        public static <T> InvertibleCodec<ISeq<T>, BitGene>
1002        ofSubSet(final ISeq<? extends T> basicSet) {
1003                requireNonNull(basicSet);
1004                Requires.positive(basicSet.length());
1005
1006                return InvertibleCodec.of(
1007                        Genotype.of(BitChromosome.of(basicSet.length())),
1008                        gt -> gt.chromosome()
1009                                .as(BitChromosome.class).ones()
1010                                .<T>mapToObj(basicSet)
1011                                .collect(ISeq.toISeq()),
1012                        values -> {
1013                                final byte[] bits = Bits.newArray(basicSet.size());
1014
1015                                int i = 0;
1016                                for (T v : values) {
1017                                        while (i < basicSet.size() && !Objects.equals(basicSet.get(i), v)) {
1018                                                ++i;
1019                                        }
1020                                        if (i >= basicSet.size()) {
1021                                                throw new IllegalArgumentException(
1022                                                        "Invalid subset; input values were not created by " +
1023                                                                "'decode' method."
1024                                                );
1025                                        }
1026
1027                                        Bits.set(bits, i);
1028                                }
1029
1030                                return Genotype.of(new BitChromosome(bits, 0, basicSet.size()));
1031                        }
1032                );
1033        }
1034
1035        /**
1036         * The subset {@link InvertibleCodec} can be used for problems where it is
1037         * required to find the best <b>fixed-size</b> subset from a given basic set.
1038         *
1039         * @since 3.4
1040         *
1041         * @see PermutationChromosome
1042         * @see PermutationChromosome#of(ISeq, int)
1043         *
1044         * @param <T> the element type of the basic set
1045         * @param basicSet the basic set, from where to choose the <i>optimal</i>
1046         *        subset.
1047         * @param size the length of the desired subsets
1048         * @return a new codec which can be used for modelling <i>subset</i>
1049         *         problems.
1050         * @throws NullPointerException if the given {@code basicSet} is
1051         *         {@code null}; {@code null} elements are allowed.
1052         * @throws IllegalArgumentException if {@code basicSet.size() < size},
1053         *         {@code size <= 0} or {@code basicSet.size()*size} will cause an
1054         *         integer overflow.
1055         */
1056        public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>> ofSubSet(
1057                final ISeq<? extends T> basicSet,
1058                final int size
1059        ) {
1060                requireNonNull(basicSet);
1061
1062                final Map<T, EnumGene<T>> genes =
1063                        IntStream.range(0, basicSet.length())
1064                                .mapToObj(i -> EnumGene.<T>of(i, basicSet))
1065                                .collect(Collectors.toMap(EnumGene::allele, identity()));
1066
1067                return InvertibleCodec.of(
1068                        Genotype.of(PermutationChromosome.of(basicSet, size)),
1069                        gt -> gt.chromosome().stream()
1070                                .map(EnumGene::allele)
1071                                .collect(ISeq.toISeq()),
1072                        values -> {
1073                                if (values.size() != size) {
1074                                        throw new IllegalArgumentException(format(
1075                                                "Expected sub-set size of %d, but got %d,",
1076                                                size, values.size()
1077                                        ));
1078                                }
1079
1080                                return Genotype.of(
1081                                        new PermutationChromosome<>(
1082                                                values.stream()
1083                                                        .map(genes::get)
1084                                                        .collect(ISeq.toISeq())
1085                                        )
1086                                );
1087                        }
1088                );
1089        }
1090
1091//      /**
1092//       * Creates a codec for a 2-dimensional affine transformation. The composed
1093//       * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
1094//       *
1095//       * @param sx the scale factor range in x direction
1096//       * @param sy the scale factor range in y direction
1097//       * @param tx the translation range in x direction
1098//       * @param ty the translation range in y direction
1099//       * @param th the rotation range (in radians)
1100//       * @param kx the shear range in x direction
1101//       * @param ky the shear range in x direction
1102//       * @return the affine transformation codec
1103//       * @throws NullPointerException if one of the arguments is {@code null}
1104//       */
1105//      static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1106//              final DoubleRange sx, final DoubleRange sy,
1107//              final DoubleRange tx, final DoubleRange ty,
1108//              final DoubleRange th,
1109//              final DoubleRange kx, final DoubleRange ky
1110//      ) {
1111//              return Codec.of(
1112//                      Genotype.of(
1113//                              // Scale
1114//                              DoubleChromosome.of(sx), DoubleChromosome.of(sy),
1115//                              // Translation
1116//                              DoubleChromosome.of(tx), DoubleChromosome.of(ty),
1117//                              // Rotation
1118//                              DoubleChromosome.of(th),
1119//                              // Shear
1120//                              DoubleChromosome.of(kx), DoubleChromosome.of(ky)
1121//                      ),
1122//                      gt -> {
1123//                              final AffineTransform at = new AffineTransform();
1124//
1125//                              at.translate(
1126//                                      gt.getChromosome(2).getGene().doubleValue(),
1127//                                      gt.getChromosome(3).getGene().doubleValue()
1128//                              );
1129//                              at.shear(
1130//                                      gt.getChromosome(5).getGene().doubleValue(),
1131//                                      gt.getChromosome(6).getGene().doubleValue()
1132//                              );
1133//                              at.scale(
1134//                                      gt.getChromosome(0).getGene().doubleValue(),
1135//                                      gt.getChromosome(1).getGene().doubleValue()
1136//                              );
1137//                              at.rotate(gt.getChromosome(4).getGene().doubleValue());
1138//
1139//                              return at;
1140//                      }
1141//              );
1142//      }
1143//
1144//      /**
1145//       * Creates a codec for a 2-dimensional affine transformation. The composed
1146//       * order of the transformation is: <i>R&bull;Sc&bull;Sh&bull;T</i>
1147//       *
1148//       * @param s the scale factor range in x and y direction
1149//       * @param t the translation range in x and y direction
1150//       * @param th the rotation angle range
1151//       * @param k the shear range in x and y direction
1152//       * @return the affine transformation codec
1153//       * @throws NullPointerException if one of the arguments is {@code null}
1154//       */
1155//      static Codec<AffineTransform, DoubleGene> ofAffineTransform(
1156//              final DoubleRange s,
1157//              final DoubleRange t,
1158//              final DoubleRange th,
1159//              final DoubleRange k
1160//      ) {
1161//              return ofAffineTransform(s, s, t, t, th, k, k);
1162//      }
1163
1164}