import ChaosDemos.*;
import java.awt.*;
import java.util.*;
import java.net.URL;
import graph.*;

/**
* Iterates and plots various 2D maps <br> 
* Uses the "Java Graph Class Library" by Leigh Brookshaw
* @version 3 August, 1997
* @author Michael Cross
*/

public class Control extends dynamicGraph  {

/** run check box number */
      private int r;
/** number of curves */
      private int ncurve=-1; 
/** index of first data curve */      
      private int ncurve0;
/** index of data curve for points near periodic orbit */      
      private int ncurve1;
/** index of data curve for points near periodic orbit */      
      private int ncurve2;
/** number of iterations to eliminate transient  */   
      private int ntrans =0; 
/** accumulated number of iterations */
      private int iterations;
/** accumulated number of points plotted */
      private int plotted;      
/** number of iterations to do before plotting */      
      private int jump=1;
/** function chosen: 0=HENON 1=DUFFING 2=BAKERS 3=YORKE
    4=SINAI 5=MYFUNCTION */      
      private int function=0;
/** number of variabels controlled by sliders */      
      private int nSliders;      
/** number of data points accumulated in plotData */      
      private int nData=0;
/** Index for variable plotted on x-axis */
      private int index0; 
/** Index for variable plotted on y-axis */
      private int index1;
/** fixed point number for control */
      private int fpNumber;
/** index of periodic point controlled to */
      private int flag;
/** control variable */
      private int controlVariable;
/** Period of orbit */
    int period=4;
/** Number of points needed before fit */
    int fitNumber=10;
/** default delay for movie */
    private int delayValue=7;
/** true after first mouse click */      
      private boolean clicked=false;
/** true on new run */
      private boolean newRun;
/** true if found first periodic orbit */
    private boolean foundFirst;      
/** true if found other periodic orbit */
    private boolean foundFirstOther;      
/** true if fit to first periodic orbit */
    private boolean fitFirst;
/** true of control cycle */
    private boolean control;    

/** iteration variable */      
      private double[] x={0.,0.,0.,0.};
/** iteration of periodic point */      
      private double[] xp={0.,0.,0.,0.};
/** mouse position */
      private double[] xmouse={0.,0.};
/** map parameter */      
      private double a;
/** change in map parameter */
      private double dp;
/** map parameter */
      private double b;
/** map parameter */       
      private double c;
/** parameter increment */
      private double change=0.01;      
/** plot ranges */      
      private double xRange,yRange;
/** reciprocal of plot ranges */      
      private double xReduce,yReduce;
/** map parameters a,b,c */ 
      private double[] params={1.4,0.3,0.};
/** save map parameters a,b,c */ 
      private double[] paramSave={1.4,0.3,0.};      
/** tolerances for periodic orbit search */
      private double eps1,eps2;      
/**  starting value of x */
      private double[] x0={0.,0.};    
/**  bottom-left corner */
      private double[] cmin={-1.,-1.};  
/**  top-right corner */
      private double[] cmax={1.,1.};
/** size of marker to be plotted */      
      private double marker=0.;
/** data for plotting */
      private double[] moredata;
      private double[] moredata1;      
      private double[] moredata2;
      private double[] fixedPointData;
      private double[] fixedPoints;
/** periodic points and linearization */
      private double[][][] fp;
      private double[] eig1;
      private double[] eig2;
      private double[][] ev1;
      private double[][] ev2;
      private double[][] fv1;
      private double[][] fv2;
      private double[] pm;
      private boolean[] fitPoint;
      
      private double[][] dfpdp;
      private int[] offset;
      
