import java.util.*;
import java.awt.*;
import java.applet.Applet;

class GraphSysGraphics
{
        boolean         is_offscreen;
        Panel           ownerpanel;
        Dimension       offscreensize;
        Graphics        offgraphics;
        Image           offscreen;

   int  ixmin;
   int  ixmax;
   int  iymin;
   int  iymax;
   int  ixpos;
   int  iypos;

   double       xmin;
   double       xmax;
   double       ymin;
   double       ymax;
   double       xpos;
   double       ypos;


    final Color nodeColor = new Color(250, 220, 100);
    final Color backColor = Color.white;
    final Color errColor = Color.gray;
    final Color functionColor = Color.blue;
    final Color axesColor     = Color.black;
    final Color pathColor     = Color.red;
    final Color lastColor = new Color(0, 128, 0);

    final Color llastColor = new Color(0, 0, 250);
    final Color llineColor = new Color(0, 250, 250);
    final Color lbackColor = new Color(40, 40, 40);

    GraphSysGraphics(Panel ownerpanel_)
    {
      this.is_offscreen = false;
      this.ownerpanel = ownerpanel_;
    }

   public void setScale(double xmin_,double ymin_,double xmax_,double ymax_)
   {
                xmin = xmin_;
                xmax = xmax_;
                ymin = ymin_;
                ymax = ymax_;
   }

   public void setColor(int paletteIndex_)
   {
      if (paletteIndex_ == -1)
         offgraphics.setColor(ownerpanel.getBackground());
      else
      if (paletteIndex_ == 0)
         offgraphics.setColor(backColor);
      else
      if (paletteIndex_ == 1)
        offgraphics.setColor(functionColor);
      else
      if (paletteIndex_ == 2)
        offgraphics.setColor(axesColor);
      else
      if (paletteIndex_ == 3)
        offgraphics.setColor(pathColor);
      else
      if (paletteIndex_ == 4)
        offgraphics.setColor(lastColor);
      else
      if (paletteIndex_ == 11)
        offgraphics.setColor(lbackColor);
      else
      if (paletteIndex_ == 12)
        offgraphics.setColor(llastColor);
      else
      if (paletteIndex_ == 13)
        offgraphics.setColor(llineColor);
      else
        offgraphics.setColor(errColor);
   }

   public int X2IX(double x_)
   {
      double xx = (x_ - xmin) / ( xmax - xmin ) * ( ixmax - ixmin ) + ixmin;
      return (int) xx;
   }

   public int Y2IY(double y_)
   {
        double yy = (y_ - ymin) / ( ymax - ymin ) * ( iymax - iymin ) + iymin;
      return (int) yy;
   }

   public double IX2X(int ix_)
   {
      double xx = ((double)(ix_ - ixmin)) / ((double)( ixmax - ixmin )) * ( xmax - xmin ) + xmin;
      return xx;
   }

   public double IY2Y(int iy_)
   {
      double yy = ((double)(iy_ - iymin)) / ((double)( iymax - iymin )) * ( ymax - ymin ) + ymin;
      return yy;
   }

   public void moveTo(double x_,double y_)
   {
      xpos = x_;
      ypos = y_;
      ixpos = X2IX( x_ );
      iypos = Y2IY( y_ );
   }

   public void lineTo(double x_,double y_)
   {
      int new_ixpos = X2IX( x_ );
      int new_iypos = Y2IY( y_ );
      offgraphics.drawLine(ixpos,iypos,new_ixpos,new_iypos);
      ixpos = new_ixpos;
      iypos = new_iypos;
      xpos = x_;
      ypos = y_;
   }

   public void createDraw(Graphics g,int pal)
   {
      is_offscreen = true;

      Dimension d = ownerpanel.size();
      offscreen = ownerpanel.createImage(d.width, d.height);
      offscreensize = d;
      offgraphics = offscreen.getGraphics();

      ixmin = 0;
      iymin = d.height;
      ixmax = d.width;
      iymax = 0;
      ixpos = 0;
      iypos = 0;
      xpos  = 0;
      ypos  = 0;
      xmin  = 0;
      xmax  = 1;
      ymin  = 0;
      ymax  = 1;

      clearDraw(g,pal);

   }

    public void clearDraw(Graphics g,int pal)
    {
      xpos  = 0;
      ypos  = 0;
      ixpos = 0;
      iypos = 0;

      setColor(pal);
      Dimension d = ownerpanel.size();
      offgraphics.fillRect(0, 0, d.width, d.height);

   }

    public void resetDraw(Graphics g,int pal)
    {
      clearDraw(g,pal);
    }

  public void openDraw(Graphics g,int pal)
  {
    if (!is_offscreen)
    {
      createDraw(g,pal);
    }

   }

  public void closeDraw(Graphics g)
  {
    g.drawImage(offscreen, 0, 0, null);
  }

}


class GraphLinePanel extends Panel   {
    GraphSys graph;
    GraphSysGraphics gr;
    GraphSysPanel panel;

    int ix_last;
    int ix_curr;
    int iy_last;
    int iy_curr;

   boolean      is_startpoint;
   boolean      is_lastpoint;
   boolean      is_reset;



    GraphLinePanel(GraphSys graph, GraphSysPanel panel) 
    {
      this.graph = graph;
      this.panel = panel;
      this.gr    = new GraphSysGraphics(this);

      this.is_reset = true;
      this.is_startpoint = false;
      this.is_lastpoint = false;

    }

//   public Dimension PreferredLayoutSize(Container parent)
//   {
//      return new Dimension(640,200);
//   };

//   public Dimension MinimumLayoutSize(Container parent)
//   {
//      return new Dimension(100,200);
//   };

    public void start()
    {
//                relaxer = new Thread(this);
//                relaxer.start();
    }
    public void stop()
    {
//                relaxer.stop();
    }


    public synchronized void resetAction()
    {
      this.is_reset = true;
      this.is_startpoint = false;
      this.is_lastpoint = false;
    }


    public synchronized void update(Graphics g)
    {
        gr.openDraw(g,11);
        drawLineGraph(g);
        gr.closeDraw(g);
    }

    public synchronized void setPoint(double x_)
    {
      if (panel.is_drawnfunction)
      {
         is_startpoint = true;
         ix_curr = gr.Y2IY(x_);
      }
    }

    public synchronized boolean mouseDown(Event evt, int ix_, int iy_)
    {
      if (panel.is_drawnfunction)
      {
         resetScaleGraph();
         setPoint(gr.IY2Y(iy_));
         repaint();
      }
      return true;
    }

    public synchronized void drawLineSingleGraph(Graphics g,int ix, int pal)
    {
      double x = gr.IY2Y(ix);
      double y = x;

      gr.setColor(pal);
      gr.moveTo( 0., x );

      for (int i = 0; i < panel.n_iters; i++ )
      {
         y = panel.f(x,y,i);

         gr.lineTo((double)i,x);

         x = y;
      }

    }

    public synchronized void resetScaleGraph()
    {
         if (panel.is_use_yscale)
         {
            gr.setScale( 0., panel.y_min, (double)panel.n_iters, panel.y_max );
         }
         else
         {
            gr.setScale( 0., panel.x_min, (double)panel.n_iters, panel.x_max );
         };
    }

    public synchronized void drawLineGraph(Graphics g)
    {
      if (is_reset)
      {
         is_reset = false;
         gr.resetDraw(g,11);
         resetScaleGraph();
      }

      if (is_startpoint)
      {
         is_startpoint = false;

         if (is_lastpoint)
         {
            drawLineSingleGraph(g,ix_last,12);
         }

         drawLineSingleGraph(g,ix_curr,13);

         ix_last = ix_curr;
         is_lastpoint = true;

      }

    }

  synchronized void relax()
  {
     repaint();
  }

  public void run()
  {
//    relax();
//    while (true)
//    {
//      try
//      {
//         Thread.sleep(100);
//      }
//      catch (InterruptedException e)
//      {
//        break;
//      }
//    }
  }

}


class GraphSysPanel extends Panel implements Runnable {
    GraphSys graph;
    GraphSysGraphics gr;
    GraphLinePanel lpanel;
    Thread relaxer;

