import algds.IOUtils; // meine Version mit readDouble()
/**
 * AlgDs, WS99, Aufgabe 17.
 * Berechnet mit Naeherungsformeln Werte fuer e, exp(x) und sqrt(x)
 *
 * @author Christian Semrau, 13.11.1999, 24.12.1999
 * <a href="mailto:Christian.Semrau@student.uni-magdeburg.de">
 * Christian.Semrau@student.uni-magdeburg.de</a>
 */
public class ChS_Aufg17 {

/**
 * @return double Die Konstante e (ist identisch mit Math.E)
 */
public static double e() {
  double a, glied = 1; // fak(0)=1

  // Anzahl der Glieder feststellen, die groesser sind als die Grenze
  int n = 0;
  do {
	n++;
	glied = glied/n; // glied = 1/fak(n)
  } while (glied>1E-18);
  //bis die Glieder kleiner als 10 hoch -18 sind

  // Die Glieder vom kleinsten zum groessten aufsummieren (weniger Rundungsfehler)
  a=0;
  int i = n;
  do {
	glied = glied*n; // glied = 1/fak(n)
	n--;
	a = a + glied;
	//System.out.println(n+" "+glied+" "+a);
  } while (n>0);
  System.out.println(i+" Schritte.");
  return(a);
}

/**
 * Erlaubt die Auswahl eines der drei Aufgabenteile.
 */
public static void main(String[] args) {
  int alg;
  double x,y;

  System.out.println("AlgDs WS99 Aufgabe 17, Christian Semrau.");
  do {
	System.out.println(
	  "1: a) Berechnet e\n"+
	  "2: b) Berechnet exp(x) (-700<=x<=700)\n"+
	  // genaue Grenze zu 0 bzw. Infinity:
	  // VisualAge von IBM  : -707.4<=x<=707.4
	  // Kommandozeilen-java: -709.7<=x<=709.7
	  "3: c) Berechnet sqrt(x) (x>=0)\n"+
	  // fuer 4e-28<=x<=4e28 innerhalb von 50 Schritten
	  "0: Ende");
	do {
	  System.out.print("Welcher Algorithmus [1,2,3,0]?");
	  alg = IOUtils.readInt();
	} while (alg<0||alg>3);

	switch (alg) {
	case 1:
	  y=e();
	  System.out.println("e=2.718281828459045235360287471352... (Matheprogramm)");
	  System.out.println("e="+y+" (+ "+(Math.E-y)+") (Naeherung)");
	  System.out.println("E="+Math.E+" (+ "+(Math.E-2.71828182845904523536)+") (Math.E)");
	  break;
	case 2:
	  System.out.print("x:"); x = IOUtils.readFloat();
	  y=my_exp(x);
	  System.out.println("y=my_exp(x)="+y+" (+ "+(Math.exp(x)-y)+")" );
	  System.out.println("exp(x)     ="+Math.exp(x));
	  System.out.println("y/exp(x)-1 ="+(y/Math.exp(x)-1));
	  break;
	case 3:
	  do {
		System.out.print("x (>=0):"); x = IOUtils.readDouble();
	  } while (x<0); // wir lassen negative Zahlen gar nicht erst zu
	  double[] a = my_sqrt(x);
	  double diff = Math.abs(a[0]*a[0]/x-1);
	  System.out.println("y=my_sqrt(x)="+a[0]+" (+ "+(Math.sqrt(x)-a[0])+")" );
	  System.out.println("sqrt(x)     ="+Math.sqrt(x));
	  System.out.println("(y*y - x)/x ="+diff+" (soll <"+0.0001+" sein)");
	  if (diff>=0.0001) {
		System.out.println("nach "+Math.round(a[1])+" Schritten geforderte"+
		" Genauigkeit nicht erreicht.");
	  } // if
	  break;
	} // switch
  } while (alg!=0);
} // main()

/**
 * @return double e hoch x (relativer Fehler ca. 1e-12)
 * @param x double Der Exponent
 */
public static double my_exp(double x) {
  double a = 0, glied = 1;
  boolean invert = (x<0);
  // Der Fall (x<0) wird auf (x>0) zurueckgefuehrt
  if (invert) x = -x;
  int n = 0;
  do {
	a = a + glied;
	n++;
	glied = glied*x/n;
	//System.out.println(n+" "+glied+" "+a);
  } while (glied*1e18>a);
  // bis die Glieder kleiner als 1 Billionstel der Summe sind
  if (invert) a=1/a;
  // Rueckumwandlung des Ergebnisses
  System.out.println(n+" Schritte.");
  return(a);
}

/**
 * Liefert eine Naeherung fuer die Quadratwurzel von x.
 *
 * @return double[2]:
 *   [0] = Sqrt (Naeherung), NaN fuer negative x
 *   [1] = Schritte (max. 50) bis abs([0]*[0]/x - 1)<1e-4
 * @param x double
 */
public static double[] my_sqrt(double x) {
  double[] res = new double[2];

  if (x==0) { res[0]=0; res[1]=1; return(res); }
  // Die Iteration versagt fuer x=0
  if (x<0) { res[0]=Double.NaN; res[1]=0; return res; }
  // Not-A-Number fuer negative Werte

  if (x<1) {
  // Rueckfuerung auf x>1
	res = my_sqrt(1/x);
	res[0]=1/res[0];
	return res;
  }

  double a;
  int i = 0;
  // Diese Anweisung anstelle der nachfolgenden wuerde sichern, dass jede
  // Zahl innerhalb von 50 Schritten bestimmt werden kann (hoffe ich)
/*
  if (x>=9) {
	a = 3;
	while (a*a*1e30<x) {a*=1e15; i++;} // wirksam nur fuer SEHR grosse..
	while (a*a*1e6 <x) {a*= 1e3; i++;} // Eingabewerte, dort enormer Gewinn
  }
  else if (x>=3) a = x/2; else a = x;
*/
  a = x/2;

  System.out.println("Start mit "+a);
  double diff;
  do {
	a = (a + x/a)/2;
	diff = Math.abs( a*a/x - 1);
	//System.out.println(i+" "+a+" "+diff);
	i++;
  } while( (diff>0.0001) && (i<50) );
  res[0] = a; res[1] = i;
  System.out.println(i + " Schritte.");
  return(res);
}
} // class ChS_Aufg17