// by jeff plotkin
// for cs201 lab 3 and extra credit
// 12-18-2005
public class A {	
	static int[] R = new int[16];
	static float[] F = new float[8];	
	static int[] stack = new int[1024*16]; //64kb stack space
	
	static int ROWS = 32, COLS = 3, WORDS = (ROWS*COLS);
	static int[] screen = new int[WORDS];	
	
	static float threshold = 10.0f;
	static float scale = 1.0f;
	
	
	public static void main(String[] args){
		float[][] p = new float[4][2];
		
		R[13]=stack.length; // SP at the end of the stack and stack grows down
		clearScreen();
		
		int count = 0, i = 0;
		
		for(int k = 0; k < 12; k++){
			R[k] = 130+k;
		}
		for(int k = 0; k < 8; k++){
			F[k] = 130+k;
		}
		
		
		switch(args.length){
			case 2:
				R[0] = Integer.parseInt(args[0]);
				R[1] = Integer.parseInt(args[1]);
				setPixel();
				break;
			
			case 3:
				for(i = 0; i < 3; i++)
					R[i] = Integer.parseInt(args[i]);
				horizonLine();
				break;
			
			case 4:
				for(i = 0; i < 4; i++)
					R[i] = Integer.parseInt(args[i]);
				plotLine();
				break;
			
			case 8:				
			case 9:
			case 10:
				do{
					float[] t = new float[2];
					t[0] = Float.parseFloat(args[i]);
					t[1] = Float.parseFloat(args[i+1]);
					p[count++] = t;
					i += 2;
				}while(i < 8);				
				if(args.length > 8) threshold = Float.parseFloat(args[8]);
				if(args.length > 9) scale = Float.parseFloat(args[9]);
				plotCurve(p[0], p[1], p[2], p[3]);
				break;	
		}
						
		
		printScreen();
		System.out.println();
		
		for(i = 0; i < 12; i++){
			System.out.println(R[i]);
		}
		for(i = 0; i < 8; i++){
			System.out.println(F[i]);
		}
	}
	
	// done
	static float[] midPoint(float[] p1, float[] p2){
		float x = p2[0] + p1[0],
			  y = p2[1] + p1[1];
			  
		float[] result = new float[2];
		result[0] = x/2;
		result[1] = y/2;
		return result;
	}
	
	// done
	static float distance(float[] p1, float[] p2){
		float x = p2[0] - p1[0],
			  y = p2[1] - p1[1];
		return (float)Math.sqrt(x*x + y*y);
	}
		