      private String blank="                              ";      
      
/** axis labesl */
      private String[] axisLabel={"  X ","  Y "};
      
/** GUI elements */      
      private textControls variables;
      private sliderControls parameters;
      private buttonControls buttons;
      private Choice functionChoice;
      private CheckboxGroup cbg ;
      private Checkbox[] cb;
      private TextField periodBox;
      private TextArea status;

/** classes used */
      private superGraph2D graph;
      private movie theMovie;
      private Map2DFunction mapFunction;
      private periodicOrbits PO;

/** parent */
      private startControl outerparent;
/** animation thread */      
      private Thread aThread=null;
      
/** for location of marker.txt */      
      private URL markerURL;
/** location of marker.txt */      
      URL documentBase;
      
/** functions */
      private static final int HENON=0;
      private static final int DUFFING=1;
      private static final int BAKERS=2;
      private static final int YORKE=3; 
      private static final int SINAI=4; 
      private static final int MYFUNCTION=5;
/* log e -> log 10 */      
      private static final double le=0.43429;
/* log e -> log 2 */      
      private static final double l2=1.4427;      
/** maximum number of points to be appended to plot */      
      private static int MAX_APPEND=1024;
/** maximum number of attempts to find good initial condition */       
      private static int MAX_TRY=1024;
/** value for testing divergence of iteration */
      private static double MAX_VALUE = 100.;      
/*    arrays for setting parameters based on function choice */
      int nParameters;


/**
* @param target starting class
* @see startControl
*/ 
        public Control(startControl target, URL in_documentBase) {
        documentBase=in_documentBase;
        graph = new superGraph2D(this);
        moredata = new double[2*jump];
        moredata1 = new double[4*jump];
        moredata2 = new double[2*jump];
        fixedPointData = new double[4];


/*
**      Load a file containing Marker definitions
*/
        try {
           markerURL = new URL(documentBase,"marker.txt");
           graph.setMarkers(new Markers(markerURL));
        } catch(Exception e) {
           System.out.println("Failed to create Marker URL!");
        }

        theMovie = new movie(this);

        this.outerparent = target;
        addNotify();   
        setBackground(Color.lightGray);

        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.fill=GridBagConstraints.BOTH;

        setLayout(gridbag);

        graph.borderRight=35;
        graph.borderTop=80;  // MCC 8-10

        Panel leftPanel = new Panel();
        leftPanel.setLayout(new BorderLayout());
        
        constraints.gridheight = 1;
        constraints.gridwidth=1;
        constraints.weightx=0.75;
        constraints.weighty = 1.0;
        constraints.insets = new Insets(10,0,10,0); 
        gridbag.setConstraints(leftPanel, constraints);
        add(leftPanel); 

        leftPanel.add("Center",graph);
        Panel bottomLeftPanel = new Panel();
        bottomLeftPanel.setLayout( new GridLayout(2,1));
        bottomLeftPanel.add(theMovie);

        theMovie.borderBottom=0;
        
        String[] buttonLabels={" Reset "," Clear ",
                    " Start ","  Stop "};
        buttons = new buttonControls((dynamicGraph) this, 
                        buttonLabels,buttonLabels.length, true);
        buttons.b_init[0] = true;
        buttons.b_stopped[3] = false;
        buttons.b_started[1] = true;
        buttons.b_started[3] = true;
        buttons.setup();
        bottomLeftPanel.add(buttons);
        leftPanel.add("South",bottomLeftPanel);

        Panel rightPanel = new Panel();       
        rightPanel.setLayout(new BorderLayout()); 
           
        Panel topRightPanel = new Panel();
        topRightPanel.setLayout(gridbag);
        rightPanel.add("Center",topRightPanel);

        nSliders = params.length;      
        String[] textboxes = new String[nSliders];
        for (int i=0;i<nSliders;i++) {
             textboxes[i]=String.valueOf(params[i]);
        } 
        
        String[] labels = {"  a","   b","   c"};
        parameters = new 
            sliderControls((dynamicGraph) this, textboxes,labels,
                     nSliders,5,change,false);
        constraints.fill=GridBagConstraints.NONE;
        constraints.gridheight = 1;
        constraints.gridwidth=2;
        constraints.weightx=1.;
        constraints.weighty = 0.8;
        constraints.gridheight=4;
        constraints.insets = new Insets(0,0,0,0); 
        gridbag.setConstraints(parameters, constraints);
        topRightPanel.add(parameters);
        parameters.setLabelText("  dp");
        

        String[] textboxes1 = {String.valueOf(cmin[0]),String.valueOf(cmin[1]),
                    String.valueOf(cmax[0]),String.valueOf(cmax[1]),
                    String.valueOf(eps1),String.valueOf(eps2),
                    String.valueOf(fitNumber)};       

        String[] labels1 = {"x_min","y_min","x_max","y_max"," eps1"," eps2",
                             "points"};       
        variables = new 
              textControls((dynamicGraph) this, textboxes1,
                       labels1,textboxes1.length,5); 
        constraints.gridwidth=GridBagConstraints.REMAINDER; 
        constraints.insets = new Insets(0,0,10,10);
        gridbag.setConstraints(variables, constraints);
        topRightPanel.add(variables);

        constraints.gridheight=1; 
        constraints.gridwidth=1;
        constraints.weightx=0.25;
        constraints.anchor=GridBagConstraints.EAST;
        Label periodLabel=new Label("Period:  ");
        gridbag.setConstraints(periodLabel,constraints);
        topRightPanel.add(periodLabel);
        periodBox=new TextField(2);
        constraints.anchor=GridBagConstraints.WEST;
//        constraints.gridwidth=GridBagConstraints.REMAINDER;
        gridbag.setConstraints(periodBox, constraints);
        topRightPanel.add( periodBox);
        periodBox.setText(String.valueOf(period));
        constraints.weightx=0.5;
        constraints.anchor=GridBagConstraints.CENTER;

        functionChoice = new Choice();
        functionChoice.addItem("Henon Map");
        functionChoice.addItem("Duffing Map");
        functionChoice.addItem("Bakers' Map");  
        functionChoice.addItem("Yorke Map");
        functionChoice.addItem("Sinai Map"); 
        functionChoice.addItem("My Function");
        constraints.weighty = 0.2;       
        constraints.gridheight=1;
        constraints.gridwidth=GridBagConstraints.REMAINDER;
        constraints.insets = new Insets(0,0,10,0);
        gridbag.setConstraints(functionChoice, constraints);  
        topRightPanel.add(functionChoice);
        
        status=new TextArea("Hit START to find periodic points",1,35,
                        TextArea.SCROLLBARS_VERTICAL_ONLY);
        gridbag.setConstraints(status, constraints);  
        topRightPanel.add(status);
        status.setEditable(false);
//        status.setText("Finding periodic points");        
        
        cb=new Checkbox[4];
        cb[0]=new  Checkbox("p");
        cb[1]=new  Checkbox("p-dp");
        cb[2]=new  Checkbox("p+dp");
        cb[3]=new Checkbox("Control");
        cbg=new CheckboxGroup();
        for(int i=0;i<4;i++)
            cb[i].setCheckboxGroup(cbg);
        constraints.anchor=GridBagConstraints.CENTER;
        constraints.gridwidth=1;
        constraints.weightx=0.25;
        gridbag.setConstraints(cb[0], constraints);
        topRightPanel.add(cb[0]);
        gridbag.setConstraints(cb[1], constraints);
        topRightPanel.add(cb[1]);
        gridbag.setConstraints(cb[2], constraints);
        topRightPanel.add(cb[2]); 
        constraints.gridwidth=GridBagConstraints.REMAINDER;
        gridbag.setConstraints(cb[3], constraints);
        topRightPanel.add(cb[3]);
        cb[0].setState(true);
        offset=new int[3];
        cb[3].disable();
        cb[2].disable();
        cb[1].disable();

        Panel bottomRightPanel = new Panel();
        bottomRightPanel.setBackground(this.getBackground().darker());      
        bottomRightPanel.setLayout(gridbag);
        
        rightPanel.add("South",bottomRightPanel);

        constraints.insets = new Insets(20,0,0,10); 
        constraints.gridheight = 1;
        constraints.weighty = 1.0;
        constraints.weightx=0.25;
        constraints.gridwidth=GridBagConstraints.REMAINDER;
        constraints.fill=GridBagConstraints.BOTH;
        constraints.insets = new Insets(10,0,10,0); 
        gridbag.setConstraints(rightPanel, constraints);
        add(rightPanel);

        graph.clearAll=true;
        repaint();
        
        /* Start off run */
        if(target.inputParameters) {
            function=target.function;
            functionChoice.select(function);
            setDefaults();
            setAxesDefaults(); 
            for(int i=0;i<target.nVariables;i++)
                  if(target.variableBoxes[i].length() != 0)
                        variables.setText(i,target.variableBoxes[i]); 
            for(int i=0;i<target.nParameters;i++)
                  if(target.parameterBoxes[i].length() != 0)
                        parameters.setText(i,target.parameterBoxes[i]);
            change=target.change;
            parameters.setText(3,String.valueOf(change));
            period=target.period;
            periodBox.setText(String.valueOf(period));
            delayValue=target.speed;
            theMovie.setScrollValue(delayValue);
        } 
        else {
              setDefaults();
        }
        updateParameters();
        updateVariables();
        
        // Create arrays
        fp=new double[3][period][2];
        eig1=new double[period];
        eig2=new double[period];
        ev1=new double[period][2];
        ev2=new double[period][2];
        fv1=new double[period][2];
        fv2=new double[period][2];
        dfpdp=new double[period][2];
        pm=new double[period];
        fitPoint=new boolean[period];
        for(int i=0;i<period;i++)
             fitPoint[i]=false;
                
        if(randomInitialCondition()) {
            restart();
            buttons.enableGo();
        }
      }

//**********************************************************************
/**
* Responds to textControls
* @see textControls
*/      
//**********************************************************************
      public void respondToText() {
            updateParameters();
            updateVariables();
            if (randomInitialCondition()) {
                restart();
                buttons.enableGo();
            }    
      }

//**********************************************************************
/**
* Responds to buttons in sliderControls
* @see sliderControls
*/      
//**********************************************************************
      public void respondToSliderButtons() {
          updateParameters();
      }

//**********************************************************************
/**
* Responds to text boxes in sliderControls
* @see sliderControls
*/      
//**********************************************************************
      public void respondToSliderText() {
          updateParameters();
      } 

//**********************************************************************
/*
 Responds to mouse event    */
      
