/**
 * Ephraim Rothschild
 * June 13th 2014
 */
import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Scanner;
import java.lang.Math;

class Trig {
   /**
    *Global Variables
    **/
	static final double pi2 = 6.28318530718;
	public long totalTime = 0L;
	static final double piover2 = 1.57079632679;
	static final double plusinfty = Double.POSITIVE_INFINITY;
	static final double minusinfty = Double.NEGATIVE_INFINITY;
	static final double factoriallist[] =
							 new double[]{
						 -6.0,120.0,-5040.0,362880.0,-39916800.0,
						 6227020800.0,-1307674368000.0,355687428096000.0,
						-121645100408832000.0,51090942171709440000.0,
						-25852016738884976640000.0,
						 15511210043330985984000000.0,
						-10888869450418352160768000000.0,
						 8841761993739701954543616000000.0
						 };
//Begin Program
	public static void main(String[] args) {
		Trig mytrig = new Trig();
		double[] input = mytrig.getInput();
		double[][] output = mytrig.calculate(input);
		mytrig.OutputResults(output);
		Trig testIt = new Trig();
		testIt.timeIt(input);
	}
	
//Begin Timing
	public void timeIt(double[] input) {
		double overhead = 0L;
		totalTime = 0L;
		
		for (int i = 0; i < 1000001; i++) {
			calculate(input);
			if (i == 0) {
				overhead = totalTime;
				totalTime = 0L;
			}
		}
		double averageTime = ((Double.valueOf(totalTime-overhead))/1000000.0);
		double perAngleTime = averageTime/input.length;
		double perOpperationTime = perAngleTime/3;
		NumberFormat formatter = new DecimalFormat("0000.0000");
		System.out.println("\n-----------------------------------------------------");
		System.out.println("                    TIME RESULTS:                    ");
		System.out.println("-----------------------------------------------------");
		System.out.println("Average Total  Runtime:.......... " + formatter.format(averageTime) + " nsec");
		System.out.println("                                = " + formatter.format(averageTime/1000) + " usec\n");
		System.out.println("Average Runtime Per Angle:....... " + formatter.format(perAngleTime) + " nsec");
		System.out.println("                                = " + formatter.format(perAngleTime/1000) + " usec\n");
		System.out.println("Average Runtime Per Opperation:.. " + formatter.format(perOpperationTime) + " nsec");
		System.out.println("                                = " + formatter.format(perOpperationTime/1000) + " usec");
	}
	
//Begin Input
	double[] getInput() {
		Scanner in;
		ArrayList<Double> input = new ArrayList<Double>();
		try {
			in = new Scanner(new File("trig.in"));
		} catch (FileNotFoundException e) {
			new FileNotFoundException("Failed to read from file. Reading from stdin instead...").printStackTrace();
			in= new Scanner(System.in);
		}
		while (in.hasNextLine()) {
			Double toadd = Double.parseDouble(in.nextLine());
			input.add(toadd);	
		}
		in.close();
		double[] returnable = new double[input.size()];
		for(int i = 0; i < input.size(); i++) {returnable[i] = input.get(i);}
		return returnable;
	}
	
//Begin OutputStream Choice
	void OutputResults(double[][] outList) {
		PrintStream out;
		try {
			out = new PrintStream("trig.out");
			PrintOutput(outList, out);
			PrintOutput(outList, System.out);
		} catch (FileNotFoundException e) {
			new FileNotFoundException("Failed to write to file. Printing to stdout instead...").printStackTrace();
			PrintOutput(outList, System.out);
		}
	}
	
//Begin Output
	static void PrintOutput(double[][] outList, PrintStream out) {
	    /**
	     *outList[0][i] holds original angles
	     *outList[1][i] holds sin values
	     *outList[2][i] holds cos values
	     *outList[3][i] holds tan values
	     */
		NumberFormat formatter = new DecimalFormat("0.0000000E0");
	    out.println("-----------------------------------------------------");
	    out.println("                    TRIG RESULTS                     ");
	    out.println("-----------------------------------------------------");
		for (int i=0; i<outList[0].length; i++) {
			out.println("For Angle: " + outList[0][i]);
			
			out.println("      sin: " + formatter.format(outList[1][i]));
			out.println("      cos: " + formatter.format(outList[2][i]));
			if (Double.valueOf(outList[3][i]).isInfinite() || Double.valueOf(outList[3][i]).isNaN()) {
				out.println("      tan: " + outList[3][i]);
			}
			else out.println("      tan: " + formatter.format(outList[3][i]));
		}
		if (out != System.out) out.close();
	}
	
//Begin Calculations
	double[][] calculate(double[] IOList) {
		double[][] outList = new double[4][IOList.length];
		double sin;
		double cos;
		double tan;
		double rads;
		int i = 0;
		long calctime = 0L;
		long startTime;
		long endTime;
		for (double input : IOList) {
			startTime = System.nanoTime();
			rads = toRad(input);
			sin=sin(rads);
			cos = ((piover2-rads)>=0) ? Math.sqrt((1.0-(sin*sin))) : -Math.sqrt((1.0-(sin*sin)));
			tan = (cos!=0.0d) ? sin/cos : ((sin>0) ? plusinfty : minusinfty);
			endTime = System.nanoTime();
			calctime = calctime + endTime - startTime;
			outList[0][i] = input;
			outList[1][i] = sin;
			outList[2][i] = cos;
			outList[3][i] = tan;
			i++;
		}
		totalTime = totalTime + calctime;
		return outList;
	}

//Convert Degrees to Radians
	double toRad(double deg) {
		double rev = deg/360.0;
		return (rev>1 || rev<0) ? Math.abs(rev - ((int)rev))*pi2 : rev*pi2;
	}
	
//Get sin
	double sin(double angle) {
		double sqr = angle*angle;
		double value = angle;
		for (double fct : factoriallist) {
			value += ((angle*=sqr)/fct);
		}
		return ((long)((value + Math.copySign(0.0000000005d, value))*10000000.0))/10000000.0;
	}	
}