public class EquatorialCoordinates {
    double RA;
    double DEC;
    double epoch;
    
    public EquatorialCoordinates() {}
    
    public EquatorialCoordinates(double ra, double dec) { RA = ra; DEC = dec; epoch=2000.0; }
    
    public EquatorialCoordinates(double ra, double dec, double aEpoch) { RA = ra; DEC = dec; epoch = aEpoch; }


    //  Precession from Lederle & Schwan, Astronomy and Astrophysics, 134, 1-6 (1984)
    public void precess(double newEpoch) {
	if(newEpoch == epoch) return; 

	double julianCentury = 36524.2198781 / 365.25;
	double day = 1.0 / 365.25;

	double tau = (epoch - 2000.0) / julianCentury;
	double t = (newEpoch - epoch) / julianCentury;

	
	double eta = (2305.6997 + (1.39744 + 0.000060 * tau) * tau 
		       + (0.30201 - 0.000270 * tau + 0.017996 * t) * t) * t * Unit.ArcSECOND;
	
	double z = (2305.6997 + (1.39744 + 0.000060 * tau) * tau 
		    + (1.09543 + 0.000390 * tau + 0.018326 * t) * t) * t * Unit.ArcSECOND;

	double theta = (2003.8746 - (0.85405 + 0.000370 * tau) * tau
			- (0.42707 + 0.000370 * tau + 0.041803 * t) * t) * t * Unit.ArcSECOND;	
	
	Matrix P = R3(-z).dot(R2(theta)).dot(R3(-eta));

	double cosDEC = Math.cos(DEC);

	double[] v = { cosDEC * Math.cos(RA), cosDEC * Math.sin(RA), Math.sin(DEC) };
	
	Matrix l0 = new Matrix(v, Matrix.COLUMN_VECTOR);

	double[] l = P.dot(l0).transpose().value[0];

	double ml = Math.sqrt(l[0] * l[0] + l[1] * l[1]);

	double oldRA = RA;
	RA = (l[0] == 0.0) ? Math.PI / 2.0 : Math.atan(l[1] / l[0]);
	DEC = (ml == 0.0) ? Math.PI / 2.0 : Math.atan(l[2] / ml);

	// Figure out the right branch of atan.
	// Increase branch number until within 1/2 branch interval of the starting value.
	// This way the correct branch is expected to be identified even when the initial and final
	// coordinates are close to, but on opposite sides of, a branch boundary.
	while(oldRA - RA > 0.5 * Math.PI) RA += Math.PI;

	epoch = newEpoch;
    }

    Matrix R1(double phi) {
	double c = Math.cos(phi);
	double s = Math.sin(phi);

	double[][] R = { {  1, 0, 0 }, 
			 {  0, c, s },
			 {  0,-s, c } };
	return new Matrix(R);
    }

    Matrix R2(double phi) {
	double c = Math.cos(phi);
	double s = Math.sin(phi);

	double[][] R = { {  c, 0,-s }, 
			 {  0, 1, 0 },
			 {  s, 0, c } };	
	return new Matrix(R);
    }
	
    Matrix R3(double phi) {
	double c = Math.cos(phi);
	double s = Math.sin(phi);
	
	double[][] R = { {  c, s, 0 }, 
			 { -s, c, 0 },
			 {  0, 0, 1 } };	
	return new Matrix(R);		
    }
}