    int n_pts;
    int n_iters;
    int n_comp;

    int ix0_zoom;
    int iy0_zoom;
    int ix1_zoom;
    int iy1_zoom;
    int ix2_zoom;
    int iy2_zoom;

    int ix_last;
    int ix_curr;
    int iy_last;
    int iy_curr;
    int f_type;

   boolean      is_autoscale;
   boolean      is_use_yscale;
   boolean      was_startpoint;
   boolean      is_startpoint;
   boolean      is_lastpoint;
   boolean      is_reset;
   boolean      is_drawnfunction;
   boolean      is_singlemode;
   boolean      is_zooming;
   boolean      is_zoom_scale;
   boolean      is_dragging;

   double       x_min;
   double       x_max;
   double       y_min;
   double       y_max;

   double       x_corr;
   double       y_corr;
   double       x_add;
   double       y_add;
   double       y_alpha;


    GraphSysPanel(GraphSys graph)
    {
      this.graph = graph;
      this.gr    = new GraphSysGraphics(this);

      this.n_pts    = 50;
      this.n_iters  = 20;
      this.n_comp   = 1;

      this.is_reset = false;
      this.was_startpoint = false;
      this.is_startpoint = false;
      this.is_drawnfunction = false;
      this.is_use_yscale = false;
      this.is_autoscale = true;
      this.is_lastpoint = false;
      this.is_singlemode = true;
      this.is_zooming = false;
      this.is_zoom_scale = false;
      this.is_dragging = false;

      this.x_min = 0;
      this.x_max = 1;
      this.y_min = 0;
      this.y_max = 1;

      this.y_alpha = 1;
      this.x_corr = 1;
      this.y_corr = 1;
      this.x_add  = 0;
      this.y_add  = 0;

      this.f_type = 1;

    }


  public void run()
  {
    while (true)
    {
      relax();
      try
      {
         Thread.sleep(100);
      }
      catch (InterruptedException e)
      {
        break;
      }
    }
  }

  synchronized void relax()
  {
     repaint();
  }

    public synchronized void resetAction()
    {
      this.is_reset = true;
      this.was_startpoint = false;
      this.is_startpoint = false;
      this.is_lastpoint = false;
      this.is_drawnfunction = false;
      this.is_singlemode = true;
      this.is_zooming = false;
      this.is_dragging = false;
      lpanel.resetAction();
    }



    public synchronized void drawFunction(Graphics g)
    {
      if (!is_drawnfunction && is_reset)
      {
         is_reset = false;
         is_drawnfunction = true;

      setFunctionRange();
      gr.resetDraw(g,0);

      double dx = ( x_max - x_min ) / n_pts;

      gr.setScale( x_min, y_min, x_max, y_max );

      gr.setColor(2);
      gr.moveTo( x_min, x_min );
      gr.lineTo( x_max, x_max );

      gr.setColor(1);
      gr.moveTo( x_min, f(x_min,x_min,0) );

      double y = x_min;
      for (double x = x_min; x <= x_max + dx ; x = x + dx )
      {
        y = f(x,y,0);
        gr.lineTo( x, y );
      }

      }

    }

    public synchronized void setFunctionRange()
    {
    	if (is_zoom_scale)
      {
      	is_zoom_scale = false;
      }
      else
      if (is_autoscale)
      {

      double dx = ( x_max - x_min ) / n_pts;

      y_min = Math.min( x_min, x_max);
      y_max = Math.max( x_min, x_max);

      double y = x_min;

      for (double x = x_min; x <= x_max + dx; x = x + dx )
      {
        y = f( x, y, 0);

         if (y < y_min) y_min = y;
         if (y > y_max) y_max = y;
      }

      graph.setDialogParms();

      }

    }

    public synchronized void drawSinglePath(Graphics g,int ix, int pal)
    {
      double x = gr.IX2X(ix);
      double y = x;

      gr.setColor(pal);
      gr.moveTo( x, y_min );

      for (int i = 0; i < n_iters; i++ )
      {
        y = f(x,y,i);

        gr.lineTo(x,y);
        gr.lineTo(y,y);

        x = y;
      }

    }


    public synchronized void drawPointPath(Graphics g)
    {
      if (is_startpoint)
      {
         is_startpoint = false;

         if (is_lastpoint)
         {
            drawSinglePath(g,ix_last,4);
         }

         is_lastpoint = true;

         drawSinglePath(g,ix_curr,3);

         ix_last = ix_curr;
      }

    }


    public synchronized void update(Graphics g)
    {
    	if (!is_dragging)
      {
	    	gr.openDraw(g,0);

      	drawFunction(g);
      	drawPointPath(g);

      	if (is_singlemode)
      	{
         	lpanel.repaint();
      	}
	     	gr.closeDraw(g);

      }
      else
      {
	    	gr.openDraw(g,0);
	     	gr.closeDraw(g);

         {
	      	gr.setColor(2);
         	int ix0 = Math.min(ix0_zoom,ix1_zoom);
         	int iy0 = Math.min(iy0_zoom,iy1_zoom);
         	int iw0 = Math.max(ix0_zoom,ix1_zoom) - ix0;
         	int ih0 = Math.max(iy0_zoom,iy1_zoom) - iy0;
	       	g.drawRect(ix0,iy0,iw0,ih0);
         }

      }

    }

    public synchronized boolean mouseDown(Event evt, int ix_, int iy_)
    {
    	if (is_zooming)
      {
         is_dragging = true;
         ix0_zoom = ix_;
         iy0_zoom = iy_;
         ix1_zoom = ix_;
         iy1_zoom = iy_;
         ix2_zoom = ix_;
         iy2_zoom = iy_;
      }
      else
      if (is_drawnfunction)
      {
	      was_startpoint = true;
  	    	is_startpoint = true;
  	    	ix_curr = ix_;
      	iy_curr = iy_;
      	lpanel.setPoint(gr.IX2X(ix_));
      	repaint();
      }
      return true;
    }

    public synchronized boolean mouseUp(Event evt, int ix_, int iy_)
    {
    	if (is_zooming)
      {
         is_dragging = false;
         is_zooming = false;

      	if (ix0_zoom > ix_)
         { ix1_zoom = ix0_zoom; ix0_zoom = ix_; }
         else
         { ix1_zoom  = ix_; }

      	if (iy0_zoom < iy_)
         { iy1_zoom = iy0_zoom; iy0_zoom = iy_; }
         else
         { iy1_zoom  = iy_; }

         if ((ix1_zoom != ix0_zoom)
         &&  (iy1_zoom != iy0_zoom))
         {
             x_min = gr.IX2X(ix0_zoom);
             x_max = gr.IX2X(ix1_zoom);
             y_min = gr.IY2Y(iy0_zoom);
             y_max = gr.IY2Y(iy1_zoom);

             is_zoom_scale = true;
         }
         else
         {
             is_zoom_scale = false;
         }

	      graph.setDialogParms();
         resetAction();
         lpanel.repaint();
      }
      return true;
    }

    public synchronized boolean mouseDrag(Event evt, int ix_, int iy_)
    {
    	if (is_dragging)
      {
         ix2_zoom = ix1_zoom;
         iy2_zoom = iy1_zoom;

         ix1_zoom = ix_;
         iy1_zoom = iy_;
         repaint();
      }
      return true;
    }

    public void start()
    {
                relaxer = new Thread(this);
                relaxer.start();
    }
    public void stop()
    {
                relaxer.stop();
    }


  public Choice getFunctionChoice()
  {
        Choice c = new Choice();
        c.addItem("x(1-x)");
        c.addItem("(1-x)");
        c.addItem("16x^3-24x^2+9x");
        c.addItem("-2x^3+3x^2-1/2x+1/4");
        c.addItem("-2(x-1/2)^2+x");
        c.addItem("1/(x+1)");
        c.addItem("exp(-x)");
        c.addItem("exp(-(5(x-1/2))^2)");
        c.addItem("sin(pi x)");
        c.addItem("x sin(4 pi x) + x");
        return c;
  }

