From 980b7cac3ffc8bfc9bc53d71ecbdfeaffa8260ed Mon Sep 17 00:00:00 2001
From: Vojtech Moravec <vojtech.moravec.st@vsb.cz>
Date: Sat, 21 Mar 2020 17:41:26 +0100
Subject: [PATCH] Merge commit from huffman branch

---
 pom.xml                                       |   2 -
 .../ScalarQuantizationBenchmark.java          |  44 +--
 .../compression/SQImageCompressor.java        |   8 +-
 .../java/azgracompress/de/DEIndividual.java   | 102 -------
 src/main/java/azgracompress/de/DESolver.java  | 262 ------------------
 .../azgracompress/de/DESolverWithArchive.java |  59 ----
 .../java/azgracompress/de/DeException.java    |   8 -
 .../java/azgracompress/de/IDEIndividual.java  |  25 --
 src/main/java/azgracompress/de/IDESolver.java |  22 --
 .../azgracompress/de/jade/JadeSolver.java     | 159 -----------
 .../de/jade/RunnablePopulationFitness.java    |  47 ----
 .../azgracompress/de/shade/ILShadeSolver.java | 198 -------------
 .../azgracompress/de/shade/LShadeSolver.java  | 241 ----------------
 .../quantization/QuantizationValueCache.java  |   3 +
 .../scalar/LloydMaxU16ScalarQuantization.java |  47 ++--
 .../scalar/RunnableLloydMseCalc.java          |   7 +-
 .../scalar/ScalarQuantizationCodebook.java    |  38 +++
 .../quantization/scalar/ScalarQuantizer.java  |  19 +-
 18 files changed, 114 insertions(+), 1177 deletions(-)
 delete mode 100644 src/main/java/azgracompress/de/DEIndividual.java
 delete mode 100644 src/main/java/azgracompress/de/DESolver.java
 delete mode 100644 src/main/java/azgracompress/de/DESolverWithArchive.java
 delete mode 100644 src/main/java/azgracompress/de/DeException.java
 delete mode 100644 src/main/java/azgracompress/de/IDEIndividual.java
 delete mode 100644 src/main/java/azgracompress/de/IDESolver.java
 delete mode 100644 src/main/java/azgracompress/de/jade/JadeSolver.java
 delete mode 100644 src/main/java/azgracompress/de/jade/RunnablePopulationFitness.java
 delete mode 100644 src/main/java/azgracompress/de/shade/ILShadeSolver.java
 delete mode 100644 src/main/java/azgracompress/de/shade/LShadeSolver.java
 create mode 100644 src/main/java/azgracompress/quantization/scalar/ScalarQuantizationCodebook.java

diff --git a/pom.xml b/pom.xml
index bbab7b7..60740bc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,6 +51,4 @@
             <version>2.8.6</version>
         </dependency>
     </dependencies>
-
-
 </project>
\ No newline at end of file
diff --git a/src/main/java/azgracompress/benchmark/ScalarQuantizationBenchmark.java b/src/main/java/azgracompress/benchmark/ScalarQuantizationBenchmark.java
index 6c0897a..3a3a5c4 100644
--- a/src/main/java/azgracompress/benchmark/ScalarQuantizationBenchmark.java
+++ b/src/main/java/azgracompress/benchmark/ScalarQuantizationBenchmark.java
@@ -2,9 +2,8 @@ package azgracompress.benchmark;
 
 import azgracompress.U16;
 import azgracompress.cli.ParsedCliOptions;
-import azgracompress.data.V3i;
-import azgracompress.de.DeException;
-import azgracompress.de.shade.ILShadeSolver;
+import azgracompress.io.IPlaneLoader;
+import azgracompress.io.PlaneLoaderFactory;
 import azgracompress.quantization.QTrainIteration;
 import azgracompress.quantization.QuantizationValueCache;
 import azgracompress.quantization.scalar.LloydMaxU16ScalarQuantization;
