Codecs.java
0001 /*
0002  * Java Genetic Algorithm Library (jenetics-8.0.0).
0003  * Copyright (c) 2007-2024 Franz Wilhelmstötter
0004  *
0005  * Licensed under the Apache License, Version 2.0 (the "License");
0006  * you may not use this file except in compliance with the License.
0007  * You may obtain a copy of the License at
0008  *
0009  *      http://www.apache.org/licenses/LICENSE-2.0
0010  *
0011  * Unless required by applicable law or agreed to in writing, software
0012  * distributed under the License is distributed on an "AS IS" BASIS,
0013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014  * See the License for the specific language governing permissions and
0015  * limitations under the License.
0016  *
0017  * Author:
0018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
0019  */
0020 package io.jenetics.engine;
0021 
0022 import static java.lang.String.format;
0023 import static java.util.Map.entry;
0024 import static java.util.Objects.requireNonNull;
0025 import static java.util.function.Function.identity;
0026 
0027 import java.util.Arrays;
0028 import java.util.HashMap;
0029 import java.util.Map;
0030 import java.util.Map.Entry;
0031 import java.util.Objects;
0032 import java.util.function.Predicate;
0033 import java.util.function.Supplier;
0034 import java.util.stream.Collectors;
0035 import java.util.stream.DoubleStream;
0036 import java.util.stream.IntStream;
0037 import java.util.stream.LongStream;
0038 import java.util.stream.Stream;
0039 
0040 import io.jenetics.AnyChromosome;
0041 import io.jenetics.AnyGene;
0042 import io.jenetics.BitChromosome;
0043 import io.jenetics.BitGene;
0044 import io.jenetics.DoubleChromosome;
0045 import io.jenetics.DoubleGene;
0046 import io.jenetics.EnumGene;
0047 import io.jenetics.Gene;
0048 import io.jenetics.Genotype;
0049 import io.jenetics.IntegerChromosome;
0050 import io.jenetics.IntegerGene;
0051 import io.jenetics.LongChromosome;
0052 import io.jenetics.LongGene;
0053 import io.jenetics.PermutationChromosome;
0054 import io.jenetics.internal.util.Bits;
0055 import io.jenetics.internal.util.Predicates;
0056 import io.jenetics.internal.util.Requires;
0057 import io.jenetics.util.DoubleRange;
0058 import io.jenetics.util.ISeq;
0059 import io.jenetics.util.IntRange;
0060 import io.jenetics.util.LongRange;
0061 
0062 /**
0063  * This class contains factory methods for creating common  problem encodings.
0064  *
0065  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
0066  @since 3.2
0067  @version 5.2
0068  */
0069 public final class Codecs {
0070 
0071     private Codecs() {}
0072 
0073     /**
0074      * Return a scalar {@link InvertibleCodec} for the given range.
0075      *
0076      @param domain the domain of the returned {@code Codec}
0077      @return a new scalar {@code Codec} with the given domain.
0078      @throws NullPointerException if the given {@code domain} is {@code null}
0079      */
0080     public static InvertibleCodec<Integer, IntegerGene>
0081     ofScalar(final IntRange domain) {
0082         requireNonNull(domain);
0083 
0084         return InvertibleCodec.of(
0085             Genotype.of(IntegerChromosome.of(domain)),
0086             gt -> gt.chromosome().gene().allele(),
0087             val -> Genotype.of(IntegerChromosome.of(IntegerGene.of(val, domain)))
0088         );
0089     }
0090 
0091     /**
0092      * Return a scalar {@link InvertibleCodec} for the given range.
0093      *
0094      @param domain the domain of the returned {@code Codec}
0095      @return a new scalar {@code Codec} with the given domain.
0096      @throws NullPointerException if the given {@code domain} is {@code null}
0097      */
0098     public static InvertibleCodec<Long, LongGene>
0099     ofScalar(final LongRange domain) {
0100         requireNonNull(domain);
0101 
0102         return InvertibleCodec.of(
0103             Genotype.of(LongChromosome.of(domain)),
0104             gt -> gt.gene().allele(),
0105             val -> Genotype.of(LongChromosome.of(LongGene.of(val, domain)))
0106         );
0107     }
0108 
0109     /**
0110      * Return a scalar {@link InvertibleCodec} for the given range.
0111      *
0112      @param domain the domain of the returned {@code Codec}
0113      @return a new scalar {@code Codec} with the given domain.
0114      @throws NullPointerException if the given {@code domain} is {@code null}
0115      */
0116     public static InvertibleCodec<Double, DoubleGene>
0117     ofScalar(final DoubleRange domain) {
0118         requireNonNull(domain);
0119 
0120         return InvertibleCodec.of(
0121             Genotype.of(DoubleChromosome.of(domain)),
0122             gt -> gt.gene().allele(),
0123             val -> Genotype.of(DoubleChromosome.of(DoubleGene.of(val, domain)))
0124         );
0125     }
0126 
0127     /**
0128      * Return a scala {@link Codec} with the given allele {@link Supplier} and
0129      * allele {@code validator}. The {@code supplier} is responsible for
0130      * creating new random alleles, and the {@code validator} can verify it.
0131      <p>
0132      * The following example shows a codec which creates and verifies
0133      * {@code BigInteger} objects.
0134      * {@snippet lang="java":
0135      * final Codec<BigInteger, AnyGene<BigInteger>> codec = Codecs.of(
0136      *     // Create new random 'BigInteger' object.
0137      *     () -> {
0138      *         final byte[] data = new byte[100];
0139      *         RandomRegistry.getRandom().nextBytes(data);
0140      *         return new BigInteger(data);
0141      *     },
0142      *     // Verify that bit 7 is set. (For illustration purpose.)
0143      *     bi -> bi.testBit(7)
0144      * );
0145      * }
0146      *
0147      @see AnyGene#of(Supplier, Predicate)
0148      @see AnyChromosome#of(Supplier, Predicate)
0149      *
0150      @param <A> the allele type
0151      @param supplier the allele-supplier which is used for creating new,
0152      *        random alleles
0153      @param validator the validator used for validating the created gene. This
0154      *        predicate is used in the {@link AnyGene#isValid()} method.
0155      @return a new {@code Codec} with the given parameters
0156      @throws NullPointerException if one of the parameters is {@code null}
0157      */
0158     public static <A> Codec<A, AnyGene<A>> ofScalar(
0159         final Supplier<? extends A> supplier,
0160         final Predicate<? super A> validator
0161     ) {
0162         return Codec.of(
0163             Genotype.of(AnyChromosome.of(supplier, validator)),
0164             gt -> gt.gene().allele()
0165         );
0166     }
0167 
0168     /**
0169      * Return a scala {@link Codec} with the given allele {@link Supplier} and
0170      * allele {@code validator}. The {@code supplier} is responsible for
0171      * creating new random alleles.
0172      *
0173      @see #ofScalar(Supplier, Predicate)
0174      @see AnyGene#of(Supplier)
0175      @see AnyChromosome#of(Supplier)
0176      *
0177      @param <A> the allele type
0178      @param supplier the allele-supplier which is used for creating new,
0179      *        random alleles
0180      @return a new {@code Codec} with the given parameters
0181      @throws NullPointerException if the parameter is {@code null}
0182      */
0183     public static <A> Codec<A, AnyGene<A>> ofScalar(
0184         final Supplier<? extends A> supplier
0185     ) {
0186         return Codec.of(
0187             Genotype.of(AnyChromosome.of(supplier)),
0188             gt -> gt.gene().allele()
0189         );
0190     }
0191 
0192     /**
0193      * Return a vector {@link InvertibleCodec} for the given range. All vector
0194      * values are restricted by the same domain.
0195      *
0196      @param domain the domain of the vector values
0197      @param length the vector length
0198      @return a new vector {@code Codec}
0199      @throws NullPointerException if the given {@code domain} is {@code null}
0200      @throws IllegalArgumentException if the {@code length} is smaller than
0201      *         one.
0202      */
0203     public static InvertibleCodec<int[], IntegerGene> ofVector(
0204         final IntRange domain,
0205         final int length
0206     ) {
0207         return ofVector(domain, IntRange.of(length));
0208     }
0209 
0210     /**
0211      * Return a vector {@link InvertibleCodec} for the given range. All vector
0212      * values are restricted by the same domain.
0213      *
0214      @since 7.0
0215      *
0216      @param domain the domain of the vector values
0217      @param length the vector length range
0218      @return a new vector {@code Codec}
0219      @throws NullPointerException if the given {@code domain} is {@code null}
0220      @throws IllegalArgumentException if the {@code length} is smaller than
0221      *         one.
0222      */
0223     public static InvertibleCodec<int[], IntegerGene> ofVector(
0224         final IntRange domain,
0225         final IntRange length
0226     ) {
0227         requireNonNull(domain);
0228         Requires.positive(length.min());
0229 
0230         return InvertibleCodec.of(
0231             Genotype.of(IntegerChromosome.of(domain, length)),
0232             gt -> gt.chromosome().as(IntegerChromosome.class).toArray(),
0233             val -> Genotype.of(
0234                 IntegerChromosome.of(
0235                     IntStream.of(val)
0236                         .mapToObj(i -> IntegerGene.of(i, domain))
0237                         .collect(ISeq.toISeq())
0238                 )
0239             )
0240         );
0241     }
0242 
0243     /**
0244      * Return a vector {@link InvertibleCodec} for the given range. All vector
0245      * values are restricted by the same domain.
0246      *
0247      @param domain the domain of the vector values
0248      @param length the vector length
0249      @return a new vector {@code Codec}
0250      @throws NullPointerException if the given {@code domain} is {@code null}
0251      @throws IllegalArgumentException if the {@code length} is smaller than
0252      *         one.
0253      */
0254     public static InvertibleCodec<long[], LongGene> ofVector(
0255         final LongRange domain,
0256         final int length
0257     ) {
0258         return ofVector(domain, IntRange.of(length));
0259     }
0260 
0261     /**
0262      * Return a vector {@link InvertibleCodec} for the given range. All vector
0263      * values are restricted by the same domain.
0264      *
0265      @since 7.0
0266      *
0267      @param domain the domain of the vector values
0268      @param length the vector length range
0269      @return a new vector {@code Codec}
0270      @throws NullPointerException if the given {@code domain} is {@code null}
0271      @throws IllegalArgumentException if the {@code length} is smaller than
0272      *         one.
0273      */
0274     public static InvertibleCodec<long[], LongGene> ofVector(
0275         final LongRange domain,
0276         final IntRange length
0277     ) {
0278         requireNonNull(domain);
0279         Requires.positive(length.min());
0280 
0281         return InvertibleCodec.of(
0282             Genotype.of(LongChromosome.of(domain, length)),
0283             gt -> gt.chromosome().as(LongChromosome.class).toArray(),
0284             val -> Genotype.of(
0285                 LongChromosome.of(
0286                     LongStream.of(val)
0287                         .mapToObj(l -> LongGene.of(l, domain))
0288                         .collect(ISeq.toISeq())
0289                 )
0290             )
0291         );
0292     }
0293 
0294     /**
0295      * Return a vector {@link InvertibleCodec} for the given range. All vector
0296      * values are restricted by the same domain.
0297      *
0298      @param domain the domain of the vector values
0299      @param length the vector length
0300      @return a new vector {@code Codec}
0301      @throws NullPointerException if the given {@code domain} is {@code null}
0302      @throws IllegalArgumentException if the {@code length} is smaller than
0303      *         one.
0304      */
0305     public static InvertibleCodec<double[], DoubleGene> ofVector(
0306         final DoubleRange domain,
0307         final int length
0308     ) {
0309         return ofVector(domain, IntRange.of(length));
0310     }
0311 
0312     /**
0313      * Return a vector {@link InvertibleCodec} for the given range. All vector
0314      * values are restricted by the same domain.
0315      *
0316      @since 7.0
0317      *
0318      @param domain the domain of the vector values
0319      @param length the vector length range
0320      @return a new vector {@code Codec}
0321      @throws NullPointerException if the given {@code domain} is {@code null}
0322      @throws IllegalArgumentException if the {@code length} is smaller than
0323      *         one.
0324      */
0325     public static InvertibleCodec<double[], DoubleGene> ofVector(
0326         final DoubleRange domain,
0327         final IntRange length
0328     ) {
0329         requireNonNull(domain);
0330         Requires.positive(length.min());
0331 
0332         return InvertibleCodec.of(
0333             Genotype.of(DoubleChromosome.of(domain, length)),
0334             gt -> gt.chromosome().as(DoubleChromosome.class).toArray(),
0335             val -> Genotype.of(
0336                 DoubleChromosome.of(
0337                     DoubleStream.of(val)
0338                         .mapToObj(d -> DoubleGene.of(d, domain))
0339                         .collect(ISeq.toISeq())
0340                 )
0341             )
0342         );
0343     }
0344 
0345     /**
0346      * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
0347      * element might have a different domain. The vector length is equal to the
0348      * number of domains.
0349      *
0350      @param domains the domain ranges
0351      @return a new vector {@code Codec}
0352      @throws NullPointerException if one of the arguments is {@code null}
0353      @throws IllegalArgumentException if the {@code domains} array is empty
0354      */
0355     public static InvertibleCodec<int[], IntegerGene>
0356     ofVector(final IntRange... domains) {
0357         if (domains.length == 0) {
0358             throw new IllegalArgumentException("Domains must not be empty.");
0359         }
0360 
0361         final ISeq<IntegerChromosome> chromosomes = Stream.of(domains)
0362             .peek(Objects::requireNonNull)
0363             .map(IntegerGene::of)
0364             .map(IntegerChromosome::of)
0365             .collect(ISeq.toISeq());
0366 
0367         return InvertibleCodec.of(
0368             Genotype.of(chromosomes),
0369             gt -> {
0370                 final int[] args = new int[gt.length()];
0371                 for (int i = gt.length(); --i >= 0;) {
0372                     args[i= gt.get(i).gene().intValue();
0373                 }
0374                 return args;
0375             },
0376             val -> Genotype.of(
0377                 IntStream.range(0, val.length)
0378                     .mapToObj(i ->
0379                         IntegerChromosome.of(IntegerGene.of(val[i], domains[i])))
0380                     .collect(ISeq.toISeq())
0381             )
0382         );
0383     }
0384 
0385     /**
0386      * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
0387      * element might have a different domain. The vector length is equal to the
0388      * number of domains.
0389      *
0390      @param domains the domain ranges
0391      @return a new vector {@code Codec}
0392      @throws NullPointerException if one of the arguments is {@code null}
0393      @throws IllegalArgumentException if the {@code domains} array is empty
0394      */
0395     public static InvertibleCodec<long[], LongGene>
0396     ofVector(final LongRange... domains) {
0397         if (domains.length == 0) {
0398             throw new IllegalArgumentException("Domains must not be empty.");
0399         }
0400 
0401         final ISeq<LongChromosome> chromosomes = Stream.of(domains)
0402             .peek(Objects::requireNonNull)
0403             .map(LongGene::of)
0404             .map(LongChromosome::of)
0405             .collect(ISeq.toISeq());
0406 
0407         return InvertibleCodec.of(
0408             Genotype.of(chromosomes),
0409             gt -> {
0410                 final long[] args = new long[gt.length()];
0411                 for (int i = gt.length(); --i >= 0;) {
0412                     args[i= gt.get(i).gene().longValue();
0413                 }
0414                 return args;
0415             },
0416             val -> Genotype.of(
0417                 IntStream.range(0, val.length)
0418                     .mapToObj(i ->
0419                         LongChromosome.of(LongGene.of(val[i], domains[i])))
0420                     .collect(ISeq.toISeq())
0421             )
0422         );
0423     }
0424 
0425     /**
0426      * Create a vector {@link InvertibleCodec} for the given ranges. Each vector
0427      * element might have a different domain. The vector length is equal to the
0428      * number of domains.
0429      *
0430      @param domains the domain ranges
0431      @return a new vector {@code Codec}
0432      @throws NullPointerException if one of the arguments is {@code null}
0433      @throws IllegalArgumentException if the {@code domains} array is empty
0434      */
0435     public static InvertibleCodec<double[], DoubleGene>
0436     ofVector(final DoubleRange... domains) {
0437         if (domains.length == 0) {
0438             throw new IllegalArgumentException("Domains must not be empty.");
0439         }
0440 
0441         final ISeq<DoubleChromosome> chromosomes = Stream.of(domains)
0442             .peek(Objects::requireNonNull)
0443             .map(DoubleGene::of)
0444             .map(DoubleChromosome::of)
0445             .collect(ISeq.toISeq());
0446 
0447         return InvertibleCodec.of(
0448             Genotype.of(chromosomes),
0449             gt -> {
0450                 final double[] args = new double[gt.length()];
0451                 for (int i = gt.length(); --i >= 0;) {
0452                     args[i= gt.get(i).gene().doubleValue();
0453                 }
0454                 return args;
0455             },
0456             val -> Genotype.of(
0457                 IntStream.range(0, val.length)
0458                     .mapToObj(i ->
0459                         DoubleChromosome.of(DoubleGene.of(val[i], domains[i])))
0460                     .collect(ISeq.toISeq())
0461             )
0462         );
0463     }
0464 
0465     /**
0466      * Return a scala {@link Codec} with the given allele {@link Supplier},
0467      * allele {@code validator} and {@code Chromosome} length. The
0468      * {@code supplier} is responsible for creating new random alleles, and the
0469      * {@code validator} can verify it.
0470      <p>
0471      * The following example shows a codec which creates and verifies
0472      * {@code BigInteger} object arrays.
0473      * {@snippet lang="java":
0474      * final Codec<BigInteger[], AnyGene<BigInteger>> codec = Codecs.of(
0475      *     // Create new random 'BigInteger' object.
0476      *     () -> {
0477      *         final byte[] data = new byte[100];
0478      *         RandomRegistry.getRandom().nextBytes(data);
0479      *         return new BigInteger(data);
0480      *     },
0481      *     // Verify that bit 7 is set. (For illustration purpose.)
0482      *     bi -> bi.testBit(7),
0483      *     // The 'Chromosome' length.
0484      *     123
0485      * );
0486      * }
0487      *
0488      @see AnyChromosome#of(Supplier, Predicate, Predicate, int)
0489      *
0490      @param <A> the allele type
0491      @param supplier the allele-supplier which is used for creating new,
0492      *        random alleles
0493      @param alleleValidator the validator used for validating the created gene.
0494      *        This predicate is used in the {@link AnyGene#isValid()} method.
0495      @param alleleSeqValidator the validator used for validating the created
0496      *        chromosome. This predicate is used in the
0497      *        {@link AnyChromosome#isValid()} method.
0498      @param length the vector length
0499      @return a new {@code Codec} with the given parameters
0500      @throws NullPointerException if one of the parameters is {@code null}
0501      @throws IllegalArgumentException if the length of the vector is smaller
0502      *         than one.
0503      */
0504     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
0505         final Supplier<? extends A> supplier,
0506         final Predicate<? super A> alleleValidator,
0507         final Predicate<? super ISeq<A>> alleleSeqValidator,
0508         final int length
0509     ) {
0510         requireNonNull(supplier);
0511         requireNonNull(alleleSeqValidator);
0512         requireNonNull(alleleSeqValidator);
0513         Requires.positive(length);
0514 
0515         return Codec.of(
0516             Genotype.of(AnyChromosome
0517                 .of(supplier, alleleValidator, alleleSeqValidator, length)),
0518             gt -> gt.chromosome().stream()
0519                 .map(Gene::allele)
0520                 .collect(ISeq.toISeq())
0521         );
0522     }
0523 
0524     /**
0525      * Return a scala {@link Codec} with the given allele {@link Supplier},
0526      * allele {@code validator} and {@code Chromosome} length. The
0527      * {@code supplier} is responsible for creating new random alleles, and the
0528      * {@code validator} can verify it.
0529      *
0530      @param <A> the allele type
0531      @param supplier the allele-supplier which is used for creating new,
0532      *        random alleles
0533      @param validator the validator used for validating the created gene. This
0534      *        predicate is used in the {@link AnyGene#isValid()} method.
0535      @param length the vector length
0536      @return a new {@code Codec} with the given parameters
0537      @throws NullPointerException if one of the parameters is {@code null}
0538      @throws IllegalArgumentException if the length of the vector is smaller
0539      *         than one.
0540      */
0541     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
0542         final Supplier<? extends A> supplier,
0543         final Predicate<? super A> validator,
0544         final int length
0545     ) {
0546         return ofVector(
0547             supplier,
0548             validator,
0549             Predicates.True(),
0550             length
0551         );
0552     }
0553 
0554     /**
0555      * Return a scala {@link Codec} with the given allele {@link Supplier} and
0556      * {@code Chromosome} length. The {@code supplier} is responsible for
0557      * creating new random alleles.
0558      *
0559      @param <A> the allele type
0560      @param supplier the allele-supplier which is used for creating new,
0561      *        random alleles
0562      @param length the vector length
0563      @return a new {@code Codec} with the given parameters
0564      @throws NullPointerException if one of the parameters is {@code null}
0565      @throws IllegalArgumentException if the length of the vector is smaller
0566      *         than one.
0567      */
0568     public static <A> Codec<ISeq<A>, AnyGene<A>> ofVector(
0569         final Supplier<? extends A> supplier,
0570         final int length
0571     ) {
0572         return ofVector(supplier, Predicates.TRUE, length);
0573     }
0574 
0575     /**
0576      * Create a permutation {@link InvertibleCodec} of integer in the range
0577      * {@code [0, length)}.
0578      *
0579      @param length the number of permutation elements
0580      @return a permutation {@code Codec} of integers
0581      @throws IllegalArgumentException if the {@code length} is smaller than
0582      *         one.
0583      */
0584     public static InvertibleCodec<int[], EnumGene<Integer>>
0585     ofPermutation(final int length) {
0586         Requires.positive(length);
0587 
0588         final PermutationChromosome<Integer> chromosome =
0589             PermutationChromosome.ofInteger(length);
0590 
0591         final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
0592             .collect(Collectors.toMap(EnumGene::allele, identity()));
0593 
0594         return InvertibleCodec.of(
0595             Genotype.of(chromosome),
0596             gt -> gt.chromosome().stream()
0597                 .mapToInt(EnumGene::allele)
0598                 .toArray(),
0599             val -> Genotype.of(
0600                 new PermutationChromosome<>(
0601                     IntStream.of(val)
0602                         .mapToObj(genes::get)
0603                         .collect(ISeq.toISeq())
0604                 )
0605             )
0606         );
0607     }
0608 
0609     /**
0610      * Create a permutation {@link InvertibleCodec} with the given alleles.
0611      *
0612      @param alleles the alleles of the permutation
0613      @param <T> the allele type
0614      @return a new permutation {@code Codec}
0615      @throws IllegalArgumentException if the given allele array is empty
0616      @throws NullPointerException if one of the alleles is {@code null}
0617      */
0618     public static <T> InvertibleCodec<ISeq<T>, EnumGene<T>>
0619     ofPermutation(final ISeq<? extends T> alleles) {
0620         if (alleles.isEmpty()) {
0621             throw new IllegalArgumentException(
0622                 "Empty allele array is not allowed."
0623             );
0624         }
0625 
0626         final Map<T, EnumGene<T>> genes =
0627             IntStream.range(0, alleles.length())
0628                 .mapToObj(i -> EnumGene.<T>of(i, alleles))
0629                 .collect(Collectors.toMap(EnumGene::allele, identity()));
0630 
0631         return InvertibleCodec.of(
0632             Genotype.of(new PermutationChromosome<>(ISeq.of(genes.values()))),
0633             gt -> gt.chromosome().stream()
0634                 .map(EnumGene::allele)
0635                 .collect(ISeq.toISeq()),
0636             val -> Genotype.of(
0637                 new PermutationChromosome<>(
0638                     val.stream()
0639                         .map(genes::get)
0640                         .collect(ISeq.toISeq())
0641                 )
0642             )
0643         );
0644     }
0645 
0646     /**
0647      * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
0648      * All matrix values are restricted by the same domain. The dimension of the
0649      * returned matrix is {@code int[rows][cols]}.
0650      *
0651      @since 4.4
0652      *
0653      @param domain the domain of the matrix values
0654      @param rows the number of rows of the matrix
0655      @param cols the number of columns of the matrix
0656      @return a new matrix {@code Codec}
0657      @throws NullPointerException if the given {@code domain} is {@code null}
0658      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
0659      *         smaller than one.
0660      */
0661     public static InvertibleCodec<int[][], IntegerGene> ofMatrix(
0662         final IntRange domain,
0663         final int rows,
0664         final int cols
0665     ) {
0666         requireNonNull(domain);
0667         Requires.positive(rows);
0668         Requires.positive(cols);
0669 
0670         return InvertibleCodec.of(
0671             Genotype.of(
0672                 IntegerChromosome.of(domain, cols).instances()
0673                     .limit(rows)
0674                     .collect(ISeq.toISeq())
0675             ),
0676             gt -> gt.stream()
0677                 .map(ch -> ch.as(IntegerChromosome.class).toArray())
0678                 .toArray(int[][]::new),
0679             matrix -> Genotype.of(
0680                 Stream.of(matrix)
0681                     .map(row ->
0682                         IntegerChromosome.of(
0683                             IntStream.of(row)
0684                                 .mapToObj(v -> IntegerGene.of(v, domain))
0685                                 .collect(ISeq.toISeq())
0686                         ))
0687                     .collect(ISeq.toISeq())
0688             )
0689         );
0690     }
0691 
0692     /**
0693      * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
0694      * All matrix values are restricted by the same domain. The dimension of the
0695      * returned matrix is {@code long[rows][cols]}.
0696      *
0697      @since 4.4
0698      *
0699      @param domain the domain of the matrix values
0700      @param rows the number of rows of the matrix
0701      @param cols the number of columns of the matrix
0702      @return a new matrix {@code Codec}
0703      @throws NullPointerException if the given {@code domain} is {@code null}
0704      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
0705      *         smaller than one.
0706      */
0707     public static InvertibleCodec<long[][], LongGene> ofMatrix(
0708         final LongRange domain,
0709         final int rows,
0710         final int cols
0711     ) {
0712         requireNonNull(domain);
0713         Requires.positive(rows);
0714         Requires.positive(cols);
0715 
0716         return InvertibleCodec.of(
0717             Genotype.of(
0718                 LongChromosome.of(domain, cols).instances()
0719                     .limit(rows)
0720                     .collect(ISeq.toISeq())
0721             ),
0722             gt -> gt.stream()
0723                 .map(ch -> ch.as(LongChromosome.class).toArray())
0724                 .toArray(long[][]::new),
0725             matrix -> Genotype.of(
0726                 Stream.of(matrix)
0727                     .map(row ->
0728                         LongChromosome.of(
0729                             LongStream.of(row)
0730                                 .mapToObj(v -> LongGene.of(v, domain))
0731                                 .collect(ISeq.toISeq())
0732                         ))
0733                     .collect(ISeq.toISeq())
0734             )
0735         );
0736     }
0737 
0738     /**
0739      * Return a 2-dimensional matrix {@link InvertibleCodec} for the given range.
0740      * All matrix values are restricted by the same domain. The dimension of the
0741      * returned matrix is {@code double[rows][cols]}.
0742      *
0743      @since 4.4
0744      *
0745      @param domain the domain of the matrix values
0746      @param rows the number of rows of the matrix
0747      @param cols the number of columns of the matrix
0748      @return a new matrix {@code Codec}
0749      @throws NullPointerException if the given {@code domain} is {@code null}
0750      @throws IllegalArgumentException if the {@code rows} or {@code cols} are
0751      *         smaller than one.
0752      */
0753     public static InvertibleCodec<double[][], DoubleGene> ofMatrix(
0754         final DoubleRange domain,
0755         final int rows,
0756         final int cols
0757     ) {
0758         requireNonNull(domain);
0759         Requires.positive(rows);
0760         Requires.positive(cols);
0761 
0762         return InvertibleCodec.of(
0763             Genotype.of(
0764                 DoubleChromosome.of(domain, cols).instances()
0765                     .limit(rows)
0766                     .collect(ISeq.toISeq())
0767             ),
0768             gt -> gt.stream()
0769                 .map(ch -> ch.as(DoubleChromosome.class).toArray())
0770                 .toArray(double[][]::new),
0771             matrix -> Genotype.of(
0772                 Stream.of(matrix)
0773                     .map(row ->
0774                         DoubleChromosome.of(
0775                             DoubleStream.of(row)
0776                                 .mapToObj(v -> DoubleGene.of(v, domain))
0777                                 .collect(ISeq.toISeq())
0778                         ))
0779                     .collect(ISeq.toISeq())
0780             )
0781         );
0782     }
0783 
0784     /**
0785      * Create a codec, which creates a mapping from the elements given in the
0786      * {@code source} sequence to the elements given in the {@code target}
0787      * sequence. The returned mapping can be seen as a function which maps every
0788      * element of the {@code target} set to an element of the {@code source} set.
0789      *
0790      * {@snippet lang="java":
0791      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
0792      * final ISeq<String> strings = ISeq.of("1", "2", "3");
0793      *
0794      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
0795      *     Codecs.ofMapping(numbers, strings, HashMap::new);
0796      * }
0797      *
0798      * If {@code source.size() > target.size()}, the created mapping is
0799      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
0800      * if {@code source.size() < target.size()}, the mapping is
0801      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
0802      * and if both sets have the same size, the returned mapping is
0803      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
0804      *
0805      @since 4.3
0806      *
0807      @param source the source elements. Will be the <em>keys</em> of the
0808      *        encoded {@code Map}.
0809      @param target the target elements. Will be the <em>values</em> of the
0810      *           encoded {@code Map}.
0811      @param mapSupplier a function which returns a new, empty Map into which
0812      *        the mapping will be inserted
0813      @param <A> the type of the source elements
0814      @param <B> the type of the target elements
0815      @param <M> the type of the encoded Map
0816      @return a new mapping codec
0817      @throws IllegalArgumentException if the {@code target} sequences are empty
0818      @throws NullPointerException if one of the arguments is {@code null}
0819      */
0820     public static <A, B, M extends Map<A, B>> InvertibleCodec<M, EnumGene<Integer>>
0821     ofMapping(
0822         final ISeq<? extends A> source,
0823         final ISeq<? extends B> target,
0824         final Supplier<M> mapSupplier
0825     ) {
0826         requireNonNull(source);
0827         requireNonNull(target);
0828         requireNonNull(mapSupplier);
0829 
0830         final Map<A, Integer> smap = IntStream.range(0, source.length())
0831             .mapToObj(i -> entry(source.get(i), i))
0832             .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
0833 
0834         final Map<B, Integer> tmap = IntStream.range(0, target.length())
0835             .mapToObj(i -> entry(target.get(i), i))
0836             .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
0837 
0838         final PermutationChromosome<Integer> chromosome =
0839             PermutationChromosome.ofInteger(target.size());
0840 
0841         final Map<Integer, EnumGene<Integer>> genes = chromosome.stream()
0842             .collect(Collectors.toMap(EnumGene::allele, identity()));
0843 
0844         final Codec<int[], EnumGene<Integer>> codec = Codec.of(
0845             Genotype.of(chromosome),
0846             gt -> gt.chromosome().stream()
0847                 .mapToInt(EnumGene::allele)
0848                 .toArray()
0849         );
0850 
0851         return codec
0852             .map(permutation -> toMapping(permutation, source, target, mapSupplier))
0853             .toInvertibleCodec(mapping -> toEncoding(mapping, smap,tmap, genes));
0854     }
0855 
0856     private static <A, B, M extends Map<A, B>> M toMapping(
0857         final int[] perm,
0858         final ISeq<? extends A> source,
0859         final ISeq<? extends B> target,
0860         final Supplier<M> mapSupplier
0861     ) {
0862         return IntStream.range(0, source.size())
0863             .mapToObj(i -> entry(source.get(i), target.get(perm[i%perm.length])))
0864             .collect(Collectors.toMap(
0865                 Entry::getKey, Entry::getValue,
0866                 (u,v-> {throw new IllegalStateException("Duplicate key " + u);},
0867                 mapSupplier));
0868     }
0869 
0870     private static <A, B> Genotype<EnumGene<Integer>> toEncoding(
0871         final Map<A, B> mapping,
0872         final Map<A, Integer> source,
0873         final Map<B, Integer> target,
0874         final Map<Integer, EnumGene<Integer>> genes
0875     ) {
0876         final int[] perm = new int[target.size()];
0877         source.forEach((key, value-> {
0878             final int i = value;
0879             final int j = target.get(mapping.get(key));
0880             perm[i%perm.length= j;
0881         });
0882 
0883         // If the target size is greater the source size, only the first
0884         // elements (source size) are filled. The rest of the 'perm' array
0885         // has to be filled with unique elements.
0886         if (target.size() > source.size()) {
0887             final int[] indexes = new int[target.size()];
0888 
0889             // Initialize the index set with all target indexes: O(|t|)
0890             for (int i = 0; i < target.size(); ++i) {
0891                 indexes[i= i;
0892             }
0893 
0894             // Mark existing permutations in the index array: O(|s|*log(|t|))
0895             for (int i = 0; i < source.size(); ++i) {
0896                 final int si = Arrays.binarySearch(indexes, perm[i]);
0897                 if (si >= 0) {
0898                     indexes[si= -1;
0899                 }
0900             }
0901 
0902             // Fill the 'perm' array with the remaining, non-duplicate indexes:
0903             // O(|t|)
0904             int j = 0;
0905             int next;
0906             for (int i = source.size(); i < target.size(); ++i) {
0907                 while ((next = indexes[j++]) == -1);
0908                 perm[i= next;
0909             }
0910         }
0911 
0912         return  Genotype.of(
0913             new PermutationChromosome<>(
0914                 IntStream.of(perm)
0915                     .mapToObj(genes::get)
0916                     .collect(ISeq.toISeq())
0917             )
0918         );
0919     }
0920 
0921     /**
0922      * Create a codec, which creates a mapping from the elements given in the
0923      * {@code source} sequence to the elements given in the {@code target}
0924      * sequence. The returned mapping can be seen as a function which maps every
0925      * element of the {@code target} set to an element of the {@code source} set.
0926      *
0927      * {@snippet lang="java":
0928      * final ISeq<Integer> numbers = ISeq.of(1, 2, 3, 4, 5);
0929      * final ISeq<String> strings = ISeq.of("1", "2", "3");
0930      *
0931      * final Codec<Map<Integer, String>, EnumGene<Integer>> codec =
0932      *     Codecs.ofMapping(numbers, strings);
0933      * }
0934      *
0935      * If {@code source.size() > target.size()}, the created mapping is
0936      * <a href="https://en.wikipedia.org/wiki/Surjective_function">surjective</a>,
0937      * if {@code source.size() < target.size()}, the mapping is
0938      * <a href="https://en.wikipedia.org/wiki/Injective_function">injective</a>
0939      * and if both sets have the same size, the returned mapping is
0940      * <a href="https://en.wikipedia.org/wiki/Bijection">bijective</a>.
0941      *
0942      @since 4.3
0943      *
0944      @param source the source elements. Will be the <em>keys</em> of the
0945      *        encoded {@code Map}.
0946      @param target the target elements. Will be the <em>values</em> of the
0947      *           encoded {@code Map}.
0948      @param <A> the type of the source elements
0949      @param <B> the type of the target elements
0950      @return a new mapping codec
0951      @throws IllegalArgumentException if the {@code target} sequences are empty
0952      @throws NullPointerException if one of the arguments is {@code null}
0953      */
0954     public static <A, B> InvertibleCodec<Map<A, B>, EnumGene<Integer>>
0955     ofMapping(final ISeq<? extends A> source, final ISeq<? extends B> target) {
0956         return ofMapping(source, target, HashMap::new);
0957     }
0958 
0959     /**
0960      * The subset {@link InvertibleCodec} can be used for problems where it is
0961      * required to find the best <b>variable-sized</b> subset from a given basic
0962      * set. A typical usage example of the returned {@code Codec} is the
0963      * Knapsack problem.
0964      <p>
0965      * The following code snippet shows a simplified variation of the Knapsack
0966      * problem.
0967      * {@snippet lang="java":
0968      * public final class Main {
0969      *      // The basic set from where to choose an 'optimal' subset.
0970      *      private final static ISeq<Integer> SET =
0971      *          ISeq.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
0972      *
0973      *      // Fitness function directly takes an 'int' value.
0974      *      private static int fitness(final ISeq<Integer> subset) {
0975      *          assert(subset.size() <= SET.size());
0976      *          final int size = subset.stream()
0977      *             .mapToInt(Integer::intValue)
0978      *             .sum();
0979      *          return size <= 20 ? size : 0;
0980      *      }
0981      *
0982      *      public static void main(final String[] args) {
0983      *          final Engine<BitGene, Double> engine = Engine
0984      *              .builder(Main::fitness, codec.ofSubSet(SET))
0985      *              .build();
0986      *          // ...
0987      *      }
0988      *  }
0989      * }
0990      *
0991      @param <T> the element type of the basic set
0992      @param basicSet the basic set, from where to choose the <i>optimal</i>
0993      *        subset.
0994      @return a new codec which can be used for modelling <i>subset</i>
0995      *         problems.
0996      @throws NullPointerException if the given {@code basicSet} is
0997      *         {@code null}; {@code null} elements are allowed.
0998      @throws IllegalArgumentException if the {@code basicSet} size is smaller
0999      *         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 }