  public void setFunctionChoice(String s)
  {
    if ( "x(1-x)" == s)
      f_type = 1;
    else
    if ( "(1-x)" == s)
      f_type = 2;
    else
    if ( "16x^3-24x^2+9x" == s)
      f_type = 3;
    else
    if ( "-2x^3+3x^2-1/2x+1/4" == s)
      f_type = 4;
    else
    if ( "-2(x-1/2)^2+x" == s)
      f_type = 5;
    else
    if ( "1/(x+1)" == s)
      f_type = 10;
    else
    if ( "exp(-x)" == s)
      f_type = 11;
    else
    if ( "exp(-(5(x-1/2))^2)" == s)
      f_type = 12;
    else
    if ( "sin(pi x)" == s)
      f_type = 15;
    else
    if ( "x sin(4 pi x) + x" == s)
      f_type = 16;
    else
      f_type = 0;

  }

  public double g(double x_,double y_,int n_)
  {
    double x = x_corr * x_ + x_add;

    if (f_type == 1)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( (1. - x) * x )));
    else
    if (f_type == 2)
       return y_add + y_corr * ( x + y_alpha * ( - x + 
       ( 1. - x )));
    else
    if (f_type == 3)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( 16 * x * x * x - 24 * x * x + 9 * x )));
    else
    if (f_type == 4)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( -2 * x * x * x + 3 * x * x - .5 * x + .25 )));
    else
    if (f_type == 5)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( x - (x - .5) * (x - .5) )));
    else
    if (f_type == 10)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( 1./(x + 1.) )));
    else
    if (f_type == 11)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( Math.exp(-x) )));
    else
    if (f_type == 12)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( Math.exp(-25*((x-.5)*(x-.5))) )));
    else
    if (f_type == 15)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( Math.sin(Math.PI * x) )));
    else
    if (f_type == 16)
       return y_add + y_corr * ( x + y_alpha * ( - x +
       ( Math.sin(4 * Math.PI * x) * x + x )));
    else
       return 0.;


  }

  public double f(double x_,double y_,int n_)
  {
    double x = x_;
    double y = y_;

    for ( int j = 0; j < n_comp; j++ )
    {
      y = g( x, y, n_ ); x = y;
    }
    return y;

  }

}



public class GraphSys extends Applet {
    GraphSysPanel panel;
    GraphLinePanel lpanel;
    TextField s_x_min;
    TextField s_x_max;
    TextField s_y_min;
    TextField s_y_max;
    TextField s_y_corr;
    TextField s_x_corr;
    TextField s_y_add;
    TextField s_x_add;
    TextField s_y_alpha;
    TextField s_n_comp;
    TextField s_n_iters;
    TextField s_n_pts;


    static LayoutManager dcLayout = new FlowLayout(FlowLayout.CENTER, 10, 5);

    public void init()
    {
     setLayout(new BorderLayout());

      panel  = new GraphSysPanel(this);
      lpanel = new GraphLinePanel(this,panel);
      panel.lpanel = lpanel;

      Panel p3 = new Panel();

      p3.add(new Checkbox("Scale"));
      p3.add(new Checkbox("Y Vert."));
      p3.add(new Label("xmin:"));
      p3.add(s_x_min = new TextField(Double.toString(panel.x_min), 6));
      p3.add(new Label("xmax:"));
      p3.add(s_x_max = new TextField(Double.toString(panel.x_max), 6));
      p3.add(new Label("ymin:"));
      p3.add(s_y_min = new TextField(Double.toString(panel.y_min), 6));
      p3.add(new Label("ymax:"));
      p3.add(s_y_max = new TextField(Double.toString(panel.y_max), 6));



      Panel p1 = new Panel();
      p1.add(new Label("x,y corr:"));
      p1.add(s_y_alpha = new TextField(Double.toString(panel.y_alpha), 6));
      p1.add(new Label("xadd:"));
      p1.add(s_x_add = new TextField(Double.toString(panel.x_add), 6));
      p1.add(new Label("xmult:"));
      p1.add(s_x_corr = new TextField(Double.toString(panel.x_corr), 6));
      p1.add(new Label("yadd:"));
      p1.add(s_y_add = new TextField(Double.toString(panel.y_add), 6));
      p1.add(new Label("ymult:"));
      p1.add(s_y_corr = new TextField(Double.toString(panel.y_corr), 6));



      Panel p2 = new Panel();

      p2.add(panel.getFunctionChoice());
      p2.add(new Label("comp:"));
      p2.add(s_n_comp = new TextField(Integer.toString(panel.n_comp), 4));
      p2.add(new Label("iters:"));
      p2.add(s_n_iters = new TextField(Integer.toString(panel.n_iters), 4));
      p2.add(new Label("points:"));
      p2.add(s_n_pts   = new TextField(Integer.toString(panel.n_pts), 4));
      p2.add(new Button("Zoom"));
      p2.add(new Button("Full"));
      p2.add(new Button("Reset"));

      Panel p = new Panel();
      p.setLayout(new BorderLayout());
      p.add("South", p2);
      p.add("East", p1);
      p.add("North", p3);

      add("North", p);
      lpanel.setLayout(new FlowLayout(FlowLayout.CENTER, 100, 350));
        add("East", lpanel);
        add("Center", panel);


    }

