package math.polynomials;

import math.utils.MathUtil;

public class PolyUtil {
    private PolyUtil() {
    }

    //----------------- jednomiany ------------------------------------------
    public static Jdm add(Jdm jdm1, Jdm jdm2) {
        double sum = 0;
        if (jdm1.isSimilarTo(jdm2)) {
            sum = jdm1.getCoeff() + jdm2.getCoeff();
        }
        Jdm jdm3 = new Jdm(sum, jdm1.getLetter(), jdm1.getExp());
        if (jdm3.toRemain()) {
            return jdm3;
        }
        return null;
    }

    public static Jdm sub(Jdm jdm1, Jdm jdm2) {
        double sum = 0;
        if (jdm1.isSimilarTo(jdm2)) {
            sum = jdm1.getCoeff() - jdm2.getCoeff();
        }
        Jdm jdm3 = new Jdm(sum, jdm1.getLetter(), jdm1.getExp());
        if (jdm3.toRemain()) {
            return jdm3;
        }
        return null;
    }

    public static Jdm mult(Jdm jdm1, int coeff) {
        return new Jdm(jdm1.getCoeff() * coeff, jdm1.getLetter(), jdm1.getExp());
    }

    public static Jdm mult(Jdm jdm1, Jdm jdm2) {
        if (jdm1.getLetter().equals(jdm2.getLetter())) {
            return new Jdm(jdm1.getCoeff() * jdm2.getCoeff(), jdm1.getLetter(),
                    jdm1.getExp() + jdm2.getExp());
        }
        return null;
    }

    public static Jdm div(Jdm jdm, double coeff) {
        return new Jdm(jdm.getCoeff() / coeff, jdm.getLetter(), jdm.getExp());
    }

    public static boolean canDivide(Jdm dzielna, Jdm dzielnik) {
        if (!dzielna.getLetter().equals(dzielnik.getLetter())) {
            return false;
        }
        //noinspection RedundantIfStatement
        if ((dzielna.getExp() - dzielnik.getExp()) < 0) {
            return false;
        }
        return true;
    }

    public static Jdm div(Jdm jdm1, Jdm jdm2) {
        if (canDivide(jdm1, jdm2)) {
            return new Jdm(jdm1.getCoeff() / jdm2.getCoeff(), jdm1.getLetter(),
                    jdm1.getExp() - jdm2.getExp());
        }
        return null;
    }

    //najmniejszy wspolny mnoznik
    public static Jdm nwm(Jdm jdm1, Jdm jdm2) {
        if (canDivide(jdm1, jdm2)) {
            double c1 = jdm1.getCoeff();
            double c2 = jdm2.getCoeff();
            double c = MathUtil.nww(c1, c2);
            int e = jdm1.getExp() - jdm2.getExp();
            return new Jdm(c, jdm1.getLetter(), e);
        }
        return null;
    }

    //najmniejsza wspolna wielokrotnosc
    public static Jdm nww(Jdm jdm1, Jdm jdm2) {
        if (canDivide(jdm1, jdm2)) {
            double c1 = jdm1.getCoeff();
            double c2 = jdm2.getCoeff();
            double c = MathUtil.nww(c1, c2);
            int e1 = jdm1.getExp();
            int e2 = jdm2.getExp();
            int e = Math.max(e1, e2);
            return new Jdm(c, jdm1.getLetter(), e);
        }
        return null;
    }

    //------------------------- wielomiany -----------------------------------
    public static Poly clone(Poly pol) {
        Jdm[] arr = PolyUtil.clone(pol.getPoly());
        return new Poly(arr);
    }

    public static Jdm[] clone(Jdm[] arr) {
        Jdm[] arr1 = new Jdm[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arr1[i] = arr[i].clone();
        }
        return arr1;
    }

    public static Poly[] clone(Poly[] arr) {
        Poly[] arr1 = new Poly[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arr1[i] = arr[i].clone();
        }
        return arr1;
    }

    public static Poly add(Poly pol, Jdm jdm) {
        Poly p1 = pol.clone();
        p1.add(jdm);
        return p1;
    }

    public static Poly add(Poly poly1, Poly poly2) {
        Poly poly3 = new Poly(poly1);
        poly3.add(poly2);
        return poly3;
    }

    public static Poly sub(Poly pol, Jdm jdm) {
        Poly p1 = pol.clone();
        p1.sub(jdm);
        return p1;
    }

    public static Poly sub(Poly poly1, Poly poly2) {
        Poly poly3 = poly1.clone();
        poly3.sub(poly2);
        return poly3;
    }

    public static Poly mult(Poly poly, int m) {
        Jdm[] pol = PolyUtil.clone(poly.getPoly());
        for (Jdm jdm : pol) {
            double a = jdm.getCoeff();
            jdm.setCoeff(a * m);
        }
        return new Poly(pol);
    }

    public static Poly mult(Poly poly, Jdm jdm) {
        Jdm[] tab = poly.getPoly().clone();
        for (Jdm jdm1 : tab) {
            try {
                jdm1.mult(jdm);
            } catch (PolyException e) {
                e.printStackTrace();
            }
        }
        return new Poly(tab);
    }

