package mathe;

/**
 * Bearbeitung von Brüchen.
 * 
 * @author Daniel Weschke
 *
 */
public class Bruch{
	int zähler;
	int nenner;
	
	/**
	 * Bruch mit Nenner 1.
	 */
	public Bruch(){
		nenner = 1;
	}
	
	/**
	 * Bruch mitübergebenen zähler und Nenner 1.
	 * 
	 * @param z zähler
	 */
	public Bruch(int z){
		this();
		zähler = z;
	}
	
	/**
	 * Bruch mitübergebenen zähler und Nenner.
	 * 
	 * @param z zähler
	 * @param n Nenner
	 */
	public Bruch(int z, int n){
		this(z);
		if(n>0) nenner = n;
	}

	/**
	 * Custom Constructor, der aus einer Gleitkommazahl einen Bruch ermittelt.
	 * Um Rundungsfehler zu vermeiden wird der Bruch ueber die Stringdarstellung der Gleitkommazahl ermittelt.
	 * @param gleitKommaZahl Zahl, die in einen Bruch umgewandelt werden soll.
	 */
	public Bruch(double gleitKommaZahl) {

		// Damit keine Rundungsfehler auftreten die Zahl in einen String wandeln
		String ds = "" + gleitKommaZahl;

		// String am Punkt splitten
		// Achtung: Beim Splitten ist der Punkt ein Sonderzeichen,
		// daher der Umweg ueber ein "beliebiges" Zeichen (hier "p")
		ds = ds.replace(".", "p");
		String[] sa = ds.split("p");

		// Zaehler und Nenner bestimmen
		nenner = (int) Math.pow(10, sa[1].length());
		zähler = (int) (gleitKommaZahl * nenner);

		// Den Bruch kuerzen
		int faktor = ggt_fast(zähler, nenner);
		nenner /= faktor;
		zähler /= faktor;
		
		// Die Assertion prueft, ob der Nenner != 0 ist.
		// Falls die Auswertung true ergibt ist alles OK
		assert nenner != 0 : "Division durch 0!";

		// Testen, ob das Ergebnis stimmt oder ob Rundungsfehler vorhanden sind
		assert 1.0 * zähler / nenner == gleitKommaZahl : "Rundungsfehler!!";
	}

	/**
	 * Addition zweier Brüche.
	 * 
	 * @param a zu addierender Bruch
	 * @return Summe
	 */
	public Bruch plus(Bruch a){
		return new Bruch(zähler*a.nenner+a.zähler*nenner,nenner*a.nenner);
	}

	/**
	 * Subtraktion zweier Brüche.
	 * 
	 * @param a zu subtrahierender Bruch
	 * @return Differenz
	 */
	public Bruch minus(Bruch a){
		Bruch b = new Bruch(-a.zähler,a.nenner);
		return this.plus(b);
	}

	/**
	 * Multikation zweier Brüche.
	 * 
	 * @param a zu multiplizierder Bruch
	 * @return Produkt
	 */
	public Bruch mal(Bruch a){
		return new Bruch(zähler*a.zähler,nenner*a.nenner);
	}

	/**
	 * Division zweier Brüche.
	 * 
	 * @param a zu dividierender Bruch
	 * @return Verhältnis
	 */
	public Bruch durch(Bruch a){
		return this.mal(a.kehrwert());
	}

	/**
	 * Kehrwert vom Bruch.
	 * 
	 * @return Reziprok
	 */
	public Bruch kehrwert(){
		return new Bruch(nenner,zähler);
	}
	
	/**
	 * Gößten gemeinsamen Teiler vom Bruch.
	 * Nach dem klassischen euklidischen Algorithmus.
	 * 
	 * @return größten gemeinsamen Teiler
	 */
	public int ggt(){
		int a = zähler<0 ? -zähler : zähler;
		int b = nenner<0 ? -nenner : nenner;
		if(a>0 && b>0){
			while(a!=b){
				if(a>b){
					a = a - b;
				} else {
					b = b - a;
				}
			}
		}
		return a;
	}
	
	/**
	 * Größter gemeinsamer Teiler vom Bruch.
	 * Nach dem modernen euklidischen Algorithmus.
	 * 
	 * @return größten gemeinsamen Teiler
	 */
	public int ggt_fast(){
		int a = zähler<0 ? -zähler : zähler;
		int b = nenner<0 ? -nenner : nenner;
		int c;
		if(a>0 && b>0){
			while(b!=0){
				c = a%b;
				a = b;
				b = c;
			}
		}
		return a;
	}
	
	/**
	 * Größter gemeinsamer Teiler vom Bruch.
	 * Nach dem modernen euklidischen Algorithmus.
	 * 
	 * @return größten gemeinsamen Teiler
	 */
	public int ggt_fast(int zähler, int nenner){
		int a = zähler<0 ? -zähler : zähler;
		int b = nenner<0 ? -nenner : nenner;
		int c;
		if(a>0 && b>0){
			while(b!=0){
				c = a%b;
				a = b;
				b = c;
			}
		}
		return a;
	}
	
	/**
	 * Kürzen des Bruches.
	 * 
	 * @return gekürzter Bruch
	 */
	public Bruch kürzen(){
		if(zähler!=0 && nenner!=0)
			return new Bruch(zähler/ggt(),nenner/ggt());
		else
			return this;
	}
	
	/**
	 * Vergleich zweier Brüche.
	 * 
	 * @param q zu vergleichender Bruch
	 * @return Bitcode:<br />
	 * <code> 1</code> Bruch ist größer<br />
	 * <code>-1</code> Bruch ist kleiner<br />
	 * <code> 0</code> Bruch ist gleich groß<br />
	 */
	public int compareTo(Bruch q){
		double a = (double) zähler/nenner;
		double b = (double) q.zähler/q.nenner;
		if(a > b) return 1;	else
		if(a < b) return -1; else
		return 0;
	}

	/**
	 * Textumwandlung zur Ausgabe.
	 */
	@Override
	public String toString(){
		Bruch a = this.kürzen();
		if(a.nenner!=1)
			return new String(a.zähler + "/" + a.nenner);
		else
			return new String(""+a.zähler);
	}
}
