/*
 * 
 */
package unholeycounters;


/**
 *
 * @author mason
 */
public class UnholeyCounters {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        new Runner(args);
        long endTime = System.currentTimeMillis();
        long elapsedTime = (endTime - startTime) / 1000;
        System.err.println("Elapsed " + elapsedTime);
        
        
    }
    
}
package unholeycounters;

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */



 /*
 * @author mason
 */
public class Runner {
   
    /**
     * @param args the command line arguments
     
     */
    public  Runner(String[] args)  {
        
        if (args.length < 2)
            usage("too few arguments");
        
        
        run(args);
    }
    void run(String args[])  {
        int n;
        
        
        if ((n = testCMD(args[0],"countEnew"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new CounterLetterECentreFull( in);    
            counter.run();
           
        } else if ((n = testCMD(args[0],"countallccf"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new CounterAllCCentreFull( in);    
            counter.run();
           
        } else if ((n = testCMD(args[0],"countr90ccf"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new CounterR90CCentreFull( in);    
            counter.run();
           
        } else if ((n = testCMD(args[0],"countr180cf"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new CounterR180CentreFull( in);    
            counter.run();
            
        } else if ((n = testCMD(args[0],"countm90c"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new CounterM90C( in);    
            counter.run();
           
        } else if ((n = testCMD(args[0],"countm90v"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new CounterM90V( in);    
            counter.run();
          
        } else if ((n = testCMD(args[0],"count2m90v"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new Counter2M90VCentreFull( in);    
            counter.run();
          
        } else if ((n = testCMD(args[0],"countr90vcf"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new CounterR90VCentreFull( in);    
            counter.run();
           
        } else if ((n = testCMD(args[0],"countallvcf"))    > 0) {
            String in = args[0].substring(n);
            Counter counter = new CounterAllVCentreFull( in);    
            counter.run();
           
        } else if ((n = testCMD(args[0],"countdomino"))    > 0) {
            String in = args[0].substring(n);
            Counter counter =  new CountDominoCentre2M90( in);  
            counter.run();
            
        } else if ((n = testCMD(args[0],"countm45"))    > 0) {
            String in = args[0].substring(n);
            Counter counter =  new CounterM45( in);  
            counter.run();
            
        } else
            usage("unknown first parameter");       
       
    }
    int testCMD(String arg, String cmd) {
        if (arg.equals(cmd))
            return arg.length();
        if (arg.startsWith(cmd + ":"))
            return cmd.length() + 1;
        return 0;
    }
    void usage(String error) {
        System.err.println(error);
       
        System.exit(1);
    }
    
    public static void printArgs(String place, String args[]) {
        String cmd = "";
        for (int i = 0 ; i < args.length ; i++) 
            cmd += args[i] + " ";
        System.err.println(place + " : " + cmd);
    }
    public static void notImplemented(String place) {
        System.err.println(place + " not implemented");
        System.exit(1);
    }
}
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;


/**
 *
 * @author mason
 * used in more recent counting filters; lists possible new squares.
 * offers exclusion from list
 * may have a position with respect to the origin distinct from associated polyomino
 */
public class InclusionListF {
    public int[][] coordArray;
    //public int[] valueArray;
    int board[][];
    int baseX , baseY ;
    public InclusionListF(int board[][], int baseX , int baseY ) {
       
        this.board = board;
        coordArray = new int[128][2];
        //valueArray = new int[128];
        this.baseX = baseX;
        this.baseY = baseY;
        
    }

    
    public int add(int x, int y, int currSize) {
        
             
        this.coordArray[currSize][0] = x;
        this.coordArray[currSize][1] = y;
        //System.err.println("add " + x + " " + y + " -1" );
        board[baseX + x][baseY + y] = -1;
        
        return currSize + 1;
        
    }
    
   
    
    
    public int getX(int i) {
        
        return coordArray[i][0];
    }
    public int getY(int i) {
        
        return coordArray[i][1];
    }
    
    public int indexOf(int x, int y, int currSize) {
      
        for (int i = 0 ; i <   currSize; i++)
            if (coordArray[i][0] == x && coordArray[i][1] == y)
                return i;
        return -1;
    }
    
   
    public String toString(int currSize) {
        String ret = // history + 
                "";
        for (int i = 0; i < currSize ; i++) {
            
            ret += coordArray[i][0] + "/" + coordArray[i][1] + " ";
        }
        return ret;
    }
    
    public InclusionListF sort(int start, int end) {
        InclusionListF newIncl = new InclusionListF(board, baseX, baseY);
        int[] tmpx, tmpy, tmpc;
                //tmpx = new int[end];
                //tmpy = new int[end];   
                tmpc = new int[end];         
        for (int i = 0; i < end ; i++) {
            newIncl.coordArray[i][0] = this.coordArray[i][0];
            newIncl.coordArray[i][1] = this.coordArray[i][1];
            
        }
            boolean flag = true;
           for (int k = start; k < end - 1; k++) {
               tmpc[k] = countNear(coordArray[k][0], coordArray[k][1]);
           }
            while (flag) {
                flag = false;
                for (int k = start; k < end - 1; k++) {
                    if (tmpc[k] < tmpc[k + 1]) {
                        int tmpx1 = coordArray[k][0];
                        int tmpy1 = coordArray[k][1];
                        int tmpc1 = tmpc[k];
                        coordArray[k][0] = coordArray[k + 1][0];
                        coordArray[k][1] = coordArray[k + 1][1];
                        tmpc[k] = tmpc[k + 1];
                        coordArray[k + 1][0] = tmpx1;
                        coordArray[k + 1][1] = tmpy1;
                        tmpc[k + 1] = tmpc1;
                        flag = true;
                    }
                }  
            }        
        return newIncl;
    }
    int countNear(int x, int y) {
        int ret = 0;
        ret += countNear(x, y, 1, 0);
        ret += countNear(x, y, -1, 0);        
        ret += countNear(x, y, 0, 1);
        ret += countNear(x, y, 0, -1);
        return ret;
    }
   
    int countNear(int x, int y, int dx, int dy) {
        if (content(x + dx,  y + dy) >= 1)
            return 1;
        else
            return 0;
    }        
    int content( int x, int y) {
        
        
        return board[baseX + x][baseY + y];
            
    }      
}
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;

/**
 *
 * @author mason
 * used to analyze parameters passed to filters
 */
public class ParameterAnalyzer {
    String resultsParameters[];
    String resultValues[];
    String rules[];
    int number = 0;
    public ParameterAnalyzer(String input, String rules[]) {
        this.rules = rules;
        resultsParameters = new String[rules.length];
        resultValues = new String[rules.length];
        while (input.length() > 0)
            input = doOne(input);
    }
    String doOne(String input) {
        int n = input.indexOf(",");
        String param = input;
        if (n >= 0) {
            param = input.substring(0, n);
            input = input.substring(n + 1);
        }
        else
            input = "";
        int m = param.indexOf("=");
        if (m < 0) {
            System.err.println("erroneous \"" + param + "\"");
            System.exit(1);            
        }
        String paramName = param.substring(0, m);
        String paramValue = param.substring(m + 1);
        for (int i = 0; i < rules.length ; i++) {
            if (paramName.equals(rules[i])) {
                resultsParameters[number] = paramName;
                resultValues[number] = paramValue;
                number++; 
                return input;
            }
        }
        System.err.println("erroneous \"" + paramName + "\"");
        String list = "";
        for (int i = 0; i < rules.length ; i++) {
            list += rules[i] + " ";
        }
        System.err.println("looking for one of " + list);
        System.exit(1);
        return null;
    }
    public String getStringValue(String paramName) {
        for (int i = 0 ; i < number; i++) {
            if (paramName.equals(this.resultsParameters[i]))
                return this.resultValues[i];
        }
        return null;
    }
    public String getStringValue(String paramName, boolean obligatory) {
        for (int i = 0 ; i < number; i++) {
            if (paramName.equals(this.resultsParameters[i]))
                return this.resultValues[i];
        }
        if (obligatory) {
            System.err.println("missing " + paramName);
            System.exit(1);            
        }
        return null;
    }
    public int getIntValue(String paramName, int def) {
        String t = getStringValue(paramName);
        if (t == null)
            return def;
        return Integer.parseInt(t);
    }
    public double getDoubleValue(String paramName, double def) {
        String t = getStringValue(paramName);
        if (t == null)
            return def;
        return Double.parseDouble(t);
    }
    public int getIntValue(String paramName) {
        String t = getStringValue(paramName);
        if (t == null) {
            System.err.println("missing " + paramName);
            System.exit(1);            
        }
        return Integer.parseInt(t);
    }
    
    public long getLongValue(String paramName, long def) {
        String t = getStringValue(paramName);
        if (t == null) {
            return def;           
        }
        return Long.parseLong(t);
    }
    
    public boolean getBooleanValue(String paramName) {
        String t = getStringValue(paramName);
        if (t == null || t.equals("false"))
            return false;
        if (t.equals("true"))
            return true;
        System.err.println("erroneous " + t);
        System.exit(1);
        return false;        
    }
    public boolean getBooleanValue(String paramName, boolean def) {
        String t = getStringValue(paramName);
        if (t == null)
            return def;
        if (t.equals("false"))
            return false;
        if (t.equals("true"))
            return true;
        System.err.println("erroneous " + t);
        System.exit(1);
        return false;        
    }
    public Boolean getTrooleanValue(String paramName, boolean obbli) {
        Boolean ret = getTrooleanValue( paramName);
        if (ret == null && obbli) {
            System.err.println("missing " + paramName);
            System.exit(1);            
        }
        return ret;
    }
    public Boolean getTrooleanValue(String paramName) {
        String t = getStringValue(paramName);
        if (t == null)
            return null;
        if (t.equals("false"))
            return Boolean.FALSE;
        if (t.equals("true"))
            return Boolean.TRUE;
        System.err.println("erroneous " + t);
        System.exit(1);
        return false;        
    }
    
}
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;




/**
 *
 * @author jmason
 * location 1,1 is the top half of the domino.
 * we put on the board only locations with x>=1 and y>=1
 * Any location with x and y > 1 corresponds to 3 other virtual locations
 * Any locations with x = 1 corresponds to 1 other virtual location
 * 
 */
public class CountDominoCentre2M90 extends CounterBase {
   
   
    public CountDominoCentre2M90( String params)   {
        super(params);
       
    
    }
  
    void printResults() {
        for (int i = 1 ; i <= targetSize; i++)
            System.out.println(i + " " + counter2[i]);
          
    }
    public void run() {
        counter2 = new long[targetSize + 1];
        root[0][0] = 1;
        root[0][1] = 1;
       
        incl.add( 1, 2, 0);        
        incl.add( 2, 1, 1);        
        setHit(1, 1, 1);
        // horrible force
        counter[2]=1;
        counter2[2]=1;
        recurse2(1, 2, 1, 1, 1, 1,  2, 0, 1); 
        printResults();
        //endFun();
    }
    boolean useThese(int x, int y, int effectiveSize) {
            if ((x > 1) && (effectiveSize + 4 > targetSize))
                return false;
            return true;
    }
    int getNewEffectiveSize(int x, int y, int effectiveSize) {
        int newEffectiveSize;
            if (x == 1) {
                newEffectiveSize = effectiveSize + 2;
                
            } else {
                newEffectiveSize = effectiveSize + 4;
            }
            return newEffectiveSize;
    }
    boolean symmetryOK(int newPolySize, int newMinX, int newMaxX, int newMinY, int newMaxY) {
        return true;
    }
    boolean proposeThese(int nx, int ny, int newEffectiveSize) {
                if ((nx > 1) && (newEffectiveSize > targetSize - 1))
                    return false;
                if (nx < 1 || ny < 1)
                    return false;
                return true;
    }  
  
    int getHoleyMultiplier(int x, int y) {
         if (x != 1)
             return 4;
         else
             return 2;
    }    
    boolean containsSpecific(int x, int y) {
        if (x < 1)
            x = -x + 2;
        if (y < 1)
            y = -y + 1;
        
        return contains(x, y);
    }   
   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;




/**
 *
 * @author jmason
 * 
 * 
 */
public abstract class Counter  {
    ParameterAnalyzer pa;
    int targetSize, distance, targetPerimeter;
    long counter[], counter2[], counter3[], counter4[];
    int board[][];
    int width, widthm1;
    InclusionListF incl;
    int hitList[][];
    int[][] root;
    String starterSize;
    
    boolean divFlag, noSymFlag, debug, squareFlag, drawFlag, repFlag, terminalFlag, treeFlag, w4cat, unholey;
    int baseX = 100, baseY = 100;
    int[] deltaX = {0, 0, 1, -1};
    int[] deltaY = {1, -1, 0, 0};   
    int deltaSize = 4;
    int[] delta3X = {-1, 1, 0, -1, 1, 0};
    int[] delta3Y = {0, 0, -1, 0, 0, 1};  
    int[] termX = { -1, -2, -1 , 0, 1, 2,  1,  0};
    int[] termY = { -1,  0,  1,  2, 1, 0, -1, -2 };
   
   
    public Counter( String params) {
      
         
        String[] rules = new String[] {"size", "only", "prefix", "start", "restart", "stopat", "startersize", "div", "rep", "tree",
            "draw", "repeat", "nosym", "debug", "square", "terminal", "distance", "width", "w4cat", "perimeter" , "unholey"};
        pa = new ParameterAnalyzer(params , rules);  
        targetSize = pa.getIntValue("size", 0);
        targetPerimeter = pa.getIntValue("perimeter", 0);
        distance = pa.getIntValue("distance", 0);
       
        counter = new long[targetSize + 1];
        starterSize = pa.getStringValue("startersize", false);
        unholey = pa.getBooleanValue("unholey");
        repFlag = pa.getBooleanValue("rep");
        treeFlag = pa.getBooleanValue("tree");
        drawFlag = pa.getBooleanValue("draw");
        debug = pa.getBooleanValue("debug");
        w4cat = pa.getBooleanValue("w4cat");
        divFlag = pa.getBooleanValue("div");
        noSymFlag = pa.getBooleanValue("nosym");
        squareFlag = pa.getBooleanValue("square");
        terminalFlag = pa.getBooleanValue("terminal");
        width = pa.getIntValue("width", 3);
        widthm1 = width - 1;        
        arrayStart();
     
           // run();
    
    }
    void arrayStart() {
        board = new int[200][200];
        
        root = new int[200][2];
        hitList = new int[200][2];   
        incl = new InclusionListF(board, baseX, baseY);
    }

   
  

    public abstract void run();
    //abstract void printResults();
    

    String cString(int size) {
        String ret = "";
        for (int j = 0 ; j < size; j++) {
            ret += root[j][0] + "," + root[j][1] + ";";

        }      
        
        return ret;
    }
  
   
    boolean contains( int x, int y) {
        boolean     newRet = false;
        
        if (content(x, y) >= 1)
            newRet =  true;
        
        return newRet;
    }  
    int content( int x, int y) {
        
        
        return board[baseX + x][baseY + y];
            
    }  
    boolean empty(int x, int y) {
        int c = content(x, y);
        if (c == 0)
            return true;
       
        return false;
    }  
    boolean matches(int x1, int y1, int x2, int y2) {
        return content(x1, y1) == content(x2, y2);
    }
    public boolean testMirrorH(int polySize, int minY, int maxY) {
        
        
        
        for (int i = 0 ; i < polySize; i++) {
            
            int x = root[i][0], y = root[i][1];
            int oppY = maxY - (y - minY);
            if (!matches(x, y, x, oppY))
                return false;
            
        }
        
        return true;
    }    
    public boolean testMirrorHnew(int polySize, int minY, int maxY) {
        
        
        
        for (int i = 0 ; i < polySize; i++) {
            
            int x = root[i][0], y = root[i][1];
            int oppY = maxY - (y - minY);
            if (!containsSpecific( x, oppY))
                return false;
            
        }
        
        return true;
    }    
    boolean containsSpecific(int x, int y) {
        return contains(x, y);
    }
    public boolean testMirrorV(int polySize, int minX, int maxX) {
        
        
        
        for (int i = polySize - 1; i >= 0 ; i--) { // for (int i = 0 ; i < polySize; i++) {
            
            int x = root[i][0], y = root[i][1];
            int oppX = maxX - (x - minX);
            if (!matches(x, y, oppX, y))
                return false;
            
        }
        
        return true;
    }    
   
    boolean testRotate90(int polySize, int minX, int minY, int maxX, int maxY) {
        for (int i = polySize - 1; i >= 0 ; i--) { // for (int i = 0 ; i < polySize; i++) {
            int x = root[i][0], y = root[i][1];
            if (!matches(x, y,   y - minY + minX, maxY - (x - minX)))
                return false;
        }
        return true;        
    }    
    boolean testRotate180(int polySize, int minX, int minY, int maxX, int maxY) {
        for (int i = polySize - 1; i >= 0 ; i--) { // for (int i = 0 ; i < polySize; i++) {
            int x = root[i][0], y = root[i][1];
            if (!matches(x, y,  maxX - x + minX, maxY - y + minY ))
                return false;
        }
        return true;        
    }    
    boolean testMirrorSWNE(int polySize, int minX, int minY, int maxX, int maxY) {
        if (maxX - minX != maxY - minY)
            return false;
        for (int i = polySize - 1; i >= 0 ; i--) { // for (int i = 0 ; i < polySize; i++) {
            int x = root[i][0], y = root[i][1];
            if (!matches(x, y,  y - minY + minX , x - minX + minY ))
                return false;
        }
        return true;
    }  
    public boolean testMirrorSENW(int polySize, int minX, int minY, int maxX, int maxY) {
        if (maxX - minX != maxY - minY)
            return false;
        for (int i = polySize - 1; i >= 0 ; i--) { // for (int i = 0 ; i < polySize; i++) {
            int x = root[i][0], y = root[i][1];
            if (!matches(x, y,  minY + maxX - y, maxX - x + minY))
                return false;
        }
        return true;
    }    
    void setHit( int x, int y, int v) {
        //System.err.println("set " + j + " " + x + " " + y + " " + v);
        if ( board[baseX + x][baseY + y]  != 0) {
            int qq = 1 / 0;
        }

        
        board[baseX + x][baseY + y] = v;        
    }
    void setRootAndHit(int pos, int x, int y) {
        //System.err.println("set " + j + " " + x + " " + y + " " + v);
        if ( board[baseX + x][baseY + y]  == 1) {
            int qq = board[baseX + x][baseY + y];
            qq /= 0;
        }
        root[pos][0] = x;
        root[pos][1] = y;
        
        board[baseX + x][baseY + y] = 1;        
    }
    void setRootAndOveride(int pos, int x, int y, int v) {
        //System.err.println("set " + j + " " + x + " " + y + " " + v);
        if ( board[baseX + x][baseY + y]  == 0) {
            int qq = 1 / 0;
        }
        root[pos][0] = x;
        root[pos][1] = y;
        
        board[baseX + x][baseY + y] = v;        
    }
    void overideHit( int x, int y, int v) {
        //System.err.println("over " + j + " " + x + " " + y + " " + v);
        if ( board[baseX + x][baseY + y]  == 0) {
            int qq = 1 / 0;
        }
        
        board[baseX + x][baseY + y] = v;        
    }
    void eraseHit(int j) {
        int bx = baseX + hitList[j][0], by = baseY + hitList[j][1];
        //System.err.println("clear " + j + " " + hitList[j][0] + " " + hitList[j][1] + " " + 0);
        if (board[bx][by] == 0) {
            int qq = 1 / 0;
        }
        board[bx][by] = 0;    
        
    } 
    void verifPoly(int polySize) {
        for (int i = 0; i < polySize; i++) {
            int x = root[i][0], y = root[i][1];
            if (board[baseX + x][baseY + y] != 1) {
                int b = board[baseX + x][baseY + y];
                int qq = 1 / 0;
            }
        }
        for (int x = -30; x < 30 ; x++)
            for (int y = -30; y < 30 ; y++) {
                if (board[baseX + x][baseY + y] == 1) {
                    if (!contains( x, y)) {
                        int qq = 1 / 0;
                    }
                }
            }
    }   
    int countNear(int x, int y) {
        int ret = 0;
        ret += countNear(x, y, 1, 0);
        ret += countNear(x, y, -1, 0);        
        ret += countNear(x, y, 0, 1);
        ret += countNear(x, y, 0, -1);
        return ret;
    }
   
    int countNear(int x, int y, int dx, int dy) {
        if (content(x + dx,  y + dy) >= 1)
            return 1;
        else
            return 0;
    } 
    long div(long a , long b) {
        if (a % b != 0) {
            System.err.println("a=" + a + " b=" + b);
            int qq = 1 / 0;
        }
        return a / b;
    }    
    boolean isHoley(int size) {
        // H = n + 1 - i - s / 2
        long sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
        for (int i = 0 ; i < size ; i++) {
            int x = root[i][0], y = root[i][1];
            
            int n = calculateAdjacent(x, y, x - 1, y - 1, x, y - 1,  x - 1, y);
            switch (n) { case 1: sum1++; break; case 2: sum2++; break; case 3: sum3++; break; case 4: sum4++; break; }
            
            n = calculateAdjacent(x, y, x + 1, y - 1, x, y - 1,  x + 1, y);
            switch (n) { case 1: sum1++; break; case 2: sum2++; break; case 3: sum3++; break; case 4: sum4++; break; }
            
            n = calculateAdjacent(x, y, x - 1, y + 1, x, y + 1,  x - 1, y);
            switch (n) { case 1: sum1++; break; case 2: sum2++; break; case 3: sum3++; break; case 4: sum4++; break; }
            
            n = calculateAdjacent(x, y, x + 1, y + 1, x, y + 1,  x + 1, y);
            switch (n) { case 1: sum1++; break; case 2: sum2++; break; case 3: sum3++; break; case 4: sum4++; break; }
            
        }
        sum2 = div(sum2, 2);
        sum3 = div(sum3, 3);
        sum4 = div(sum4, 4);
        long h = size + 1 - sum4 - div(sum1 + sum2 + sum3, 2) ;
        if (h > 0)
            return true;
        return false;
    }
    int calculateAdjacent(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
        int ret = 0;
        
        boolean b1 = contains(x1, y1);
        boolean b2 = contains(x2, y2);
        boolean b3 = contains(x3, y3);
        boolean b4 = contains(x4, y4);
        
        if (b1)
            ret++;
        if (b2)
            ret++;
        if (b3)
            ret++;
        if (b4)
            ret++;
        
        if (ret == 2) {
            if (b1 && b2)
                ret = 0;
            if (b3 && b4)
                ret = 0;
        }
        
        return ret;
    }    
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;




/**
 *
 * @author jmason
 * 
 * 
 */
public class Counter2M90VCentreFull extends CounterBase {
   
   
    public Counter2M90VCentreFull(  String params)   {
        super( params);
       
    
    }
    
  
    void printResults() {
        printResults(2);           
    }
    public void run() {
        
        counter2 = new long[targetSize + 1];
        int currInclSize = incl.add(1, 1, 0);
        recurse2(0, 0, 1, 1, 1, 1,  currInclSize, 0, 0); 
        printResults();
    }
    boolean useThese(int x, int y, int effectiveSize) {
            if (x < 1 || y < 1) {
                int qq = 1 /0;
            }
           
            if ((effectiveSize + 4 > targetSize))
                return false;
            return true;
    }
    int getNewEffectiveSize(int x, int y, int effectiveSize) {
        int newEffectiveSize;
            if (x == 1 && y == 1) {
                newEffectiveSize = 4;                
            } else  {
                newEffectiveSize = effectiveSize + 4;                
            } 
            return newEffectiveSize;
    }
    boolean symmetryOK(int newPolySize, int newMinX, int newMaxX, int newMinY, int newMaxY) {
        return !testMirrorSWNE(newPolySize, newMinX, newMinY, newMaxX, newMaxY);
    }
    boolean proposeThese(int nx, int ny, int newEffectiveSize) {
                if ((newEffectiveSize > targetSize - 4))
                    return false;
                
                if (nx < 1 || ny < 1) {
                    return false;
                    
                }
                return true;
    }   



    int getHoleyMultiplier(int x, int y) {
        return 4;
    }
    boolean containsSpecific(int x, int y) {
        int tx = x, ty = y;
        
        if (x < 1 ) {
            tx = flip(x);
        } if (y < 1) {
            ty = flip(y);
            
        }
        
        boolean ret;
        ret = contains(tx, ty);
        if (debug)
            System.err.println(x + "," + y + " = " + ret);
        return ret;
    }      
    int flip(int z) {
        return -z + 1;
    }
   
    

   
   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;



/**
 *
 * @author jmason
 * 
 * 
 */
public class CounterAllCCentreFull extends CounterBase {
   
   
    public CounterAllCCentreFull( String params)   {
        super(params);
       
    
    }
  
    void printResults() {
        printResults(1);
        
    }
    public void run() {
        
        counter2 = new long[targetSize + 1];
        int currInclSize = incl.add(1, 1, 0);
        recurse2(0, 0, 1, 1, 1, 1,  currInclSize, 0, 0); 
        printResults();
    }
    boolean useThese(int x, int y, int effectiveSize) {
            if (x < 1 || y < 1) {
                int qq = 1 /0;
            }
            if ((x > 1 && y > 1 && x != y) && (effectiveSize + 8 > targetSize))
                return false;
            if ((effectiveSize + 4 > targetSize))
                return false;
            return true;
    }
    int getNewEffectiveSize(int x, int y, int effectiveSize) {
            int newEffectiveSize;
            if (x == 1 && y == 1) {
                newEffectiveSize = effectiveSize + 1;                
            } else if (x == 1 || y == 1 || x == y) {
                newEffectiveSize = effectiveSize + 4;                
            } else {
                newEffectiveSize = effectiveSize + 8;
            }
            return newEffectiveSize;
    }
    boolean symmetryOK(int newPolySize, int newMinX, int newMaxX, int newMinY, int newMaxY) {
        return true;
    }
    boolean proposeThese(int nx, int ny, int newEffectiveSize) {
                if ((nx > 1 && ny > 1 && nx != ny) && (newEffectiveSize > targetSize - 8))
                    return false;
                if ((newEffectiveSize > targetSize - 4))
                    return false;
                if (nx < 1 || ny < 1 || ny > nx)
                    return false;
                return true;
    }       
    

    int getHoleyMultiplier(int x, int y) {
            int multiplier = 1;
            if (x != 1 && y != 1 && x != y) {
                multiplier = 8;
                
            } else if (x == 1 && y == 1) {
                // noop
            } else  {
                multiplier = 4;
           
            } 
            return multiplier;
    }
    boolean containsSpecific(int x, int y) {
        if (x < 1)
            x = flip(x);
        if (y < 1)
            y = flip(y);
        boolean ret;
        if (y > x)
            ret = contains(y, x);
        else
            ret = contains(x, y);
        if (debug)
            System.err.println(x + "," + y + " = " + ret);
        return ret;
    }      
    int flip(int z) {
        return -z + 2;
    }

   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;




/**
 *
 * @author jmason
 * 
 * 
 */
public class CounterAllVCentreFull extends CounterBase {
   
   
    public CounterAllVCentreFull(String params)   {
        super( params);
       
    
    }
    
   
    void printResults() {
        printResults(1);
                  
    }
    public void run() {
        
        counter2 = new long[targetSize + 1];
        int currInclSize = incl.add(1, 1, 0);
        recurse2(0, 0, 1, 1, 1, 1,  currInclSize, 0, 0); 
        printResults();
    }
    boolean useThese(int x, int y, int effectiveSize) {
            if (x < 1 || y < 1) {
                int qq = 1 /0;
            }
            if ((x > 1 && y > 1 && x != y) && (effectiveSize + 8 > targetSize))
                return false;
            if ((effectiveSize + 4 > targetSize))
                return false;
            return true;
    }
    int getNewEffectiveSize(int x, int y, int effectiveSize) {
            int newEffectiveSize;
            if (x == 1 && y == 1) {
                newEffectiveSize = 4;                
            } else if (x == y) {
                newEffectiveSize = effectiveSize + 4;                
            } else {
                newEffectiveSize = effectiveSize + 8;
            }
            return newEffectiveSize;
    }
    boolean symmetryOK(int newPolySize, int newMinX, int newMaxX, int newMinY, int newMaxY) {
        return true;
    }
    boolean proposeThese(int nx, int ny, int newEffectiveSize) {
                if ((nx != ny) && (newEffectiveSize > targetSize - 8))
                    return false;
                if ((newEffectiveSize > targetSize - 4))
                    return false;
                if (nx < 1 || ny < 1 || ny > nx)
                    return false;
                return true;
    }          
 


    int getHoleyMultiplier(int x, int y) {
            int multiplier = 4;
            if (x != y) {
                multiplier = 8;
                
            } 
            return multiplier;
    }
    boolean containsSpecific(int x, int y) {
        if (x < 1)
            x = flip(x);
        if (y < 1)
            y = flip(y);
        boolean ret;
        if (y > x)
            ret = contains(y, x);
        else
            ret = contains(x, y);
        if (debug)
            System.err.println(x + "," + y + " = " + ret);
        return ret;
    }      
    int flip(int z) {
        return -z + 1;
    }
   
    

   
   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;



/**
 *
 * @author jmason
 * 
 * 
 */
public abstract class CounterBase extends CounterWithMultipleCopies {
   
   
    public CounterBase(  String params)   {
        super( params);
    }
 
    abstract boolean useThese(int x, int y, int effectiveSize);
    abstract int getNewEffectiveSize(int x, int y, int effectiveSize);
    abstract boolean symmetryOK(int newPolySize, int newMinX, int newMaxX, int newMinY, int newMaxY);
    abstract boolean proposeThese(int nx, int ny, int newEffectiveSize);
    void recurse2(int currPolySize, int effectiveSize, int minX, int maxX, int minY, int maxY, int currInclSize, int startPos, int hitListSize) {
      /* effectiveSize should not be greater than targetSize but for some reason, in CounterAllVCentreFull thsi happens
        if (effectiveSize > this.targetSize) {
            int qq = 1 / 0;
        } */
        if (effectiveSize >= this.targetSize)
            return;
        
        int   newInclSize = currInclSize;
      
        
        int newMaxY = maxY,  newMinY = minY;
        int newMaxX = maxX,  newMinX = minX;
        int newPolySize = currPolySize + 1;
      
        for (int i = startPos; i < currInclSize ; i++) {
          
            int x = incl.getX(i), y = incl.getY(i) ;
           
            if (!useThese(x, y, effectiveSize))
                continue;
            if (y > newMaxY)
                newMaxY = y;
            if (y < newMinY) {
                newMinY = y;                       
            }            
            if (x > newMaxX)
                newMaxX = x;
            if (x < newMinX) {
                newMinX = x;                       
            }            
            root[currPolySize][0] = x;
            root[currPolySize][1] = y;
            
            int newHitListSize = hitListSize ;
            overideHit( x, y, 1);
           
            
            int newEffectiveSize = getNewEffectiveSize(x, y, effectiveSize);
            
            if (newEffectiveSize <= targetSize) {                
                if (symmetryOK(newPolySize, newMinX,  newMaxX, newMinY, newMaxY))
                {
                    if (!unholey || !isHoleySpecific(newPolySize, newEffectiveSize))
                        counter2[newEffectiveSize]++;  
                    counter[newEffectiveSize]++;                     
                } else {  
                   // noop
                }
            }
            
           
            for (int j = 0; j < 4 ; j++) {
                int dx = deltaX[j], dy = deltaY[j];
                int nx = x + dx;
                int ny = y + dy;
                
               
                if (!proposeThese(nx, ny, newEffectiveSize))
                    continue;
                
                if (empty( nx, ny)) {

                    newInclSize = incl.add( nx, ny , newInclSize) ;
                    hitList[newHitListSize][0] = nx;
                    hitList[newHitListSize][1] = ny;

                    newHitListSize++;                       
                }
                              
            }
            
            recurse2( newPolySize, newEffectiveSize, newMinX, newMaxX, newMinY, newMaxY, newInclSize, i + 1, newHitListSize);
            for (int j = hitListSize ; j < newHitListSize; j++) {
                eraseHit(j);                
            }   
            overideHit( x, y, -1);
            recurse2(currPolySize, effectiveSize, minX, maxX, minY, maxY, currInclSize, i + 1, hitListSize + 1);
            
            break;           
            
        }     
        
    }
 
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;



/**
 *
 * @author mason
 */
public abstract class CounterCommon extends Counter {
    public CounterCommon(String params)   {
        super( params);    
    }    
    void printResults(int divisor) {
        for (int i = 1 ; i <= targetSize; i++) {
            
            if (unholey)
                System.out.println(i + " " + div(counter2[i] , divisor) );
            else
                System.out.println(i + " " + div(counter[i] , divisor) );
        }
                  
    }    
}
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;


/**
 *
 * @author jmason
 * This counts letter E but only those with 1,1 full
 * 
 */
public class CounterLetterECentreFull extends CounterBase {
   
   
    public CounterLetterECentreFull(  String params)   {
        super( params);
       
    
    }
    
 
    void printResults() {
        printResults(2);          
    }
    public void run() {
        counter2 = new long[targetSize + 1];
       
        int currInclSize = incl.add(1, 1, 0);
        recurse2(0, 0, 1, 1, 1, 1,  currInclSize, 0, 0); 
        printResults();
    }
    boolean useThese(int x, int y, int effectiveSize) {
            if (x < 1 || y < 1) {
                int qq = 1 /0;
            }
            if ((x > 1 && y > 1) && (effectiveSize + 4 > targetSize))
                return false;
            if ((effectiveSize + 2 > targetSize))
                return false;
            return true;
    }
    int getNewEffectiveSize(int x, int y, int effectiveSize) {
            int newEffectiveSize;
            if (x == 1 && y == 1) {
                newEffectiveSize = effectiveSize + 1;                
            } else if (x == 1 || y == 1) {
                newEffectiveSize = effectiveSize + 2;                
            } else {
                newEffectiveSize = effectiveSize + 4;
            }
            return newEffectiveSize;
    }
    boolean symmetryOK(int newPolySize, int newMinX, int newMaxX, int newMinY, int newMaxY) {
        return !testMirrorSWNE(newPolySize, newMinX, newMinY, newMaxX, newMaxY);
    }
    boolean proposeThese(int nx, int ny, int newEffectiveSize) {
                if ((nx > 1 && ny > 1) && (newEffectiveSize > targetSize - 4))
                    return false;
                if ((newEffectiveSize > targetSize - 2))
                    return false;
                if (nx < 1 || ny < 1)
                    return false;
                return true;
    }      
   


    int getHoleyMultiplier(int x, int y) {
            int multiplier = 1;
            
            
            if (x != 1 && y != 1) {
                multiplier = 4;
            } else if (x == 1 && y == 1) {
                // noop
            } else 
                multiplier = 2;
            
            return multiplier;
    }
    boolean containsSpecific(int x, int y) {
        if (x < 1)
            x = -x + 2;
        if (y < 1)
            y = -y + 2;
        
        return contains(x, y);
    }      
    

   
   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;


/**
 *
 * @author jmason
 * 
 * 
 */
public class CounterM45 extends CounterWithMultipleCopies {
   
   
    public CounterM45( String params)   {
        super( params);
       
    
    }
    void initLocalData() {
        
    }
 
    void printResults() {
        
            for (int i = 1 ; i <= targetSize; i++) {
                if (this.unholey)
                    System.out.println(i + ";" + div(counter2[i] , 2) + ";" + div(counter3[i] , 2) + ";" + div(counter4[i] , 2));
                else
                    System.out.println(div(counter[i] , 2));
                
            }
        
    }
    public void run() {
        counter2 = new long[targetSize + 1];
        counter3 = new long[targetSize + 1];
        counter4 = new long[targetSize + 1];
        root[0][0] = 1;
        root[0][1] = 1;
        incl.add( 2, 1, 0);        
        incl.add( 1, 0, 1);        
        setHit(1, 1, 1);
        recurse2(1, 1, 1, 1, 1, 1, 2, 0, 2); 
        printResults();
    }
    
    void recurse2(int currPolySize, int effectiveSize, int minX, int maxX, int minY, int maxY, int currInclSize, int startPos, int hitListSize) {
   
        
        //String dbg = currentPoly.drawing();
        if (effectiveSize > this.targetSize) {
            int qq = 1 / 0;
        }
        if (effectiveSize >= this.targetSize)
            return;
        
        int   newInclSize = currInclSize;
        //long newExcluded1 = excluded1, newExcluded2 = excluded2;
        
        int newMaxX = maxX,  newMinX = minX;
        int newMaxY = maxY,  newMinY = minY;
        int newPolySize = currPolySize + 1;
        //incl.verify2(currentPoly);
        //incl.verifDupl(currInclSize);
        for (int i = startPos; i < currInclSize ; i++) {
          
            int x = incl.getX(i), y = incl.getY(i) ;
            if (x == y && y < 1) {
                int qq = 1 /0;
            }
            if ((x > y) && (effectiveSize + 2 > targetSize))
                continue;
            
            if (y > newMaxY)
                newMaxY = y;
            if (y < newMinY) {
                newMinY = y;
                       
            }            
            if (x > newMaxX)
                newMaxX = x;
            if (x < newMinX) {
                newMinX = x;
                       
            }            
            root[currPolySize][0] = x;
            root[currPolySize][1] = y;

            int newHitListSize = hitListSize ;
            overideHit( x, y, 1);          
            
            int newEffectiveSize;
            if (x == y) {
                newEffectiveSize = effectiveSize + 1;
                
            } else {
                newEffectiveSize = effectiveSize + 2;
            }
            
            if (newEffectiveSize <= targetSize) {
               
                if (!testMirrorSENW(newPolySize, newMinX, newMinY, newMaxX, newMaxY)) {
                    
                    if (this.unholey && !isHoleySpecific(newPolySize, newEffectiveSize))
                        counter2[newEffectiveSize]++; 
                    
                    counter[newEffectiveSize]++; 
                }   else if (this.unholey && !isHoleySpecific(newPolySize, newEffectiveSize)) {
                    boolean fH = testMirrorHnew(newPolySize, newMinY, newMaxX); // maxY forced according to maxX
                    if (!fH ) {
                        if (newEffectiveSize % 2 == 1)
                            counter3[newEffectiveSize]++; 
                        else
                            counter4[newEffectiveSize]++; 
                        //System.out.println(gen(newPolySize, newMinX, newMinY).drawing());
                    }
                }    
           
            }
            
            //newInclSize = increase(newPolySize, x, y, newEffectiveSize, newInclSize); 
            for (int j = 0; j < 4 ; j++) {
                int dx = deltaX[j], dy = deltaY[j];
                int nx = x + dx;
                int ny = y + dy;
                if ((nx > ny) && (newEffectiveSize > targetSize - 1))
                    continue;
                if (nx == ny && ny < 1)
                    continue;
                if (nx >= ny) {
                    if (empty( nx, ny)) {
                        
                        newInclSize = incl.add( nx, ny , newInclSize) ;
                        hitList[newHitListSize][0] = nx;
                        hitList[newHitListSize][1] = ny;
                        
                        newHitListSize++;                       
                    }
                }                
            }
            
          
            recurse2( newPolySize, newEffectiveSize, newMinX, newMaxX, newMinY, newMaxY,  newInclSize, i + 1, newHitListSize);
            for (int j = hitListSize ; j < newHitListSize; j++) {
                eraseHit(j);                
            }          
            overideHit( x, y, -1);            
            recurse2(currPolySize,  effectiveSize, minX, maxX, minY, maxY,  currInclSize, i + 1, hitListSize + 1);
            
         
            break;           
            
        }        
    }
    
 
    boolean containsSpecific(int x, int y) {
        if (x >= y)
            return contains(x, y);
        else
            return contains(y, x);
    }
    int getHoleyMultiplier(int x, int y) {
         if (x == y)
             return 1;
         else
             return 2;
    }
    
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;



/**
 *
 * @author jmason
 * 
 * 
 */
public class CounterM90C extends CounterBase {
   
   
    public CounterM90C(  String params)   {
        super( params);
       
    
    }
    
  
    void printResults() {
        
        printResults(2);
               
    }
    public void run() {
        counter2 = new long[targetSize + 1];
        counter3 = new long[targetSize + 1];
        counter4 = new long[targetSize + 1];        
        root[0][0] = 1;
        root[0][1] = 1;
        //board[baseX + 1][baseY + 1] = 1;
        setHit(1, 1, 1);
        incl.add( 2, 1, 0);        
        incl.add( 1, 2, 1);        
       
        recurse2(1, 1, 1, 1, 1, 1,  2, 0, 2); 
        printResults();
    }
    boolean useThese(int x, int y, int effectiveSize) {
            if (x == 1 && y < 1) {
                int qq = 1 /0;
            }
            if ((x > 1) && (effectiveSize + 2 > targetSize))
                return false;
            return true;
    }
    int getNewEffectiveSize(int x, int y, int effectiveSize) {
        int newEffectiveSize;
            if (x == 1) {
                newEffectiveSize = effectiveSize + 1;                
            } else {
                newEffectiveSize = effectiveSize + 2;
            }
            return newEffectiveSize;
    }
    boolean symmetryOK(int newPolySize, int newMinX, int newMaxX, int newMinY, int newMaxY) {
        return !testMirrorH(newPolySize, newMinY, newMaxY);
    }
    boolean proposeThese(int nx, int ny, int newEffectiveSize) {
                if ((nx > 1) && (newEffectiveSize > targetSize - 1))
                    return false;
                if (nx == 1 && ny < 1)
                    return false;
                if (nx >= 1) 
                    return true;
                return false;
    }    
    

    int getHoleyMultiplier(int x, int y) {
            int multiplier = 1;
            if (x != 1)
                multiplier = 2;
            
            return multiplier;
    }
    boolean containsSpecific(int x, int y) {
        if (x < 1)
            x = flip(x);
       
        boolean ret;
        
        ret = contains(x, y);
        if (debug)
            System.err.println(x + "," + y + " = " + ret);
        return ret;
    }      
    int flip(int z) {
        return -z + 2;
    }
    

   
   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;



/**
 *
 * @author jmason
 * 
 * 
 */
public class CounterM90V extends CounterBase {
   
   
    public CounterM90V(  String params)   {
        super( params);
       
    
    }
    
   
    void printResults() {
        
        printResults(2);
               
    }
    public void run() {
        counter2 = new long[targetSize + 1];
        root[0][0] = 1;
        root[0][1] = 1;
        //board[baseX + 1][baseY + 1] = 1;
        setHit(1, 1, 1);
        incl.add( 2, 1, 0);        
        incl.add( 1, 2, 1);        
       
        recurse2(1, 2, 1, 1, 1, 1,  2, 0, 2); 
        printResults();
    }
    boolean useThese(int x, int y, int effectiveSize) {
        if (x == 1 && y < 1) {
            int qq = 1 /0;
        }
        return true;
    }
    int getNewEffectiveSize(int x, int y, int effectiveSize) {
        return  effectiveSize + 2;
    }
    boolean symmetryOK(int newPolySize, int newMinX, int newMaxX, int newMinY, int newMaxY) {
        return !testMirrorH(newPolySize, newMinY, newMaxY);
    }
    boolean proposeThese(int nx, int ny, int newEffectiveSize) {
        if (nx == 1 && ny < 1)
            return false;
        if (nx >= 1) 
            return true;
        return false;
    }



    int getHoleyMultiplier(int x, int y) {
            int multiplier = 2;
          
            
            return multiplier;
    }
    boolean containsSpecific(int x, int y) {
        if (x < 1)
            x = flip(x);
       
        boolean ret;
        
        ret = contains(x, y);
        if (debug)
            System.err.println(x + "," + y + " = " + ret);
        return ret;
    }      
    int flip(int z) {
        return -z + 1;
    }
    

   
   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;



/**
 *
 * @author jmason
 
 * 
 */
public class CounterR180CentreFull extends Counter {
    
    
    
    
       
    boolean loopR90 = true,  domino = false;  
    
    
   
    //Propagator prop;
    
    public CounterR180CentreFull(  String params) {
        super( params); 
    }
   
    public void setDebug() {
        debug = true;
    }
    
    public void run() {     
        if (starterSize.equals("1x1")) {
            loopR90 = true;
        } else if (starterSize.equals("2x1")) {
            loopR90 = false;
            domino = true;
        } else if (starterSize.equals("2x2")) {
            loopR90 = true;
        } else {
            System.err.println("bad starter size");
            System.exit(1);
        }     
        outerGrowth();
        printResults();

    }
    void printResults() {
        

        

        int  divc = 0;

        
        if (!loopR90) {
            divc = 2;

        } else {
            divc = 4;

        }                
       
       
           
        for (int i = 1; i <= targetSize; i++)
            System.out.println(i + " " + div(counter[i], divc));
        
       
    }
    long div(long c, long d) {
        if (c == 0 && d == 0)
            return 0;
        if (c % d != 0) {
            System.err.println("c=" + c + ",d=" + d);
            int qq = 1 / 0;
        }
        return c / d;
    }
   
    
    void verify(boolean b) {
        if (!b) {
            int qq = 1 / 0;
        }
    }
   
   
    void outerGrowth() {
       
        if (starterSize.equals("1x1")) {
            incl.add(1, 2, 0);
            incl.add(2, 1, 1);
            setHit(0, 1, -1);
            setHit(1, 0, -1);
            
            root[0][0] = 1;
            root[0][1] = 1;
            setHit( 1, 1, 1);
            recurse2(2, 1, 1, 1, 1, 1, 0, 2); 
        } else if  (starterSize.equals("2x2")) {
            incl.add(0, 2, 0);
            incl.add(1, 3, 1);
            incl.add(2, 3, 2);
            incl.add(3, 2, 3);
            setHit(0, 1, -1);
            setHit(1, 0, -1);
            setHit(2, 0, -1);
            setHit(3, 1, -1);
            
            root[0][0] = 1;            root[0][1] = 1;
            root[1][0] = 1;            root[1][1] = 2;
            root[2][0] = 2;            root[2][1] = 1;
            root[3][0] = 2;            root[3][1] = 2;
            
            setHit( 1, 1, 1);
            setHit( 1, 2, 1);
            setHit( 2, 1, 1);
            setHit( 2, 2, 1);
            recurse2(4, 4, 1, 1, 2, 2, 0, 4); 
        } else if  (starterSize.equals("2x1")) {
            incl.add(1, 2, 0);
            incl.add(2, 2, 1);
            incl.add(3, 1, 2);
            
            setHit(0, 1, -1);            
            setHit(1, 0, -1);            
            setHit(2, 0, -1);
            
            root[0][0] = 1;
            root[0][1] = 1;
            root[1][0] = 2;
            root[1][1] = 1;
            
            setHit( 1, 1, 1);
            setHit( 2, 1, 1);
            
            recurse2(3, 2, 1, 1, 2, 1, 0, 3); 
        } else {
            System.err.println("wrong starter size");
            System.exit(1);
        }

    }    
   
    void recurse2(int currInclSize, int currPolySize,  
            int minX, int minY, int maxX, int maxY, int startPos, int hitListSize) {
        //verifPoly(currPolySize);
       
        int newMinX = minX,  newMinY = minY,  newMaxX = maxX,  newMaxY = maxY;
            
        if (currPolySize >= this.targetSize)
            return;
      
        
       
        for (int i = startPos; i < currInclSize ; i++) {
            
            int x = incl.coordArray[i][0], y = incl.coordArray[i][1];
            int oppX = oppCoord(x, minX, maxX), oppY = oppCoord(y, minY, maxY);
                   
            int newPolySize = currPolySize + 2;
            
            if (x > newMaxX)
                newMaxX = x;
            
            if (x < newMinX)
                newMinX = x;
            
            if (y > newMaxY)
                newMaxY = y;
            
            if (y < newMinY)
                newMinY = y;
        
            root[currPolySize][0] = x;
            root[currPolySize][1] = y;
           
            if (oppX > newMaxX)
                newMaxX = oppX;
            
            if (oppX < newMinX)
                newMinX = oppX;
            
            if (oppY > newMaxY)
                newMaxY = oppY;
            
            if (oppY < newMinY)
                newMinY = oppY;            
          
            root[currPolySize + 1][0] = oppX;
            root[currPolySize + 1][1] = oppY;    
            
            int newHitListSize = hitListSize ;
            overideHit( x, y, 1);                    
            overideHit( oppX, oppY, 1);                    
                        
            if (newPolySize <= targetSize && (!unholey || !isHoley(newPolySize))) {
                
                if (noSymFlag) {
                    //counter[outerChiral]++;  
                } else  {
                    boolean r90 = loopR90 && testRotate90(newPolySize, newMinX, newMinY, newMaxX, newMaxY);
                   
                    boolean m45 = !domino && testMirrorSWNE(newPolySize, newMinX, newMinY, newMaxX, newMaxY);
                    boolean achiral = false;
                    if (m45) {
                        achiral = true;
                        //System.err.println("m45");
                    }
                    else {
                        achiral = testMirrorH(newPolySize, newMinY, newMaxY);
                    }
                    if (r90) {
                        // noop
                    } else {
                        if (achiral) {
                         // noop
                        }
                        else
                            counter[newPolySize]++;
                    }
                    
                }
            }
            //System.err.println(newPoly.drawing(newMinX, newMinY, newMaxX, newMaxY));
            //incl.verify(newPoly);
            //int newInclSize = increase( currInclSize,  newPolySize,  x, y, minX, minY, maxX, maxY);
            int newInclSize = currInclSize;
            for (int j = 0; j < 4 ; j++) {
                int dx = deltaX[j], dy = deltaY[j];
                int nx = x + dx;
                int ny = y + dy;


                if (empty( nx, ny)) {
                    if (inRestrictedSet(nx, ny, newMinX, newMinY, newMaxX, newMaxY)) {
                       
                        newInclSize = incl.add( nx, ny , newInclSize) ;
                        setHit(oppCoord(nx, newMinX, newMaxX), oppCoord(ny, newMinY, newMaxY), -1);
                        hitList[newHitListSize][0] = nx;
                        hitList[newHitListSize][1] = ny;

                        newHitListSize++;                          
                       
                    }                        
                                     
                }
                               
            }            
            for (int j = 0; j < 4 ; j++) {
                int dx = deltaX[j], dy = deltaY[j];
                int nx = oppX + dx;
                int ny = oppY + dy;


                if (empty( nx, ny)) {
                    if (inRestrictedSet(nx, ny, newMinX, newMinY, newMaxX, newMaxY)) {
                        
                        newInclSize = incl.add( nx, ny , newInclSize) ;
                        setHit(oppCoord(nx, newMinX, newMaxX), oppCoord(ny, newMinY, newMaxY), -1);
                        hitList[newHitListSize][0] = nx;
                        hitList[newHitListSize][1] = ny;

                        newHitListSize++;                          
                        
                    }                        
                                     
                }
                               
            }            
            //newInclSize = increase( newInclSize,  newPolySize,  oppX, oppY, minX, minY, maxX, maxY);
            
           
            
            
            recurse2(newInclSize, newPolySize,   newMinX, newMinY, newMaxX, newMaxY, i + 1, newHitListSize);
            for (int j = hitListSize ; j < newHitListSize; j++) {
                eraseHit(j);         
                eraseOppHit( j,   newMinX,  newMinY,  newMaxX,  newMaxY);
            }  
            overideHit( x, y, -1);
            overideHit( oppCoord(x, newMinX, newMaxX), oppCoord(y, newMinY, newMaxY), -1);
            recurse2(currInclSize, currPolySize,   minX, minY, maxX, maxY, i + 1, hitListSize + 1);
                        
            break;
             
            
        }        
    }   
    void eraseOppHit(int j, int  newMinX, int newMinY, int newMaxX, int newMaxY) {
        int bx = baseX + oppCoord(hitList[j][0],newMinX, newMaxX), by = baseY + oppCoord(hitList[j][1], newMinY, newMaxY);
        //System.err.println("clear " + j + " " + hitList[j][0] + " " + hitList[j][1] + " " + 0);
        if (board[bx][by] == 0) {
            int qq = 1 / 0;
        }
        board[bx][by] = 0;    
        
    }     
    int oppCoord(int z, int minZ, int maxZ) {
        return  maxZ - z + minZ;
    }
    
    public boolean inRestrictedSet(int x, int y, int localMinX, int localMinY, int localMaxX, int localMaxY) {
       
        x = x - localMinX + 1;
        y = y - localMinY + 1;
        localMaxY = localMaxY - localMinY + 1;
        localMaxX = localMaxX - localMinX + 1;
        /*
        if (maxX == 1) {
            return (x >= 1 && y > maxY / 2);
        }
        */
        if (localMaxY % 2 == 0) {
            return y > localMaxY / 2;
        }
        
        int halfV = (localMaxY + 1) / 2;
       
        if (y > halfV)
            return true;
        if (y < halfV)
            return false;
        int halfH = (localMaxX + 1) / 2;
        return (x > halfH);
    }    
    
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;

/**
 *
 * @author jmason
 * Does not suit extending CounterBase
 * 
 */
public class CounterR90CCentreFull extends CounterWithMultipleCopies {
   
   
    public CounterR90CCentreFull( String params)   {
        super( params);
       
    
    }
    
  
    void printResults() {
        printResults(2);
        //System.err.println("Results need dividing");            
    }
    public void run() {
        
        counter2 = new long[targetSize + 1];
        int currInclSize = incl.add(1, 1, 0);
        recurse2(0, 0, 1, 1, 1, 1,  currInclSize, 0, 0); 
        printResults();
    }
   
    void recurse2(int currPolySize, int effectiveSize, int minX, int maxX, int minY, int maxY, int currInclSize, int startPos, int hitListSize) {
        //System.err.println("recurse " + hitListSize );
        //verifPoly(currPolySize);
        
        if (effectiveSize > this.targetSize) {
            return;
        }
        if (effectiveSize == this.targetSize)
            return;
        
        int   newInclSize = currInclSize;
        //long newExcluded1 = excluded1, newExcluded2 = excluded2;
        
        int newMaxY = maxY,  newMinY = minY;
        int newMaxX = maxX,  newMinX = minX;
        int newPolySize = currPolySize + 1;
        //incl.verify2(currentPoly);
        //incl.verifDupl(currInclSize);
        for (int i = startPos; i < currInclSize ; i++) {
          
            int x = incl.getX(i), y = incl.getY(i) ;
            if (x < 1 || y < 1) {
                int qq = 1 /0;
            }
            
            if ((effectiveSize + 4 > targetSize))
                continue;
            
            if (y > newMaxY)
                newMaxY = y;
            if (y < newMinY) {
                newMinY = y;                       
            }            
            if (x > newMaxX)
                newMaxX = x;
            if (x < newMinX) {
                newMinX = x;                       
            }            
            root[currPolySize][0] = x;
            root[currPolySize][1] = y;
            //hitList[hitListSize][0] = x;
            //hitList[hitListSize][1] = y;
            int newHitListSize = hitListSize ;
            overideHit( x, y, 1);
            
            int newEffectiveSize;
            if (x == 1 && y == 1) {
                newEffectiveSize = effectiveSize + 1;                
            } else  {
                newEffectiveSize = effectiveSize + 4;                
            } 
            boolean fH = testMirrorHnew(newPolySize, 2 - newMaxY, newMaxY); // minY forced according to maxY
            
            if (!fH && newEffectiveSize <= targetSize) {                
                
                if (this.unholey && !isHoleySpecific(newPolySize, newEffectiveSize)) {
                    //fH = testMirrorHnew(newPolySize, 2 - newMaxY, newMaxY);
                    counter2[newEffectiveSize]++;   
                    //System.err.println(gen(newPolySize).drawing());
                }
                    
                      
                    
                counter[newEffectiveSize]++;
                     
               
 
            }
            
            //newInclSize = increase(newPolySize, x, y, newEffectiveSize, newInclSize); 
            for (int j = 0; j < 4 ; j++) {
                int dx = deltaX[j], dy = deltaY[j];
                int nx = x + dx;
                int ny = y + dy;
                if ((newEffectiveSize > targetSize - 4))
                    continue;
                if (nx == 1) {
                    nx = ny;
                    ny = 1;
                }
                if (ny == 0) {
                    ny = nx;
                    nx = 2;
                }
                if (x == 1 && y == 1) {
                    if (nx < 2)
                        continue;
                }
                if (nx < 1 || ny < 1) {
                    int qq = 1 / 0;
                }
                if (empty( nx, ny)) {

                    newInclSize = incl.add( nx, ny , newInclSize) ;
                    hitList[newHitListSize][0] = nx;
                    hitList[newHitListSize][1] = ny;

                    newHitListSize++;                       
                }
                                
            }
            
            
            recurse2( newPolySize, newEffectiveSize, newMinX, newMaxX, newMinY, newMaxY, newInclSize, i + 1, newHitListSize);
            for (int j = hitListSize ; j < newHitListSize; j++) {
                eraseHit(j);                
            }          
            overideHit( x, y, -1);
            recurse2(currPolySize, effectiveSize, minX, maxX, minY, maxY, currInclSize, i + 1, hitListSize + 1);
            
            
            break;           
            
        }     
        //System.err.println("return " );
    }

    int getHoleyMultiplier(int x, int y) {
            int multiplier = 1;
          
            if (x != 1 || y != 1 ) 
                multiplier = 4;
            return multiplier;
    }
    boolean containsSpecific(int x, int y) {
        int tx = x, ty = y;
        
        if (x <= 1 && y > 1) {
            tx = y;
            ty = flip(x);
        } else if (x < 1 && y <= 1) {
            tx = flip(x);
            ty = flip(y);
        } else if (x >= 1 && y < 1) {
            tx = flip(y);
            ty = x;
        }
        
        boolean ret;
        ret = contains(tx, ty);
        if (debug) {
            //System.err.println(x + "," + y + " " + tx + "," + ty + " = " + ret);
        }
        return ret;
    }     
    
    int flip(int z) {
        return -z + 2;
    }

   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;




/**
 *
 * @author jmason
 * Does not suit extending CounterBase
 * 
 */
public class CounterR90VCentreFull extends CounterWithMultipleCopies {
   
   
    public CounterR90VCentreFull(  String params)   {
        super( params);
       
    
    }
    
  
    void printResults() {
        printResults(2);           
    }
    public void run() {
        
        counter2 = new long[targetSize + 1];
        int currInclSize = incl.add(1, 1, 0);
        recurse2(0, 0, 1, 1, 1, 1,  currInclSize, 0, 0); 
        printResults();
    }
   
    void recurse2(int currPolySize, int effectiveSize, int minX, int maxX, int minY, int maxY, int currInclSize, int startPos, int hitListSize) {
        //System.err.println("recurse " + hitListSize );
        //verifPoly(currPolySize);
        
        if (effectiveSize > this.targetSize) {
            return;
        }
        if (effectiveSize == this.targetSize)
            return;
        
        int   newInclSize = currInclSize;
        //long newExcluded1 = excluded1, newExcluded2 = excluded2;
        
        int newMaxY = maxY,  newMinY = minY;
        int newMaxX = maxX,  newMinX = minX;
        int newPolySize = currPolySize + 1;
        //incl.verify2(currentPoly);
        //incl.verifDupl(currInclSize);
        for (int i = startPos; i < currInclSize ; i++) {
          
            int x = incl.getX(i), y = incl.getY(i) ;
            if (x < 1 || y < 1) {
                int qq = 1 /0;
            }
           
            if ((effectiveSize + 4 > targetSize))
                continue;
            
            if (y > newMaxY)
                newMaxY = y;
            if (y < newMinY) {
                newMinY = y;                       
            }            
            if (x > newMaxX)
                newMaxX = x;
            if (x < newMinX) {
                newMinX = x;                       
            }            
            root[currPolySize][0] = x;
            root[currPolySize][1] = y;
            //hitList[hitListSize][0] = x;
            //hitList[hitListSize][1] = y;
            int newHitListSize = hitListSize ;
            overideHit( x, y, 1);
            
            int newEffectiveSize;
            if (x == 1 && y == 1) {
                newEffectiveSize = 4;                
            } else  {
                newEffectiveSize = effectiveSize + 4;                
            } 
            boolean fH = testMirrorHnew(newPolySize, 1 - newMaxY, newMaxY); // minY forced according to maxY
            if (!fH && newEffectiveSize <= targetSize) {                
                
                if (this.unholey && !isHoleySpecific(newPolySize, newEffectiveSize))
                    counter2[newEffectiveSize]++;    
                    
                      
                    
                counter[newEffectiveSize]++;
                     
               
 
            }
            
            //newInclSize = increase(newPolySize, x, y, newEffectiveSize, newInclSize); 
            for (int j = 0; j < 4 ; j++) {
                int dx = deltaX[j], dy = deltaY[j];
                int nx = x + dx;
                int ny = y + dy;
                
                if ((newEffectiveSize > targetSize - 4))
                    continue;
                
                if (nx == 0) {
                    nx = ny;
                    ny = 1;
                }
                if (ny == 0) {
                    ny = nx;
                    nx = 1;
                }
                if (x == 1 && y == 1) {
                    if (nx < 1 || ny < 1)
                        continue;
                }
                if (nx < 1 || ny < 1) {
                    int qq = 1 / 0;
                }
                
                if (empty( nx, ny)) {

                    newInclSize = incl.add( nx, ny , newInclSize) ;
                    hitList[newHitListSize][0] = nx;
                    hitList[newHitListSize][1] = ny;

                    newHitListSize++;                       
                }
                                
            }
            
            //System.err.println("before " + hitListSize + " " + newHitListSize);
            recurse2( newPolySize, newEffectiveSize, newMinX, newMaxX, newMinY, newMaxY, newInclSize, i + 1, newHitListSize);
            for (int j = hitListSize ; j < newHitListSize; j++) {
                eraseHit(j);                
            }          
            overideHit( x, y, -1);
            recurse2(currPolySize, effectiveSize, minX, maxX, minY, maxY, currInclSize, i + 1, hitListSize + 1);
            
            //System.err.println("after " + hitListSize + " " + newHitListSize);
            //System.err.println("break "  );
            break;           
            
        }     
        
    }


    int getHoleyMultiplier(int x, int y) {
        return 4;
    }
    boolean containsSpecific(int x, int y) {
        int tx = x, ty = y;
        
        if (x < 1 && y > 0) {
            tx = y;
            ty = flip(x);
        } else if (x < 1 && y <= 0) {
            tx = flip(x);
            ty = flip(y);
        } else if (x >= 1 && y < 1) {
            tx = flip(y);
            ty = x;
        }
        
        boolean ret;
        ret = contains(tx, ty);
        if (debug)
            System.err.println(x + "," + y + " = " + ret);
        return ret;
    }      
    int flip(int z) {
        return -z + 1;
    }
   
    

   
   
}

    
 
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package unholeycounters;



/**
 *
 * @author mason
 */
public abstract class CounterWithMultipleCopies extends CounterCommon {
    public CounterWithMultipleCopies(String params)   {
        super( params);
       
    
    }    
    long sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
    //abstract void isHoleySpecificLoop(int size);
    abstract boolean containsSpecific(int x, int y);
    boolean isHoleySpecific(int size, int effectiveSize) {
        // H = n + 1 - i - s / 2
        //System.err.println("start");
        sum1 = 0; sum2 = 0; sum3 = 0; sum4 = 0;
        //isHoleySpecificLoop(size);
        for (int i = 0 ; i < size ; i++) {
            int x = root[i][0], y = root[i][1];
            if (debug)
                System.err.println("start x=" + x + " y=" + y);
            
            isHoleySingleSquareSpecific(x, y, getHoleyMultiplier(x, y));
                      
            
        }        
        sum2 = div(sum2, 2);
        sum3 = div(sum3, 3);
        sum4 = div(sum4, 4);
        
        long h = effectiveSize + 1 - sum4 - div(sum1 + sum2 + sum3, 2) ;
        
        if (debug) {
            System.err.println("sum1 = " + sum1);
            System.err.println("sum2 = " + sum2);
            System.err.println("sum3 = " + sum3);
            System.err.println("sum4 = " + sum4);
            System.err.println("size = " + effectiveSize);
            System.err.println("h    = " + h);
            
        }
        
        if (h > 0)
            return true;
        return false;
    }    
    abstract int getHoleyMultiplier(int x, int y) ;
    void isHoleySingleSquareSpecific(int x, int y, int multiplier) {
        if (debug) {
            System.err.println("test x=" + x + " y=" + y);
        }
            
        int n = calculateAdjacentSpecific(x, y, x - 1, y - 1, x, y - 1,  x - 1, y);
        switch (n) { case 1: sum1 += multiplier; break; case 2: sum2 += multiplier; break; case 3: sum3 += multiplier; break; case 4: sum4 += multiplier; break; }

        n = calculateAdjacentSpecific(x, y, x + 1, y - 1, x, y - 1,  x + 1, y);
        switch (n) { case 1: sum1 += multiplier; break; case 2: sum2 += multiplier; break; case 3: sum3 += multiplier; break; case 4: sum4 += multiplier; break; }

        n = calculateAdjacentSpecific(x, y, x - 1, y + 1, x, y + 1,  x - 1, y);
        switch (n) { case 1: sum1 += multiplier; break; case 2: sum2 += multiplier; break; case 3: sum3 += multiplier; break; case 4: sum4 += multiplier; break; }

        n = calculateAdjacentSpecific(x, y, x + 1, y + 1, x, y + 1,  x + 1, y);
        switch (n) { case 1: sum1 += multiplier; break; case 2: sum2 += multiplier; break; case 3: sum3 += multiplier; break; case 4: sum4 += multiplier; break; }        
    }
    int calculateAdjacentSpecific(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
        int ret = 0;
        
        boolean b1 = containsSpecific(x1, y1);
        boolean b2 = containsSpecific(x2, y2);
        boolean b3 = containsSpecific(x3, y3);
        boolean b4 = containsSpecific(x4, y4);
        
        if (b1)
            ret++;
        if (b2)
            ret++;
        if (b3)
            ret++;
        if (b4)
            ret++;
        
        if (ret == 2) {
            if (b1 && b2)
                ret = 0;
            if (b3 && b4)
                ret = 0;
        }
        if (debug) {
            //System.err.println("(" + x1 + "," + y1 + ")(" + x2 + "," + y2 + ")(" + x3 + "," + y3 + ")(" + x4 + "," + y4 + ") : " + ret);
        }
        return ret;
    }           
}