package pl.umk.fizyka.ulamekdemo;

import android.support.annotation.NonNull;

class UłamekException extends Exception
{
    public UłamekException(String message)
    {
        super(message);
    }
}

class MianownikUłamkaRównyZero extends UłamekException
{
    public MianownikUłamkaRównyZero(String message)
    {
        super(message);
    }

    public MianownikUłamkaRównyZero()
    {
        super("Ułamek: mianownik równy zeru");
    }

}

public class Ułamek implements Cloneable, Comparable<Ułamek>
{
    private int licznik = 0, mianownik = 1;

    private static Ułamek twórzZero() {
        try {
            return new Ułamek(0, 1);
        } catch (Exception exc) {
            //nic nie robimy
            return null;
        }
    }

    public static Ułamek ZERO = twórzZero();

    public void setLicznik(int licznik) {
        this.licznik = licznik;
    }

    public int getLicznik() {
        return licznik;
    }

    public void setMianownik(int mianownik) throws MianownikUłamkaRównyZero {
        if (mianownik == 0) throw new MianownikUłamkaRównyZero();
        this.mianownik = mianownik;
    }

    public int getMianownik() {
        return mianownik;
    }

    public void get(int[] arg) {
        arg[0] = licznik;
        arg[1] = mianownik;
    }

    public int[] get() {
        int[] result = new int[2];
        result[0] = licznik;
        result[1] = mianownik;
        return result;
    }

    public Ułamek(int licznik, int mianownik)
            throws MianownikUłamkaRównyZero {
        setLicznik(licznik);
        setMianownik(mianownik);
    }

    public Ułamek(int licznik)
            throws MianownikUłamkaRównyZero
    {
        this(licznik, 1);
    }

    public Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }

    //działania arytmetyczne
    public static Ułamek iloczyn(Ułamek u1, Ułamek u2) throws MianownikUłamkaRównyZero
    {
        Ułamek wynik = new Ułamek(
                u1.getLicznik() * u2.getLicznik(),
                u1.getMianownik() * u2.getMianownik());
        wynik.uprość();
        return wynik;
    }

    public static Ułamek iloraz(Ułamek u1, Ułamek u2) throws MianownikUłamkaRównyZero
    {
        Ułamek wynik = new Ułamek(
                u1.getLicznik() * u2.getMianownik(),
                u1.getMianownik() * u2.getLicznik());
        wynik.uprość();
        return wynik;
    }

    public static Ułamek suma(Ułamek u1, Ułamek u2) throws MianownikUłamkaRównyZero
    {
        Ułamek wynik = new Ułamek(
                u1.getLicznik() * u2.getMianownik() + u2.getLicznik() * u1.getMianownik(),
                u1.getMianownik() * u2.getMianownik());
        wynik.uprość();
        return wynik;
    }

    public static Ułamek zmianaZnaku(Ułamek u)
    {
        try
        {
            return new Ułamek(-u.getLicznik(), u.getMianownik());
        }
        catch(Exception exc)
        {
            return null;
        }
    }

    public static Ułamek różnica(Ułamek u1, Ułamek u2) throws MianownikUłamkaRównyZero
    {
        Ułamek wynik = new Ułamek(
                u1.getLicznik() * u2.getMianownik() - u2.getLicznik() * u1.getMianownik(),
                u1.getMianownik() * u2.getMianownik());
        wynik.uprość();
        return wynik;
    }

    //konwersje
    public double toDouble()
    {
        return getLicznik() / (double)getMianownik();
    }

    //porównywanie
    @Override
    public int compareTo(@NonNull Ułamek u)
    {
        double wynik = toDouble() - u.toDouble();
        return (wynik == 0) ? (int)wynik : (int) (wynik / Math.abs(wynik));
    }

    public boolean isEqualTo(Ułamek u)
    {
        return compareTo(u) == 0;
    }

    public boolean isNotEqualTo(Ułamek u)
    {
        return compareTo(u) != 0;
    }

    public boolean isLesserThan(Ułamek u)
    {
        return compareTo(u) < 0;
    }

    public boolean isLesserOrEqualTo(Ułamek u)
    {
        return compareTo(u) <= 0;
    }

    public boolean isGreaterThan(Ułamek u)
    {
        return compareTo(u) > 0;
    }

    public boolean isGreaterOrEqualTo(Ułamek u)
    {
        return compareTo(u) >= 0;
    }

    @Override
    public String toString()
    {
        return String.valueOf(getLicznik()) + "/" + String.valueOf(getMianownik());
    }

    private static int nwd(int a, int b)
    {
        if(b == 0) return a;
        else return nwd(b, a % b);
    }

    public void uprość()
            throws MianownikUłamkaRównyZero
    {
        int nwd = nwd(licznik, mianownik);
        licznik /= nwd;
        mianownik /= nwd;

        //znaki
        if(licznik * mianownik < 0)
        {
            setLicznik(-Math.abs(getLicznik()));
            setMianownik(Math.abs(getMianownik()));
        }
        else
        {
            setLicznik(Math.abs(getLicznik()));
            setMianownik(Math.abs(getMianownik()));
        }
    }

    public static Ułamek uprość(Ułamek u) throws MianownikUłamkaRównyZero
    {
        Ułamek kopia;
        try
        {
            kopia = (Ułamek)u.clone();
            kopia.uprość();
            return kopia;
        }
        catch(CloneNotSupportedException exc)
        {
            return null;
        }
    }
}
