All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jesg.dither.Ipog Maven / Gradle / Ivy

There is a newer version: 0.2.0
Show newest version
package com.github.jesg.dither;

/*
 * #%L
 * dither
 * %%
 * Copyright (C) 2015 Jason Gowan
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

class Ipog {

    private final int t;
    private final Object[][] inputParams;
    private final Map origIndex = new HashMap();
    private final Map inverseOrigIndex = new HashMap();
    private final Pair[][] constraints;
    private final int[][] previouslyTested;
    private final Pair[][] pairCache;
    private final int[] mergeScratch;
    private final ConstraintHandler constraintHandler;

    public Ipog(final Object[][] input, final int t) {
        this(input, t, new Integer[][] {}, new Object[][] {});
    }

    public Ipog(final Object[][] input, final int t,
            final Integer[][] constraints, final Object[][] tested) {
        this.t = t;
        this.inputParams = input;
        this.mergeScratch = new int[input.length];
        this.previouslyTested = new int[tested.length][input.length];

        // sort input params and create a shared object pool
        final IndexArrayPair[] tmp = new IndexArrayPair[input.length];
        for (int k = 0; k < tmp.length; k++) {
            tmp[k] = new IndexArrayPair(k, input[k]);
        }
        Arrays.sort(tmp, new ArrayLengthComparator());
        final Map> origParamMap = new HashMap>();
        this.pairCache = new Pair[tmp.length][];
        for (int k = 0; k < tmp.length; k++) {
            this.origIndex.put(k, tmp[k].getI());
            this.inverseOrigIndex.put(tmp[k].getI(), k);
            this.pairCache[k] = new Pair[tmp[k].getArr().length];

            origParamMap.put(tmp[k].getI(),
                    new HashMap());
            for (int h = 0; h < this.pairCache[k].length; h++) {
                this.pairCache[k][h] = new Pair(k, h);
                origParamMap.get(tmp[k].getI()).put(tmp[k].getArr()[h],
                        this.pairCache[k][h]);
            }
        }

        for (int j = 0; j < previouslyTested.length; j++) {
            final Object[] innerTestCase = tested[j];
            for (int k = 0; k < innerTestCase.length; k++) {
                previouslyTested[j][k] = origParamMap.get(k).get(innerTestCase[k]).j;
            }
        }

        // setup constraints
        this.constraints = new Pair[constraints.length][];
        for (int i = 0; i < constraints.length; i++) {
            final Integer[] constraint = constraints[i];
            final List tmpConstraint = new ArrayList(constraint.length);

            for (int k = 0; k < constraint.length; k++) {
                if (constraint[k] != null) {
                    tmpConstraint.add(pairCache[inverseOrigIndex.get(k)][constraint[k]]);
                }
            }
            this.constraints[i] = tmpConstraint.toArray(new Pair[]{});
        }
        final int[] bounds = new int[pairCache.length];
        for(int i = 0; i < bounds.length; i++) {
            bounds[i] = pairCache[i].length;
        }
        this.constraintHandler = new ConstraintHandler(this.constraints, bounds);
    }

    List allCombinations() {

        final int[] prodarr = new int[t];
        for (int k = 0; k < prodarr.length; k++) {
            prodarr[k] = pairCache[k].length;
        }

        final int[][] prodArrResult = CombinatoricHelper.product(prodarr);

        final List results = new LinkedList();
        for(int i = 0; i < prodArrResult.length; i++) {
            final int[] result = Arrays.copyOf(prodArrResult[i], pairCache.length);
            Arrays.fill(result, t, result.length, -1);
            if(!hasTested(t, result)) {
                results.add(result);
            }
        }
        return results;
    }

    List combinations(final int i) {
        final int[] innerParams = new int[i];
        for (int k = 0; k < innerParams.length; k++) {
            innerParams[k] = k;
        }

        final List combinations = CombinatoricHelper.getCombinations(
                t - 1, innerParams);
        final List combt = new ArrayList(combinations.size());
        for (int k = 0; k < combinations.size(); k++) {
            final int[] newArr = Arrays.copyOf(combinations.get(k), t);
            newArr[t - 1] = i;
            combt.add(newArr);
        }

        final List results = new LinkedList();
        for (final int[] comb : combt) {

            final int[] prodarr = new int[comb.length];
            for (int k = 0; k < prodarr.length; k++) {
                prodarr[k] = pairCache[comb[k]].length;
            }

            final int[][] prodArrResult = CombinatoricHelper.product(prodarr);

            for (int j = 0; j < prodArrResult.length; j++) {
                final int[] innerArr = prodArrResult[j];
                final Pair[] pairs = new Pair[innerArr.length];
                for (int k = 0; k < innerArr.length; k++) {
                    pairs[k] = pairCache[comb[k]][innerArr[k]];
                }
                if (!hasTested(pairs)) {
                    results.add(pairs);
                }
            }
        }

        return results;
    }

    private boolean hasTested(final int[] testCase) {
        for (final int[] innerCase : previouslyTested) {
            if(Arrays.equals(testCase, innerCase)) {
                return true;
            }
        }
        return false;
    }

    private boolean hasTested(final int t, final int[] testCase) {
outer:
        for (final int[] innerCase : previouslyTested) {
            for(int i = 0; i < t; i++) {
                if(innerCase[i] != testCase[i]) {
                    continue outer;
                }
            }
            return true;
        }
        return false;
    }

    private boolean hasTested(final Pair[] testCase) {
outer:
        for (final int[] innerCase : previouslyTested) {
            for(final Pair pair : testCase) {
                if(innerCase[pair.i] != pair.j) {
                    continue outer;
                }
            }
            return true;
        }
        return false;
    }

    Object[][] run() {
        final List testSet = allCombinations();
        final List unbound = new LinkedList();
        for (int k = t; k < pairCache.length; k++) {
            final List pi = combinations(k);

            // horizontal extension for parameter i
            final ListIterator iterTestSet = testSet.listIterator();
            while(iterTestSet.hasNext()) {
                final int[] testCase = iterTestSet.next();
                final int max = maximizeCoverage(k,
                        testCase, pi);
                if(max < 0) {
                    iterTestSet.remove();
                } else {
                    // remove matches
                    final ListIterator iter = pi.listIterator();
iter:
                    while(iter.hasNext()) {
                        for(final Pair pair : iter.next()) {
                            if(testCase[pair.i] != pair.j) {
                                continue iter;
                            }
                        }
                        iter.remove();
                    }
                }
            }

            // vertical extension for parameter i
            final ListIterator piIter = pi.listIterator();
            while (piIter.hasNext()) {
                final Pair[] testCase = piIter.next();
                // remove constraint violation
                boolean isCaseCovered = violateConstraints(testCase);
                if(!isCaseCovered) {
                    for (final int[] innerTestCase : testSet) {
                        boolean match = true;
                        for(final Pair pair : testCase) {
                            if(innerTestCase[pair.i] != pair.j) {
                                match = false;
                                break;
                            }
                        }
                        if(match) {
                            isCaseCovered = true;
                            break;
                        }
                    }
                }

                if (!isCaseCovered) {
                    final ListIterator unboundIter = unbound.listIterator();
                    boolean isMerged = false;
                    int[] innerTestCase = null;
                    while(unboundIter.hasNext()) {
                        innerTestCase = unboundIter.next();
                        // -1 => no merge, 0 perfect merge (no unbound), 1 partial merge
                        final int mergeResult = merge(k, testCase, innerTestCase);
                        if(mergeResult == 0) {
                            unboundIter.remove();
                            testSet.add(innerTestCase);
                            isMerged = true;
                            break;
                        } else if(mergeResult == 1) {
                            isMerged = true;
                            break;
                        }
                    }

                    if (!isMerged) {
                        final int[] unboundTestCase = new int[pairCache.length];
                        Arrays.fill(unboundTestCase, -1);
                        for(final Pair pair : testCase) {
                            unboundTestCase[pair.i] = pair.j;
                        }
                        if(!violateConstraints(unboundTestCase)) {
                            unbound.add(unboundTestCase);
                        }
                    }
                }
                piIter.remove();
            }
        }
        return testSetToArray(testSet, unbound);
    }

    // -1 no merge, 0 perfect merge (no unbound), 1 partial merge
    public int merge(final int k, final Pair[] pairs, final int[] testCase) {
        // verify merge
        for(final Pair pair : pairs) {
            int value = testCase[pair.i];
            if(!(value == -1 || value == pair.j)) {
                return -1;
            }
        }

        for(int i = 0; i < mergeScratch.length; i++) {
            mergeScratch[i] = testCase[i];
        }

        for(final Pair pair : pairs) {
            mergeScratch[pair.i] = pair.j;
        }

        if(violateConstraints(mergeScratch)) {
            return -1;
        }

        // merge
        for(final Pair pair : pairs) {
            testCase[pair.i] = pair.j;
        }

        // find unbound
        for(int i = 0; i < k; i++) {
            if(testCase[i] == -1) { return 1; }
        }
        return 0;
    }

    private Object[][] testSetToArray(final List testSet, final List unbound) {

        final List results = new ArrayList(testSet.size() + unbound.size());
        for(final int[] boundResult : testSet) {
            final Object[] result = new Object[boundResult.length];
            for (int k = 0; k < boundResult.length; k++) {
                final int value = boundResult[k];
                final int i = origIndex.get(k);
                result[i] = inputParams[i][value];
            }
            if(!violateConstraints(boundResult) && !hasTested(boundResult)) {
                results.add(result);
            }
        }

outer:
        for(final int[] unboundResult : unbound) {
            final Object[] result = new Object[unboundResult.length];
            final int[] groundResult = constraintHandler.groundSolution(unboundResult);
            if(groundResult == null) {
                continue outer;
            }
            for(int k = 0; k < unboundResult.length; k++) {
                final int i = origIndex.get(k);
                final int value = groundResult[k];
                result[i] = inputParams[i][value];
            }
            if(!hasTested(unboundResult)) {
                results.add(result);
            }
        }

        return results.toArray(new Object[][]{});
    }

    boolean violateConstraints(final int[] testCase) {
        if(constraints.length == 0) {
            return false;
        }
        return constraintHandler.violateConstraints(testCase);
    }

    boolean violateConstraints(final Pair[] testCase) {
        if(constraints.length == 0) {
            return false;
        }
        return constraintHandler.violateConstraints(testCase);
    }

    private int maximizeCoverage(final int i,
            final int[] testCase, final List pi) {
        int currentMax = -1;
        int currentJ = 0;

        for (int j = 0; j < pairCache[i].length; j++) {
            final Pair currentPair = pairCache[i][j];
            testCase[currentPair.i] = currentPair.j;

            if (!violateConstraints(testCase)) {
                int count = 0;
outer:
                for (final Pair[] innerTestCase : pi) {
                    for(final Pair pair : innerTestCase) {
                        if (testCase[pair.i] != pair.j) {
                            continue outer;
                        }
                    }
                    count++;
                }

                if (count > currentMax) {
                    currentMax = count;
                    currentJ = j;
                }
            }
            testCase[i] = -1;
        }

        if (currentMax == -1) {
            return -1;
        }

        final Pair pair = pairCache[i][currentJ];
        testCase[pair.i] = pair.j;
        return currentMax;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy