public class DrawShape {
    OctantDrawer o;
    // the constructor simply remembers which OctantDrawer object to use
    public DrawShape( OctantDrawer drawer) { o = drawer; } 

    public void draw( int rx, int ry) { // called by p1.java
        int r = (int) Math.sqrt( rx*rx + ry*ry);
        drawDiamond( r);     // hyper-circle of degree 1
        drawCircle( r);      // hyper-circle of degree 2
        drawHyperCircle( r); // hyper-circle of degree 3
    }

    void drawDiamond( int r) { // local method 
        int x = 0, y = r;
        // gradient of the diamond's edge is always -1
        while ( x<=y ) {
            o.drawInAllOctants( x, y);
            // drawInAllInOctants draws the point in the first octant
            // at the given coordinates, and repeats the point in
            // the other 7 octants
            x++; y--; // x++ increments x and y-- decrements y
	}
    }

    void drawCircle( int r) {
        int x = 0, y = r;
        int d = - r; // the decision variable
        // d satisfies the following invariant
        // d = x^2 + (y-0.5)^2 - r^2 - 0.25
        int xinc, yinc;
        while ( x<=y ) {
            o.drawInAllOctants( x, y);
            // << is the Java left-shift operator
            // a<<b takes the binary representation of integer
            // a and shifts all its bits left by b places
            xinc = (x<<1) + 1; // xinc = 2*x + 1;
            yinc = 2 - (y<<1); // yinc = 2 - 2*y;
            if ( d >= 0 ) {
                d += xinc + yinc; // a += b is equivalent to a = a+b
                y--;
            } else {
                d += xinc;
            }
            x++;
        }
    }

    /*void drawHyperCircle( int r) { // initial version, just draws a box
        int x = 0, y = r;
        while ( x<=y ) {
            o.drawInAllOctants( x, y);
            x++;
        }
    }*/

	void drawHyperCircleOld(int r)
	{
		int x = 0, y = r;
		int G = -12*r*r + 6*r - 1;	// the decision variable
		while(x <= y)
		{
			o.drawInAllOctants(x,y);

			int incrE = 8*(3*x*(x + 1) + 1);
			int incrSE = 24*(x*x+x-y*y+2*y) - 18;

			if(G <= 0)
			{
				G += incrE;
			}
			else
			{
				G += incrSE;
				--y;
			}

			++x;
		}
	}

	void drawHyperCircleOld2(int r)
	{
		// We're going to cache 24x^2 + 24x + 8, and -24y^2 + 48y - 26. We'll do it for all values of x and y
		// from 0 to r.

		if(r < 3) r = 3;	// FIXME: This is because our table must be at least this width (see below).

		int tableWidth = r+1;
		int[][] func1Table = new int[3][tableWidth], func2Table = new int[3][tableWidth];

		// Calculate the first few values explicitly.
		for(int i=0; i<3; ++i)
		{
			func1Table[0][i] = 24*i*i + 24*i + 8;
			func2Table[0][i] = -24*i*i + 48*i - 26;
		}

		// Calculate what we can on the second row.
		for(int i=0; i<2; ++i)
		{
			func1Table[1][i] = func1Table[0][i+1] - func1Table[0][i];
			func2Table[1][i] = func2Table[0][i+1] - func2Table[0][i];
		}

		// Calculate the bottom-left value.
		func1Table[2][0] = func1Table[1][1] - func1Table[1][0];
		func2Table[2][0] = func2Table[1][1] - func2Table[1][0];

		// Fill in the bottom row.
		for(int i=1; i<tableWidth-2; ++i)
		{
			func1Table[2][i] = func1Table[2][0];
			func2Table[2][i] = func2Table[2][0];
		}

		// Calculate the remaining values on the other rows.
		for(int i=2; i<tableWidth-1; ++i)
		{
			func1Table[1][i] = func1Table[1][i-1] + func1Table[2][i-1];
			func1Table[0][i+1] = func1Table[0][i] + func1Table[1][i];
			func2Table[1][i] = func2Table[1][i-1] + func2Table[2][i-1];
			func2Table[0][i+1] = func2Table[0][i] + func2Table[1][i];
		}

		/*for(int i=0; i<10; ++i)
		{
			System.out.print(-24*i*i + 48*i - 26);
			System.out.print("\t");
		}
		System.out.println();
		for(int i=0; i<3; ++i)
		{
			for(int j=0; j<10 && j<tableWidth; ++j)
			{
				System.out.print(func2Table[i][j] + "\t");
			}
			System.out.println();
		}
		System.out.println();*/

		// End caching section

		int x = 0, y = r;
		int G = -12*r*r + 6*r - 1;	// the decision variable
		while(x <= y)
		{
			o.drawInAllOctants(x,y);

			int incrE = func1Table[0][x];
			int incrS = func2Table[0][y];

			if(G <= 0)
			{
				G += incrE;
			}
			else
			{
				G += incrE + incrS;
				--y;
			}

			++x;
		}
	}

	void drawHyperCircle(int r)
	{
		// Let f(x) = 24x^2 + 24x + 8, g(y) = -24y^2 + 48y - 26
		// df(x) = f(x+1) - f(x) = 48(x + 1), dg(y) = g(y-1) - g(y) = -24(-2y + 1) - 48 = 48y - 72
		// d^2f(x) = df(x+1) - df(x) = 48, d^2g(y) = dg(y-1) - dg(y) = -48

		// f(0) = 8, df(0) = 48
		int f = 8, df = 48;

		// g(r) = -24r^2 + 48r - 26, dg(r) = 48r - 72
		int g = -24*r*r + 48*r - 26, dg = 48*r - 72;

		// d^2f(x) = 48, d^2g(y) = -48
		final int d2f = 48, d2g = -48;

		int x = 0, y = r;
		int D = -12*r*r + 6*r - 1;	// the decision variable (referred to as G in the rough working)
		while(x <= y)
		{
			o.drawInAllOctants(x,y);

			if(D <= 0)
			{
				D += f;

				// Update the values of the functions.
				f += df;
				df += d2f;
			}
			else
			{
				D += f + g;

				--y;

				// Update the values of the functions.
				f += df;
				df += d2f;
				g += dg;
				dg += d2g;
			}

			++x;
		}
	}
}