@@ -43,7 +42,8 @@ public class ScalarQuantizationBenchmark extends BenchmarkBase {
             QuantizationValueCache cache = new QuantizationValueCache(cacheFolder);
             try {
                 final int[] quantizationValues = cache.readCachedValues(inputFile, codebookSize);
-                quantizer = new ScalarQuantizer(U16.Min, U16.Max, quantizationValues);
+                // TODO(Moravec): FIXME!
+                quantizer = null;//new ScalarQuantizer(U16.Min, U16.Max, quantizationValues);
             } catch (IOException e) {
                 System.err.println("Failed to read quantization values from cache file.");
                 e.printStackTrace();
@@ -57,7 +57,8 @@ public class ScalarQuantizationBenchmark extends BenchmarkBase {
                 return;
             }
             if (useDiffEvolution) {
-                quantizer = trainDifferentialEvolution(refPlaneData, codebookSize);
+                assert (false) : "DE is depracated";
+                quantizer = null;
             } else {
                 quantizer = trainLloydMaxQuantizer(refPlaneData, codebookSize);
             }
@@ -75,8 +76,10 @@ public class ScalarQuantizationBenchmark extends BenchmarkBase {
 
 
             if (!hasGeneralQuantizer) {
+
                 if (useDiffEvolution) {
-                    quantizer = trainDifferentialEvolution(planeData, codebookSize);
+                    assert (false) : "DE is depracated";
+                    quantizer = null;
                 } else {
                     quantizer = trainLloydMaxQuantizer(planeData, codebookSize);
                 }
@@ -136,22 +139,23 @@ public class ScalarQuantizationBenchmark extends BenchmarkBase {
 
         //saveQTrainLog(String.format("p%d_cb_%d_lloyd.csv", planeIndex, codebookSize), trainingReport);
 
-        return new ScalarQuantizer(U16.Min, U16.Max, lloydMax.getCentroids());
+        // TODO(Moravec): FIXME
+        return new ScalarQuantizer(U16.Min, U16.Max, null);//lloydMax.getCentroids());
     }
 
-    private ScalarQuantizer trainDifferentialEvolution(final int[] data,
-                                                       final int codebookSize) {
-        ILShadeSolver ilshade = new ILShadeSolver(codebookSize, 100, 2000, 15);
-        ilshade.setTrainingData(data);
-
-        try {
-            ilshade.train();
-        } catch (DeException deEx) {
-            deEx.printStackTrace();
-            return null;
-        }
-        return new ScalarQuantizer(U16.Min, U16.Max, ilshade.getBestSolution().getAttributes());
-    }
+    //    private ScalarQuantizer trainDifferentialEvolution(final int[] data,
+    //                                                       final int codebookSize) {
+    //        ILShadeSolver ilshade = new ILShadeSolver(codebookSize, 100, 2000, 15);
+    //        ilshade.setTrainingData(data);
+    //
+    //        try {
+    //            ilshade.train();
+    //        } catch (DeException deEx) {
+    //            deEx.printStackTrace();
+    //            return null;
+    //        }
+    //        return new ScalarQuantizer(U16.Min, U16.Max, ilshade.getBestSolution().getAttributes());
+    //    }
 
 
     public boolean isUseDiffEvolution() {
diff --git a/src/main/java/azgracompress/compression/SQImageCompressor.java b/src/main/java/azgracompress/compression/SQImageCompressor.java
index 150a573..62fed9e 100644
--- a/src/main/java/azgracompress/compression/SQImageCompressor.java
+++ b/src/main/java/azgracompress/compression/SQImageCompressor.java
@@ -31,7 +31,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
                                                                                    codebookSize,
                                                                                    options.getWorkerCount());
         lloydMax.train(false);
-        return new ScalarQuantizer(U16.Min, U16.Max, lloydMax.getCentroids());
+        return new ScalarQuantizer(U16.Min, U16.Max, lloydMax.getCodebook());
     }
 
     /**
@@ -65,8 +65,10 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
     private ScalarQuantizer loadQuantizerFromCache() throws ImageCompressionException {
         QuantizationValueCache cache = new QuantizationValueCache(options.getCodebookCacheFolder());
         try {
-            final int[] quantizationValues = cache.readCachedValues(options.getInputFile(), codebookSize);
-            return new ScalarQuantizer(U16.Min, U16.Max, quantizationValues);
+            final int[] quantizationValues = cache.readCachedValues(options.getInputFileInfo().getFilePath(),
+                                                                    codebookSize);
+            // TODO(Moravec): FIXME the null value.
+            return new ScalarQuantizer(U16.Min, U16.Max, null);
         } catch (IOException e) {
             throw new ImageCompressionException("Failed to read quantization values from cache file.", e);
         }
diff --git a/src/main/java/azgracompress/de/DEIndividual.java b/src/main/java/azgracompress/de/DEIndividual.java
deleted file mode 100644
index c2c0e63..0000000
--- a/src/main/java/azgracompress/de/DEIndividual.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package azgracompress.de;
-
-import org.apache.commons.math3.distribution.UniformRealDistribution;
-
-import java.util.Arrays;
-
-public class DEIndividual implements IDEIndividual, Comparable<DEIndividual> {
-
-    protected Double m_fitness = null;
-    protected double m_mutationFactor;
-    protected double m_crossoverProbability;
-    protected int[] m_attributes;
-
-    protected DEIndividual(final int dimensionCount) {
-        m_attributes = new int[dimensionCount];
-    }
-
-    protected DEIndividual(final int[] attributes) {
-        m_attributes = attributes;
-    }
-
-    @Override
-    public double getFitness() {
-        assert (m_fitness != null);
-        return m_fitness;
-    }
-
-    @Override
-    public void setFitness(double fitness) {
-        m_fitness = fitness;
-    }
-
-    @Override
-    public boolean isFitnessCached() {
-        return (m_fitness != null);
-    }
-
-    @Override
-    public int compareTo(final DEIndividual other) {
-        return Double.compare(m_fitness, other.m_fitness);
-    }
-
-    @Override
-    public double getCrossoverProbability() {
-        return m_crossoverProbability;
-    }
-
-    @Override
-    public void setCrossoverProbability(final double cr) {
-        m_crossoverProbability = cr;
-    }
-
-    @Override
-    public double getMutationFactor() {
-        return m_mutationFactor;
-    }
-
-    @Override
-    public void setMutationFactor(final double f) {
-        m_mutationFactor = f;
-    }
-
-    @Override
-    public int[] getAttributes() {
-        return m_attributes;
-    }
-
-    @Override
-    public int getAttribute(final int dimension) {
-        return m_attributes[dimension];
-    }
-
-    @Override
-    public int hashCode() {
-        return Arrays.hashCode(m_attributes);
-    }
-
-    @Override
-    public DEIndividual createOffspringBinominalCrossover(final int[] mutationVector, final int jRand,
-                                                          UniformRealDistribution rndCrDist) {
-        assert (m_attributes.length == mutationVector.length);
-        DEIndividual offspring = new DEIndividual(m_attributes.length);
-        for (int j = 0; j < m_attributes.length; j++) {
-            double crRnd = rndCrDist.sample();
-            if ((j == jRand) || (crRnd < m_crossoverProbability)) {
-                offspring.m_attributes[j] = mutationVector[j];
-            } else {
-                offspring.m_attributes[j] = m_attributes[j];
-            }
-        }
-        return offspring;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof DEIndividual) {
-            return (this.hashCode() == ((DEIndividual) obj).hashCode());
-        } else {
-            return super.equals(obj);
-        }
-    }
-}
diff --git a/src/main/java/azgracompress/de/DESolver.java b/src/main/java/azgracompress/de/DESolver.java
deleted file mode 100644
index 199e9ba..0000000
--- a/src/main/java/azgracompress/de/DESolver.java
+++ /dev/null
@@ -1,262 +0,0 @@
-package azgracompress.de;
-
-import azgracompress.U16;
-import azgracompress.de.jade.RunnablePopulationFitness;
-import azgracompress.utilities.Utils;
-import org.apache.commons.math3.distribution.CauchyDistribution;
-import org.apache.commons.math3.distribution.NormalDistribution;
-import org.apache.commons.math3.distribution.UniformIntegerDistribution;
-import org.apache.commons.math3.random.MersenneTwister;
-
-import java.util.Arrays;
-
-public abstract class DESolver implements IDESolver {
-
-    public static final int MINIMAL_POPULATION_SIZE = 5;
-    protected int minConstraint = U16.Min;
-    protected int maxConstraint = U16.Max;
-    protected int populationSize;
-    protected int generationCount;
-    protected int dimensionCount;
-    protected int[] trainingData;
-    protected int threadCount;
-    protected int currentPopulationSize;
-
-    protected DEIndividual bestSolution = null;
-
-    protected DEIndividual[] currentPopulation;
-    protected DEIndividual[] currentPopulationSorted;
-
-    public DESolver(final int dimension, final int populationSize, final int generationCount) {
-        //threadCount = Runtime.getRuntime().availableProcessors() - 1;
-        // NOTE(Moravec): Let's go with 4 threads for now.
-        threadCount = 4;
-        //assert (threadCount > 1);
-
-        this.dimensionCount = dimension;
-        this.populationSize = populationSize;
-        this.generationCount = generationCount;
-        this.currentPopulationSize = populationSize;
-    }
-
-    /**
-     * Generate individual attributes, so that all are unique.
-     *
-     * @param distribution Uniform integer distribution with constraints.
-     * @return Array of unique attributes.
-     */
-    private int[] generateIndividualAttribues(UniformIntegerDistribution distribution) {
-
-        int[] attributes = new int[dimensionCount];
-        // NOTE(Moravec):   We are cheting here, when we set the first attribute to be zero, because we know that this is the best value there is.
-        attributes[0] = minConstraint;
-        for (int dim = 1; dim < dimensionCount; dim++) {
-            int rndValue = distribution.sample();
-            while (Utils.arrayContainsToIndex(attributes, dim, rndValue)) {
-                rndValue = distribution.sample();
-            }
-            attributes[dim] = rndValue;
-        }
-        Arrays.sort(attributes);
-        return attributes;
-    }
-
-    /**
-     * Generate initial population based on population size and constraints. Also allocates archive.
-     *
-     * @throws DeException Throws exception on wrong population size or wrong dimension.
-     */
-    protected void generateInitialPopulation() throws DeException {
-        assertPopulationSize();
-        assertDimension();
-
-        currentPopulation = new DEIndividual[populationSize];
-        UniformIntegerDistribution uniformIntDistribution = new UniformIntegerDistribution(new MersenneTwister(), minConstraint, maxConstraint);
-
-        for (int individualIndex = 0; individualIndex < populationSize; individualIndex++) {
-            int[] attributes = generateIndividualAttribues(uniformIntDistribution);
-            currentPopulation[individualIndex] = new DEIndividual(attributes);
-        }
-    }
-
-    protected double getMseFromCalculatedFitness(final DEIndividual[] population) {
-        double mse = 0.0;
-        for (final DEIndividual individual : population) {
-            assert (individual.isFitnessCached());
-            mse += individual.getFitness();
-        }
-        return (mse / (double) population.length);
-    }
-
-    /**
-     * Parallelized calculation of fitness values for individuals in current population.
-     */
-    protected double calculateFitnessForPopulationParallel(DEIndividual[] population) {
-
-        double avg = 0.0;
-        RunnablePopulationFitness[] workerInfos = new RunnablePopulationFitness[threadCount];
-        Thread[] workers = new Thread[threadCount];
-        int threadWorkSize = population.length / threadCount;
-
-        for (int workerId = 0; workerId < threadCount; workerId++) {
-            int workerFrom = workerId * threadWorkSize;
-            int workerTo = (workerId == (threadCount - 1)) ? population.length : (workerId * threadWorkSize) + threadWorkSize;
-            workerInfos[workerId] = new RunnablePopulationFitness(trainingData, population, workerFrom, workerTo);
-            workers[workerId] = new Thread(workerInfos[workerId]);
-            workers[workerId].start();
-        }
-
-        try {
-            for (int workerId = 0; workerId < threadCount; workerId++) {
-                workers[workerId].join();
-                avg += workerInfos[workerId].getTotalMse();
-            }
-        } catch (InterruptedException ignored) {
-        }
-
-
-        avg /= (double) population.length;
-        return avg;
-    }
-
-    /**
-     * Select random individual from p*100% top individuals.
-     *
-     * @param pBestDistribution Distribution for p*100% random.
-     * @param others            Other individuals.
-     * @return Random individual from p*100%.
-     */
-    protected DEIndividual getRandomFromPBest(UniformIntegerDistribution pBestDistribution,
-                                              final DEIndividual... others) {
-        assert (currentPopulationSorted != null);
-
-        int rndIndex = pBestDistribution.sample();
-        while (Utils.arrayContains(others, currentPopulationSorted[rndIndex])) {
-            rndIndex = pBestDistribution.sample();
-        }
-        return currentPopulationSorted[rndIndex];
-    }
-
-    /**
-     * Get random individual from current population, distinct from the other.
-     *
-     * @param rndIndDist Distribution of current population random.
-     * @param others     Other individuals.
-     * @return Distinct random individual from the another one.
-     */
-    protected DEIndividual getRandomFromCurrentPopulation(UniformIntegerDistribution rndIndDist,
-                                                          final DEIndividual... others) {
-        DEIndividual rndIndiv = currentPopulation[rndIndDist.sample()];
-        while (Utils.arrayContains(others, rndIndiv)) {
-            rndIndiv = currentPopulation[rndIndDist.sample()];
-        }
-        return rndIndiv;
-    }
-
-    protected int[] createMutationVectorCurrentToPBest(final DEIndividual current,
-                                                       final DEIndividual x_p_Best,
-                                                       final DEIndividual x_r1,
-                                                       final DEIndividual x_r2) {
-        int[] mutationVector = new int[dimensionCount];
-
-        double mutationFactor = current.getMutationFactor();
-        for (int j = 0; j < dimensionCount; j++) {
-
-            mutationVector[j] = (int) Math.floor(current.getAttribute(j) +
-                    (mutationFactor * ((double) x_p_Best.getAttribute(j) - current.getAttribute(j))) +
-                    (mutationFactor * ((double) x_r1.getAttribute(j) - x_r2.getAttribute(j))));
-
-            if (mutationVector[j] < minConstraint) {
-                mutationVector[j] = (minConstraint + current.getAttribute(j)) / 2;
-            } else if (mutationVector[j] > maxConstraint) {
-                mutationVector[j] = (maxConstraint + current.getAttribute(j)) / 2;
-            }
-        }
-        return mutationVector;
-    }
-
-    protected DEIndividual[] createSortedCopyOfCurrentPopulation() {
-        currentPopulationSorted = Arrays.copyOf(currentPopulation, currentPopulation.length);
-        Arrays.sort(currentPopulationSorted);
-        return currentPopulationSorted;
-    }
-
-    protected double generateMutationFactor(CauchyDistribution dist) {
-        double factor = dist.sample();
-        while (factor <= 0.0) { // || Double.isNaN(factor)) {
-            factor = dist.sample();
-        }
-        if (factor > 1.0) {
-            factor = 1.0;
-        }
-        assert (factor > 0.0 && factor <= 1.0);
-        return factor;
-    }
-
-    protected double generateCrossoverProbability(NormalDistribution dist) {
-        double prob = dist.sample();
-
-        // NOTE(Moravec): Sometimes dist.sample() returns NaN...
-        while (Double.isNaN(prob)) {
-            prob = dist.sample();
-        }
-
-
-        if (prob < 0.0) {
-            prob = 0.0;
-        } else if (prob > 1.0) {
-            prob = 1.0;
-        }
-        assert (prob >= 0.0 && prob <= 1.0);
-        return prob;
-    }
-
-    protected void assertPopulationSize() throws DeException {
-        if (populationSize < MINIMAL_POPULATION_SIZE) {
-            throw new DeException("Population size is too low. Required population size >= 5.");
-        }
-    }
-
-    protected void assertDimension() throws DeException {
-        if (dimensionCount < 1) {
-            throw new DeException("Dimension is too low. Required dimension >= 1.");
-        }
-    }
-
-    @Override
-    public void setMinimalValueConstraint(int min) {
-        minConstraint = min;
-    }
-
-    @Override
-    public void setMaximalValueConstraint(int max) {
-        maxConstraint = max;
-    }
-
-    @Override
-    public void setPopulationSize(int populationSize) throws DeException {
-        assertPopulationSize();
-        this.populationSize = populationSize;
-        currentPopulationSize = populationSize;
-    }
-
-    @Override
-    public void setGenerationCount(int generationCount) {
-        this.generationCount = generationCount;
-    }
-
-    @Override
-    public void setDimensionCount(int dimensionCount) {
-        this.dimensionCount = dimensionCount;
-    }
-
-    @Override
-    public void setTrainingData(int[] data) {
-        trainingData = data;
-    }
-
-    @Override
-    public IDEIndividual getBestSolution() {
-        return bestSolution;
-    }
-}
diff --git a/src/main/java/azgracompress/de/DESolverWithArchive.java b/src/main/java/azgracompress/de/DESolverWithArchive.java
deleted file mode 100644
index 3602614..0000000
--- a/src/main/java/azgracompress/de/DESolverWithArchive.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package azgracompress.de;
-
-import azgracompress.utilities.Utils;
-import org.apache.commons.math3.distribution.UniformIntegerDistribution;
-
-import java.util.ArrayList;
-import java.util.Random;
-
-public abstract class DESolverWithArchive extends DESolver {
-    protected int maxArchiveSize;
-    protected ArrayList<DEIndividual> archive;
-
-    protected DESolverWithArchive(int dimension, int currentPopulationSize, int generationCount, int maxArchiveSize) {
-        super(dimension, currentPopulationSize, generationCount);
-        this.maxArchiveSize = maxArchiveSize;
-        archive = new ArrayList<DEIndividual>(maxArchiveSize);
-    }
-
-
-    protected void truncateArchive() {
-        int deleteCount = archive.size() - maxArchiveSize;
-        if (deleteCount > 0) {
-            Random random = new Random();
-
-            for (int i = 0; i < deleteCount; i++) {
-                archive.remove(random.nextInt(archive.size()));
-            }
-        }
-        assert (archive.size() <= maxArchiveSize);
-    }
-
-
-    /**
-     * Get random individual (different from others) from union of current population and archive.
-     *
-     * @param rndUnionDist Random distribution for the union of current population and archive.
-     * @param others       Other individuals.
-     * @return Random individual from union of current population and archive.
-     */
-    protected DEIndividual getRandomFromPopulationAndArchive(UniformIntegerDistribution rndUnionDist,
-                                                           final DEIndividual... others) {
-        int rndIndex = rndUnionDist.sample();
-        DEIndividual rndIndiv = (rndIndex >= currentPopulationSize) ? archive.get(rndIndex - currentPopulationSize) : currentPopulation[rndIndex];
-        while (Utils.arrayContains(others, rndIndiv)) {
-            rndIndex = rndUnionDist.sample();
-            rndIndiv = (rndIndex >= currentPopulationSize) ? archive.get(rndIndex - currentPopulationSize) : currentPopulation[rndIndex];
-        }
-
-        return rndIndiv;
-    }
-
-    public int getMaxArchiveSize() {
-        return maxArchiveSize;
-    }
-
-    public void setMaxArchiveSize(int maxArchiveSize) {
-        this.maxArchiveSize = maxArchiveSize;
-    }
-}
diff --git a/src/main/java/azgracompress/de/DeException.java b/src/main/java/azgracompress/de/DeException.java
deleted file mode 100644
index e5d55fa..0000000
--- a/src/main/java/azgracompress/de/DeException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package azgracompress.de;
-
-public class DeException extends Exception {
-
-    public DeException(final String message) {
-        super(message);
-    }
-}
diff --git a/src/main/java/azgracompress/de/IDEIndividual.java b/src/main/java/azgracompress/de/IDEIndividual.java
deleted file mode 100644
index ca83fa4..0000000
--- a/src/main/java/azgracompress/de/IDEIndividual.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package azgracompress.de;
-
-import org.apache.commons.math3.distribution.UniformRealDistribution;
-
-public interface IDEIndividual {
-    double getFitness();
-
-    void setFitness(final double fitness);
-
-    boolean isFitnessCached();
-
-    double getCrossoverProbability();
-
-    void setCrossoverProbability(final double cr);
-
-    double getMutationFactor();
-
-    void setMutationFactor(final double f);
-
-    int[] getAttributes();
-
-    int getAttribute(final int dimension);
-
-    IDEIndividual createOffspringBinominalCrossover(final int[] mutationVector, final int jRand, UniformRealDistribution rndCrDist);
-}
diff --git a/src/main/java/azgracompress/de/IDESolver.java b/src/main/java/azgracompress/de/IDESolver.java
deleted file mode 100644
index 5eea7fa..0000000
--- a/src/main/java/azgracompress/de/IDESolver.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package azgracompress.de;
-
-import azgracompress.quantization.QTrainIteration;
-
-public interface IDESolver {
-
-    void setMinimalValueConstraint(final int min);
-
-    void setMaximalValueConstraint(final int max);
-
-    void setPopulationSize(final int populationSize) throws DeException;
-
-    void setGenerationCount(final int generationCount);
-
-    void setDimensionCount(final int dimensionCount);
-
-    QTrainIteration[] train() throws DeException;
-
-    void setTrainingData(final int[] data);
-
-    IDEIndividual getBestSolution();
-}
diff --git a/src/main/java/azgracompress/de/jade/JadeSolver.java b/src/main/java/azgracompress/de/jade/JadeSolver.java
deleted file mode 100644
index 5a4520b..0000000
--- a/src/main/java/azgracompress/de/jade/JadeSolver.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package azgracompress.de.jade;
-
-import azgracompress.U16;
-import azgracompress.de.DEIndividual;
-import azgracompress.de.DESolverWithArchive;
-import azgracompress.de.DeException;
-import azgracompress.quantization.QTrainIteration;
-import azgracompress.utilities.Means;
-import azgracompress.utilities.Stopwatch;
-import azgracompress.utilities.Utils;
-import org.apache.commons.math3.distribution.CauchyDistribution;
-import org.apache.commons.math3.distribution.NormalDistribution;
-import org.apache.commons.math3.distribution.UniformIntegerDistribution;
-import org.apache.commons.math3.distribution.UniformRealDistribution;
-import org.apache.commons.math3.random.MersenneTwister;
-import org.apache.commons.math3.random.RandomGenerator;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-public class JadeSolver extends DESolverWithArchive {
-    private double muCr = 0.5;
-    private double muF = 0.5;
-
-    private double parameterAdaptationRate = 0.05;
-    private double mutationGreediness = 0.1;
-
-    private DEIndividual[] currentPopulationSorted = null;
-
-
-    public JadeSolver(final int dimension, final int populationSize, final int generationCount) {
-
-        super(dimension, populationSize, generationCount, populationSize);
-    }
-
-    public JadeSolver(final int dimension, final int populationSize, final int generationCount,
-                      final double parameterAdaptationRate, final double mutationGreediness) {
-
-        this(dimension, populationSize, generationCount);
-        this.parameterAdaptationRate = parameterAdaptationRate;
-        this.mutationGreediness = mutationGreediness;
-    }
-
-    @Override
-    public QTrainIteration[] train() throws DeException {
-        final String delimiter = "-------------------------------------------";
-        QTrainIteration[] solutionHistory = new QTrainIteration[generationCount];
-        if (trainingData == null || trainingData.length <= 0) {
-            throw new DeException("Training data weren't set.");
-        }
-        muCr = 0.5;
-        muF = 0.5;
-
-        generateInitialPopulation();
-        double avgFitness = calculateFitnessForPopulationParallel(currentPopulation);
-        System.out.println(String.format("Generation %d average fitness(COST): %.5f", 0, avgFitness));
-
-        ArrayList<Double> successfulCr = new ArrayList<Double>();
-        ArrayList<Double> successfulF = new ArrayList<Double>();
-
-        Stopwatch stopwatch = new Stopwatch();
-        RandomGenerator rg = new MersenneTwister();
-
-        int pBestUpperLimit = (int) Math.floor(populationSize * mutationGreediness);
-        UniformIntegerDistribution rndPBestDist = new UniformIntegerDistribution(rg, 0, (pBestUpperLimit - 1));
-        UniformIntegerDistribution rndIndDist = new UniformIntegerDistribution(rg, 0, (populationSize - 1));
-        UniformIntegerDistribution rndJRandDist = new UniformIntegerDistribution(rg, 0, (dimensionCount - 1));
-        UniformRealDistribution rndCrDist = new UniformRealDistribution(rg, 0.0, 1.0);
-        DEIndividual[] offsprings = new DEIndividual[populationSize];
-
-        for (int generation = 0; generation < generationCount; generation++) {
-
-            stopwatch.restart();
-            StringBuilder generationLog = new StringBuilder(String.format("%s\nGeneration: %d\n", delimiter, (generation + 1)));
-            currentPopulationSorted = createSortedCopyOfCurrentPopulation();
-
-            successfulCr.clear();
-            successfulF.clear();
-
-
-            UniformIntegerDistribution rndPopArchiveDist =
-                    new UniformIntegerDistribution(rg, 0, ((populationSize - 1) + archive.size()));
-
-            NormalDistribution crNormalDistribution = new NormalDistribution(rg, muCr, 0.1);
-            CauchyDistribution fCauchyDistribution = new CauchyDistribution(rg, muF, 0.1);
-
-            for (int i = 0; i < populationSize; i++) {
-                DEIndividual current = currentPopulation[i];
-                current.setCrossoverProbability(generateCrossoverProbability(crNormalDistribution));
-                current.setMutationFactor(generateMutationFactor(fCauchyDistribution));
-
-                DEIndividual x_p_Best = getRandomFromPBest(rndPBestDist, current);
-                DEIndividual x_r1 = getRandomFromCurrentPopulation(rndIndDist, current, x_p_Best);
-                DEIndividual x_r2 = getRandomFromPopulationAndArchive(rndPopArchiveDist, current, x_p_Best, x_r1);
-
-                int[] mutationVector = createMutationVectorCurrentToPBest(current, x_p_Best, x_r1, x_r2);
-                int jRand = rndJRandDist.sample();
-                offsprings[i] = current.createOffspringBinominalCrossover(mutationVector, jRand, rndCrDist);
-            }
-
-            calculateFitnessForPopulationParallel(offsprings);
-
-            // NOTE(Moravec): We are minimalizing!
-            for (int i = 0; i < populationSize; i++) {
-                if (offsprings[i].getFitness() <= currentPopulation[i].getFitness()) {
-                    final DEIndividual old = currentPopulation[i];
-                    currentPopulation[i] = offsprings[i];
-                    successfulCr.add(old.getCrossoverProbability());
-                    successfulF.add(old.getMutationFactor());
-                    archive.add(old);
-                }
-            }
-
-            double oldMuCr = muCr, oldMuF = muF;
-            muCr = ((1.0 - parameterAdaptationRate) * muCr) + (parameterAdaptationRate * Means.arithmeticMean(successfulCr));
-            muF = ((1.0 - parameterAdaptationRate) * muF) + (parameterAdaptationRate * Means.lehmerMean(successfulF));
-
-            generationLog.append(String.format("|S_Cr| = %d  |S_F| = %d\n", successfulCr.size(), successfulF.size()));
-            generationLog.append(String.format("Old μCR: %.5f    New μCR: %.5f\nOld  μF: %.5f     New μF: %.5f\n",
-                    oldMuCr, muCr, oldMuF, muF));
-
-            truncateArchive();
-            generationLog.append(String.format("Archive size after truncate: %d\n", archive.size()));
-            avgFitness = getMseFromCalculatedFitness(currentPopulation);
-            stopwatch.stop();
-
-            final double currentBestFitness = currentPopulationSorted[0].getFitness();
-            final double avgPsnr = Utils.calculatePsnr(avgFitness, U16.Max);
-            generationLog.append("Current best fitness: ").append(currentBestFitness);
-            generationLog.append(String.format("\nAverage fitness(cost): %.6f\nIteration finished in: %d ms", avgFitness, stopwatch.totalElapsedMilliseconds()));
-
-            System.out.println(generationLog.toString());
-
-            solutionHistory[generation] = new QTrainIteration(generation, avgFitness, currentBestFitness, Utils.calculatePsnr(currentBestFitness, U16.Max), avgPsnr);
-        }
-
-        Arrays.sort(currentPopulationSorted);
-        bestSolution = currentPopulationSorted[0];
-
-        return solutionHistory;
-    }
-
-
-    public double getParameterAdaptationRate() {
-        return parameterAdaptationRate;
-    }
-
-    public void setParameterAdaptationRate(double parameterAdaptationRate) {
-        this.parameterAdaptationRate = parameterAdaptationRate;
-    }
-
-    public double getMutationGreediness() {
-        return mutationGreediness;
-    }
-
-    public void setMutationGreediness(double mutationGreediness) {
-        this.mutationGreediness = mutationGreediness;
-    }
-}
diff --git a/src/main/java/azgracompress/de/jade/RunnablePopulationFitness.java b/src/main/java/azgracompress/de/jade/RunnablePopulationFitness.java
deleted file mode 100644
index 2ab7c0c..0000000
--- a/src/main/java/azgracompress/de/jade/RunnablePopulationFitness.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package azgracompress.de.jade;
-
-import azgracompress.quantization.scalar.ScalarQuantizer;
-import azgracompress.de.DEIndividual;
-
-public class RunnablePopulationFitness implements Runnable {
-
-    private double mse = 0.0;
-    private final int[] testData;
-    private final DEIndividual[] population;
-    private final int fromIndex;
-    private final int toIndex;
-
-    public RunnablePopulationFitness(final int[] testData, final DEIndividual[] population, final int popFrom, final int popTo) {
-        this.testData = testData;
-        this.population = population;
-        this.fromIndex = popFrom;
-        this.toIndex = popTo;
-    }
-
-    @Override
-    public void run() {
-        double mse = 0.0;
-        for (int i = fromIndex; i < toIndex; i++) {
-
-            double indivMse;
-            if (population[i].isFitnessCached()) {
-                indivMse = population[i].getFitness();
-            } else {
-                ScalarQuantizer quantizer = new ScalarQuantizer(0, 0xffff, population[i].getAttributes());
-                indivMse = quantizer.getMse(testData);
-            }
-            population[i].setFitness(indivMse);
-            mse += indivMse;
-        }
-        this.mse = mse;
-    }
-
-    public double getTotalMse() {
-        return mse;
-    }
-
-    public double getAvgMse() {
-        return (mse / (double) (toIndex - fromIndex));
-    }
-
-}
diff --git a/src/main/java/azgracompress/de/shade/ILShadeSolver.java b/src/main/java/azgracompress/de/shade/ILShadeSolver.java
deleted file mode 100644
index 34add6b..0000000
--- a/src/main/java/azgracompress/de/shade/ILShadeSolver.java
+++ /dev/null
@@ -1,198 +0,0 @@
-package azgracompress.de.shade;
-
-import azgracompress.U16;
-import azgracompress.de.DeException;
-import azgracompress.quantization.QTrainIteration;
-import azgracompress.utilities.Means;
-import azgracompress.utilities.Utils;
-import org.apache.commons.math3.distribution.UniformIntegerDistribution;
-import org.apache.commons.math3.distribution.UniformRealDistribution;
-import org.apache.commons.math3.random.MersenneTwister;
-import org.apache.commons.math3.random.RandomGenerator;
-import azgracompress.de.DEIndividual;
-import azgracompress.utilities.Stopwatch;
-
-import java.util.ArrayList;
-
-public class ILShadeSolver extends LShadeSolver {
-
-    private double currentMutationGreediness;
-    private double minMutationGreediness = 0.1;
-
-    public ILShadeSolver(int dimension, int populationSize, int generationCount, int memorySize) {
-        super(dimension, populationSize, generationCount, memorySize);
-
-        maxMutationGreediness = 0.2;
-        minMutationGreediness = 0.1;
-        currentMutationGreediness = maxMutationGreediness;
-    }
-
-    @SuppressWarnings("DuplicatedCode")
-    @Override
-    public QTrainIteration[] train() throws DeException {
-        final String delimiter = "-------------------------------------------";
-        int maxNfe = (populationSize * generationCount);
-        int nfe = 0;
-        QTrainIteration[] solutionHistory = new QTrainIteration[generationCount];
-
-        RandomGenerator rg = new MersenneTwister();
-        initializeMemory(0.8, 0.5);
-        ArrayList<Double> successfulCr = new ArrayList<Double>();
-        ArrayList<Double> successfulF = new ArrayList<Double>();
-        ArrayList<Double> absDelta = new ArrayList<Double>();
-
-        generateInitialPopulation();
-        double averageMSE = calculateFitnessForPopulationParallel(currentPopulation);
-
-        UniformIntegerDistribution memoryIndexDist = new UniformIntegerDistribution(rg, 0, (memorySize - 1));
-        UniformIntegerDistribution jRandDist = new UniformIntegerDistribution(rg, 0, (dimensionCount - 1));
-        UniformRealDistribution crDist = new UniformRealDistribution(rg, 0.0, 1.0);
-
-        Stopwatch stopwatch = new Stopwatch();
-
-        for (int generation = 0; generation < generationCount; generation++) {
-            stopwatch.restart();
-
-            successfulCr.clear();
-            successfulF.clear();
-            absDelta.clear();
-            StringBuilder generationLog = new StringBuilder(String.format("%s\niL-SHADE\nGeneration: %d\n", delimiter, (generation + 1)));
-
-            currentPopulationSorted = createSortedCopyOfCurrentPopulation();
-            DEIndividual[] offsprings = new DEIndividual[currentPopulationSize];
-
-            UniformIntegerDistribution rndPopArchiveDist =
-                    new UniformIntegerDistribution(rg, 0, ((currentPopulationSize - 1) + archive.size()));
-            int pBestUpperLimit = (int) Math.floor(currentPopulationSize * currentMutationGreediness);
-            UniformIntegerDistribution rndPBestDist = new UniformIntegerDistribution(rg, 0, (pBestUpperLimit - 1));
-            UniformIntegerDistribution rndIndDist = new UniformIntegerDistribution(rg, 0, (currentPopulationSize - 1));
-
-            for (int i = 0; i < currentPopulationSize; i++) {
-                int randomMemIndex = memoryIndexDist.sample();
-                if (randomMemIndex == (memorySize - 1)) {
-                    memoryCr[randomMemIndex] = 0.9;
-                    memoryF[randomMemIndex] = 0.9;
-                }
-                currentPopulation[i].setCrossoverProbability(iLShadeGenerateCrossoverProbability(randomMemIndex, generation));
-                currentPopulation[i].setMutationFactor(iLShadeGenerateMutationFactor(randomMemIndex, generation));
-
-                DEIndividual x_p_Best = getRandomFromPBest(rndPBestDist, currentPopulation[i]);
-                DEIndividual x_r1 = getRandomFromCurrentPopulation(rndIndDist, currentPopulation[i], x_p_Best);
-                DEIndividual x_r2 = getRandomFromPopulationAndArchive(rndPopArchiveDist, currentPopulation[i], x_p_Best, x_r1);
-
-                final int[] mutationVector = createMutationVectorCurrentToPBest(currentPopulation[i], x_p_Best, x_r1, x_r2);
-                offsprings[i] = currentPopulation[i].createOffspringBinominalCrossover(mutationVector, jRandDist.sample(), crDist);
-            }
-
-            calculateFitnessForPopulationParallel(offsprings);
-            nfe += currentPopulationSize;
-
-            DEIndividual[] nextPopulation = new DEIndividual[currentPopulationSize];
-            // NOTE(Moravec): We are minimalizing!
-            for (int i = 0; i < currentPopulationSize; i++) {
-                final DEIndividual old = currentPopulation[i];
-                if (offsprings[i].getFitness() <= old.getFitness()) {
-                    nextPopulation[i] = offsprings[i];
-
-                    if (offsprings[i].getFitness() < old.getFitness()) {
-                        archive.add(old);
-                        absDelta.add(Math.abs(offsprings[i].getFitness() - currentPopulation[i].getFitness()));
-                        successfulCr.add(old.getCrossoverProbability());
-                        successfulF.add(old.getMutationFactor());
-                    }
-                } else {
-                    nextPopulation[i] = currentPopulation[i];
-                }
-            }
-
-            updateMemory(successfulCr, successfulF, absDelta);
-            currentPopulation = nextPopulation;
-            applyLinearReductionOfPopulationSize(nfe, maxNfe);
-            truncateArchive();
-            updateMutationGreediness(nfe, maxNfe);
-
-            averageMSE = getMseFromCalculatedFitness(currentPopulation);
-
-            // NOTE(Moravec): After LRPS the population is sorted according.
-            final double bestMSE = currentPopulation[0].getFitness();
-            final double bestPSNR = Utils.calculatePsnr(bestMSE, U16.Max);
-            final double averagePSNR = Utils.calculatePsnr(averageMSE, U16.Max);
-
-            solutionHistory[generation] = new QTrainIteration(generation, averageMSE, bestMSE, averagePSNR, bestPSNR);
-
-            stopwatch.stop();
-
-            generationLog.append(String.format("Current population size: %d\n", currentPopulationSize));
-            generationLog.append(String.format("Mutation greediness: %.5f\n", currentMutationGreediness));
-            generationLog.append(String.format("Current best fitness: %.5f Current PSNR: %.5f dB", bestMSE, bestPSNR));
-            generationLog.append(String.format("\nAvg. cost(after LPSR): %.6f\nAvg. PSNR (after LPSR): %.6f dB\nIteration finished in: %d ms", averageMSE, averagePSNR, stopwatch.totalElapsedMilliseconds()));
-            System.out.println(generationLog.toString());
-        }
-
-        return solutionHistory;
-    }
-
-    private double iLShadeGenerateCrossoverProbability(final int memIndex, final int currentGeneration) {
-        double cr = generateCrossoverProbability(memIndex);
-        if ((double) currentGeneration < (0.25 * (double) generationCount)) {
-            cr = Math.max(cr, 0.5);
-        } else if ((double) currentGeneration < (0.5 * (double) generationCount)) {
-            cr = Math.max(cr, 0.25);
-        }
-        return cr;
-    }
-
-
-    private double iLShadeGenerateMutationFactor(final int memIndex, final int currentGeneration) {
-        double f = generateMutationFactor(memIndex);
-        if ((double) currentGeneration < (0.25 * (double) generationCount)) {
-            f = Math.max(f, 0.7);
-        } else if ((double) currentGeneration < (0.5 * (double) generationCount)) {
-            f = Math.max(f, 0.8);
-        } else if ((double) currentGeneration < (0.75 * (double) generationCount)) {
-            f = Math.max(f, 0.9);
-        }
-        return f;
-    }
-
-    private void updateMutationGreediness(final int nfes, final int maxNfes) {
-        currentMutationGreediness = (((maxMutationGreediness - minMutationGreediness) / (double) maxNfes) * nfes) + minMutationGreediness;
-    }
-
-    @Override
-    protected void updateMemory(final ArrayList<Double> successfulCr,
-                                final ArrayList<Double> successfulF,
-                                final ArrayList<Double> absDelta) {
-
-        if ((!successfulCr.isEmpty()) && (!successfulF.isEmpty())) {
-            assert ((absDelta.size() == successfulCr.size()) && (successfulCr.size() == successfulF.size()));
-
-            double[] weights = calculateLehmerWeihts(absDelta);
-
-            if ((Double.isNaN(memoryCr[memoryIndex])) || (Utils.arrayListMax(successfulCr) == 0)) {
-                memoryCr[memoryIndex] = Double.NaN;
-            } else {
-                memoryCr[memoryIndex] = ((Means.weightedLehmerMean(successfulCr, weights) + memoryCr[memoryIndex]) / 2.0);
-            }
-            memoryF[memoryIndex] = ((Means.weightedLehmerMean(successfulF, weights) + memoryF[memoryIndex]) / 2.0);
-            ++memoryIndex;
-            if (memoryIndex >= memorySize) {
-                memoryIndex = 0;
-            }
-        }
-    }
-
-    public double getMinMutationGreediness() {
-        return minMutationGreediness;
-    }
-
-    public void setMinMutationGreediness(double minMutationGreediness) {
-        this.minMutationGreediness = minMutationGreediness;
-    }
-
-    @Override
-    public void setMaxMutationGreediness(double maxMutationGreediness) {
-        super.setMaxMutationGreediness(maxMutationGreediness);
-        currentMutationGreediness = maxMutationGreediness;
-    }
-}
diff --git a/src/main/java/azgracompress/de/shade/LShadeSolver.java b/src/main/java/azgracompress/de/shade/LShadeSolver.java
deleted file mode 100644
index dc74533..0000000
--- a/src/main/java/azgracompress/de/shade/LShadeSolver.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package azgracompress.de.shade;
-
-import azgracompress.U16;
-import azgracompress.de.DeException;
-import azgracompress.quantization.QTrainIteration;
-import azgracompress.utilities.Utils;
-import org.apache.commons.math3.distribution.CauchyDistribution;
-import org.apache.commons.math3.distribution.NormalDistribution;
-import org.apache.commons.math3.distribution.UniformIntegerDistribution;
-import org.apache.commons.math3.distribution.UniformRealDistribution;
-import org.apache.commons.math3.random.MersenneTwister;
-import org.apache.commons.math3.random.RandomGenerator;
-import azgracompress.de.DEIndividual;
-import azgracompress.de.DESolverWithArchive;
-import azgracompress.utilities.Means;
-import azgracompress.utilities.Stopwatch;
-
-import java.util.ArrayList;
-
-public class LShadeSolver extends DESolverWithArchive {
-    protected int memorySize;
-    protected int memoryIndex = 0;
-    protected double[] memoryCr;
-    protected double[] memoryF;
-
-    protected double maxMutationGreediness = 0.1;
-
-    protected int minimalPopulationSize = MINIMAL_POPULATION_SIZE;
-
-    public LShadeSolver(int dimension, int populationSize, int generationCount, int memorySize) {
-        super(dimension, populationSize, generationCount, populationSize);
-        this.memorySize = memorySize;
-    }
-
-    protected void initializeMemory(final double initialCrValue, final double initialFValue) {
-        memoryIndex = 0;
-        memoryCr = new double[memorySize];
-        memoryF = new double[memorySize];
-        for (int memIndex = 0; memIndex < memorySize; memIndex++) {
-            memoryCr[memIndex] = initialCrValue;
-            memoryF[memIndex] = initialFValue;
-        }
-    }
-
-
-    protected double generateCrossoverProbability(final int memIndex) {
-        double memCr = memoryCr[memIndex];
-        if (Double.isNaN(memCr)) {
-            return 0.0;
-        } else {
-            return generateCrossoverProbability(new NormalDistribution(memCr, 0.1));
-        }
-    }
-
-    protected double generateMutationFactor(final int memIndex) {
-        return generateMutationFactor(new CauchyDistribution(memoryF[memIndex], 0.1));
-    }
-
-    @Override
-    public QTrainIteration[] train() throws DeException {
-        final String delimiter = "-------------------------------------------";
-        int maxNfe = (populationSize * generationCount);
-        int nfe = 0;
-        QTrainIteration[] solutionHistory = new QTrainIteration[generationCount];
-
-        RandomGenerator rg = new MersenneTwister();
-        initializeMemory(0.5, 0.5);
-        ArrayList<Double> successfulCr = new ArrayList<Double>();
-        ArrayList<Double> successfulF = new ArrayList<Double>();
-        ArrayList<Double> absDelta = new ArrayList<Double>();
-
-        generateInitialPopulation();
-        double averageMSE = calculateFitnessForPopulationParallel(currentPopulation);
-
-        UniformIntegerDistribution memoryIndexDist = new UniformIntegerDistribution(rg, 0, (memorySize - 1));
-        UniformIntegerDistribution jRandDist = new UniformIntegerDistribution(rg, 0, (dimensionCount - 1));
-        UniformRealDistribution crDist = new UniformRealDistribution(rg, 0.0, 1.0);
-
-        Stopwatch stopwatch = new Stopwatch();
-
-        for (int generation = 0; generation < generationCount; generation++) {
-            stopwatch.restart();
-
-            successfulCr.clear();
-            successfulF.clear();
-            absDelta.clear();
-            StringBuilder generationLog = new StringBuilder(String.format("%s\nGeneration: %d\n", delimiter, (generation + 1)));
-
-            currentPopulationSorted = createSortedCopyOfCurrentPopulation();
-            DEIndividual[] offsprings = new DEIndividual[currentPopulationSize];
-
-            UniformIntegerDistribution rndPopArchiveDist =
-                    new UniformIntegerDistribution(rg, 0, ((currentPopulationSize - 1) + archive.size()));
-            int pBestUpperLimit = (int) Math.floor(currentPopulationSize * maxMutationGreediness);
-            UniformIntegerDistribution rndPBestDist = new UniformIntegerDistribution(rg, 0, (pBestUpperLimit - 1));
-            UniformIntegerDistribution rndIndDist = new UniformIntegerDistribution(rg, 0, (currentPopulationSize - 1));
-
-            for (int i = 0; i < currentPopulationSize; i++) {
-                int randomMemIndex = memoryIndexDist.sample();
-                currentPopulation[i].setCrossoverProbability(generateCrossoverProbability(randomMemIndex));
-                currentPopulation[i].setMutationFactor(generateMutationFactor(randomMemIndex));
-
-                DEIndividual x_p_Best = getRandomFromPBest(rndPBestDist, currentPopulation[i]);
-                DEIndividual x_r1 = getRandomFromCurrentPopulation(rndIndDist, currentPopulation[i], x_p_Best);
-                DEIndividual x_r2 = getRandomFromPopulationAndArchive(rndPopArchiveDist, currentPopulation[i], x_p_Best, x_r1);
-
-                final int[] mutationVector = createMutationVectorCurrentToPBest(currentPopulation[i], x_p_Best, x_r1, x_r2);
-                offsprings[i] = currentPopulation[i].createOffspringBinominalCrossover(mutationVector, jRandDist.sample(), crDist);
-            }
-
-            calculateFitnessForPopulationParallel(offsprings);
-            nfe += currentPopulationSize;
-            DEIndividual[] nextPopulation = new DEIndividual[currentPopulationSize];
-
-            // NOTE(Moravec): We are minimalizing!
-            for (int i = 0; i < currentPopulationSize; i++) {
-                final DEIndividual old = currentPopulation[i];
-                if (offsprings[i].getFitness() <= old.getFitness()) {
-                    nextPopulation[i] = offsprings[i];
-
-                    if (offsprings[i].getFitness() < old.getFitness()) {
-                        archive.add(old);
-                        absDelta.add(Math.abs(offsprings[i].getFitness() - currentPopulation[i].getFitness()));
-                        successfulCr.add(old.getCrossoverProbability());
-                        successfulF.add(old.getMutationFactor());
-                    }
-                } else {
-                    nextPopulation[i] = currentPopulation[i];
-                }
-            }
-
-            updateMemory(successfulCr, successfulF, absDelta);
-            currentPopulation = nextPopulation;
-            applyLinearReductionOfPopulationSize(nfe, maxNfe);
-            truncateArchive();
-            averageMSE = getMseFromCalculatedFitness(currentPopulation);
-
-            // NOTE(Moravec): After LRPS the population is sorted according.
-            final double bestMSE = currentPopulation[0].getFitness();
-            final double bestPSNR = Utils.calculatePsnr(bestMSE, U16.Max);
-            final double averagePSNR = Utils.calculatePsnr(averageMSE, U16.Max);
-
-            solutionHistory[generation] = new QTrainIteration(generation, averageMSE, bestMSE, averagePSNR, bestPSNR);
-
-            stopwatch.stop();
-
-            generationLog.append(String.format("Archive size after truncate: %d\n", archive.size()));
-            generationLog.append(String.format("Current population size: %d\n", currentPopulationSize));
-            generationLog.append(String.format("Current best fitness: %.5f Current PSNR: %.5f dB", bestMSE, bestPSNR));
-            generationLog.append(String.format("\nAvg. cost(after LPSR): %.6f\nAvg. PSNR (after LPSR): %.6f dB\nIteration finished in: %d ms", averageMSE, averagePSNR, stopwatch.totalElapsedMilliseconds()));
-            System.out.println(generationLog.toString());
-        }
-
-        return solutionHistory;
-    }
-
-    protected void applyLinearReductionOfPopulationSize(final int nfe, final int maxNfe) {
-        final int oldPopulationSize = currentPopulationSize;
-        currentPopulationSize = getNewPopulationSize(nfe, maxNfe);
-        maxArchiveSize = currentPopulationSize;
-        if (currentPopulationSize < oldPopulationSize) {
-            DEIndividual[] reducedPopulation = new DEIndividual[currentPopulationSize];
-            System.arraycopy(currentPopulationSorted, 0, reducedPopulation, 0, currentPopulationSize);
-            currentPopulation = reducedPopulation;
-        }
-    }
-
-    private int getNewPopulationSize(final int nfe, final int maxNfe) {
-        int newPopulationSize = (int) Math.round(((((double) minimalPopulationSize - (double) populationSize) / (double) maxNfe) * (double) nfe) + (double) populationSize);
-        return newPopulationSize;
-    }
-
-    protected double[] calculateLehmerWeihts(final ArrayList<Double> absDelta) {
-        int kCount = absDelta.size();
-        double[] weights = new double[kCount];
-        for (int k = 0; k < kCount; k++) {
-
-            final double numerator = absDelta.get(k);
-            final double denominator = Utils.arrayListSum(absDelta);
-            weights[k] = (numerator / denominator);
-        }
-        return weights;
-    }
-
-    protected void updateMemory(final ArrayList<Double> successfulCr,
-                                final ArrayList<Double> successfulF,
-                                final ArrayList<Double> absDelta) {
-
-        if ((!successfulCr.isEmpty()) && (!successfulF.isEmpty())) {
-            assert ((absDelta.size() == successfulCr.size()) && (successfulCr.size() == successfulF.size()));
-            double[] weights = calculateLehmerWeihts(absDelta);
-
-            if ((Double.isNaN(memoryCr[memoryIndex])) || (Utils.arrayListMax(successfulCr) == 0)) {
-                memoryCr[memoryIndex] = Double.NaN;
-            } else {
-                memoryCr[memoryIndex] = Means.weightedLehmerMean(successfulCr, weights);
-            }
-            memoryF[memoryIndex] = Means.weightedLehmerMean(successfulF, weights);
-            ++memoryIndex;
-            if (memoryIndex >= memorySize) {
-                memoryIndex = 0;
-            }
-        }
-
-//        StringBuilder sb = new StringBuilder();
-//        sb.append("MEMORY F: ");
-//        for (int i = 0; i < memoryF.length; i++) {
-//            sb.append(String.format("%.5f    ", memoryF[i]));
-//        }
-//        sb.append("\n");
-//        sb.append("MEMORY Cr: ");
-//        for (int i = 0; i < memoryCr.length; i++) {
-//            sb.append(String.format("%.5f    ", memoryCr[i]));
-//        }
-//        System.out.println(sb.toString());
-    }
-
-    public int getMemorySize() {
-        return memorySize;
-    }
-
-    public void setMemorySize(final int memorySize) {
-        this.memorySize = memorySize;
-    }
-
-    public void setMaxMutationGreediness(final double maxMutationGreediness) {
-        this.maxMutationGreediness = maxMutationGreediness;
-    }
-
-    public double getMaxMutationGreediness() {
-        return maxMutationGreediness;
-    }
-
-    public int getMinimalPopulationSize() {
-        return minimalPopulationSize;
-    }
-
-    public void setMinimalPopulationSize(int minimalPopulationSize) {
-        this.minimalPopulationSize = minimalPopulationSize;
-    }
-}
diff --git a/src/main/java/azgracompress/quantization/QuantizationValueCache.java b/src/main/java/azgracompress/quantization/QuantizationValueCache.java
index 78797c3..bc33702 100644
--- a/src/main/java/azgracompress/quantization/QuantizationValueCache.java
+++ b/src/main/java/azgracompress/quantization/QuantizationValueCache.java
@@ -5,6 +5,9 @@ import azgracompress.quantization.vector.CodebookEntry;
 
 import java.io.*;
 
+// TODO(Moravec):   If we want to use Huffman codes we have to save additional information with the codebook.
+//                  This information can be probability or the absolute frequencies of codebook indices.
+
 public class QuantizationValueCache {
 
     private final String cacheFolder;
diff --git a/src/main/java/azgracompress/quantization/scalar/LloydMaxU16ScalarQuantization.java b/src/main/java/azgracompress/quantization/scalar/LloydMaxU16ScalarQuantization.java
index 4179ac8..a625461 100644
--- a/src/main/java/azgracompress/quantization/scalar/LloydMaxU16ScalarQuantization.java
+++ b/src/main/java/azgracompress/quantization/scalar/LloydMaxU16ScalarQuantization.java
@@ -7,6 +7,7 @@ import azgracompress.utilities.Stopwatch;
 import azgracompress.utilities.Utils;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 
 public class LloydMaxU16ScalarQuantization {
@@ -15,9 +16,11 @@ public class LloydMaxU16ScalarQuantization {
 
     private int dataMin;
     private int dataMax;
-    private int dataSpan;
+
+    private long[] frequencies;
     private int[] centroids;
     private int[] boundaryPoints;
+
     private double[] pdf;
 
     private final int workerCount;
@@ -35,6 +38,7 @@ public class LloydMaxU16ScalarQuantization {
     }
 
     private void initialize() {
+        frequencies = new long[codebookSize];
         centroids = new int[codebookSize];
 
         boundaryPoints = new int[codebookSize + 1];
@@ -42,7 +46,7 @@ public class LloydMaxU16ScalarQuantization {
         MinMaxResult<Integer> minMax = Utils.getMinAndMax(trainingData);
         dataMin = minMax.getMin();
         dataMax = minMax.getMax();
-        dataSpan = dataMax - dataMin;
+        final int dataSpan = dataMax - dataMin;
         centroids[0] = dataMin;
 
         boundaryPoints[0] = dataMin;
@@ -59,8 +63,8 @@ public class LloydMaxU16ScalarQuantization {
         Stopwatch s = new Stopwatch();
         s.start();
 
-        for (int i = 0; i < trainingData.length; i++) {
-            pdf[trainingData[i]] += 1.0;
+        for (final int trainingDatum : trainingData) {
+            pdf[trainingDatum] += 1.0;
         }
 
         s.stop();
@@ -120,24 +124,23 @@ public class LloydMaxU16ScalarQuantization {
     public int quantize(final int value) {
         for (int intervalId = 1; intervalId <= codebookSize; intervalId++) {
             if ((value >= boundaryPoints[intervalId - 1]) && (value <= boundaryPoints[intervalId])) {
+                ++frequencies[intervalId - 1];
                 return centroids[intervalId - 1];
             }
         }
         throw new RuntimeException("Value couldn't be quantized!");
     }
 
-    private double calculateMAE() {
-        double mae = 0.0;
-        for (final int trainingDatum : trainingData) {
-            int quantizedValue = quantize(trainingDatum);
-            mae += Math.abs((double) trainingDatum - (double) quantizedValue);
-        }
-        return (mae / (double) trainingData.length);
+    /**
+     * Reset the frequencies array to zeros.
+     */
+    private void resetFrequencies() {
+        Arrays.fill(frequencies, 0);
     }
 
-
     private double getCurrentMse() {
         double mse = 0.0;
+        resetFrequencies();
 
         Stopwatch s = new Stopwatch();
         s.start();
@@ -163,6 +166,7 @@ public class LloydMaxU16ScalarQuantization {
             try {
                 for (int wId = 0; wId < workerCount; wId++) {
                     workers[wId].join();
+                    addWorkerFrequencies(runnables[wId].getFrequencies());
                     mse += runnables[wId].getMse();
                 }
             } catch (InterruptedException e) {
@@ -184,6 +188,13 @@ public class LloydMaxU16ScalarQuantization {
         return mse;
     }
 
+    private void addWorkerFrequencies(final long[] workerFrequencies) {
+        assert (frequencies.length == workerFrequencies.length) : "Frequency array length mismatch.";
+        for (int i = 0; i < frequencies.length; i++) {
+            frequencies[i] += workerFrequencies[i];
+        }
+    }
+
     public QTrainIteration[] train(final boolean shouldBeVerbose) {
         this.verbose = shouldBeVerbose;
         final int RECALCULATE_N_TIMES = 10;
@@ -224,21 +235,15 @@ public class LloydMaxU16ScalarQuantization {
                 recalculateCentroids();
             }
 
-            currMAE = calculateMAE();
-
             prevMse = currentMse;
             currentMse = getCurrentMse();
             mseImprovement = prevMse - currentMse;
 
-            //            System.out.println(String.format("Improvement: %.4f", mseImprovement));
-
             psnr = Utils.calculatePsnr(currentMse, U16.Max);
             solutionHistory.add(new QTrainIteration(++iteration, currentMse, currentMse, psnr, psnr));
-            //            dist = (prevMse - currentMse) / currentMse;
 
             if (verbose) {
-                System.out.println(String.format("Current MAE: %.4f MSE: %.4f PSNR: %.4f dB",
-                                                 currMAE,
+                System.out.println(String.format("Current MSE: %.4f PSNR: %.4f dB",
                                                  currentMse,
                                                  psnr));
             }
@@ -260,5 +265,9 @@ public class LloydMaxU16ScalarQuantization {
     public int[] getCentroids() {
         return centroids;
     }
+
+    public ScalarQuantizationCodebook getCodebook() {
+        return new ScalarQuantizationCodebook(centroids, frequencies);
+    }
 }
 
diff --git a/src/main/java/azgracompress/quantization/scalar/RunnableLloydMseCalc.java b/src/main/java/azgracompress/quantization/scalar/RunnableLloydMseCalc.java
index 31d9d73..d4bdc58 100644
--- a/src/main/java/azgracompress/quantization/scalar/RunnableLloydMseCalc.java
+++ b/src/main/java/azgracompress/quantization/scalar/RunnableLloydMseCalc.java
@@ -8,6 +8,7 @@ public class RunnableLloydMseCalc implements Runnable {
     final int[] boundaryPoints;
     final int codebookSize;
     double mse = 0.0;
+    final long[] frequencies;
 
     public RunnableLloydMseCalc(int[] trainingData, int fromIndex, int toIndex, int[] centroids, int[] boundaryPoints,
                                 final int codebookSize) {
@@ -17,13 +18,16 @@ public class RunnableLloydMseCalc implements Runnable {
         this.centroids = centroids;
         this.boundaryPoints = boundaryPoints;
         this.codebookSize = codebookSize;
+        this.frequencies = new long[centroids.length];
     }
 
+    public long[] getFrequencies() {
+        return frequencies;
+    }
 
     @Override
     public void run() {
         mse = 0.0;
-
         for (int i = fromIndex; i < toIndex; i++) {
             mse += Math.pow((double) trainingData[i] - (double) quantize(trainingData[i]), 2);
         }
@@ -36,6 +40,7 @@ public class RunnableLloydMseCalc implements Runnable {
     private int quantize(final int value) {
         for (int intervalId = 1; intervalId <= codebookSize; intervalId++) {
             if ((value >= boundaryPoints[intervalId - 1]) && (value <= boundaryPoints[intervalId])) {
+                ++frequencies[intervalId - 1];
                 return centroids[intervalId - 1];
             }
         }
diff --git a/src/main/java/azgracompress/quantization/scalar/ScalarQuantizationCodebook.java b/src/main/java/azgracompress/quantization/scalar/ScalarQuantizationCodebook.java
new file mode 100644
index 0000000..82dc681
--- /dev/null
+++ b/src/main/java/azgracompress/quantization/scalar/ScalarQuantizationCodebook.java
@@ -0,0 +1,38 @@
+package azgracompress.quantization.scalar;
+
+
+public class ScalarQuantizationCodebook {
+    /**
+     * Quantization values.
+     */
+    final int[] centroids;
+
+    /**
+     * Absolute frequencies of quantization values.
+     */
+    final long[] indexFrequencies;
+
+    final int codebookSize;
+
+    /**
+     * @param centroids        Quantization values.
+     * @param indexFrequencies Absolute frequencies of quantization values.
+     */
+    public ScalarQuantizationCodebook(final int[] centroids, final long[] indexFrequencies) {
+        this.centroids = centroids;
+        this.indexFrequencies = indexFrequencies;
+        this.codebookSize = this.centroids.length;
+    }
+
+    public int[] getCentroids() {
+        return centroids;
+    }
+
+    public long[] getIndicesFrequency() {
+        return indexFrequencies;
+    }
+
+    public int getCodebookSize() {
+        return codebookSize;
+    }
+}
diff --git a/src/main/java/azgracompress/quantization/scalar/ScalarQuantizer.java b/src/main/java/azgracompress/quantization/scalar/ScalarQuantizer.java
index 8c1b90b..58c80f1 100644
--- a/src/main/java/azgracompress/quantization/scalar/ScalarQuantizer.java
+++ b/src/main/java/azgracompress/quantization/scalar/ScalarQuantizer.java
@@ -3,12 +3,12 @@ package azgracompress.quantization.scalar;
 public class ScalarQuantizer {
     private final int min;
     private final int max;
-    private int[] centroids;
+    private final ScalarQuantizationCodebook codebook;
     private int[] boundaryPoints;
 
-    public ScalarQuantizer(final int min, final int max, final int[] centroids) {
-        this.centroids = centroids;
-        boundaryPoints = new int[centroids.length + 1];
+    public ScalarQuantizer(final int min, final int max, final ScalarQuantizationCodebook codebook) {
+        this.codebook = codebook;
+        boundaryPoints = new int[codebook.getCodebookSize() + 1];
         this.min = min;
         this.max = max;
 
@@ -63,14 +63,15 @@ public class ScalarQuantizer {
 
     private void calculateBoundaryPoints() {
         boundaryPoints[0] = min;
-        boundaryPoints[centroids.length] = max;
+        boundaryPoints[codebook.getCodebookSize()] = max;
+        final int[] centroids = codebook.getCentroids();
         for (int j = 1; j < centroids.length; j++) {
-            boundaryPoints[j] = (this.centroids[j] + this.centroids[j - 1]) / 2;
+            boundaryPoints[j] = (centroids[j] + centroids[j - 1]) / 2;
         }
     }
 
     public int quantizeIndex(final int value) {
-        for (int intervalId = 1; intervalId <= centroids.length; intervalId++) {
+        for (int intervalId = 1; intervalId <= codebook.getCodebookSize(); intervalId++) {
             if ((value >= boundaryPoints[intervalId - 1]) && (value <= boundaryPoints[intervalId])) {
                 return (intervalId - 1);
             }
@@ -79,7 +80,7 @@ public class ScalarQuantizer {
     }
 
     public int quantize(final int value) {
-        return centroids[quantizeIndex(value)];
+        return codebook.getCentroids()[quantizeIndex(value)];
     }
 
     public double getMse(final int[] data) {
@@ -93,6 +94,6 @@ public class ScalarQuantizer {
     }
 
     public int[] getCentroids() {
-        return centroids;
+        return codebook.getCentroids();
     }
 }
-- 
GitLab