    public <E extends Entity> List<E> crossover(List<E> parentCollection) {
        checkState(parentCollection.size() >= 3, "There must be a minimum of three parents to perform UNDX crossover.");
        checkState(numberOfOffspring.getParameter() > 0, "At least one offspring must be generated. Check 'numberOfOffspring'.");
        List<Vector> solutions = Entities.<Vector, E>getCandidateSolutions(parentCollection);
        List<E> offspring = Lists.newArrayListWithCapacity((int) numberOfOffspring.getParameter());
        UniformDistribution randomParent = new UniformDistribution();
        final int k = solutions.size();
        final int n = solutions.get(0).size();
        for (int os = 0; os < numberOfOffspring.getParameter(); os++) {
            int parent = (int) randomParent.getRandomNumber(0.0, k);
            Collections.swap(solutions, parent, k - 1);
            List<Vector> e_zeta = new ArrayList<>();
            Vector g = Vectors.mean(fj.data.List.iterableList(solutions.subList(0, k - 1))).valueE("Error in getting mean");
            for (int i = 0; i < k - 1; i++) {
                Vector d = solutions.get(i).subtract(g);
                if (!d.isZero()) {
                    double dbar = d.length();
                    Vector e = d.orthogonalize(e_zeta);
                    if (!e.isZero()) {
                        e_zeta.add(e.normalize().multiply(dbar));
                    }
                }
            }
            final double D = solutions.get(k - 1).subtract(g).length();
            fj.data.List<Vector> e_eta = fj.data.List.nil();
            e_eta.snoc(solutions.get(k - 1).subtract(g));
            for (int i = 0; i < n - e_zeta.size() - 1; i++) {
                Vector d = Vector.newBuilder().copyOf(g).buildRandom();
                e_eta.snoc(d);
            }
            e_eta = Vectors.orthonormalize(fj.data.List.iterableList(e_eta));
            Vector variables = Vector.copyOf(g);
            if (!useIndividualProviders) {
                for (int i = 0; i < e_zeta.size(); i++) {
                    variables = variables.plus(e_zeta.get(i).multiply(random.getRandomNumber(0.0, sigma1.getParameter())));
                }
                for (int i = 0; i < e_eta.length(); i++) {
                    variables = variables.plus(e_eta.index(i).multiply(D * random.getRandomNumber(0.0, sigma2.getParameter() / Math.sqrt(n))));
                }
            } else {
                for (int i = 0; i < e_zeta.size(); i++) {
                    variables = variables.plus(e_zeta.get(i).multiply(new P1<Number>() {
                        @Override
                        public Number _1() {
                            return random.getRandomNumber(0.0, sigma1.getParameter());
                        }
                    }));
                }
                for (int i = 0; i < e_eta.length(); i++) {
                    variables = variables.plus(e_eta.index(i).multiply(new P1<Number>() {
                        @Override
                        public Number _1() {
                            return D * random.getRandomNumber(0.0, sigma2.getParameter() / Math.sqrt(n));
                        }
                    }));
                }
            }
            E child = (E) parentCollection.get(parent).getClone();
            child.setCandidateSolution(variables);
            offspring.add(child);
        }
        return offspring;
    }

    public <E extends Entity> List<E> crossover(List<E> parentCollection) {
        checkState(parentCollection.size() >= 3, "There must be a minimum of three parents to perform UNDX crossover.");
        checkState(numberOfOffspring.getParameter() > 0, "At least one offspring must be generated. Check 'numberOfOffspring'.");
        List<Vector> solutions = Entities.<Vector, E>getCandidateSolutions(parentCollection);
        List<E> offspring = Lists.newArrayListWithCapacity((int) numberOfOffspring.getParameter());
        UniformDistribution randomParent = new UniformDistribution();
        final int k = solutions.size();
        final int n = solutions.get(0).size();
        for (int os = 0; os < numberOfOffspring.getParameter(); os++) {
            int parent = (int) randomParent.getRandomNumber(0.0, k);
            Collections.swap(solutions, parent, k - 1);
            List<Vector> e_zeta = new ArrayList<>();
            Vector g = Vectors.mean(fj.data.List.iterableList(solutions.subList(0, k - 1))).valueE("Error in getting mean");
            for (int i = 0; i < k - 1; i++) {
                Vector d = solutions.get(i).subtract(g);
                if (!d.isZero()) {
                    double dbar = d.length();
                    Vector e = d.orthogonalize(e_zeta);
                    if (!e.isZero()) {
                        e_zeta.add(e.normalize().multiply(dbar));
                    }
                }
            }
            final double D = solutions.get(k - 1).subtract(g).length();
            fj.data.List<Vector> e_eta = fj.data.List.nil();
            e_eta = e_eta.snoc(solutions.get(k - 1).subtract(g));
            for (int i = 0; i < n - e_zeta.size() - 1; i++) {
                Vector d = Vector.newBuilder().copyOf(g).buildRandom();
                e_eta = e_eta.snoc(d);
            }
            e_eta = Vectors.orthonormalize(fj.data.List.iterableList(e_eta));
            Vector variables = Vector.copyOf(g);
            if (!useIndividualProviders) {
                for (int i = 0; i < e_zeta.size(); i++) {
                    variables = variables.plus(e_zeta.get(i).multiply(random.getRandomNumber(0.0, sigma1.getParameter())));
                }
                for (int i = 0; i < e_eta.length(); i++) {
                    variables = variables.plus(e_eta.index(i).multiply(D * random.getRandomNumber(0.0, sigma2.getParameter() / Math.sqrt(n))));
                }
            } else {
                for (int i = 0; i < e_zeta.size(); i++) {
                    variables = variables.plus(e_zeta.get(i).multiply(new P1<Number>() {
                        @Override
                        public Number _1() {
                            return random.getRandomNumber(0.0, sigma1.getParameter());
                        }
                    }));
                }
                for (int i = 0; i < e_eta.length(); i++) {
                    variables = variables.plus(e_eta.index(i).multiply(new P1<Number>() {
                        @Override
                        public Number _1() {
                            return D * random.getRandomNumber(0.0, sigma2.getParameter() / Math.sqrt(n));
                        }
                    }));
                }
            }
            E child = (E) parentCollection.get(parent).getClone();
            child.setCandidateSolution(variables);
            offspring.add(child);
        }
        return offspring;
    }
