no.sintef.ict.splcatool.CoveringArrayChvatal.java Source code

Java tutorial

Introduction

Here is the source code for no.sintef.ict.splcatool.CoveringArrayChvatal.java

Source

/*******************************************************************************
 * Copyright (c) 2011, 2012 SINTEF, Martin F. Johansen.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     SINTEF, Martin F. Johansen - the implementation
 *******************************************************************************/
package no.sintef.ict.splcatool;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeoutException;

import org.apache.commons.math.util.MathUtils;
import org.sat4j.core.VecInt;
import org.sat4j.specs.ContradictionException;
import org.sat4j.specs.IVecInt;

import splar.core.constraints.BooleanVariableInterface;

public class CoveringArrayChvatal extends CoveringArray {

    private CNF cnf;
    private long coverage = 0;

    public long getCoverage() {
        return (coverage);
    }

    public CoveringArrayChvatal(int t, CNF cnf, Map<Integer, String> nrid, Map<String, Integer> idnr) {
        this.t = t;
        this.cnf = cnf;
        this.nrid = nrid;
        this.idnr = idnr;
    }

    @Override
    public void generate(int coverLimit, Integer sizelimit) throws TimeoutException {
        if (t == 1) {
            try {
                generate1(coverLimit, sizelimit);
            } catch (org.sat4j.specs.TimeoutException e) {
                throw new TimeoutException();
            }
        } else if (t == 2) {
            generate2(coverLimit, sizelimit);
        } else if (t == 3) {
            generate3(coverLimit, sizelimit);
        } else if (t == 4) {
            generate4(coverLimit, sizelimit);
        } else
            throw new UnsupportedOperationException();
    }

    @Override
    public void generate() throws TimeoutException {
        generate(100, Integer.MAX_VALUE);
    }

    public static Set<Pair2> intersect(Set<Pair2> set1, Set<Pair2> set2) {
        Set<Pair2> intersection = new HashSet<Pair2>(set1);
        intersection.retainAll(new HashSet<Pair2>(set2));
        return intersection;
    }

