/**
 * This class stores oracle bands.
 * Such bands are defined in
 * the appendix entitled "Compiling Banded Oracle"
 * of Tuc07a- arXiv:0706.0479.
 * For example, we say 
 * (x0,x1,x2,x3) \in {0,1}^4 has bands _matrix={{0,0},{2,3}}
 * if (x0,x1,x2,x3) = (1,0,1,1)
 *
 */
public class OracleBands {
	private int _numBits;
	private int _numBands;
	private int[][] _matrix;
	
/**
 * 
 */
	public OracleBands(int numBits, int[][] mat){
		
		_numBits = numBits;
		_numBands = mat.length;	
		Throw.If(mat[0].length != 2, 
				"oracle bands matrix has wrong size");
		_matrix = new int[_numBands][2];
		int numLvs = 1<<numBits;		
		Throw.If( (mat[0][0] < 0) || (mat[_numBands-1][1] >= numLvs),
				"oracle band is out of bounds");
		for(int j=0; j < _numBands; j++){
			Throw.If( mat[j][0]>mat[j][1], 
				"oracle band is undefined");
			if(j < _numBands - 1){
				Throw.If((mat[j][1] + 1) >= mat[j+1][0], 
					"oracle bands overlap or can be merged");
			}	
			_matrix[j][0] = mat[j][0];
			_matrix[j][1] = mat[j][1];
		}
	}
/**
 * 
 */
	public OracleBands(OracleBands bands){
		_numBits = bands._numBits;
		_numBands = bands._numBands;
		_matrix = new int[_numBands][2];
		for(int j=0; j < _numBands; j++){
			_matrix[j][0] = bands._matrix[j][0];
			_matrix[j][1] = bands._matrix[j][1];
		}
	}
/**
 * 
 */
	public int getLvsNumBits(){
		return _numBits;
	}
/**
 * 
 */	
	public int getNumBands(){
		return _numBands;
	}
/**
 * 
 */
	public int getFloorOfBand(int j){
		Throw.If(j<0 || j>= _numBands, 
				"band index is out of bounds");
		return _matrix[j][0];
	}
/**
 * 
 */
	public int getCeilingOfBand(int j){
		Throw.If(j<0 || j>= _numBands, 
				"band index is out of bounds");
		return _matrix[j][1];
	}

/**
 * 
 */
	public String toString(){
		String output = "Oracle Bands = {";
		for(int j=0; j<_numBands ; j++){
			output += "{" + 
				_matrix[j][0] + "," +
				_matrix[j][1] +
				"}";
			if(j<_numBands-1){
				output += ",";
			}else{
				output += "}";
			}
		}
		return output;
	}
}