      public void respondToMouse(double xcoord, boolean xcoordValid,
                   double ycoord, boolean ycoordValid){
             double x1[] = new double[2];
             double cump[] = new double[2];
             if(xcoordValid && ycoordValid) {       
                  if(!clicked) {
                       xmouse[0]=xRange*xcoord+cmin[0];
                       xmouse[1]=yRange*ycoord+cmin[1];
                       clicked=true;
                       if(aThread != null) {
                            clicked = false;
                            x[0]=xmouse[0];
                            x[1]=xmouse[1];
//                          System.out.println("New i.c.: x= "+x[0]+" y= "+x[1]);
                            mapFunction.winding=0;
                            mapFunction.total=0;
                            if(ntrans>0) {
                                for(int i=0;i<ntrans;i++) {
                                    mapFunction.iterate(x);
                                }    
                            }
                       }
                  }
                  else {
                       clicked=false;
                       xcoord=xRange*xcoord+cmin[0];
                       ycoord=yRange*ycoord+cmin[1];
                       if(xcoord > xmouse[0]) {
                            variables.setText(0,String.valueOf(xmouse[0]));
                            variables.setText(2,String.valueOf(xcoord));
                       }
                       else if(xcoord < xmouse[0]) {
                            variables.setText(2,String.valueOf(xmouse[0]));
                            variables.setText(0,String.valueOf(xcoord));
                       }
                       if(ycoord > xmouse[1]) {     
                            variables.setText(1,String.valueOf(xmouse[1]));
                            variables.setText(3,String.valueOf(ycoord));
                       }
                       else if (ycoord < xmouse[1]){
                            variables.setText(3,String.valueOf(xmouse[1]));
                            variables.setText(1,String.valueOf(ycoord));
                       
                       }     
//                       updateParameters();
                       updateVariables();
                       if(randomInitialCondition()) {
                            restart();
                            buttons.enableGo();
                       }    
                  }
//                  if(checkBounds(x) > -1) 
//                    restart();
//                  else iterationBox.setText("Invalid i.c.!");

            }
            else {
                  if(aThread==null) {
                       clicked=false;
                       function=functionChoice.getSelectedIndex();
                       setAxesDefaults();
                       updateParameters();
                       updateVariables();
                       if(randomInitialCondition()) {
                            restart();
                            buttons.enableGo();
                       }
                   }
             }
      }

//**********************************************************************
                       
      public Insets insets() {
            return new Insets(10,0,20,10);     
      }

//************************************************************************
/**
* Stop movie thread 
*/
//************************************************************************

      public void movieStop() {
                if(aThread!=null) {
//                    aThread.stop();        Sun advises not to use this
                    theMovie.stopIterate();
                    aThread=null;
                }
      }

//************************************************************************
/**
* Start movie thread 
*/
//************************************************************************
      public void movieStart() {
                status.appendText(blank);               
                if(aThread==null) {
                    aThread = new Thread(theMovie);
//                    aThread.setPriority(Thread.MIN_PRIORITY);
                    aThread.start();
                }
      }

//************************************************************************
/**
* Event handler:
* Stops iteration on minimising and handles close window event<br>
* (May fail under Windows95)
*/
//************************************************************************
      
        public boolean handleEvent(Event evt) {
            switch (evt.id) {
                case Event.WINDOW_DESTROY:
                    movieStop();
                    outerparent.hideWindow();
                    return super.handleEvent(evt);
                case Event.WINDOW_ICONIFY:
                    movieStop();
                    buttons.enableGo();
                    enableAll();
                    return super.handleEvent(evt); 
                case Event.ACTION_EVENT:
                    if(evt.target == functionChoice)  {
                         function = functionChoice.getSelectedIndex();
                         setDefaults();
                         updateParameters();
                         updateVariables();
                         if(randomInitialCondition()) restart();
                         status.appendText("\nHit START to find periodic points");
                         return super.handleEvent(evt); 
                    }
                    else if(evt.target == cb[0]) {
                         for(int i=0;i<4;i++)
                            parameters.enable(i);
                         cb[1].disable();
                         cb[2].disable();
                         cb[3].disable();
                         stopMovie();
                         periodBox.enable();
                         status.appendText(blank);
                         status.appendText("\nFind periodic points");
                         return super.handleEvent(evt);
                    }
                    else if(evt.target == cb[1]) {
                         for(int i=0;i<4;i++)
                            parameters.disable(i);
                         cb[2].disable();
                         cb[3].disable();
                         stopMovie();
                         functionChoice.disable();
                         periodBox.disable();
                         status.appendText(blank);
                         status.appendText("\nFind periodic points for p - dp");
                         return super.handleEvent(evt);
                    }
                    else if(evt.target == cb[2]) {
                         for(int i=0;i<4;i++)
                            parameters.disable(i);
                         cb[3].disable();
                         cb[1].disable();
                         stopMovie();    
                         functionChoice.disable();
                         periodBox.disable();
                         status.appendText(blank);
                         status.appendText("\nFind periodic points for p + dp");
                         return super.handleEvent(evt);
                    }
                    else if(evt.target == cb[3]) {
                         parameters.enable(3);
                         cb[1].disable();
                         cb[2].disable();
                         stopMovie();
                         functionChoice.disable();
                         periodBox.disable();
//                         status.appendText("\nSeeking control...");
                         offset[1]=findOffset(fp[0],fp[1]);
                         offset[2]=findOffset(fp[0],fp[2]);
                         for(int i=0;i<period;i++) {
                             dfpdp[i][0]=0.5*(fp[2][modplus(i,offset[2],period)][0]-fp[1][modplus(i,offset[1],period)][0])/change;
                             dfpdp[i][1]=0.5*(fp[2][modplus(i,offset[2],period)][1]-fp[1][modplus(i,offset[1],period)][1])/change;
                             System.out.println("Periodic point "+i+". Derivative: ("+
                                (float)dfpdp[i][0]+","+(float)dfpdp[i][1]+")");
                             status.appendText("\nPoint "+i+" derivative: ("+
                                (float)dfpdp[i][0]+","+(float)dfpdp[i][1]+")");
                             pm[i]=PO.eig1[i]/((PO.eig1[i]-1)*(dfpdp[i][0]*fv1[i][0]+dfpdp[i][1]*fv1[i][1]));
                             System.out.println("Periodic point "+i+". pm: "+(float)pm[i]);
                             double mod=1./Math.sqrt(Math.pow(dfpdp[i][0],2)+Math.pow(dfpdp[i][1],2));
                             status.appendText("\nPoint "+i+" pm: "+(float)pm[i]);
                             status.appendText("\n Seeking control...");
                             if(fitPoint[i]) {
                                 dfpdp[i][0]=dfpdp[i][0]*mod;
                                 dfpdp[i][1]=dfpdp[i][1]*mod;
                                 mod=checkVector(fp[0][i],dfpdp[i]);
                                 plotVector(fp[0][i],dfpdp[i],mod,Color.red);
                             }    
                         }
                         flag=-1;    
                         return super.handleEvent(evt);
                    }
                    else if(evt.target == periodBox) {
                        period = parseTextField(periodBox,period,true);
                        fp=new double[3][period][2];
                        eig1=new double[period];
                        eig2=new double[period];
                        ev1=new double[period][2];
                        ev2=new double[period][2];
                        fv1=new double[period][2];
                        fv2=new double[period][2];
                        dfpdp=new double[period][2];
                        pm=new double[period];
                        fitPoint=new boolean[period];
                        for(int i=0;i<period;i++)
                            fitPoint[i]=false;
                        stopMovie();
                        status.appendText("\nHit START to find periodic points");
                        return super.handleEvent(evt);
                    }    
                default:
                    return super.handleEvent(evt);
            }
       }
       
//************************************************************************
/**
* Disables text input in variables
*/
//************************************************************************
      