	// done
	static void plotCurve(float[] p0, float[] p1, float[] p2, float[] p3){		
		float[] p01, p12, p23, p012, p123, p0123;
		if(distance(p0, p3) < threshold){
			
			stack[--R[13]] = R[0];
			stack[--R[13]] = R[1];
			stack[--R[13]] = R[2];
			stack[--R[13]] = R[3];
			
			R[0] = (int)Math.round(p0[0]);
			R[1] = (int)Math.round(p0[1]);
			R[2] = (int)Math.round(p3[0]);
			R[3] = (int)Math.round(p3[1]);
			
			plotLine();
			
			R[3] = stack[R[13]++];
			R[2] = stack[R[13]++];
			R[1] = stack[R[13]++];
			R[0] = stack[R[13]++];
			return;
		}	
		p01 = midPoint(p0, p1);
		p12 =  midPoint(p1, p2);
		p23 = midPoint(p2, p3);
		p012 = midPoint(p01, p12);
		p123 = midPoint(p12, p23);
		p0123 = midPoint(p012, p123);
		
		plotCurve(p0, p01, p012, p0123);
		plotCurve(p0123, p123, p23, p3);			
	}
	
	
	static void fixPoints(){		
		if(R[0] > R[2]){
			stack[--R[13]] = R[4];  // push r4
			//x
			R[4] = R[0];
			R[0] = R[2];
			R[2] = R[4];
			
			//y
			R[4] = R[3];
			R[3] = R[1];
			R[1] = R[4];
			
			R[4] = stack[R[13]++]; // pop r4
		}
		
		// MOV PC, LR		
	}
	
	
	// x1,y1, x2,y2 in R0-R3
	static void plotLine(){
		stack[--R[13]] = R[0];
		stack[--R[13]] = R[1];
		stack[--R[13]] = R[2];
		stack[--R[13]] = R[3];
		stack[--R[13]] = R[4];
		stack[--R[13]] = R[5];
		stack[--R[13]] = Float.floatToIntBits(F[0]);
		stack[--R[13]] = Float.floatToIntBits(F[1]);
		
		fixPoints();
		
		R[5] = R[0]; // x_initial
		R[4] = R[2]; // x final
		
		stack[--R[13]] = R[1]; // push y initial
		
		R[0] = R[2]-R[0]; // delta x
		R[1] = R[3]-R[1]; // delta y
		
		R[3] = stack[R[13]++]; // pop y initial
		
		F[0] = ((float)R[1])/((float)R[0]); // m, slope
		if(Math.abs(F[0]) > 1.0){
			F[1] = Float.intBitsToFloat(stack[R[13]++]);
			F[0] = Float.intBitsToFloat(stack[R[13]++]);
			R[5] = stack[R[13]++];
			R[4] = stack[R[13]++];
			R[3] = stack[R[13]++];
			R[2] = stack[R[13]++];
			R[1] = stack[R[13]++];
			R[0] = stack[R[13]++];
			plotLineVector();
		} else {
			System.out.println(R[0]);
			System.out.println(R[1]);
			System.out.println(F[0]);
			R[0] = R[5];
			
			do {
				
				R[1] = R[0] - R[5]; // (x - x1)
				F[1] = F[0] * R[1]; // m(x - x1)
				R[1] = (int)Math.round(F[1]);
				R[1] = R[1] + R[3]; // m(x - x1) + y1
				
				setPixel();
				R[0]++;
			}while(R[0] <= R[4]);
			
			
			F[1] = Float.intBitsToFloat(stack[R[13]++]);
			F[0] = Float.intBitsToFloat(stack[R[13]++]);
			R[5] = stack[R[13]++];
			R[4] = stack[R[13]++];
			R[3] = stack[R[13]++];
			R[2] = stack[R[13]++];
			R[1] = stack[R[13]++];
			R[0] = stack[R[13]++];	
		}			
	}
	
	
	// x1,y1, x2,y2 in R0-R3
	static void plotLineVector(){
		System.out.println("plotLineVector");
		stack[--R[13]] = R[0]; // x0
		stack[--R[13]] = R[1]; // y0
		stack[--R[13]] = R[2]; // x1
		stack[--R[13]] = R[3]; // y1
		stack[--R[13]] = R[4];
		stack[--R[13]] = R[5];
		stack[--R[13]] = R[6];
		stack[--R[13]] = R[7];
		stack[--R[13]] = R[8];
		stack[--R[13]] = Float.floatToIntBits(F[0]);
		stack[--R[13]] = Float.floatToIntBits(F[1]);
		stack[--R[13]] = Float.floatToIntBits(F[2]);
		
		fixPoints();
		
		R[4] = R[2]-R[0]; // delta x
		R[5] = R[3]-R[1]; // delta y
		F[0] = R[4]*R[4];
		F[1] = R[5]*R[5];
		F[1] = F[1] + F[0]; // dx^2 + dy^2
		F[0] = (float)Math.sqrt(F[1]); // length
		F[1] = R[4]/F[0];	// x
		F[2] = R[5]/F[0];	// y
		
		R[8] = 0;
		
		do {			
			R[6] = (int)(F[1]*R[8]); // xt
			R[6] = R[6] + R[0];
			R[7] = (int)(F[2]*R[8]); // yt
			R[7] = R[7] + R[1];
			
			stack[--R[13]] = R[0];
			stack[--R[13]] = R[1];
			
			R[0] = R[6];
			R[1] = R[7];			
			setPixel();
			
			R[1] = stack[R[13]++];
			R[0] = stack[R[13]++];	
			
			R[8]++;
		}while(R[8] < (int)F[0]); // t < length
		
		
		
		F[2] = Float.intBitsToFloat(stack[R[13]++]);
		F[1] = Float.intBitsToFloat(stack[R[13]++]);
		F[0] = Float.intBitsToFloat(stack[R[13]++]);
		R[8] = stack[R[13]++];
		R[7] = stack[R[13]++];
		R[6] = stack[R[13]++];
		R[5] = stack[R[13]++];
		R[4] = stack[R[13]++];
		R[3] = stack[R[13]++];
		R[2] = stack[R[13]++];
		R[1] = stack[R[13]++];
		R[0] = stack[R[13]++];		
	}
	
	
	static void plotLineEndPoints(float[] p1, float[] p2){		
		stack[--R[13]] = R[0];
		stack[--R[13]] = R[1];
		
		
		R[0] = Math.round(p1[0]);
		R[1] = Math.round(p1[1]);
		setPixel();
		
		R[0] = Math.round(p2[0]);
		R[1] = Math.round(p2[1]);
		setPixel();	
		
		
		R[1] = stack[R[13]++];
		R[0] = stack[R[13]++];
	}	
	
	
	