    public static Poly mult(Poly poly1, Poly poly2) {
        Jdm[] arr1 = poly1.getPoly().clone();
        int len1 = arr1.length;
        Jdm[] arr2 = poly2.getPoly().clone();
        int len2 = arr2.length;
        Jdm[] arr3 = new Jdm[len1 * len2];
        int k = 0;
        for (Jdm jdm1 : arr1) {
            for (Jdm jdm : arr2) {
                arr3[k] = PolyUtil.mult(jdm1, jdm);
                k++;
            }
        }
        return new Poly(arr3);
    }

    @SuppressWarnings("RedundantIfStatement")
    public static boolean canDivide(Poly dzielna, Poly dzielnik) {
        if (!dzielna.getPoly()[0].getLetter().equals(
                dzielnik.getPoly()[0].getLetter())) {
            return false;
        }
        if ((dzielna.getPoly()[0].getExp() - dzielnik.getPoly()[0].getExp()) < 0) {
            return false;
        }
        return true;
    }

    public static Poly[] div(Poly dzielna, Poly dzielnik) {
        if (PolyUtil.canDivide(dzielna, dzielnik)) {
            Poly rx = dzielna.clone();
            Poly px = dzielnik.clone();
            Poly sx = new Poly();
            Jdm jdm = PolyUtil.nwm(rx.getPoly()[0], px.getPoly()[0]);
            while (jdm != null) {
                rx = PolyUtil.mult(rx,
                        (int) (jdm.getCoeff() / rx.getPoly()[0].getCoeff()));
                px = dzielnik.clone();
                px = PolyUtil.mult(px,
                        new Jdm(jdm.getCoeff() / px.getPoly()[0].getCoeff(),
                                jdm.getLetter(), jdm.getExp()));
                px.reduce(px.getMax());
                rx = PolyUtil.sub(rx, px);
                rx.reduce(rx.getMax());
                sx.add(jdm);
                sx.reduce(sx.getMax());
                jdm = PolyUtil.nwm(rx.getPoly()[0], dzielnik.getPoly()[0]);
            }
            return new Poly[]{dzielna, dzielnik, sx, rx};
        }
        return null;
    }

    public static Poly[] divHorner(Poly poly, Poly dwumian) {
        double[] tab = poly.getCoeffsHorner(dwumian.getPoly()[1].getCoeff()
                * -1);
        int len = tab.length - 1;
        Poly pol = new Poly();
        int j = poly.degree() - 1;
        for (int i = 0; i < len; i++) {
            pol.add(new Jdm(tab[i], "x", j));
            j--;
        }
        if (tab[len] == 0) {
            return new Poly[]{poly, dwumian, pol};
        }
        Jdm jdm = new Jdm(tab[len], "x", 0);
        Poly reszta = new Poly();
        reszta.add(jdm);
        return new Poly[]{poly, dwumian, pol, reszta};
    }

    public static Poly nwd(Poly poly1, Poly poly2) {
        if (poly2 == null) {
            return null;
        }
        if (poly2.degree() == 0) {
            return poly1;
        }
        Poly[] p = div(poly1, poly2);
        while (p[3].degree() > 1) {
            p = div(p[1], p[3]);
        }
        return p[3];
    }

    //--------------------- equations ---------------------------------------
    @SuppressWarnings("RedundantIfStatement")
    public static boolean isFullQuadEquat(Poly poly) {
        poly.reduce(new MaxFirstComp());
        Jdm[] arr = poly.getPoly();
        if (arr.length != 3) {
            return false;
        }
        if ((arr[0].getExp() == 2) && (arr[1].getExp() == 1)
                && (arr[2].getExp() == 0)) {
            return true;
        }
        return false;
    }

    public static QuadEquat toQuadEquat(Poly poly) {
        if (isFullQuadEquat(poly)) {
            poly.reduce(new MaxFirstComp());
            Jdm[] arr = poly.getPoly();
            return new QuadEquat(arr[0].getCoeff(), arr[1].getCoeff(),
                    arr[2].getCoeff());
        }
        return null;
    }

    @SuppressWarnings("RedundantIfStatement")
    public static boolean isFullCubicEquat(Poly poly) {
        poly.reduce(new MaxFirstComp());
        Jdm[] arr = poly.getPoly();
        if (arr.length != 4) {
            return false;
        }
        if ((arr[0].getExp() == 3) && (arr[1].getExp() == 2)
                && (arr[2].getExp() == 1) && (arr[3].getExp() == 0)) {
            return true;
        }
        return false;
    }

    public static CubicEquat toCubicEquat(Poly poly) {
        if (isFullCubicEquat(poly)) {
            poly.reduce(new MaxFirstComp());
            Jdm[] arr = poly.getPoly();
            return new CubicEquat(arr[0].getCoeff(), arr[1].getCoeff(),
                    arr[2].getCoeff(), arr[3].getCoeff());
        }
        return null;
    }

    public static Poly toPoly(QuadEquat equat) {
        Jdm[] arr = new Jdm[3];
        arr[0] = new Jdm(equat.getA(), "x", 2);
        arr[1] = new Jdm(equat.getB(), "x", 1);
        arr[2] = new Jdm(equat.getC(), "x", 0);
        return new Poly(arr);
    }

    public static Poly toPoly(CubicEquat equat) {
        Jdm[] arr = new Jdm[4];
        arr[0] = new Jdm(equat.getA(), "x", 3);
        arr[1] = new Jdm(equat.getB(), "x", 2);
        arr[2] = new Jdm(equat.getC(), "x", 1);
        arr[3] = new Jdm(equat.getD(), "x", 0);
        return new Poly(arr);
    }
}