package real;

public class Formats {
	
	private static int defaultprec = 6;
	private final static String Plus = " ";
	private final static String Minus = "-";
	private final static long maxLong = (1L << 63) - 1L;

	final static double tenpower[] = {
		1.0, 
		10.0, 
		100.0, 
		1000.0, 
		10000.0, 
		100000.0, 
		1000000.0, 
		10000000.0, 
		100000000.0, 
		1000000000.0, 
		10000000000.0, 
		100000000000.0, 
		1000000000000.0, 
		10000000000000.0, 
		100000000000000.0, 
		1000000000000000.0, 
		10000000000000000.0, 
		100000000000000000.0,
		1000000000000000000.0, 
		10000000000000000000.0};
		
	public static String toScientific(double x, int w) {
		return(new DecimalDouble(x).toString(w));
	}

	
	// fits x packed right into w characters if possible
	public static String toString(double x, int w) {
		int n = w-2;
		// n digits + sign + point
		double ax = Math.abs(x);		
		if (ax >= (tenpower[n] - 0.5) || ax < (5/tenpower[n])) {
			return(toScientific(x, w));
		}
		
		// x now in range
		// Example: say n = 3
		// 0.005 <= x < 999.5 
		// 99.95 <= x < 999.5 = (10^3 - 0.5) => xxx.
		// 9.995 <= x < 99.95 => xx.x
		// 0.005 <= x < 9.995 = 10 - 0.005 => x.xx
		char word[] = new char[w];
		if (x < 0) {
			word[0] = '-';
			x = -x;
		} else word[0] = ' ';
		double T = 10 - 5/tenpower[n];
		if (x < T) {
			// round up: x = 0.7754 => x + 0.005 = 0.7804 => 0.78
			x += 5/tenpower[n];
			int N = (int) x;
			word[1] = (char) ('0' + N);
			word[2] = '.';
			for (int i=3;i<w;i++) {
				x -= N;
				x *= 10;
				N = (int) x;
				word[i] = (char) ('0' + N);
			}
			return(new String(word));
		}
		// 9.995 <= x < 99.95 or ...
		// 10^e <= x/0.9995 < 10^{e+1} => [e+1 x's].xx
		DecimalDouble X = new DecimalDouble(10*x/T);
		// do the integral part
		x += 5/tenpower[n-X.e];
		double y = X.f;
		int N = (int) y;
		word[1] = (char) ('0' + N);
		int i = 2;
		while (i < 2+X.e) {
			// X.e + 1 digits in all before .
			y -= N;
			y *= 10;
			N = (int) y;
			word[i++] = (char) ('0' + N);
		}
		// do the fractional part
		word[i++] = '.';
		while (i < w) {
			y -= N;
			y *= 10;
			N = (int) y;
			word[i++] = (char) ('0' + N);
		}
		return(new String(word));
	}
	
	static int strlen(String s) {
		int i = 0;
		while (i < s.length() && s.charAt(i) != '\0') {
			i++;
		}
		return(i);
	}

	public static String toString(double x) {
		return(toString(x, defaultprec));
	}

	static public String oldToString(double x, int prec) {
		char S[] = new char[prec];
		long m;
		int i;
		char c;
		double p = 1.0;
		double y;
		String sign = Plus;
		int e = 0;

		if (Double.isNaN(x)) {
			return("***");
		}
		if (x < 0) {
			sign = Minus;
			x = -x;
		} 
		if (x < 5*Math.pow(10, -prec-1)) {
			char word[] = new char[prec+3];
			word[0] = ' ';
			word[1] = '0';
			word[2] = '.';
			for (int k=0;k<prec;k++) word[3+k] = '0';
			return(new String(word));
		}

		if (x > Math.pow(10, 16 - prec)) {
			// e = log_{10} x
			e = (int) (Math.log(x)/Math.log(10));
			// x out of range
			// replace x by x*10^-e
			x *= Math.pow(10, -e);
			if (x > 10) {
				x /= 10.0;
				e += 1;
			} else if (x < 1) {
				x *= 10.0;
				e -= 1;
			}
		}
		// find 10^prec and round off x 
		if (prec >= 17) prec = 16;
		p = tenpower[prec];
		x *= p;
		// System.err.print("[x]="+x);
		x += 0.5;
		x /= p;
		long intx = (long) x;
		x -= (double) intx;		
		//System.err.print("[x]="+x);
		boolean isZero = true;
		//System.err.print("[x]="+x);
		for (i=0;i<prec;i++) {
			x = 10.0*x;
			//System.err.println("x="+x);
			m = (int) x;
			//System.err.println("m="+m);
			x = x - m;
			c = (char) (m + '0');
			S[i] = c;
			if (m > 0) isZero = false;
		}
		// s[i] = (char) 0;
		if (intx == 0 && isZero) {
			sign = "";
		}
		if (e != 0) {
			return(sign + String.valueOf(intx) + "." + new String(S) + "e" + e);
		} else if (prec > 0) {
			return(sign + String.valueOf(intx) + "." + new String(S));
		}
		else {
			return(sign + String.valueOf(intx));
		}
	}
	