      public void disableAll() {
          int i;
          for(i=0;i<variables.ncontrols();i++)
                 variables.disableText(i);
          functionChoice.disable();
          periodBox.disable();
      }

//*********************************************************************
/** 
* Enables text input in variables
*/ 
//*********************************************************************

      public void enableAll() {
          for(int i=0;i<variables.ncontrols();i++)
                variables.enableText(i);
          if(cb[0].getState()) {
            functionChoice.enable();
            periodBox.enable();
          }  
      }


//********************************************************************
/**
* Updates parameters from the sliderControls 
*/ 
//********************************************************************
      
      public void updateParameters() {
            int i;
            
            graph.setTitle(mapFunction.title);

            for(i=0;i<nParameters;i++) {
                params[i]= parameters.parseTextField(i, params[i]);
                paramSave[i]=params[i];
                if(parameters.getCheckState(i)) controlVariable=i;
            }
            change=parameters.parseTextField(nSliders, change);
            
            if(cb[1].getState()) {
                for(i=0;i<nParameters;i++)
                     if(parameters.getCheckState(i))
                        params[i]=params[i]-change;
            }
            if(cb[2].getState()) {
                for(i=0;i<nParameters;i++)
                     if(parameters.getCheckState(i))
                        params[i]=params[i]+change;
            }            
            mapFunction.setParameters(params);
      }

//********************************************************************      
/**
* Update parameters form the textControls 
*/
//********************************************************************      
      public void updateVariables() {
            int i;
            cmin[0] = variables.parseTextField(0,cmin[0]);
            cmin[1] = variables.parseTextField(1,cmin[1]);
            cmax[0] = variables.parseTextField(2,cmax[0]);
            cmax[1] = variables.parseTextField(3,cmax[1]);
            xRange=cmax[0]-cmin[0];
            xReduce=1./xRange;
            yRange=cmax[1]-cmin[1];
            yReduce=1/yRange;
            eps1 = variables.parseTextField(4,eps1);
            eps2 = variables.parseTextField(5,eps2);
//            period = parseTextField(periodBox,period,true);
            fitNumber=variables.parseTextField(6,fitNumber);
      }      
                
//*********************************************************************
/**  Resets plot by deleting all curves and restarting
*/
//*********************************************************************
      
      public void restart() {
            double data1[] = new double[4];
            double data2[] = new double[2];
            
            int i,j;
            int inBounds;
            int markerType;
            double markerScale;
            double mult;
            
            flag=-1;
            for(i=0;i<3;i++) {
                if(cb[i].getState()) {
                    r=i;
                }
            }
            control=cb[3].getState();
            foundFirst=false;
            fitFirst=false;
            foundFirstOther=false;
            if(!control) {   
                fixedPoints = new double[4*period];
                PO = new periodicOrbits(period);
                PO.setTolerance(eps1, eps2);
                PO.setFitNumber(fitNumber);
            }
            graph.clearAll=true;
            if(ncurve>=0) ncurve = graph.deleteAllCurves();
            clicked=false;
            newRun=true;
            index0=0;
            index1=1;
            iterations=0;
            plotted=0;
            mapFunction.winding=0;
            mapFunction.total=0;
            data1[0]=0;
            data1[1]=0;
            data1[2]=1;
            data1[3]=1;
            ncurve = graph.addCurve(data1,2,Color.lightGray,0,7,0.5);
            data2[0]=scaleX(x[index0]);
            data2[1]=scaleY(x[index1]);
            if(marker<=0.01){
                markerType=7;
                markerScale=1.;
            }    
            else {
                markerType=1;
                markerScale=marker;
            }
            
            ncurve = graph.addCurve(data2,1,Color.blue,0,markerType,markerScale);
            ncurve0=ncurve;
            if(control) {
                for(i=0;i<period;i++) {
                    if(checkBounds(fp[0][i])==1  && fitPoint[i]) {
                        mult=checkVector(fp[0][i],ev1[i]);
                        plotVector(fp[0][i],ev1[i],mult,Color.blue);
                        mult=checkVector(fp[0][i],ev2[i]);
                        plotVector(fp[0][i],ev2[i],mult,Color.blue);
                        mult=checkVector(fp[0][i],dfpdp[i]);
                        plotVector(fp[0][i],dfpdp[i],mult,Color.red);
                    }
                }
            }
            graph.paintAll=true; 
            graph.repaint();
      }

//*********************************************************************
/**
* Iterates map and updates graph  
*/
//*********************************************************************
      
