import java.util.*;//has HashSet

/**
 * This class writes English and Picture files
 * for the exact compilation of a "banded oracle", as discussed
 * in the appendix entitled "Compiling Banded Oracle"
 * of Tuc07a- arXiv:0706.0479.
 */

public class OracleSEO_writer extends SEO_writer{
	//private String 	_filePrefix;
	//private int 		_numBits;
	private double 	_strength;
	private OracleBands _bands;
    
/**
 * 
 */
    public OracleSEO_writer(
        String 	filePrefix,
        int numBits,  
        double strength,
		OracleBands bands )
    { 	
    	super(filePrefix + "_qorac", numBits);

		_strength = strength;
		Throw.If(bands.getLvsNumBits()!=numBits-1, 
			"bands out of bounds");
		_bands = new OracleBands(bands);

 
		write();
		
        closeFiles();
   }
 /**
  * 
  */
    public OracleSEO_writer(
    		java.io.PrintWriter  englishOut,
    		java.io.PrintWriter  pictureOut,
    		Boolean doWrite,
    		int numBits,
    		int numBitsInP0Pad,  
            double strength,
    		OracleBands bands )
        {
        	super(englishOut, pictureOut, numBits, numBitsInP0Pad);
 
    		_strength = strength;
    		Throw.If(bands.getLvsNumBits()!=numBits-1, 
    			"bands out of bounds");
    		_bands = new OracleBands(bands);
        	
        	if(doWrite){write();}
        }
    
 /**
  * 
  */
	public void write(){
		
		int b, controlCounter;
		Boolean controlType;
		for(int j=0; j<_bands.getNumBands(); j++){
			HashSet<Pair>[] sets = GetBandControlsSet(
					_bands.getFloorOfBand(j), 
					_bands.getCeilingOfBand(j));
			//System.out.println(set);
			for(int k=0; k<2; k++){
				for(Pair p:sets[k]){
					Controls controls = new Controls(_numBits, _numBits - p._x1 -1);
					controlCounter = 0;
		            for(b=_numBits-2; b>= p._x1; b--){
		            	controlType = (((p._x2>>(b - p._x1))&1)==1);
		                controls.setControl(controlCounter, b, controlType);
		                controlCounter++;
		            }
					writeControlledGate(
						_numBits - 1, //targetBitPosition, 
			            controls, 
			            new OneBitRot((2*k-1)*_strength, OneBitRot.X_AXIS));
				}
			}
		}
		
		
		
	}
    
 /**
  * 
  * A front band is one that starts at 0. A general band 
  * can be obtained from two front bands as follows: 
  * (1) calculate the set l of gates
  * for the front band that ends in band._last
  * (2) calculate the set f of gates
  * for the front band that ends in band._first
  * (3) Equal gates cancel so
  * Take (l-f) union (f-l) as set of gates
  * 
  */   
   private HashSet<Pair> GetFrontBandControlsSet(int bandTop){
	   
	   HashSet<Pair> set = new HashSet<Pair>();
	   //a Pair will be used to store a multiply controlled gate.
	   //First component of Pair = number of controls
	   //Second component of Pair = position of first control
	   if(bandTop>=0){
		   if(bandTop%2 ==1){
			   set.add(new Pair(0, bandTop));
			   set.add(new Pair(0, bandTop-1));
		   }else{
			   set.add(new Pair(0, bandTop));
		   }
		   int j;
		   for(j=1; j<_numBits - 1; j++){
			   if(((bandTop>>j)&1)==1){
				   //subtract 1 from (bandTop>>j) to
				   //change its lowest bit to 0
				   set.add(new Pair(j, (bandTop>>j) - 1));
			   }
		   }
		   //System.out.println(set);
	   }

	   return set;
   }
/**
 * 
 */
   private HashSet<Pair>[] GetBandControlsSet(int bandFloor, int bandCeiling){
	   
	   HashSet<Pair> top = GetFrontBandControlsSet(bandCeiling);
	   HashSet<Pair> bot = GetFrontBandControlsSet(bandFloor - 1);
	   HashSet<Pair> thinTop =  new HashSet<Pair>(top);
	   HashSet<Pair> thinBot =  new HashSet<Pair>(bot);
	   thinTop.removeAll(bot);	   
	   thinBot.removeAll(top);
	   HashSet[] sets  = new HashSet[2];
	   sets[0] = thinBot;
	   sets[1] = thinTop;
	   return sets;
	     
   }
}
