/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package mandelbrotset;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import org.apfloat.Apcomplex;
import org.apfloat.Apfloat;



/**
 *
 * @author matt
 */
public class FloatTester {
    static int iterationsMax = 100;
    static int precision = 512;
    final static int RUNTIMES = 1;
    final static String INITIAL = "0.25";

    public static void main(String[] args) {
        int[] iters = {10,100,500,1000};
        int[] prec = {64, 128, 256, 512, 1024};

        System.out.println("format: {ApFloat_runtime, BigDecimal_runtime}");

        for(int curMaxIter : iters){
            System.out.print("    " + curMaxIter);
        }
        System.out.println();

        for(int curPrec : prec){
            System.out.print(curPrec + ": ");
            for(int curMaxIter : iters){
                testItersPrecision(curMaxIter, curPrec);
            }
            System.out.println();
        }
    }

    private static void testItersPrecision(int maxIters, int precision) {
        long time = System.currentTimeMillis();
//        for (int i = 0; i < RUNTIMES; i++) {
//            doitDouble(maxIters, precision);
//        }
//        System.out.print("{" + prettyTime(time));

        System.out.print("{");
        time = System.currentTimeMillis();
        for (int i = 0; i < RUNTIMES; i++) {
            doitApFloat(maxIters, precision);
        }
        System.out.print(prettyTime(time));


        time = System.currentTimeMillis();
        for (int i = 0; i < RUNTIMES; i++) {
            doitBigDecimal(maxIters, precision);
        }
        System.out.print(", " + prettyTime(time) + "}");
    }

    public static void doitDouble(int maxIters, int precision){
        double d = Double.valueOf(INITIAL);
        DoubleComplex initialComplex = new DoubleComplex(d, d), complex = initialComplex;

        for(int i = 0; i < maxIters; i++){
            complex = complex.multiply(complex).add(initialComplex);
        }
//        System.out.println("DoubleComplex result: " +complex);
    }


    public static void doitApFloat(int maxIters, int precision){
        Apfloat initial = new Apfloat(INITIAL, precision);
        Apcomplex initialComplex = new Apcomplex(initial, initial), complex = initialComplex;
        // T(n+1) = Tn^2 + T1
        for(int i = 0; i < maxIters; i++){
            complex = complex.multiply(complex).add(initialComplex);
        }
//        System.out.println("ApFloat result: " +complex);
    }


    public static void doitBigDecimal(int maxIters, int precision){

        MathContext mc = new MathContext(precision, RoundingMode.HALF_EVEN);
        BDcomplex initial = new BDcomplex(new BigDecimal(INITIAL, mc), new BigDecimal(INITIAL, mc), mc);
        BDcomplex initialComplex = new BDcomplex(initial, mc), complex = initialComplex;

        // (a + bi)^2 = a^2 + 2abi - b^2
        for(int i = 0; i < maxIters; i++){
            complex = complex.multiply(complex).add(initialComplex);
        }
//        System.out.println("BigDecimal result: " +complex);
    }

    private static String prettyTime(long time) {
        time = System.currentTimeMillis() - time;
        if(time < 1000){
            return (time) + "ms";
        }
        else{
            return (time)/1000 + "s";
        }
    }


}

class BDcomplex{
    BigDecimal r;
    BigDecimal i;
    MathContext precision;

    BDcomplex(BDcomplex that, MathContext precision){
        this.r = that.r;
        this.i = that.i;
        this.precision = precision;
    }

    BDcomplex(BigDecimal real, BigDecimal imag, MathContext precision){
        r = real;
        i = imag;
        this.precision = precision;
    }

    // (a + bi)(c + di)
    // = ac - bd +adi + bci
    // = (ac - bd) + (ad + bc)i
    BDcomplex multiply(BDcomplex that){
        return new BDcomplex(
                this.r.multiply(that.r, precision).subtract(this.i.multiply(that.i,precision),precision)
                , this.r.multiply(that.i, precision).add(this.i.multiply(that.r, precision), precision)
                , this.precision
        );
    }

    BDcomplex add(BDcomplex that){
        return new BDcomplex(
                this.r.add(that.r, precision)
                , this.i.add(that.i, precision)
                , this.precision
        );
    }

    @Override
    public String toString(){
        return "(" + r + ", " + i + ")" ;
    }
}

//stolen from internets.
class DoubleComplex {

    private double x,y;

    /**
        Constructs the complex number z = u + i*v
        @param u Real part
        @param v Imaginary part
    */
    public DoubleComplex(double u,double v) {
        x=u;
        y=v;
    }

    /**
        Real part of this Complex number
        (the x-coordinate in rectangular coordinates).
        @return Re[z] where z is this Complex number.
    */
    public double real() {
        return x;
    }

    /**
        Imaginary part of this Complex number
        (the y-coordinate in rectangular coordinates).
        @return Im[z] where z is this Complex number.
    */
    public double imag() {
        return y;
    }

    /**
        Addition of Complex numbers (doesn't change this Complex number).
        <br>(x+i*y) + (s+i*t) = (x+s)+i*(y+t).
        @param w is the number to add.
        @return z+w where z is this Complex number.
    */
    public DoubleComplex add(DoubleComplex w) {
        return new DoubleComplex(x+w.real(),y+w.imag());
    }

    /**
        Complex multiplication (doesn't change this Complex number).
        @param w is the number to multiply by.
        @return z*w where z is this Complex number.
    */
    public DoubleComplex multiply(DoubleComplex w) {
        return new DoubleComplex(x*w.real()-y*w.imag(),x*w.imag()+y*w.real());
    }


    /**
        String representation of this Complex number.
        @return x+i*y, x-i*y, x, or i*y as appropriate.
    */
    public String toString() {
        if (x!=0 && y>0) {
            return x+" + "+y+"i";
        }
        if (x!=0 && y<0) {
            return x+" - "+(-y)+"i";
        }
        if (y==0) {
            return String.valueOf(x);
        }
        if (x==0) {
            return y+"i";
        }
        // shouldn't get here (unless Inf or NaN)
        return x+" + i*"+y;

    }
}