      public boolean iterate() {
            
            int inBounds;
            int i,j,n,n1,n2,nplot,nplot1,nplot2;
            int foundOrbit=-1;
            n=0;
            n1=0;
            n2=0;
            nplot=0;
            nplot1=0;
            nplot2=0;
            foundOrbit=-1;  
                      
            mapFunction.iterate(x);
            inBounds=checkBounds(x);
            if (inBounds == -1) {  
                alertDialog alert = new alertDialog(this, "Value diverged: stop and restart");
                return false;
            }
            else if(inBounds == 1) {
                moredata[nplot++]=scaleX(x[index0]);
                moredata[nplot++]=scaleY(x[index1]);
                n=n+1;
            }
            if(control) {
                fpNumber=nearFixedPoint(x);
                if(fpNumber>=0 && fitPoint[fpNumber]) {
                  if(inBounds==1) {
//                      dp=pm[fpNumber]*((x[0]-fp[0][fpNumber][0])*fv1[fpNumber][0]
//                            +(x[1]-fp[0][fpNumber][1])*fv1[fpNumber][1]);
//                      System.out.println(flag+" dp= "+dp);
//                      if(Math.abs(dp)<change) {
//                          if(flag==-1) {
//                              delayValue=theMovie.getScrollValue();
//                              theMovie.setScrollValue(2);
//                          }
                        if(flag==fpNumber || flag == -1) {
                            dp=pm[fpNumber]*((x[0]-fp[0][fpNumber][0])*fv1[fpNumber][0]
                                +(x[1]-fp[0][fpNumber][1])*fv1[fpNumber][1]);
                        if(flag>=0)
                            if(period > 1) status.appendText("\nControlled to point "+flag+" dp: "+(float)dp);
                            else status.appendText("\ndp: "+(float)dp);
                        if(Math.abs(dp)<change) {
                            moredata1[nplot1++]=scaleX(x[index0]);
                            moredata1[nplot1++]=scaleY(x[index1]);
                            n1=n1+1;
                            if(flag==-1) {
                                delayValue=theMovie.getScrollValue();
                                theMovie.setScrollValue(2);
                                ncurve = graph.addCurve(moredata1,1,Color.red,0,1,1.0); 
                                ncurve1=ncurve;
                                nplot1=0;
                                n1=0;
                            }

                            flag=fpNumber;
                            params[controlVariable]=paramSave[controlVariable]+dp;
                            mapFunction.setParameters(params); 
//                          if(!foundFirst) {
//                              ncurve = graph.addCurve(moredata1,1,Color.red,0,1,1.0); 
//                              ncurve1=ncurve;
//                              nplot1=0;
//                              n1=0;
//                              foundFirst=true;
//                          }
                        }
                        else {
                            if(flag==fpNumber) {
                                flag=-1;
//                                status.setText(blank);
                                status.appendText("\nSeeking control...");
                                theMovie.setScrollValue(delayValue);
                            }    
                        }
                    }
                  }      
                }
                else {
                    if(flag>=0) {
                        flag=-1;
//                        status.setText(blank);
                        status.appendText("\nSeeking control...");
                        theMovie.setScrollValue(delayValue);
                    }
                }      
            } 
            else {
                foundOrbit=PO.testPoint(x);
                if(foundOrbit==1) {
                    if(inBounds == 1) {
                        moredata1[nplot1++]=scaleX(x[index0]);
                        moredata1[nplot1++]=scaleY(x[index1]);
                        moredata1[nplot1++]=scaleX(x[index0+2]);
                        moredata1[nplot1++]=scaleY(x[index1+2]);
                        n1=n1+2;
                        if(!foundFirst) {
                            ncurve = graph.addCurve(moredata1,2,Color.red,0,1,1.0); 
                            ncurve1=ncurve;
//                          System.out.println("Added periodic curve");
                            nplot1=0;
                            n1=0;
                            foundFirst=true;
                        }
                    }    
                    for(i=0;i<period;i++) {
                        if(PO.foundFixedPoint[i] && PO.addPoint[i]) {
                             if(checkBounds(PO.fp[i])==1) {
                                plotPeriodicPoint(i);
                             }
                             fp[r][i][0]=PO.fp[i][0];
                             fp[r][i][1]=PO.fp[i][1];
                             System.out.println("Periodic point "+i+". Fixed point: ("+
                                        (float)PO.fp[i][0]+","+(float)PO.fp[i][1]+")");
                             status.appendText("\nPoint "+i+": ("+
                                        (float)PO.fp[i][0]+","+(float)PO.fp[i][1]+")");           
                             if(!fitFirst) {
                                xp[0]=PO.fp[i][0];
                                xp[1]=PO.fp[i][1];
                                for(int k=1;k<period;k++) {
                                     mapFunction.iterate(xp);
                                     fp[r][modplus(i,k,period)][0]=xp[0];
                                     fp[r][modplus(i,k,period)][1]=xp[1];
//                                   System.out.println("r= "+r+" i= "+modplus(i,k,period)+" xf= "+xp[0]+" yf= "+xp[1]);
                                }
                                PO.updateFit(fp[r]);
                                fitFirst=true;
                                cb[r+1].enable();
                             }
                             if(cb[0].getState()) {
                                 eig1[i]=PO.eig1[i];
                                 eig2[i]=PO.eig2[i];
                                 System.out.println("Periodic point "+i+". Eigenvalues: "+
                                       (float)eig1[i]+" "+(float)eig2[i]);
                                 status.appendText("\nPt "+i+" e-values: "+
                                       (float)eig1[i]+" "+(float)eig2[i]);      
                                 for(j=0;j<2;j++) {
                                    ev1[i][j]=PO.ev1[i][j];
                                    ev2[i][j]=PO.ev2[i][j];
                                    fv1[i][j]=PO.fv1[i][j];
                                    fv2[i][j]=PO.fv2[i][j];
                                    fitPoint[i]=true;
                                 }
                             }
                        }     
                    }
                }   
                else if(foundOrbit==2 && inBounds==1) {
                    moredata2[nplot2++]=scaleX(x[index0]);
                    moredata2[nplot2++]=scaleY(x[index1]);
                    n2=n2+1;
                    if(!foundFirstOther) {
                        ncurve = graph.addCurve(moredata2,1,Color.green,0,1,1.0); 
                        ncurve2=ncurve;
//                      System.out.println("Added another periodic curve");
                        nplot2=0;
                        n2=0;
                        foundFirstOther=true;
                    }
                }   
            }
//            if(n > MAX_APPEND) break;
//            if(n1 > MAX_APPEND) break;
//            if(n2 > MAX_APPEND) break;
            
            graph.paintAll=false;     // Don't paint while updating data

            if(n>0) graph.appendToCurve(moredata,n,ncurve0);
            if(n1>0) graph.appendToCurve(moredata1,n1,ncurve1);
            if(n2>0) graph.appendToCurve(moredata2,n2,ncurve2);

            graph.paintAll=true;
            graph.clearAll=false;
            graph.repaint(); 
            plotted=plotted+n;

            return true;  
      }

//**********************************************************************
/**
* Stop thread and close fit window
*/
//**********************************************************************
      