    private void generate2(int coverLimit, Integer sizelimit) {
        // Get a list of vars
        List<BooleanVariableInterface> vars = new ArrayList<BooleanVariableInterface>(cnf.getVariables());
        List<List<Integer>> solutions = new ArrayList<List<Integer>>(initial);

        // Calculate uncovered tuples
        List<Pair2> uncovered = new ArrayList<Pair2>();
        List<BooleanVariableInterface> vars2 = new ArrayList<BooleanVariableInterface>(vars);
        long ignored = 0;
        long alreadyCovered = 0;
        for (BooleanVariableInterface var1 : vars) {
            vars2.remove(var1);
            for (BooleanVariableInterface var2 : vars2) {

                // Set pairs
                Pair2 unc;
                if (!coverOnlyOnes) {
                    if (coverZerosOnly) {
                        unc = new Pair2(idnr);
                        unc.v1 = var1;
                        unc.b1 = false;
                        unc.v2 = var2;
                        unc.b2 = false;
                        if (!CALib.isCovered(idnr, unc, solutions)) {
                            uncovered.add(unc);
                        } else {
                            alreadyCovered++;
                        }
                    }
                    unc = new Pair2(idnr);
                    unc.v1 = var1;
                    unc.b1 = false;
                    unc.v2 = var2;
                    unc.b2 = true;
                    if (!CALib.isCovered(idnr, unc, solutions)) {
                        uncovered.add(unc);
                    } else {
                        alreadyCovered++;
                    }
                    unc = new Pair2(idnr);
                    unc.v1 = var1;
                    unc.b1 = true;
                    unc.v2 = var2;
                    unc.b2 = false;
                    if (!CALib.isCovered(idnr, unc, solutions)) {
                        uncovered.add(unc);
                    } else {
                        alreadyCovered++;
                    }
                }
                unc = new Pair2(idnr);
                unc.v1 = var1;
                unc.b1 = true;
                unc.v2 = var2;
                unc.b2 = true;
                if (!CALib.isCovered(idnr, unc, solutions)) {
                    uncovered.add(unc);
                } else {
                    alreadyCovered++;
                }
            }
        }

        System.out.println("Uncovered pairs left: " + uncovered.size());

        // If starting from a covering array or doing a cover limit, start by finding invalids
        boolean invalidRemoved = false;
        long invalids = 0;
        if (coverLimit != 100 || initial.size() > 0) {
            System.out.println(
                    "Removing invalid first when given a cover limit or a size limit or an initial covering array");
            int diff = uncovered.size();
            uncovered = getInvalid(0, uncovered);
            diff -= uncovered.size();
            System.out.println("Invalid: " + diff);
            invalids = diff;
            invalidRemoved = true;
        }

        // Check
        if (coverOnlyOnes) {
            if (uncovered.size() + alreadyCovered + invalids != MathUtils.binomialCoefficient(vars.size(), 2)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        } else if (!coverZerosOnly) {
            if (uncovered.size() + alreadyCovered + invalids != 3 * MathUtils.binomialCoefficient(vars.size(), 2)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        } else {
            if (uncovered.size() + alreadyCovered + invalids != 4 * MathUtils.binomialCoefficient(vars.size(), 2)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        }

        // Get solver
        SAT4JSolver satSolver = null;
        try {
            satSolver = cnf.getSAT4JSolver();
        } catch (ContradictionException e) {
        }

        // Cover
        long grandTotal = uncovered.size() + alreadyCovered;
        while (true) {
            // Calculate coverage
            coverage = (grandTotal - uncovered.size()) * 100 / grandTotal;

            // Stop at limit
            if (invalidRemoved && coverLimit <= coverage)
                break;

            // Check for limit
            if (solutions.size() >= sizelimit)
                break;

            // Mix
            Set<Pair2> mix = new HashSet<Pair2>(uncovered);

            List<Pair2> canBeSet = new ArrayList<Pair2>();
            List<Pair2> x = new ArrayList<Pair2>(uncovered);

            // assumptions
            Set<Integer> sol = new HashSet<Integer>();

            //System.out.println("Uncovered: " +uncovered.size());
            for (int i = 0; i < x.size(); i++) {
                if (i % 1000 == 0)
                    System.out.println(i + "/" + x.size());

                // Get the two pairs
                boolean b1 = x.get(i).b1;
                boolean b2 = x.get(i).b2;
                BooleanVariableInterface v1 = x.get(i).v1;
                BooleanVariableInterface v2 = x.get(i).v2;
                Pair p1 = new Pair();
                p1.v = v1;
                p1.b = b1;
                Pair p2 = new Pair();
                p2.v = v2;
                p2.b = b2;

                // Set it
                int var1nr, var2nr;
                var1nr = (b1 ? 1 : -1) * idnr.get(v1.getID());
                var2nr = (b2 ? 1 : -1) * idnr.get(v2.getID());

                // Check
                try {
                    // List
                    List<Integer> assumpsList = new ArrayList<Integer>();
                    for (int a : sol) {
                        assumpsList.add(a);
                    }
                    if (assumpsList.contains(-var1nr))
                        continue;
                    if (assumpsList.contains(-var2nr))
                        continue;

                    assumpsList.add(var1nr);
                    assumpsList.add(var2nr);

                    // Convert
                    int assumpsArray[] = new int[assumpsList.size()];
                    int c = 0;
                    for (int a : assumpsList) {
                        assumpsArray[c] = a;
                        c++;
                    }
                    IVecInt assumps = new VecInt(assumpsArray);

                    // Check
                    if (satSolver.solver.isSatisfiable(assumps)) {
                        sol.add(var1nr);
                        sol.add(var2nr);
                        canBeSet.add(x.get(i));
                        mix.remove(x.get(i));
                    }
                } catch (org.sat4j.specs.TimeoutException e1) {
                }
            }

            uncovered = new ArrayList<Pair2>(mix);

            // Convert
            int asssumpsArray[] = new int[sol.size()];
            int c = 0;
            for (int a : sol) {
                asssumpsArray[c] = a;
                c++;
            }
            IVecInt assumps = new VecInt(asssumpsArray);

            try {
                satSolver.solver.isSatisfiable(assumps);
            } catch (org.sat4j.specs.TimeoutException e1) {
            }
            int[] s = satSolver.solver.model();
            List<Integer> solution = new ArrayList<Integer>();
            for (int z : s)
                solution.add(z);

            // Remove invalid at some round
            if (!invalidRemoved) {
                if ((int) Math.log10(canBeSet.size()) <= (int) Math.log10(cnf.getVariables().size())) {
                    System.out.println("Removing invalid");
                    int diff = uncovered.size();
                    uncovered = getInvalid(0, uncovered);
                    diff -= uncovered.size();
                    System.out.println("Invalid: " + diff);
                    invalidRemoved = true;
                }
            }

            // Calculate coverage
            coverage = (grandTotal - uncovered.size()) * 100 / grandTotal;

            // Check if done
            if (canBeSet.size() == 0) {
                System.out.println("Breaking at " + uncovered.size() + " invalids");
                break;
            } else {
                System.out.println("Covered at " + (uncovered.size() + canBeSet.size()) + ", " + canBeSet.size()
                        + ", progress: " + coverage + "%");
                //System.out.println(canBeSet);
            }

            // Return
            solutions.add(solution);
        }

        // Done
        result = solutions;
    }

    private List<List<Integer>> result;
    private List<Integer> nrs = null;

    private List<Pair2> getInvalid(int coveredInitially, List<Pair2> uncovered) {
        int uncTotal = uncovered.size() + coveredInitially;

        // This should run
        Runtime runtime = Runtime.getRuntime();
        int threads = runtime.availableProcessors();
        System.out.println("Threads for this task: " + threads);

        // Remove invalid
        List<List<Pair2>> uncSplit = new ArrayList<List<Pair2>>();
        int beg = 0, range = uncovered.size() / threads + 1;
        for (int i = 0; i < threads; i++) {
            if (beg + range > uncovered.size())
                range = uncovered.size() - beg;
            uncSplit.add(uncovered.subList(beg, beg + range));
            //System.out.println(beg + " ->" + (beg+range));
            beg += range;
        }

        List<RIThread> rits = new ArrayList<RIThread>();
        List<Thread> ts = new ArrayList<Thread>();
        for (int i = 0; i < threads; i++) {
            RIThread rit = new RIThread(cnf, uncSplit.get(i), nrid, idnr);
            rits.add(rit);
            Thread t = new Thread(rit);
            ts.add(t);
        }

        for (int i = 0; i < threads; i++) {
            ts.get(i).start();
        }

        // Start monitoring thread
        List<ProgressReporter> prs = new ArrayList<ProgressReporter>(rits);
        ProgressThread pt = new ProgressThread("Find invalid", prs, uncTotal);
        Thread ptt = new Thread(pt);
        ptt.start();

        // Wait for all threads to finish
        for (int i = 0; i < threads; i++) {
            try {
                ts.get(i).join();
            } catch (InterruptedException e) {
            }
        }

        // Stop monitoring
        pt.stop();

        // Collect
        uncovered = new ArrayList<Pair2>();
        for (int i = 0; i < threads; i++) {
            uncovered.addAll(rits.get(i).getValid());
        }

        // Return
        return uncovered;
    }

    @Override
    public Integer[] getRow(int n) {
        if (nrs == null) {
            nrs = new ArrayList<Integer>(nrid.keySet());
            Collections.sort(nrs);
        }

        List<Integer> nl = result.get(n);
        Integer[] res = new Integer[nl.size()];

        for (int i = 0; i < nrs.size(); i++) {
            int x = nrs.get(i);
            for (int j = 0; j < nl.size(); j++) {
                if (Math.abs(nl.get(j)) == x)
                    res[i] = nl.get(j) < 0 ? 1 : 0;
            }
        }

        return res;
    }

    @Override
    public int getRowCount() {
        return result.size();
    }

    @Override
    public void setTimeout(int timeout) {
        // TODO Auto-generated method stub

    }

    @Override
    public double estimateGenerationTime() {
        int F = cnf.getVariables().size();

        if (t == 2)
            return Math.exp(0.37 * Math.log(F) + 1.30);
        if (t == 3)
            return Math.exp(1.09 * Math.log(F) + 0.00);

        throw new UnsupportedOperationException();
    }

    @Override
    public String getAlgorithmName() {
        return "Chvatal's algorithm adopted for Covering Array generation";
    }

    private void generate1(int coverLimit, Integer sizelimit)
            throws TimeoutException, org.sat4j.specs.TimeoutException {
        List<BooleanVariableInterface> vars = new ArrayList<BooleanVariableInterface>(cnf.getVariables());
        List<List<Integer>> solutions = new ArrayList<List<Integer>>(initial);

        // Cover the uncovered
        // Check uncovered
        List<Pair> uncovered = new ArrayList<Pair>();
        long alreadyCovered = 0;
        for (BooleanVariableInterface v : vars) {
            Pair p;
            if (coverZerosOnly && !coverOnlyOnes) {
                p = new Pair();
                p.b = false;
                p.v = v;
                if (!CALib.isCovered1(idnr, p, solutions))
                    uncovered.add(p);
                else
                    alreadyCovered++;
            }
            p = new Pair();
            p.b = true;
            p.v = v;
            if (!CALib.isCovered1(idnr, p, solutions))
                uncovered.add(p);
            else
                alreadyCovered++;
        }

        // If starting from a covering array or doing a cover limit, start by finding invalids
        List<Pair> invalid = new ArrayList<Pair>();
        if (coverLimit != 100 || initial.size() > 0) {
            getInvalid1(uncovered, invalid);

            // Remove from uncovered
            uncovered.removeAll(invalid);
        }

        // Check
        if (coverZerosOnly && !coverOnlyOnes) {
            if (uncovered.size() + alreadyCovered + invalid.size() != 2
                    * MathUtils.binomialCoefficient(vars.size(), 1)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.out.println(uncovered.size() + alreadyCovered + invalid.size());
                System.out.println(2 * MathUtils.binomialCoefficient(vars.size(), 1));
                return;
            }
        } else {
            if (uncovered.size() + alreadyCovered + invalid.size() != MathUtils.binomialCoefficient(vars.size(),
                    1)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.out.println(uncovered.size() + alreadyCovered + invalid.size());
                System.out.println(MathUtils.binomialCoefficient(vars.size(), 1));
                return;
            }
        }

        // Start
        Set<Pair> mix = new HashSet<Pair>(uncovered);
        uncovered = new ArrayList<Pair>(mix);

        long grandTotal = uncovered.size() + invalid.size() + alreadyCovered;
        while (true) {
            // Calculate coverage
            coverage = (grandTotal - uncovered.size()) * 100 / grandTotal;
            // Stop at limit
            if (coverLimit <= coverage)
                break;
            // Check for limit
            if (solutions.size() >= sizelimit)
                break;

            // Mix
            mix = new HashSet<Pair>(uncovered);
            uncovered = new ArrayList<Pair>(mix);

            // Cover as many as possible
            List<Pair> canBeSet = new ArrayList<Pair>();
            List<Pair> x = new ArrayList<Pair>(uncovered);
            Set<Integer> sol = new HashSet<Integer>();

            for (int i = 0; i < x.size(); i++) {
                SAT4JSolver satSolver = null;
                try {
                    satSolver = cnf.getSAT4JSolver();
                } catch (ContradictionException e) {
                }

                Pair p = x.get(i);

                // Set it
                int var1nr = (p.b ? 1 : -1) * cnf.getNr(p.v.getID());

                // Check
                try {
                    // List
                    List<Integer> assumpsList = new ArrayList<Integer>();
                    for (int a : sol) {
                        assumpsList.add(a);
                    }

                    if (assumpsList.contains(-var1nr))
                        continue;

                    assumpsList.add(var1nr);

                    // Convert
                    int assumpsArray[] = new int[assumpsList.size()];
                    int c = 0;
                    for (int a : assumpsList) {
                        assumpsArray[c] = a;
                        c++;
                    }
                    IVecInt assumps = new VecInt(assumpsArray);

                    //System.out.println(assumps);

                    // Check
                    if (satSolver.solver.isSatisfiable(assumps)) {
                        sol.add(var1nr);
                        canBeSet.add(p);
                        uncovered.remove(p);
                        //System.out.println("sat: " + varnr);
                    } else {
                        //System.out.println("unsat: " + varnr + ", " + p.v.getID());
                    }

                    /*if(varnr == -101){
                       System.exit(-1);
                    }*/
                } catch (org.sat4j.specs.TimeoutException e1) {
                }
            }

            SAT4JSolver satSolver = null;
            try {
                satSolver = cnf.getSAT4JSolver();
            } catch (ContradictionException e) {
            }

            // Break if no more covered
            if (canBeSet.size() == 0) {
                System.out.println("Breaking at " + uncovered.size() + " invalids");
                break;
            } else {
                System.out.println("Covered at " + (uncovered.size() + canBeSet.size()) + ", " + canBeSet.size());
                //System.out.println(canBeSet);
            }

            // Convert
            int assumpsArray[] = new int[sol.size()];
            int c = 0;
            for (int a : sol) {
                assumpsArray[c] = a;
                c++;
            }
            IVecInt assumps = new VecInt(assumpsArray);

            // Solve
            try {
                satSolver.solver.isSatisfiable(assumps);
            } catch (org.sat4j.specs.TimeoutException e1) {
            }
            int[] s = satSolver.solver.model();

            List<Integer> solution = new ArrayList<Integer>();
            for (int i : s)
                solution.add(i);

            // Add solution
            solutions.add(solution);
        }

        // Done
        result = solutions;
    }

    private void getInvalid1(List<Pair> uncovered, List<Pair> invalid) {
        // Get SAT-solver
        SAT4JSolver satSolver = null;
        try {
            satSolver = cnf.getSAT4JSolver();
        } catch (ContradictionException e) {
        }

        // Find invalid
        for (int i = 0; i < uncovered.size(); i++) {

            Pair p = uncovered.get(i);

            // Set it
            int var1nr = (p.b ? 1 : -1) * cnf.getNr(p.v.getID());

            // Check
            try {
                // List
                List<Integer> assumpsList = new ArrayList<Integer>();
                assumpsList.add(var1nr);

                // Convert
                int assumpsArray[] = new int[assumpsList.size()];
                int c = 0;
                for (int a : assumpsList) {
                    assumpsArray[c] = a;
                    c++;
                }
                IVecInt assumps = new VecInt(assumpsArray);

                // Check
                if (!satSolver.solver.isSatisfiable(assumps)) {
                    invalid.add(p);
                }
            } catch (org.sat4j.specs.TimeoutException e1) {
            }
        }
    }

    private void generate3(int coverLimit, Integer sizelimit) {
        // Get a list of vars
        List<BooleanVariableInterface> vars = new ArrayList<BooleanVariableInterface>(cnf.getVariables());
        List<List<Integer>> solutions = new ArrayList<List<Integer>>(initial);

        // Calculate uncovered tuples
        List<Pair3> uncovered = new ArrayList<Pair3>();
        List<BooleanVariableInterface> vars2 = new ArrayList<BooleanVariableInterface>(vars);
        List<BooleanVariableInterface> vars3 = new ArrayList<BooleanVariableInterface>(vars);
        long ignored = 0;
        long alreadyCovered = 0;
        for (int i = 0; i < vars.size(); i++) {
            BooleanVariableInterface var1 = vars.get(i);
            for (int j = i + 1; j < vars2.size(); j++) {
                BooleanVariableInterface var2 = vars2.get(j);
                for (int k = j + 1; k < vars3.size(); k++) {
                    BooleanVariableInterface var3 = vars3.get(k);

                    // Set triples
                    Pair3 unc;
                    if (!coverOnlyOnes) {
                        if (coverZerosOnly) {
                            unc = new Pair3(idnr);
                            unc.v1 = var1;
                            unc.b1 = false;
                            unc.v2 = var2;
                            unc.b2 = false;
                            unc.v3 = var3;
                            unc.b3 = false;
                            if (!CALib.isCovered3(idnr, unc, solutions))
                                uncovered.add(unc);
                            else
                                alreadyCovered++;
                        }
                        unc = new Pair3(idnr);
                        unc.v1 = var1;
                        unc.b1 = false;
                        unc.v2 = var2;
                        unc.b2 = false;
                        unc.v3 = var3;
                        unc.b3 = true;
                        if (!CALib.isCovered3(idnr, unc, solutions))
                            uncovered.add(unc);
                        else
                            alreadyCovered++;
                        unc = new Pair3(idnr);
                        unc.v1 = var1;
                        unc.b1 = false;
                        unc.v2 = var2;
                        unc.b2 = true;
                        unc.v3 = var3;
                        unc.b3 = false;
                        if (!CALib.isCovered3(idnr, unc, solutions))
                            uncovered.add(unc);
                        else
                            alreadyCovered++;
                        unc = new Pair3(idnr);
                        unc.v1 = var1;
                        unc.b1 = false;
                        unc.v2 = var2;
                        unc.b2 = true;
                        unc.v3 = var3;
                        unc.b3 = true;
                        if (!CALib.isCovered3(idnr, unc, solutions))
                            uncovered.add(unc);
                        else
                            alreadyCovered++;
                        unc = new Pair3(idnr);
                        unc.v1 = var1;
                        unc.b1 = true;
                        unc.v2 = var2;
                        unc.b2 = false;
                        unc.v3 = var3;
                        unc.b3 = false;
                        if (!CALib.isCovered3(idnr, unc, solutions))
                            uncovered.add(unc);
                        else
                            alreadyCovered++;
                        unc = new Pair3(idnr);
                        unc.v1 = var1;
                        unc.b1 = true;
                        unc.v2 = var2;
                        unc.b2 = false;
                        unc.v3 = var3;
                        unc.b3 = true;
                        if (!CALib.isCovered3(idnr, unc, solutions))
                            uncovered.add(unc);
                        else
                            alreadyCovered++;
                        unc = new Pair3(idnr);
                        unc.v1 = var1;
                        unc.b1 = true;
                        unc.v2 = var2;
                        unc.b2 = true;
                        unc.v3 = var3;
                        unc.b3 = false;
                        if (!CALib.isCovered3(idnr, unc, solutions))
                            uncovered.add(unc);
                        else
                            alreadyCovered++;
                    }
                    unc = new Pair3(idnr);
                    unc.v1 = var1;
                    unc.b1 = true;
                    unc.v2 = var2;
                    unc.b2 = true;
                    unc.v3 = var3;
                    unc.b3 = true;
                    if (!CALib.isCovered3(idnr, unc, solutions))
                        uncovered.add(unc);
                    else
                        alreadyCovered++;
                }
            }
        }

        System.out.println("Uncovered triples left: " + uncovered.size());

        System.out.println("alreadyCovered: " + alreadyCovered);

        System.out.println("expected: " + 8 * MathUtils.binomialCoefficient(vars.size(), 3));

        // Check
        if (coverOnlyOnes) {
            if (uncovered.size() + alreadyCovered != MathUtils.binomialCoefficient(vars.size(), 3)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        } else if (!coverZerosOnly) {
            if (uncovered.size() + alreadyCovered != 7 * MathUtils.binomialCoefficient(vars.size(), 3)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        } else {
            if (uncovered.size() + alreadyCovered != 8 * MathUtils.binomialCoefficient(vars.size(), 3)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        }

        // If starting from a covering array or doing a cover limit, start by finding invalids
        boolean invalidRemoved = false;
        long invalids = 0;
        if (coverLimit != 100 || initial.size() > 0) {
            System.out.println(
                    "Removing invalid first when given a cover limit or a size limit or an initial covering array");
            int diff = uncovered.size();
            uncovered = getInvalid3(0, uncovered);
            diff -= uncovered.size();
            System.out.println("Invalid: " + diff);
            invalids = diff;
            invalidRemoved = true;
        }

        // Get solver
        SAT4JSolver satSolver = null;
        try {
            satSolver = cnf.getSAT4JSolver();
        } catch (ContradictionException e) {
        }

        // Cover
        long grandTotal = uncovered.size() + alreadyCovered;
        while (true) {
            // Calculate coverage
            coverage = (grandTotal - uncovered.size()) * 100 / grandTotal;
            // Stop at limit
            if (invalidRemoved && coverLimit <= coverage)
                break;
            // Check for limit
            if (solutions.size() >= sizelimit)
                break;

            // Mix
            Set<Pair3> mix = new HashSet<Pair3>(uncovered);

            List<Pair3> canBeSet = new ArrayList<Pair3>();
            List<Pair3> x = new ArrayList<Pair3>(uncovered);

            // assumptions
            Set<Integer> sol = new HashSet<Integer>();

            //System.out.println("Uncovered: " +uncovered.size());
            for (int i = 0; i < x.size(); i++) {
                if (i % 1000 == 0)
                    System.out.println(i + "/" + x.size());

                // Get the two pairs
                boolean b1 = x.get(i).b1;
                boolean b2 = x.get(i).b2;
                boolean b3 = x.get(i).b3;
                BooleanVariableInterface v1 = x.get(i).v1;
                BooleanVariableInterface v2 = x.get(i).v2;
                BooleanVariableInterface v3 = x.get(i).v3;
                Pair p1 = new Pair();
                p1.v = v1;
                p1.b = b1;
                Pair p2 = new Pair();
                p2.v = v2;
                p2.b = b2;
                Pair p3 = new Pair();
                p3.v = v3;
                p3.b = b3;

                // Set it
                int var1nr, var2nr, var3nr;
                var1nr = (b1 ? 1 : -1) * idnr.get(v1.getID());
                var2nr = (b2 ? 1 : -1) * idnr.get(v2.getID());
                var3nr = (b3 ? 1 : -1) * idnr.get(v3.getID());

                // Check
                try {
                    // List
                    List<Integer> assumpsList = new ArrayList<Integer>();
                    for (int a : sol) {
                        assumpsList.add(a);
                    }
                    if (assumpsList.contains(-var1nr))
                        continue;
                    if (assumpsList.contains(-var2nr))
                        continue;
                    if (assumpsList.contains(-var3nr))
                        continue;

                    assumpsList.add(var1nr);
                    assumpsList.add(var2nr);
                    assumpsList.add(var3nr);

                    // Convert
                    int assumpsArray[] = new int[assumpsList.size()];
                    int c = 0;
                    for (int a : assumpsList) {
                        assumpsArray[c] = a;
                        c++;
                    }
                    IVecInt assumps = new VecInt(assumpsArray);

                    // Check
                    if (satSolver.solver.isSatisfiable(assumps)) {
                        sol.add(var1nr);
                        sol.add(var2nr);
                        sol.add(var3nr);
                        canBeSet.add(x.get(i));
                        mix.remove(x.get(i));
                    }
                } catch (org.sat4j.specs.TimeoutException e1) {
                }
            }

            uncovered = new ArrayList<Pair3>(mix);

            // Convert
            int asssumpsArray[] = new int[sol.size()];
            int c = 0;
            for (int a : sol) {
                asssumpsArray[c] = a;
                c++;
            }
            IVecInt assumps = new VecInt(asssumpsArray);

            try {
                satSolver.solver.isSatisfiable(assumps);
            } catch (org.sat4j.specs.TimeoutException e1) {
            }
            int[] s = satSolver.solver.model();
            List<Integer> solution = new ArrayList<Integer>();
            for (int z : s)
                solution.add(z);

            // Remove invalid at some round
            if (!invalidRemoved) {
                if ((int) Math.log10(canBeSet.size()) <= (int) Math.log10(cnf.getVariables().size())) {
                    System.out.println("Removing invalid");
                    int diff = uncovered.size();
                    uncovered = getInvalid3(0, uncovered);
                    diff -= uncovered.size();
                    System.out.println("Invalid: " + diff);
                    invalidRemoved = true;
                }
            }

            // Check if done
            if (canBeSet.size() == 0) {
                System.out.println("Breaking at " + uncovered.size() + " invalids");
                break;
            } else {
                System.out.println("Covered at " + (uncovered.size() + canBeSet.size()) + ", " + canBeSet.size());
                //System.out.println(canBeSet);
            }

            // Return
            solutions.add(solution);
        }

        // Done
        result = solutions;
    }

    private List<Pair3> getInvalid3(int coveredInitially, List<Pair3> uncovered) {
        int uncTotal = uncovered.size() + coveredInitially;

        // This should run
        Runtime runtime = Runtime.getRuntime();
        int threads = runtime.availableProcessors();
        System.out.println("Threads for this task: " + threads);

        // Remove invalid
        List<List<Pair3>> uncSplit = new ArrayList<List<Pair3>>();
        int beg = 0, range = uncovered.size() / threads + 1;
        for (int i = 0; i < threads; i++) {
            if (beg + range > uncovered.size())
                range = uncovered.size() - beg;
            uncSplit.add(uncovered.subList(beg, beg + range));
            //System.out.println(beg + " ->" + (beg+range));
            beg += range;
        }

        List<RIThread3> rits = new ArrayList<RIThread3>();
        List<Thread> ts = new ArrayList<Thread>();
        for (int i = 0; i < threads; i++) {
            RIThread3 rit = new RIThread3(cnf, uncSplit.get(i), nrid, idnr);
            rits.add(rit);
            Thread t = new Thread(rit);
            ts.add(t);
        }

        for (int i = 0; i < threads; i++) {
            ts.get(i).start();
        }

        // Start monitoring thread
        List<ProgressReporter> prs = new ArrayList<ProgressReporter>(rits);
        ProgressThread pt = new ProgressThread("Find invalid", prs, uncTotal);
        Thread ptt = new Thread(pt);
        ptt.start();

        // Wait for all threads to finish
        for (int i = 0; i < threads; i++) {
            try {
                ts.get(i).join();
            } catch (InterruptedException e) {
            }
        }

        // Stop monitoring
        pt.stop();

        // Collect
        uncovered = new ArrayList<Pair3>();
        for (int i = 0; i < threads; i++) {
            uncovered.addAll(rits.get(i).getValid());
        }

        // Return
        return uncovered;
    }

    private void generate4(int coverLimit, Integer sizelimit) {
        // Get a list of vars
        List<BooleanVariableInterface> vars = new ArrayList<BooleanVariableInterface>(cnf.getVariables());
        List<List<Integer>> solutions = new ArrayList<List<Integer>>(initial);

        // Calculate uncovered tuples
        List<Pair4> uncovered = new ArrayList<Pair4>();
        List<BooleanVariableInterface> vars2 = new ArrayList<BooleanVariableInterface>(vars);
        List<BooleanVariableInterface> vars3 = new ArrayList<BooleanVariableInterface>(vars);
        List<BooleanVariableInterface> vars4 = new ArrayList<BooleanVariableInterface>(vars);
        long ignored = 0;
        long alreadyCovered = 0;
        for (int i = 0; i < vars.size(); i++) {
            BooleanVariableInterface var1 = vars.get(i);
            for (int j = i + 1; j < vars2.size(); j++) {
                BooleanVariableInterface var2 = vars2.get(j);
                for (int k = j + 1; k < vars3.size(); k++) {
                    BooleanVariableInterface var3 = vars3.get(k);
                    for (int l = k + 1; l < vars4.size(); l++) {
                        BooleanVariableInterface var4 = vars4.get(l);

                        // Set pair
                        boolean tf[] = new boolean[] { false, true };
                        for (boolean a : tf)
                            for (boolean b : tf)
                                for (boolean c : tf)
                                    for (boolean d : tf) {
                                        Pair4 unc = new Pair4(idnr);
                                        unc.v1 = var1;
                                        unc.b1 = a;
                                        unc.v2 = var2;
                                        unc.b2 = b;
                                        unc.v3 = var3;
                                        unc.b3 = c;
                                        unc.v4 = var4;
                                        unc.b4 = d;
                                        if (coverOnlyOnes && (a != true || b != true || c != true || d != true))
                                            continue;
                                        if (!coverZerosOnly
                                                && (a == false && b == false && c == false && d == false))
                                            continue;
                                        if (!CALib.isCovered4(idnr, unc, solutions))
                                            uncovered.add(unc);
                                        else
                                            alreadyCovered++;
                                    }
                    }
                }
            }
        }

        System.out.println("Uncovered tuples left: " + uncovered.size());

        // Check
        if (coverOnlyOnes) {
            if (uncovered.size() + alreadyCovered != MathUtils.binomialCoefficient(vars.size(), 4)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        } else if (!coverZerosOnly) {
            if (uncovered.size() + alreadyCovered != 15 * MathUtils.binomialCoefficient(vars.size(), 4)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        } else {
            if (uncovered.size() + alreadyCovered != 16 * MathUtils.binomialCoefficient(vars.size(), 4)) {
                System.out.println("Internal error: Wrong number of tuples");
                System.exit(-1);
            }
        }

        // If starting from a covering array or doing a cover limit, start by finding invalids
        boolean invalidRemoved = false;
        List<Pair4> invalid = new ArrayList<Pair4>();
        if (coverLimit != 100 || initial.size() > 0) {
            System.out.println(
                    "Removing invalid first when given a cover limit or a size limit or an initial covering array");
            getInvalid4(uncovered, invalid);
            System.out.println("Invalid: " + invalid.size());
            uncovered.removeAll(invalid);
            invalidRemoved = true;
        }

        // Get solver
        SAT4JSolver satSolver = null;
        try {
            satSolver = cnf.getSAT4JSolver();
        } catch (ContradictionException e) {
        }

        // Cover
        long grandTotal = uncovered.size() + alreadyCovered;
        while (true) {
            // Calculate coverage
            coverage = (grandTotal - uncovered.size()) * 100 / grandTotal;
            // Stop at limit
            if (invalidRemoved && coverLimit <= coverage)
                break;
            // Check for limit
            if (solutions.size() >= sizelimit)
                break;

            // Mix
            Set<Pair4> mix = new HashSet<Pair4>(uncovered);

            List<Pair4> canBeSet = new ArrayList<Pair4>();
            List<Pair4> x = new ArrayList<Pair4>(uncovered);

            // assumptions
            Set<Integer> sol = new HashSet<Integer>();

            //System.out.println("Uncovered: " +uncovered.size());
            for (int i = 0; i < x.size(); i++) {
                if (i % 1000 == 0)
                    System.out.println(i + "/" + x.size());

                // Get the two pairs
                boolean b1 = x.get(i).b1;
                boolean b2 = x.get(i).b2;
                boolean b3 = x.get(i).b3;
                boolean b4 = x.get(i).b4;
                BooleanVariableInterface v1 = x.get(i).v1;
                BooleanVariableInterface v2 = x.get(i).v2;
                BooleanVariableInterface v3 = x.get(i).v3;
                BooleanVariableInterface v4 = x.get(i).v4;
                Pair p1 = new Pair();
                p1.v = v1;
                p1.b = b1;
                Pair p2 = new Pair();
                p2.v = v2;
                p2.b = b2;
                Pair p3 = new Pair();
                p3.v = v3;
                p3.b = b3;
                Pair p4 = new Pair();
                p4.v = v4;
                p4.b = b4;

                // Set it
                int var1nr, var2nr, var3nr, var4nr;
                var1nr = (b1 ? 1 : -1) * idnr.get(v1.getID());
                var2nr = (b2 ? 1 : -1) * idnr.get(v2.getID());
                var3nr = (b3 ? 1 : -1) * idnr.get(v3.getID());
                var4nr = (b4 ? 1 : -1) * idnr.get(v4.getID());

                // Check
                try {
                    // List
                    List<Integer> assumpsList = new ArrayList<Integer>();
                    for (int a : sol) {
                        assumpsList.add(a);
                    }
                    if (assumpsList.contains(-var1nr))
                        continue;
                    if (assumpsList.contains(-var2nr))
                        continue;
                    if (assumpsList.contains(-var3nr))
                        continue;
                    if (assumpsList.contains(-var4nr))
                        continue;

                    if (!assumpsList.contains(var1nr))
                        assumpsList.add(var1nr);
                    if (!assumpsList.contains(var2nr))
                        assumpsList.add(var2nr);
                    if (!assumpsList.contains(var3nr))
                        assumpsList.add(var3nr);
                    if (!assumpsList.contains(var4nr))
                        assumpsList.add(var4nr);

                    // Convert
                    int assumpsArray[] = new int[assumpsList.size()];
                    int c = 0;
                    for (int a : assumpsList) {
                        assumpsArray[c] = a;
                        c++;
                    }
                    IVecInt assumps = new VecInt(assumpsArray);

                    //System.out.println(assumps);

                    // Check
                    if (satSolver.solver.isSatisfiable(assumps)) {
                        sol.add(var1nr);
                        sol.add(var2nr);
                        sol.add(var3nr);
                        sol.add(var4nr);
                        canBeSet.add(x.get(i));
                        mix.remove(x.get(i));
                    }
                } catch (org.sat4j.specs.TimeoutException e1) {
                }
            }

            uncovered = new ArrayList<Pair4>(mix);

            // Convert
            int asssumpsArray[] = new int[sol.size()];
            int c = 0;
            for (int a : sol) {
                asssumpsArray[c] = a;
                c++;
            }
            IVecInt assumps = new VecInt(asssumpsArray);

            try {
                satSolver.solver.isSatisfiable(assumps);
            } catch (org.sat4j.specs.TimeoutException e1) {
            }
            int[] s = satSolver.solver.model();
            List<Integer> solution = new ArrayList<Integer>();
            for (int z : s)
                solution.add(z);

            // Check if done
            if (canBeSet.size() == 0) {
                System.out.println("Breaking at " + uncovered.size() + " invalids");
                break;
            } else {
                System.out.println("Covered at " + (uncovered.size() + canBeSet.size()) + ", " + canBeSet.size());
                //System.out.println(canBeSet);
            }

            // Return
            solutions.add(solution);
        }

        // Done
        result = solutions;
    }

    private void getInvalid4(List<Pair4> uncovered, List<Pair4> invalid) {
        // Get SAT-solver
        SAT4JSolver satSolver = null;
        try {
            satSolver = cnf.getSAT4JSolver();
        } catch (ContradictionException e) {
        }

        // Find invalid
        for (int i = 0; i < uncovered.size(); i++) {

            Pair4 p = uncovered.get(i);

            // Set it
            boolean b1 = p.b1;
            boolean b2 = p.b2;
            boolean b3 = p.b3;
            boolean b4 = p.b4;
            BooleanVariableInterface v1 = p.v1;
            BooleanVariableInterface v2 = p.v2;
            BooleanVariableInterface v3 = p.v3;
            BooleanVariableInterface v4 = p.v4;
            Pair p1 = new Pair();
            p1.v = v1;
            p1.b = b1;
            Pair p2 = new Pair();
            p2.v = v2;
            p2.b = b2;
            Pair p3 = new Pair();
            p3.v = v3;
            p3.b = b3;
            Pair p4 = new Pair();
            p4.v = v4;
            p4.b = b4;

            // Set it
            int var1nr, var2nr, var3nr, var4nr;
            var1nr = (b1 ? 1 : -1) * idnr.get(v1.getID());
            var2nr = (b2 ? 1 : -1) * idnr.get(v2.getID());
            var3nr = (b3 ? 1 : -1) * idnr.get(v3.getID());
            var4nr = (b4 ? 1 : -1) * idnr.get(v4.getID());

            // Check
            try {
                // List
                List<Integer> assumpsList = new ArrayList<Integer>();
                assumpsList.add(var1nr);
                assumpsList.add(var2nr);
                assumpsList.add(var3nr);
                assumpsList.add(var4nr);

                // Convert
                int assumpsArray[] = new int[assumpsList.size()];
                int c = 0;
                for (int a : assumpsList) {
                    assumpsArray[c] = a;
                    c++;
                }
                IVecInt assumps = new VecInt(assumpsArray);

                // Check
                if (!satSolver.solver.isSatisfiable(assumps)) {
                    invalid.add(p);
                }
            } catch (org.sat4j.specs.TimeoutException e1) {
            }
        }
    }
}