	static void printScreen(){			
		stack[--R[13]] = R[0];
		stack[--R[13]] = R[1];
		stack[--R[13]] = R[2];
		
		R[1] = 0;
		R[2] = -1;
		do {			
			
			if(R[2] == 2){
				R[0] = (int)'\n';
				System.out.print((char)R[0]);
				R[2] = 0;
			} else {
				R[2]++;
			}
			
			R[0] = screen[R[1]];
			printBin();
			R[1]++;
			
		}while(R[1] < WORDS);
		
		R[2] = stack[R[13]++];
		R[1] = stack[R[13]++];
		R[0] = stack[R[13]++];
	}
	
	
	
	// arguments in R[0],R[1]
	// kills R2
	static void fitToScreen(){			
		R[2] = COLS*32;
		if(R[0] >= R[2]) R[0] = R[2] - 1;		
		if(R[1] >= ROWS) R[1] = ROWS - 1;
		
		if(R[0] < 0) R[0] = 0;
		if(R[1] < 0) R[1] = 0;
		
		R[2] = R[1] >> 5; // divide by 32
		R[2] = R[2] << 5; // mult by 32
		R[2] = R[1] - R[2];
	}
	
	
	// arguments in R[0],R[1]
	static void setPixel(){
			stack[--R[13]] = R[4];
			stack[--R[13]] = R[3];
			stack[--R[13]] = R[2];  // push r2
			stack[--R[13]] = R[1];  // push r2
			stack[--R[13]] = R[0];  // push r2
			
			fitToScreen();			
			getIndex();
			R[2] = R[0];
			R[2] = 31 - R[2]; // reverse subtract
			R[4] = 1 << R[2];  // set the target bit in an empty word
			R[2] = screen[R[3]]; // load the screen word at the index
			R[2] = R[2] | R[4]; // sit the target bit in the screen word
			screen[R[3]] = R[2]; // store the screen word back to the array
			
			R[0] = stack[R[13]++];
			R[1] = stack[R[13]++];
			R[2] = stack[R[13]++];
			R[3] = stack[R[13]++];
			R[4] = stack[R[13]++];
	}
	
	
	// arguments in R[0],R[1]
	// result in R[3]
	static void getIndex(){
		stack[--R[13]] = R[0];  // push r0
		stack[--R[13]] = R[1];  // push r1
		
		R[0] = R[0] >> 5;	
		R[1] = R[1]*COLS;
		R[3] = 	R[0]+R[1];
		
		R[1] = stack[R[13]++];
		R[0] = stack[R[13]++];
	}
	
	
	static void horizonLine(){
		
		stack[--R[13]] = R[1];  // push r1
		stack[--R[13]] = R[2];  // push r1
		stack[--R[13]] = R[3];  // push r1
		// r0,r1,r2 -> x0,x1,y
		R[3] = R[2];
		R[2] = R[1];
		R[1] = R[3];
		// r0,r1,r2,r3 -> x0,y,x1,y
		System.out.println(R[0] + " " +
		R[1] + " " +
		R[2] + " " +
		R[3] + " ");
		plotLine();
		
		R[3] = stack[R[13]++];
		R[2] = stack[R[13]++];
		R[1] = stack[R[13]++];
	
	}
	
	
	static void clearScreen(){ 
		stack[--R[13]] = R[1];// push r1
		R[1] = 0; 
		
		do {				
			screen[R[1]] = 0;				
			R[1]++;			
		}while(R[1] < WORDS);
		
		R[1] = stack[R[13]++];
	}
	
	
	
	
	// mostly done
	// print each bit of R[0]
	static void printBin(){
		stack[--R[13]] = R[0];  // push r1
		stack[--R[13]] = R[1];  // push r2
		stack[--R[13]] = R[2];  // push r3
		stack[--R[13]] = R[12];  // push ip
		
		R[12] = R[13];
		
		R[1] = 0;
		do {
			R[2] = R[0] & 1;
			stack[--R[13]] = R[2];
			
			R[0] = R[0] >> 1;			
			R[1]++;
		}while(R[1] < 32);
		
		do {
			R[0] = stack[R[13]++];
			if(R[0] == 0) R[1] = (int)'.';
			else R[1] = (int)'*';
			System.out.print( ((char)R[1]) );
		}while(R[13] != R[12]);
		
		R[12]= stack[R[13]++];  // pop ip
		R[2] = stack[R[13]++];
		R[1] = stack[R[13]++];
		R[0] = stack[R[13]++];
	}
	
}