      public void stop() {
            movieStop();
            enableAll();
            buttons.enableGo();
      }
      
//**********************************************************************      
/**
* Respond to buttonControls
* @see      buttonControls
* @param    buttonIndex index of button pushed
*/
//**********************************************************************
      public void respondToButtons(int buttonIndex) {
            if(buttonIndex==0) {
//                 setDefaults();
                 updateParameters();
                 updateVariables();
                 if(randomInitialCondition()) {
                    restart();
                    buttons.enableGo();  
                 }   
            }
            else if(buttonIndex==1) {
                graph.clearAll=false;
                if(aThread != null) {
                  movieStop();
                  if(initialCondition(x)) {
                        restart();
                        newRun=false;
                        movieStart();
                  }      
                }
                else {
                  updateVariables();
                  boolean dummy=randomInitialCondition();
                  restart();
                }
            }
            else if(buttonIndex==2) {
                if(clicked) {
                    clicked=false;
                    x[0]=xmouse[0];
                    x[1]=xmouse[1];
                    x[2]=x[0];
                    x[3]=x[1];
                    if (!initialCondition(x)) return; 
                }
                else {
                   if(!newRun) {
                         if (!randomInitialCondition()) return;
                   }      
                   else newRun=false;      
                }     
                buttons.disableGo();
                disableAll();
                mapFunction.windingAdd=1;
                graph.allowDrag=false;
                movieStart();
            }
            else if(buttonIndex==3) {
                mapFunction.windingAdd=0;
                buttons.enableGo();
                movieStop();
                graph.allowDrag=true;
                enableAll();
            }

      }      

//*********************************************
/**
* Checks that x is within bounds cmin, cmax
* @param input value
* @return -1 if "diverges" set by MAX_VALUE, 0 if within bounds,
*  1 if outside bounds
*/
//*********************************************
      
      private int checkBounds(double[] x) {
        if(Math.abs(x[index0]) > MAX_VALUE || Math.abs(x[index1]) > MAX_VALUE) {
            return -1; 
        }
        if(x[index0] < cmin[0] || x[index0] > cmax[0] || 
                    x[index1] < cmin[1] || x[index1] > cmax[1])
                 return 0;
        else return 1;
      }

//**********************************************************************
/**
* Picks random intial condition, eliminates transient and test for
* divergences. Returns true if valid, false if cannot find a valid i.c.
* in MAX_TRY attempts.
* @return true if valid starting point found 
*/
//********************************************************************** 
       
      private boolean randomInitialCondition() {
           boolean diverged=true;
           int nTry=0;
           while(diverged && nTry < MAX_TRY) {
                nTry++;
                diverged = false;
                x[0]=cmin[0]+Math.random()*(cmax[0]-cmin[0]);
                x[1]=cmin[1]+Math.random()*(cmax[1]-cmin[1]);
                if(ntrans>0) {
                    for(int i=0;i<ntrans;i++) {
                        mapFunction.iterate(x);
                        if(checkBounds(x) <0) {
                             diverged=true;
                             break;
                        }     
                    }
                }
                if(!diverged) {
                    while(checkBounds(x) <=0) {
                        mapFunction.iterate(x);  
                        if(checkBounds(x) < 0 ) {
                            diverged = true;
                            break;
                        }    
                    }
                }
                if(!diverged) return true ;   

            } 
            alertDialog alert = new alertDialog(this, "Failed to find valid initial condition");
            return false;
      }

//**********************************************************************
/**
* Eliminates transient from initial condition testing for divergence
* @param initial condition
* @return true if valid point found after transient
*/
//********************************************************************** 
      
      private boolean initialCondition(double[] xm) {
                boolean diverged = false;
                if(ntrans>0) {
                    for(int i=0;i<ntrans;i++) {
                        mapFunction.iterate(xm);
                        if(checkBounds(xm) <0) {
                             diverged=true;
                             break;
                        }     
                    }
                }
                if(!diverged) {
                    while(checkBounds(x) <=0) {
                        mapFunction.iterate(x);  
                        if(checkBounds(x) < 0 ) {
                            diverged = true;
                            break;
                        }    
                    }
                }
                if (!diverged) return true;
                else {
                    alertDialog alert = new alertDialog
                            ("Invalid i.c., please try again");
                    return false;
                }
       }

//**********************************************************************
/**
* Sets parameter and variables to default values for each function
*/
//**********************************************************************
      
