/**
 * 
 */
package csp;

/**
 * @author mohsen The RelationNumberUtil class provides a set of methods for
 *         synthesizing relation numbers. Note that the created relation numbers
 *         can be manipulated using bitwise operations provided that they share
 *         the same variables and same variable ordering Variable positions are
 *         0 based for example: int x = 0; int y = 1; int z = 2; int w = 3; int
 *         r_or_xy = RelationNumberUtil.or(4,x,y) int r_or_zw =
 *         RelationNumberUtil.or(4,z,w) int r_or_xyzw = r_or_xy|r_or_zw
 * 
 * The relation provides: or(arbitrary number of variables, and(arbitrary number
 * of variables, imply (two vars) equiv (two vars) xor (two vars)
 * 
 * Use RelationCore.getMagicNumber(rank,x,1) to get a relation representing x
 * Use RelationCore.getMagicNumber(rank,x,0) to get a relation representing !x
 * 
 */

public class RelationNumberUtil {

    /**
     * Given a set of variable positions, return a relation that is the or of
     * these variables
     * 
     * @param rank
     *            the rank of the created relation
     * @param variablePositions
     *            an arbitrary number of variable positions
     * @return a relation number that represents the oring of the variables at
     *         the given positions
     */
    public static int or(int rank, int... variablePositions){
        int relationNumber = 0;
        for (int variablePosition : variablePositions) {
            // Note that the getMagicNumber will check for the validity of the
            // given variablePosition
            relationNumber |= RelationCore.getMagicNumber(rank, variablePosition, 1);
        }
        return relationNumber;
    }

    /**
     * Given a set of variable positions, return a relation that is the anding
     * of these variables
     * 
     * @param rank
     *            the rank of the created relation
     * @param variablePositions
     *            an arbitrary number of variable positions
     * @return a relation number that represents the anding of the variables at
     *         the given positions
     */
    public static int and(int rank, int... variablePositions){
        int relationNumber = RelationCore.getMask(rank);
        for (int variablePosition : variablePositions) {
            // Note that the getMagicNumber will check for the validity of the
            // given variablePosition
            relationNumber &= RelationCore.getMagicNumber(rank, variablePosition, 1);
        }
        return relationNumber;
    }

    /**
     * nMaps a number of variables in a certain relation
     * 
     * @param relationNumber
     * @param rank
     *            the rank of the created relation
     * @param variablePositions
     *            an arbitrary number of variable positions
     * @return a relation number that represents the given relation with the
     *         given set of variables renamed
     */
    public static int nMap(int relationNumber, int rank, int... variablePositions){
        for (int variablePosition : variablePositions) {
            // Note that the getMagicNumber will check for the validity of the
            // given variablePosition
            relationNumber = RelationCore.nMap(relationNumber, rank, variablePosition);
        }
        return relationNumber;
    }

    /**
     * Given two variables x,y returns a relation that represents x==>y
     * 
     * @param rank
     *            the rank of the created relation
     * @param variablePositions
     *            two variable positions
     * @return a relation number that represents the x=>y where x,y are the
     *         given two variable positions
     */
    public static int imply(int rank, int... variablePositions){
        if (variablePositions.length > 2)
            throw new IllegalArgumentException("imply supports up to 2 variables. Passed " + variablePositions.length);

        int relationNumber = or(rank, variablePositions[0], variablePositions[1]);
        return nMap(relationNumber, rank, variablePositions[0]);
    }

    /**
     * Given two variables x,y returns a relation that represents x=y
     * 
     * @param rank
     *            the rank of the created relation
     * @param variablePositions
     *            two variable positions
     * @return a relation number that represents the x=y where x,y are the given
     *         two variable positions
     */
    public static int equiv(int rank, int... variablePositions){
        if (variablePositions.length > 2)
            throw new IllegalArgumentException("equiv supports up to 2 variables. Passed " + variablePositions.length);

        int bothNeg = and(rank, variablePositions[0], variablePositions[1]);
        bothNeg = nMap(bothNeg, rank, variablePositions);
        int bothPos = and(rank, variablePositions[0], variablePositions[1]);
        return bothNeg | bothPos;
    }

    /**
     * Given two variables x,y returns a relation that represents xor(x,y)
     * 
     * @param rank
     *            the rank of the created relation
     * @param variablePositions
     *            two variable positions
     * @return a relation number that represents the xor(x,y) where x,y are the
     *         given two variable positions
     */
    public static int xor(int rank, int... variablePositions){
        if (variablePositions.length > 2)
            throw new IllegalArgumentException("xor supports up to 2 variables. Passed " + variablePositions.length);

        int posNeg = and(rank, variablePositions[0], variablePositions[1]);
        posNeg = nMap(posNeg, rank, variablePositions[1]);
        int negPos = and(rank, variablePositions[0], variablePositions[1]);
        negPos = nMap(negPos, rank, variablePositions[0]);
        return posNeg | negPos;
    }

    /**
     * for 1in3 use xTrueVars(3,1)
     * 
     * @param rank
     * @param numberOfTrueVars
     * @return an integer representing the relation number which is true only
     *         when the given number of vars is true
     */
    public static int xTrueVars(int rank, int numberOfTrueVars){
        int relationNumber = 0;
        for (int i = 0; i < (1 << rank); i++) {
            // RelationCore.ones(i, 3) a truth table row can have up to 5
            // columns
            // RelationCore.ones(i, 3) will count ones up to the 8th column
            if (RelationCore.ones(i, 3) == numberOfTrueVars)
                relationNumber |= (1 << i);
        }
        return relationNumber;
    }

    /**
     * @param args
     */
    public static void main(String[] args){
        System.out.println("" + (-3 & 0xFFFFFFFF));
        System.out.println("" + (-3 & 0xFFFFFFFF));
        System.out.println(xTrueVars(3, 0));
        System.out.println(xTrueVars(3, 1));
        System.out.println(xTrueVars(3, 2));
        System.out.println(xTrueVars(3, 3));
        int[] all = { 0, 1, 2 };
        System.out.println(nMap(xTrueVars(3, 1), 3, all));
        System.out.println(and(3, all));
        System.out.println(or(3, all));
    }

}
