package ProGAL.geom2d;

import java.awt.Color;

import ProGAL.math.Matrix;

public class ApolloniusSolver {

	/** Solves the Apollonius problem of finding a circle tangent to three other circles in the plane. 
	 * The method uses approximately 68 heavy operations (multiplication, division, square-roots).
	 * @param c1 One of the circles in the problem 
	 * @param c2 One of the circles in the problem
	 * @param c3 One of the circles in the problem
	 * @param s1 An indication if the solution should be externally or internally tangent (-1/+1) to c1
	 * @param s2 An indication if the solution should be externally or internally tangent (-1/+1) to c2
	 * @param s3 An indication if the solution should be externally or internally tangent (-1/+1) to c3
	 * @return The solution to the problem of Apollonius. 
	 */
	public static Circle solveApollonius(Circle c1, Circle c2, Circle c3, int s1, int s2, int s3){
		Point[] centers = {c1.center, c2.center, c3.center};
		double[] radii = {c1.radius, c2.radius, c3.radius};
		int[] s = {s1,s2,s3};
		return solveApollonius(centers, radii, s);
	}

	/**
	 * @hops 66
	 */
	private static Circle solveApollonius(Point[] centers, double[] radii, int[] s){
		
		//Step 1. Rewrite to linear system
		Matrix A = new Matrix(2,4);
		for(int i=0;i<2;i++){//i: row
			for(int j=0;j<2;j++){ //j: col
				A.set(  i, j, 2*(centers[i+1].get(j)-centers[0].get(j))  );//1HOp * 2 * 2 = 4
			}
			A.set(  i, 2, 2*(s[0]*radii[0]-s[i+1]*radii[i+1])  );//3HOp * 2 = 6

			double sum = 0;
			for(int j=0;j<2;j++) 
				sum+=centers[i+1].get(j)*centers[i+1].get(j) - centers[0].get(j)*centers[0].get(j);//2HOp * 2 * 2
			sum+=radii[0]*radii[0]-radii[i+1]*radii[i+1];//2HOp * 2
			A.set(i, 3, sum);
		}

		//Step 2. Simplify linear system
		A.reduceThis();// m*n+(n-1)^2*m = 2*4+3*3*2 = 26HOp
		double M = A.get(0, 3);
		double N = -A.get(0, 2);
		double P = A.get(1, 3);
		double Q = -A.get(1, 2);

		//Step3. Find tangent sphere
		//First find r_s
		double a = N*N+Q*Q-1;//2HOp
		double b = 2*(  (M-centers[0].get(0))*N + (P-centers[0].get(1))*Q + s[0]*radii[0]  );//4HOp
		double c = 
				(M-centers[0].get(0))*(M-centers[0].get(0)) + 
				(P-centers[0].get(1))*(P-centers[0].get(1)) - 
				radii[0]*radii[0]; //3HOp
		double r_s = (-b+Math.signum(a)*Math.sqrt(b*b-4*a*c))/(2*a);//7HOp
		double x_s = M+N*r_s;//1HOp
		double y_s = P+Q*r_s;//1HOp
		return new Circle(new Point(x_s, y_s), r_s);
	}


}