      private void setDefaults() {
           function=functionChoice.getSelectedIndex();
            switch (function) {
                case HENON:
                    mapFunction = new HenonFunction();
                    break;
                case DUFFING:
                    mapFunction = new DuffingMapFunction();
                    break;       
                case BAKERS:
                    mapFunction = new BakersFunction();
                    break;     
                case YORKE:
                    mapFunction = new YorkeFunction();
                    break;   
                case SINAI:
                    mapFunction = new SinaiFunction();
                    break;     
                case MYFUNCTION:
                    mapFunction = new Map2DMyFunction();
                    break;
                default:
                    mapFunction = new HenonFunction();
                    break;
            }
           nParameters = mapFunction.getNParameters();
           setAxesDefaults();
           parameters.setText(0,String.valueOf(mapFunction.aDefault[0]));
           if(nParameters>1) {
                  parameters.enable(1);
                  parameters.setText(1,String.valueOf(mapFunction.aDefault[1]));
           }       
           else {
                  parameters.setText(1,"");
                  parameters.disable(1);
           }
           if(nParameters>2) {
                  parameters.enable(2);
                  parameters.setText(2,String.valueOf(mapFunction.aDefault[2]));
           }
           else {
                  parameters.setText(2,"");
                  parameters.disable(2);     
           }
        
      }     


//**********************************************************************
/**
* Adds to graph
* @param g Graphics context
* @param r data rectangle of graph
*/
//**********************************************************************      
     
      public void addToGraph( Graphics g, Rectangle r)  {
              
      }

//**********************************************************************
/**
* Sets axes defaults based on type of plot
*/
//**********************************************************************    
      public void setAxesDefaults() {
                 variables.setText(0,String.valueOf(mapFunction.xminDefault));
                 variables.setText(1,String.valueOf(mapFunction.yminDefault));
                 variables.setText(2,String.valueOf(mapFunction.xmaxDefault));
                 variables.setText(3,String.valueOf(mapFunction.ymaxDefault));
                 graph.setXAxisTitle("X");
                 graph.setYAxisTitle("Y");
       }
       
       private double scaleX(double inX) {
            return (inX-cmin[0])*xReduce;
       }
       private double scaleY(double inY) {
            return (inY-cmin[1])*yReduce;
       }
       
       private void plotPeriodicPoint(int i) {
           int j,ifp;
           double mult1,mult2;
           
           mult1=checkVector(PO.fp[i],PO.ev1[i]);
           mult2=checkVector(PO.fp[i],PO.ev2[i]);
           if(mult2<mult1) mult1=mult2;
           
           plotVector(PO.fp[i],PO.ev1[i],mult1,Color.blue);
           plotVector(PO.fp[i],PO.ev2[i],mult1,Color.blue);
           
           PO.addPoint[i]=false;
           xp[0]=PO.fp[i][0];
           xp[1]=PO.fp[i][1];
           ifp=0;
           for(j=0;j<period;j++) {
               mapFunction.iterate(xp);
               if(checkBounds(xp)==1) {
                   fixedPoints[ifp++]=scaleX(xp[0]);
                   fixedPoints[ifp++]=scaleY(xp[1]);
               }
           }
           if(ifp>0) ncurve=graph.addCurve(fixedPoints,ifp,Color.black,0,1,2.0);
      }
      
    private double checkVector(double[] p, double[] v) {
        double mult=1.;
        int nib=0;
        double[] xt=new double[2];
        while(nib<2) {
            mult=mult/2.;
            nib=0;
            xt[0]=p[0]+mult*v[0];
            xt[1]=p[1]+mult*v[1];
            nib=nib+checkBounds(xt);
            xt[0]=p[0]-mult*v[0];
            xt[1]=p[1]-mult*v[1];
            nib=nib+checkBounds(xt);
        }
        return mult;
    }
    
    private void plotVector(double[] p, double[] v, double mult, Color c) {
        double[] data=new double[4];
        data[0]=scaleX(p[0]+mult*v[0]);
        data[1]=scaleY(p[1]+mult*v[1]);
        data[2]=scaleX(p[0]-mult*v[0]);
        data[3]=scaleY(p[1]-mult*v[1]);
        ncurve=graph.addCurve(data,2,c);
        graph.repaint();
    }
        
              
      
    private int modplus(int i1, int i2, int i3) {
        int result;
        result=i1+i2;
        while(result >= i3)
            result=result-i3;
        return result;
    }
    
    private int findOffset(double[][] p1, double[][] p2) {
        double dist,minDist;
        int i,j,k,offset;
        minDist=1.e6;
        offset=0;
        for(i=0;i<period;i++) {
            dist=0;
            for(j=0;j<period;j++)
                for(k=0;k<2;k++)
                    dist=dist+Math.abs(p2[modplus(j,i,period)][k]
                                -p1[j][k]);
            if(dist<minDist) {
                offset=i;
                minDist=dist;
            }
         }
         return offset;
    }
                                                           
    private int parseTextField(TextField t, int i, boolean positive) {
            int iNew;
            try {
                iNew=(new Integer(t.getText())).intValue();
            }
            catch (NumberFormatException e) {
                t.setText(String.valueOf(i));
//                alert = new alertDialog(this,"Try an integer");
                return i;
            }
            if(((iNew < 0) && positive) || ((iNew>0) && !positive)) {
                t.setText(String.valueOf(i));
//                if(positive) alert = new alertDialog(this,"Must be positive");
//                else alert = new alertDialog(this,"Must be negative");
                return i;
            }
            return iNew;
   }
   
   private double dist(double[] p1, double[] p2) {
        return Math.abs(p1[0]-p2[0])+Math.abs(p1[1]-p2[1]);
   }
   
   private int nearFixedPoint(double[] x) {
       int i;
       for(i=0;i<period;i++) {
            if(dist(x,fp[0][i])<eps1) 
                return i;
       }
       return -1;
   }
   
   private void stopMovie() {
        mapFunction.windingAdd=0;
        buttons.enableGo();
        movieStop();
        graph.allowDrag=true;
        enableAll();
        updateParameters();
        updateVariables();
        if(randomInitialCondition()) {
           restart();
           buttons.enableGo();  
        }
   }
      
}      
/*******************************************************************/