	// pads out to width w if necessary
	// may overflow at left if necessary
	public static String toString(double x, int prec, int wd) {
		StringBuffer t;
		String s = oldToString(x, prec);
		int len = strlen(s);

		// s.length doesn't work!

		if (wd > len) {
			t = new StringBuffer(wd);
			for (int i=len;i<wd;i++) {
				t.append(" ");
			}
			t.append(s);
			s = t.toString();
			return(s);
		}
		else {
			return(s);
		}
	}
}

class DecimalDouble {
	// simulates floats:
	// fractional part, exponent, sign
	public double f;
	public int e;
	public int sign;
	
	public DecimalDouble(double x) {
		if (x < 0) {
			sign = -1;
			x = -x;
		} else if (x > 0) {
			sign = 1;
		} else {
			// x = 0.0
			sign = 0;
		}
		e = realFloor(Math.log(x)/Math.log(10.0));
		// 10^e <= x < 10^{e+1}
		// set f = x*10^-e
		f = x/Math.pow(10, e);
	}
	
	public double getValue() {
		return(sign*f*Math.pow(10, e));
	}
	
	public String toString() {
		return(toString(16));
	}
	
	int charsIn(int m) {
		int n;
		// |m| < 900
		if (m < 0) {
			m = -m;
			n = 1;
		} else n = 0;
		if (m == 0) n += 0;
		else if (m < 10) n += 1;
		else if (m < 100) n += 2;
		else n += 3;
		return(n);
	}
	
	public String toString(int w) {
		// w digits after the decimal
		// (sign)x.[w]e(sign)[4]
		// originally, w must be at least 6
		// but actually we should do this only 
		// if overflow develops as we go
		char word[];
		int I = 0;
		if (w < 1) w = 1;
		if (sign == 0) {
			// the number is 0
			if (w < 2) w = 2;
			word = new char[w];
			word[I++] = ' ';
			word[I++] = '0';
			if (w > 2) word[I++] = '.';
			for (int i=0;i<w-3;i++) {
				word[I++] = '0';
			}
			return(new String(word));
		}
		word = new char[w+9];
		if (Double.isNaN(f)) {
			return("#####");
		}
		if (sign < 0) {
			word[I++] = '-';
		} 
		// 0.005 in 3 digits = 0.01 so add in 0.005 = 5/1000
		double f0= f + 5*Math.pow(10, -w-1);
		if (e == 0 && f0 < 10) {
			// no "e"
			// just write it out without an "e"
			int N = (int) f0;
			word[I++] = (char) ('0' + N);
			word[I++] = '.';
			for (int i=0;i<w;i++) {
				f0 -= N;
				f0 *= 10;
				N = (int) f0;
				word[I++] = (char) ('0' + N);
			}
			return(new String(word));
		}
		else if (e == -1 && f0 >= 10) {
			// special carry 
			word[I++] = '1';
			word[I++] = '.';
			for (int i=0;i<w;i++) {
				word[I++] = '0';
			}
			return(new String(word));
		}
		
		int N = (int) f0;
		word[I++] = (char) ('0' + N);
		word[I++] = '.';
		for (int i=0;i<w;i++) {
			f0 -= N;
			f0 *= 10;
			N = (int) f0;
			word[I++] = (char) ('0' + N);
		}
		word[I++] = 'e';
		return(new String(word) + e);
	}
	
	public static int realFloor(double x) {
		int N = (int) x;
		if (N <= x) return(N);
		else return(N - 1);
	}
	
}

