// Written by Steve Butler
// last updated on 3 June 2009
//
// to use the program compile and from command line run
//   > java ABCpert FILENAME A B C n1 n2 n3 ...
// the program starts with an initial block coloring of
// n1 red, followed by n2 blue, followed by n3 red, ...
// it then perturbs first randomly and then back and forth
// sweeps until we get to a locally optimal coloring 
// minimizing the number of monochromatic solutions to
// the equation Ax+By=Cz, for x,y,z in 1,...,n1+n2+n3+...
// then outputs the block structure to FILENAME.txt with
// under various modulos.  We then randomly flip ~40%
// of the colors and start over repeating the process
// 20 times
//
import java.math.*;
import java.lang.Integer;
import java.io.*;

public class ABCpert {
	public static void main (String[] args){
		int length=0;
		String filename=args[0];
		int a=Integer.parseInt(args[1]);
		int b=Integer.parseInt(args[2]);
		int c=Integer.parseInt(args[3]);
		int numBlocks=args.length-4;
		int counter=1;
//load the initial pattern
for(int i=1; i<=numBlocks;i++){
			length=length+Integer.parseInt(args[i+3]);
		}
		int[] array=new int[length+2];
		int currentPosition=1; int currentColor=1;
		for(int i=1;i<=numBlocks;i++){
			for(int j=1; j<=Integer.parseInt(args[i+3]); j++){
				array[currentPosition]=currentColor;
				currentPosition++;
			}
			currentColor=1-currentColor;
		}

		int round=1,  iteration=0;
		long startingcount=countAPs(array,length,a,b,c);

		try{
			PrintStream out = new PrintStream(new FileOutputStream(filename+".txt"));
			out.println("Looks at perturbing coloring of interval length "+length);
			out.println("for minimizing monochromatic solutions of "+a+"x+"+b+"y="+c+"z.");
			out.println("");
			while(round<=20){ // 20 = number of rounds
				boolean trimmed=true;
				boolean skipRandom=false;
				while(trimmed==true){	
					trimmed=false;
					iteration++;
                    if(skipRandom==false){ // first update randomly
						int Rcounter=0;
						for(int tempi=1; tempi<=length; tempi++){
							int i=(int)((double)(length)*Math.random()+1);
							if(change(array,length,i,a,b,c)==true) { array[i]=1-array[i]; Rcounter++; }
						}
						if(Rcounter<length/1000){ skipRandom=true; } trimmed=true;
						System.out.print("."); // indicate end of a random round of updating
					}
					else{ // now do a sweep
						if(iteration%2==1){
							for(int i=1; i<=length; i++){
								if(change(array,length,i,a,b,c)==true) { array[i]=1-array[i]; trimmed=true; }
							}
						}
						else{
							for(int i=length; i>=1; i--){
								if(change(array,length,i,a,b,c)==true) { array[i]=1-array[i]; trimmed=true; }
							}
						}
					System.out.print("*"); // indicate end of a sweep round of updating
					}
				}
				int switches=1;
				for(int i=2; i<=length; i++){
					if(array[i]!=array[i-1]){ switches++; }
				}
				long numAPs=countAPs(array,length,a,b,c);
				System.out.println("");
				System.out.println("round "+round +": "+switches +"  (solutions: "+startingcount+" -> "+numAPs+")");
				out.println("Pattern for round "+round+" (uses "+switches+" blocks, has "+numAPs+" monochromatic solutions):");
				out.print("      ");
				int current=array[1]; int startoBlock=1;
				for(int i=2; i<=length; i++){
					if(array[i]!=current){
						out.print((i-startoBlock)+",");
						current=array[i];
						startoBlock=i;
					}
				}
				out.println(1+length-startoBlock);
				
				int[] skiparray=new int[length];
				for(int p=1; p<=3; p++){ // look at terms modular
					int i=1;
					     if (p==1){ i=a;} 
					else if (p==2){ i=b;} 
					else if (p==3){ i=c;} 
					out.println("   Patterns from looking at terms modulo "+i+":");
					for(int j=0; j<i; j++){
						for(int k=1; k<length/i; k++){
							skiparray[k]=array[k*i-j];
						}
						current=skiparray[1];
						startoBlock=1;
						out.print("      ");
						for(int k=2; k<length/i; k++){
							if(skiparray[k]!=current){
								out.print((k-startoBlock)+",");
								current=skiparray[k];
								startoBlock=k;
							}
						}
						out.println(1+(length/i)-startoBlock);
					}
				}
				out.println("");
				
                for(int i=1; i<=length; i++){ // randomly flip ~40% of integers
					if(Math.random()<0.33333){ array[i]=1-array[i]; }
				}
				startingcount=countAPs(array,length,a,b,c);
				round++; // now to the next round
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

// count the total number of monochromatic solutions	
	public static long countAPs(int[] array, int length,int a, int b, int c){
		long runningCount=0;
		for(int i=1;a*i+b<=c*length&&i<=length;i++){
			for(int j=1;a*i+b*j<=c*length&&j<=length;j++){
				if((a*i+b*j)%c==0){
					int k=(a*i+b*j)/c;
					if(array[j]==array[k]&&array[i]==array[j]){
						runningCount++;
					}
				}
			}
		}
		return runningCount;
	}

// test if we should change the ith color
	public static boolean change(int[] array, int n, int i, int a, int b, int c){
		int[] count=new int[2];
		for(int j=1; j<=n; j++){
			int k;
			// x=i, y=j
			k=(a*i+b*j)/c;
			if(((a*i+b*j)%c==0)&&(k>=1)&&(k<=n)&&(array[j]==array[k])&&(i!=j)){
				count[array[j]]++; 
			}
			// x=j, y=i
			k=(a*j+b*i)/c;
			if(((a*j+b*i)%c==0)&&(k>=1)&&(k<=n)&&(array[j]==array[k])&&(i!=j)){
				count[array[j]]++;
			}
			// x=j, z=i
			k=(c*i-a*j)/b;
			if(((c*i-a*j)%b==0)&&(k>=1)&&(k<=n)&&(array[j]==array[k])&&(i!=j)){
				count[array[j]]++;
			}
		}
		if((count[0]<count[1]&&array[i]==1)||(count[1]<count[0]&&array[i]==0)){ return true; } else { return false; }
	}
}