    public void setDialogParms()
    {
      s_x_min.setText(Double.toString(panel.x_min));
      s_x_max.setText(Double.toString(panel.x_max));
      s_y_min.setText(Double.toString(panel.y_min));
      s_y_max.setText(Double.toString(panel.y_max));

      s_y_alpha.setText(Double.toString(panel.y_alpha));

      s_x_corr.setText(Double.toString(panel.x_corr));
      s_y_corr.setText(Double.toString(panel.y_corr));
      s_x_add.setText( Double.toString(panel.x_add));
      s_y_add.setText( Double.toString(panel.y_add));

      s_n_comp.setText( Integer.toString(panel.n_comp));
      s_n_iters.setText(Integer.toString(panel.n_iters));
      s_n_pts.setText(  Integer.toString(panel.n_pts));
    }

    public void getDialogParms()
    {

           panel.x_min = Double.valueOf(s_x_min.getText()).doubleValue();
           panel.x_max = Double.valueOf(s_x_max.getText()).doubleValue();
           panel.y_min = Double.valueOf(s_y_min.getText()).doubleValue();
           panel.y_max = Double.valueOf(s_y_max.getText()).doubleValue();

            panel.y_alpha = Double.valueOf(s_y_alpha.getText()).doubleValue();

            panel.x_corr = Double.valueOf(s_x_corr.getText()).doubleValue();
            panel.y_corr = Double.valueOf(s_y_corr.getText()).doubleValue();
            panel.x_add  = Double.valueOf(s_x_add.getText()).doubleValue();
            panel.y_add  = Double.valueOf(s_y_add.getText()).doubleValue();

            panel.n_comp  = Integer.parseInt(s_n_comp.getText());
            panel.n_iters = Integer.parseInt(s_n_iters.getText());
            panel.n_pts   = Integer.parseInt(s_n_pts.getText());
    }

    public void start()
    {
                panel.start();
                lpanel.start();
    }
    public void stop()
    {
                lpanel.stop();
                panel.stop();
    }


    public boolean action(Event evt, Object arg) {
        if (evt.target instanceof Choice)
        {
            panel.setFunctionChoice((String)arg);
        }
        else
        if (arg instanceof Boolean)
        {
            if (((Checkbox)evt.target).getLabel().equals("Scale"))
            {
                panel.is_autoscale = ! ((Boolean)arg).booleanValue();
            }
            else
            if (((Checkbox)evt.target).getLabel().equals("Y Vert."))
            {
                panel.is_use_yscale = ! ((Boolean)arg).booleanValue();
            }
            return true;
        }
        if ("Reset".equals(arg))
        {
	      	panel.is_zoom_scale = false;
            getDialogParms();
            panel.resetAction();
            lpanel.repaint();
            return true;
        }
        else
        if ("Full".equals(arg))
        {
	      	panel.is_zoom_scale = false;
            getDialogParms();
            panel.x_min = 0;
            panel.x_max = 1;
            setDialogParms();
            panel.resetAction();
            lpanel.repaint();
            return true;
        }
        else
        if ("Zoom".equals(arg))
        {
            panel.is_zooming = true;
            return true;
        }
        return false;
    }
}