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 Map2D extends dynamicGraph  {

/** number of curves */
      private int ncurve=-1; 
/** index of first data curve */      
      private int ncurve1;
/** number of iterations to eliminate transient  */   
      private int ntrans =10; 
/** 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=CIRCLE 2=DUFFING 3=BAKERS 4=YORKE 5=STANDARD
    6=GOY 7=SINAI 8=MYFUNCTION */      
      private int function=0;
/** Number of subdivisions for box counting */      
      private int nDiv=4;      
/** box size in dimesnion algorithm */
      private double boxSize;
/** number of boxes across each dimension */      
      private int nBoxes;
/** nuber of subdivisions of boxes done */      
      private int divided=-1;
/** number of variabels controlled by sliders */      
      private int numSliders;      
/** number of data points accumulated in plotData */      
      private int nData=0;
/** Order of dimension calculation  */    
      private int q;
/** Index for variable plotted on x-axis */
      private int index0; 
/** Index for variable plotted on y-axis */
      private int index1;                    

/** true if box-counting thread running */      
      private boolean running=false;
/** true after first mouse click */      
      private boolean clicked=false;
/** true if boxes should be painted on graph */      
      private boolean paintBoxes=false;
/** true on new run */
      private boolean newRun;
/** true for return map */
      private boolean returnMap;
/** true to calculate Lyapunov exponents */
      private boolean lyapunov=false;            

/** iteration variable */      
      private double[] x={0.,0.,0.,0.};
/** tangent vectors */
      private double[] t1={1,0};
      private double[] t2={0,1};
/** Cumulative Lyapunov totals */
      private double[] cum={0,0};
/** mouse position */            
      private double[] xmouse={0.,0.};
/** map parameter */      
      private double a;     
/** map parameter */                           
      private double b;
/** map parameter */       
      private double c;
/** 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.};
/**  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.5;
/** data for pltting */
      private double[] plotData;      
      
/** axis labesl */
      private String[] axisLabel={"  X ","  Y "};
      
/** GUI elements */      
      private textControls variables;
      private sliderControls parameters;
      private buttonControls buttons;
      private Button dimButton;
      private Choice functionChoice;

      private CheckboxGroup cbg ;
      private Checkbox cbYes;
      private Checkbox cbNo ;
      
      private CheckboxGroup mapCbg ;
      private Checkbox cb2d;
      private Checkbox cb1x;
      private Checkbox cb1y;
      private Checkbox cbl;          
      

/** classes used */
      private boxCalculate myBox;      
      private superGraph2D graph;
      private movie theMovie;
      private linearFitPlot win=null;
      private Map2DFunction mapFunction;

/** parent */
      private startMap2D outerparent;
/** animation thread */      
      private Thread aThread=null;
/** box counting thread */      
      private Thread bThread=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 CIRCLE=1;      
      private static final int DUFFING=2;
      private static final int BAKERS=3;
      private static final int YORKE=4; 
      private static final int STANDARD=5;
      private static final int GOY=6; 
      private static final int SINAI=7; 
      private static final int MYFUNCTION=8;           
/* 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 startMap2D
*/ 
        public Map2D(startMap2D target, URL in_documentBase) {
        documentBase=in_documentBase;
        graph = new superGraph2D(this);
        
        /*
**      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);

        numSliders = params.length;      
        String[] textboxes = new String[numSliders];
        for (int i=0;i<numSliders;i++) {
             textboxes[i]=String.valueOf(params[i]);
        } 
        
        String[] labels = {"  a","   b","   c"};
        parameters = new 
            sliderControls((dynamicGraph) this, textboxes,labels,
                     numSliders,5,0.01);                          
        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);
        
// function choice went here
        

        String[] textboxes1 = {String.valueOf(cmin[0]),String.valueOf(cmin[1]),
                    String.valueOf(cmax[0]),String.valueOf(cmax[1]),
                    String.valueOf(ntrans),
                    String.valueOf(jump),
                    String.valueOf(marker)};       

        String[] labels1 = {"x_min","y_min","x_max","y_max","transient",
                             "Points"," Mark"};       
        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);

        functionChoice = new Choice();
        functionChoice.addItem("Henon Map");
        functionChoice.addItem("Circle Map");
        functionChoice.addItem("Duffing Map");        
        functionChoice.addItem("Bakers' Map");  
        functionChoice.addItem("Yorke Map");
        functionChoice.addItem("Standard Map");
        functionChoice.addItem("GOY 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);   
        
        cb2d=new  Checkbox("2D");
        cb1x=new  Checkbox("1D-X");
        cb1y=new  Checkbox("1D-Y");
        cbl=new Checkbox("L.E.");
        mapCbg=new CheckboxGroup();
        cb2d.setCheckboxGroup(mapCbg);
        cb1x.setCheckboxGroup(mapCbg);
        cb1y.setCheckboxGroup(mapCbg);
        cbl.setCheckboxGroup(mapCbg);
        constraints.gridwidth=1;
        constraints.weightx=0.25;
        gridbag.setConstraints(cb2d, constraints);                      
        topRightPanel.add(cb2d);
        gridbag.setConstraints(cbl, constraints);                      
        topRightPanel.add(cbl);        
        gridbag.setConstraints(cb1x, constraints);                      
        topRightPanel.add(cb1x); 
        constraints.gridwidth=GridBagConstraints.REMAINDER;                
        gridbag.setConstraints(cb1y, constraints);                      
        topRightPanel.add(cb1y);               
        cb2d.setState(true);                           

//        Panel bottomRightOuterPanel = new Panel();
//        bottomRightOuterPanel.setLayout(new GridLayout(1,1,5,5));
//        bottomRightOuterPanel.setBackground(getBackground().darker());
               
        
        Panel bottomRightPanel = new Panel();
        bottomRightPanel.setBackground(this.getBackground().darker());      
        bottomRightPanel.setLayout(gridbag);        
        dimButton = new Button("Calculate Dimension");
        constraints.gridwidth=GridBagConstraints.REMAINDER;
//        constraints.weighty = 0.5;       
//        constraints.gridheight=2; 
        constraints.insets = new Insets(5,0,0,0);                             
        gridbag.setConstraints(dimButton, constraints);  
        bottomRightPanel.add(dimButton); 
        dimButton.disable(); 
                                        
        Label l= new Label("Show Boxes?");
        constraints.gridwidth=2;
        constraints.weightx=0.5;
        gridbag.setConstraints(l, constraints);                                          
        bottomRightPanel.add(l);
        cbYes=new  Checkbox("Yes");
        cbNo=new  Checkbox("No ");
        cbg=new CheckboxGroup();
        cbYes.setCheckboxGroup(cbg);
        cbNo.setCheckboxGroup(cbg);
        constraints.gridwidth=1;
        constraints.weightx=0.25;
        gridbag.setConstraints(cbYes, constraints);                      
        bottomRightPanel.add(cbYes);
        constraints.gridwidth=GridBagConstraints.REMAINDER;
        gridbag.setConstraints(cbNo, constraints);                      
        bottomRightPanel.add(cbNo);
        cbYes.setState(true);          

        myBox = new boxCalculate();
        
        constraints.insets = new Insets(5,0,0,0);         
        gridbag.setConstraints(myBox, constraints);                 
        bottomRightPanel.add(myBox);
        
        rightPanel.add("South",bottomRightPanel);
//        bottomRightOuterPanel.add(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);
            q=target.q;
            myBox.setQ(q);
            nDiv=target.nDiv;
            myBox.setNDiv(nDiv);
            setDefaults();
            if(target.mapType==0) cb2d.setState(true);
            else if(target.mapType==1) cb1x.setState(true);
            else if(target.mapType==2) cb1y.setState(true);
            else cbl.setState(true);
            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]);
//            setAxesDefaults();                                     
        } 
        else {
              setDefaults();         
        }        
        updateParameters();
        updateVariables();
        if(cbl.getState() ) {
              lyapunov=true;
              cum[0]=0;
              cum[1]=0;
        }        
        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];
                            if(lyapunov) {
                                 cump[0]=0;
                                 cump[1]=0;
                            }    
                            mapFunction.winding=0;
                            mapFunction.total=0;                                                                                      
                            if(ntrans>0) {                                                
                                for(int i=0;i<ntrans;i++) {
                                    if(lyapunov) {
                                          mapFunction.iterateTangent(x,t2);
                                          renormalize(t1,t2,cump);                                        
                                    }
                                    mapFunction.iterate(x);                                          
                                }    
                            }
                            if(returnMap) {                              
                              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) {
                    theMovie.stopIterate();
//                    aThread.stop();
                    aThread=null;
                }
      }

//************************************************************************
/**
* Start movie thread 
*/
//************************************************************************
      public void movieStart() {
                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();
                    if(win!=null) win.dispose();                    
                    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();
                         return super.handleEvent(evt); 
                    }
                    else if( evt.target==cb2d || evt.target==cb1x || evt.target==cb1y
                        || evt.target==cbl) {                         
                         setAxesDefaults();
                         updateVariables();
                         if(randomInitialCondition()) restart();
                         return super.handleEvent(evt); 
                    }                                                              
//                        if (randomInitialCondition()) {
//                            restart();
//                            buttons.enableGo();
//                        }                             
//                     if(evt.target == xFunctionBox || evt.target == yFunctionBox)  {
//                            updateParameters();
//                            updateVariables();
//                     }


                     else if(evt.target == dimButton) {
                        if(divided<0 ) {                         
                             nDiv = myBox.getNDiv();
                             q=myBox.getQ();
                             plotData = new double[2*nDiv];
                             nData=0;
                             running=true;
                             int np=graph.nPoints(ncurve1);                                  
                             myBox.setText("Finding dimension for "+np+" points");                         
                             double[] xdata=graph.getData(ncurve1,np);
                             int ixdata[]= new int[2*np];
                             nBoxes=(int) (Math.pow(2.,(double)nDiv));                       
                             for(int i=0;i<2*np;i++) {  
                                  ixdata[i]=(int) (nBoxes*xdata[i]);
                             }     
                             buttons.disableAll();
                             dimButton.enable();
                             disableAll();
                             myBox.setup(ixdata);
//                             myBox.setQ(q);
                             if(bThread!=null) {
//                                bThread.stop();
                                myBox.stopRequest();
                                bThread=null; 
                             }
                             bThread = new Thread(myBox);
                             bThread.start();                
                             boxSize=1.0;
                             nBoxes=1;
                             divided++;
                             dimButton.setLabel("Continue");
                             myBox.textBoxDisable();                                          
    
                      }
                     else if(divided < nDiv) {
                         if(bThread.isAlive()) {
                              myBox.setText("Push Stop to end calculation");

                         }
                         else {
                             if(!myBox.completed) {
                              myBox.stopRequest();
                              dimButton.disable();
                              myBox.setText("");
                              buttons.enableGo();
                              enableAll();
                              paintBoxes=false;
                              graph.repaint();
                              divided=-1;
                              running=false;
                             }
                             else{                                                                   
                                 dimButton.disable();
                                 divided++;
                                 boxSize=boxSize/2.;
                                 nBoxes=nBoxes*2;                                 
                                 calculateDimensionPoint();
//                                 dimButton.enable();
                                 if(divided<nDiv)
                                    dimButton.setLabel("Continue");
                                 else
                                    dimButton.setLabel("Plot it");                                    
                                 if(cbYes.getState()) {
                                     graph.clearAll=true;
                                     paintBoxes=true;
                                     graph.paintAll=true;
                                     graph.repaint();
                                 }
                                 dimButton.enable();

                             }                                                   
                         }    
                     }                            
                     else if(divided == nDiv)  {
                          if(nData>0) { 
                            win = new linearFitPlot(plotData, documentBase);
                            win.setTitle("Dimension Plot");
                            win.setShowIntercept(false);
                            if(q==0)
                              win.setAxisStrings("log(1/box size)","log(number of boxes)");
                            else if(q==1)
                              win.setAxisStrings("log(1/box size)","Information");
                            else if(q==2)
                              win.setAxisStrings("log(1/box size)","-log(P{^"+q+"})");
                            else
                              win.setAxisStrings("log(1/box size)","-log(P{^"+q+"})/"+(q-1));
                            win.setSlopeString("Dimension=");                                                        
                            win.resize(400,400);
                            win.show();
                            dimButton.enable();
                          }
                          else myBox.setText("No points to plot");
                          dimButton.setLabel("Dimension");
                          myBox.textBoxEnable();
                          dimButton.enable();
                          myBox.setText("");
                          buttons.enableGo();
                          enableAll();
                          paintBoxes=false;
                          graph.repaint();
                          divided=-1;
                          running=false;
                     }
                     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(); 
          cb2d.disable(); 
          cb1x.disable();
          cb1y.disable();
          cbl.disable();    
//          for(i=0;i<parameters.ncontrols();i++) 
//                 parameters.t[i].disable();
      }

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

      public void enableAll() {
          for(int i=0;i<variables.ncontrols();i++)
                variables.enableText(i);
          functionChoice.enable();
          cb2d.enable(); 
          cb1x.enable();
          cb1y.enable(); 
          cbl.enable();                 
//          for(int i=0;i<parameters.ncontrols();i++)
//                parameters.enableText(i);
      }


//********************************************************************
/**
* 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]);
            }
            
//            a = params[0];
//            b = params[1];
//            c = params[2]; 
            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]);
//            if(cb1x.getState())cmin[1]=cmin[0];
//            else if(cb1y.getState())cmin[0]=cmin[1];       
            cmax[0] = variables.parseTextField(2,cmax[0]);
            cmax[1] = variables.parseTextField(3,cmax[1]);
//            if(cb1x.getState())cmax[1]=cmax[0];
//            else if(cb1y.getState())cmax[0]=cmax[1];            
            xRange=cmax[0]-cmin[0];
            xReduce=1./xRange;            
            yRange=cmax[1]-cmin[1];
            yReduce=1/yRange;
            ntrans = variables.parseTextField(4,ntrans);
            jump = variables.parseTextField(5,jump);
            marker=variables.parseTextField(6,marker);           

      }      
                
//*********************************************************************
/**  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;
            graph.clearAll=true;
            if(ncurve>=0) ncurve = graph.deleteAllCurves();                              
            clicked=false;
            newRun=true;
            if(cb2d.getState() || cbl.getState() ) {
                  returnMap=false;
                  index0=0;
                  index1=1;
            }      
            else {
                  returnMap=true;
                  if(cb1x.getState()) {
                        index0=2;
                        index1=0;
                  }
                  else if(cb1y.getState()) {
                        index0=3;
                        index1=1;
                  }
            }
            if(cbl.getState() ) {
                  lyapunov=true;
                  cum[0]=0;
                  cum[1]=0;
            }
            else lyapunov=false;             
            iterations=0;
            plotted=0;
            mapFunction.winding=0;
            mapFunction.total=0;
            divided=-1;
            paintBoxes=false;
            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]=(x[index0]-cmin[0])*xReduce;
            data2[1]=(x[index1]-cmin[1])*yReduce;
            if(marker<=0.01){
                markerType=7;
                markerScale=1.;
            }    
            else {
                markerType=1;
                markerScale=marker;
            }
            
            ncurve = graph.addCurve(data2,1,Color.blue,0,markerType,markerScale);            
            ncurve1=ncurve;
//            graph.clearAll= false ;                              
            graph.paintAll=true; 
            graph.repaint();
//            iterations=0;
            myBox.setText("");                
      }

//*********************************************************************
/**
* Iterates map and updates graph  
*/
//*********************************************************************
      
      public boolean iterate() {
            
            int inBounds;
            int i,n,nplot;
            double[] moredata = new double[2*jump];
            n=0;
            nplot=0;
            for(i=0; i<2*jump; i=i+2) {
                    if(lyapunov) {
                        mapFunction.iterateTangent(x,t1);
                        mapFunction.iterateTangent(x,t2);
                        renormalize(t1,t2,cum);
                        iterations++;
                    }                
                    mapFunction.iterate(x);
                    inBounds = checkBounds(x);                    
                    if(inBounds == 1) {
                        moredata[nplot++]=(x[index0]-cmin[0])*xReduce;
                        moredata[nplot++]=(x[index1]-cmin[1])*yReduce;
                        n=n+1;                    
                    }                    
                    else if (inBounds == -1) {  
                        alertDialog alert = new alertDialog(this, "Value diverged: stop and restart");
                        return false;                        
                    }               
                    if(n > MAX_APPEND) break;
            }    
            if(n==0) return true;
            
            graph.paintAll=false;     // Don't paint while updating data
//          if(graph.nPoints(ncurve)>200) {
//                graph.deleteFromCurve(100,ncurve);
//          }     

            graph.appendToCurve(moredata,n,ncurve1);
            graph.paintAll=true;
            graph.clearAll=false;
            graph.repaint(); 
            plotted=plotted+n;
            if(theMovie.iterate) {
                if(mapFunction.showWinding && plotted > 0)
                   myBox.setText("Winding number "+
                   String.valueOf((float) mapFunction.winding/(float) mapFunction.total));
                else
                   myBox.setText("Iteration number "+String.valueOf(plotted));     
            }
            if (checkBounds(x) <0 ) {
                    return false;
            }
            else
                return true;  
      }

//**********************************************************************
/**
* Stop thread and close fit window
*/
//**********************************************************************
      
      public void stop() {
            movieStop();
            if(win!=null) win.dispose();            
            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) {
/*               updateParameters();
                 updateVariables();
                 if(randomInitialCondition()) {
                    restart();
                    buttons.enableGo();  
                 }       */
                graph.clearAll=false; //6/27/96
                if(aThread != null) {
                  movieStop();
                  if(initialCondition(x)) {
                        restart();
                        newRun=false;
                        movieStart();
                  }      
                }
                else {
//                  updateParameters();
                  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();
                dimButton.enable();
                if(lyapunov && iterations > 0) {
                    myBox.setText("Exp: "+(float)(cum[0]/((double) iterations))+", "+
                        (float)(cum[1]/((double) iterations)));
                }            
            }

      }      

//*********************************************
/**
* 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;
           double cump[] = new double[2];
           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(lyapunov) {
                     t1[0]=Math.random();
                     t1[1]=Math.sqrt(1-t1[0]*t1[0]);
                     t2[0]=-t1[1];
                     t2[1]=t1[0];
                     cump[0]=0;
                     cump[1]=0;
//                     iterations=0;
                }                               
                if(ntrans>0) {                
                    for(int i=0;i<ntrans;i++) {
                        if(lyapunov) {
                              mapFunction.iterateTangent(x,t2);
                              renormalize(t1,t2,cump);                                        
                        }                        
                        mapFunction.iterate(x);
                        if(checkBounds(x) <0) {
                             diverged=true;
                             break;
                        }                           
                    }
                }
                if(returnMap) {
                  mapFunction.iterate(x);
                  if(checkBounds(x) <0) {
                       diverged=true;
                  }                      
                }                
                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(lyapunov) {
                     t1[0]=Math.random();
                     t1[1]=Math.sqrt(1-t1[0]*t1[0]);
                     t2[0]=-t1[1];
                     t2[1]=t1[0];
                     cum[0]=0;
                     cum[1]=0;
                     iterations=0;
                }                    
                if(ntrans>0) {                
                    for(int i=0;i<ntrans;i++) {
                        if(lyapunov) {
                              mapFunction.iterateTangent(x,t2);
                              renormalize(t1,t2,cum);                                        
                        }                        
                        mapFunction.iterate(xm);
                        if(checkBounds(xm) <0) {
                             diverged=true;
                             break;
                        }     
                    }
                }
                if(returnMap) {
                        mapFunction.iterate(xm);
                        if(checkBounds(xm) <0) {
                             diverged=true;
                        }
                }
                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 CIRCLE:
                    mapFunction = new Circle2DFunction();
                    break;
                case DUFFING:
                    mapFunction = new DuffingMapFunction();
                    break;       
                case BAKERS:
                    mapFunction = new BakersFunction();
                    break;     
                case YORKE:
                    mapFunction = new YorkeFunction();
                    break;   
                case STANDARD:
                    mapFunction = new StandardFunction();
                    break;    
                case GOY:
                    mapFunction = new Map2DGOYFunction();
                    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 boxes to graph
* @param g Graphics context
* @param r data rectangle of graph
*/
//**********************************************************************      
     
      public void addToGraph( Graphics g, Rectangle r)  {
           double xBoxSize, yBoxSize;

           if (paintBoxes) {
               g.setColor(Color.red);
               int start = 2*myBox.divIndex[divided];
               int end = 2*myBox.divIndex[divided-1];
               xBoxSize = r.width*boxSize;
               yBoxSize = r.height*boxSize;

               if(cbYes.getState())
                  for (int i=start;i< end;i=i+2) 
                     g.drawRect(r.x+(int)(xBoxSize*myBox.output[i]),
                                r.y+r.height-(int) (yBoxSize*(myBox.output[i+1]+1)),
                                 (int)xBoxSize,(int)yBoxSize);           
//               myBox.setText((end-start)/2+" boxes size "+(float)boxSize);                         
           }                  
      }

//**********************************************************************
/**
* Sets axes defaults based on type of plot
*/
//**********************************************************************    
      public void setAxesDefaults() {
            if( cb2d.getState() || cbl.getState() ) {
                 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");
            }   
            else if(cb1x.getState()) {
                 variables.setText(0,String.valueOf(mapFunction.xminDefault));
                 variables.setText(1,String.valueOf(mapFunction.xminDefault));
                 variables.setText(2,String.valueOf(mapFunction.xmaxDefault));
                 variables.setText(3,String.valueOf(mapFunction.xmaxDefault));
                 graph.setXAxisTitle("X_n");
                 graph.setYAxisTitle("X_n+1");                 
            }
            else if(cb1y.getState()) {
                 variables.setText(0,String.valueOf(mapFunction.yminDefault));
                 variables.setText(1,String.valueOf(mapFunction.yminDefault));
                 variables.setText(2,String.valueOf(mapFunction.ymaxDefault));
                 variables.setText(3,String.valueOf(mapFunction.ymaxDefault));
                 graph.setXAxisTitle("Y_n");
                 graph.setYAxisTitle("Y_n+1");
            }
       }
       
//**********************************************************************
/**
* Performs Gramm-Schmidt orthogonalization and calculates increase in norm of
* the vectors
* @param t1 Tangent vector of larger eigenvalue
* @param t2 Tangent vector of smaller eigenvalue
* @param c cumulated log of growth of vector norms
*/
//**********************************************************************          
       private void renormalize(double[] t1, double[] t2, double[] c) {
             double norm1, norm2, dot;
             
             norm1=Math.sqrt(t1[0]*t1[0]+t1[1]*t1[1]);
             t1[0]=t1[0]/norm1;
             t1[1]=t1[1]/norm1;
             dot=t2[0]*t1[0]+t2[1]*t1[1];
             t2[0]=t2[0]-dot*t1[0];
             t2[1]=t2[1]-dot*t1[1];
             norm2=Math.sqrt(t2[0]*t2[0]+t2[1]*t2[1]);
             t2[0]=t2[0]/norm2;
             t2[1]=t2[1]/norm2;
             
             c[0]=c[0]+Math.log(norm1);
             c[1]=c[1]+Math.log(norm2);
             
       } 
            
//**********************************************************************
/**
* Calculates point for generalized dimension plot
*/
//**********************************************************************   
      private void calculateDimensionPoint() {             
            int range= (myBox.divIndex[divided-1]-myBox.divIndex[divided]);
            if(range>0) {
               plotData[nData++]=l2*Math.log((double)nBoxes);
               if(q==0)  {
                     plotData[nData++]=l2*Math.log((double)range); 
                     myBox.setText(range+" boxes size "+(float)boxSize);
               }        
               else if(q==1) {
                     double probability=0;
                     for(int i=myBox.divIndex[divided];i<myBox.divIndex[divided-1];i++) 
                     probability=probability-
                           l2*((double)myBox.boxCount[i]/(double)myBox.total)*
                           Math.log(((double)myBox.boxCount[i]/(double)myBox.total));
                     plotData[nData++]=probability;
                     myBox.setText("Box size "+(float)boxSize
                           +": Information: "+(float)probability);  
               }
               else {
                     double probability=0;
                     for(int i=myBox.divIndex[divided];i<myBox.divIndex[divided-1];i++) { 
                          probability=probability+
                            Math.pow(((double)myBox.boxCount[i]/(double)myBox.total),q);
                     }
                     plotData[nData++]=-l2*Math.log(probability)/((double)(q-1));
                     myBox.setText("Box size "+(float)boxSize
                           +": P^"+q+"= "+(float)probability);                                           
               }                                    
              
            }
      }                                                                                
      
}   
/*******************************************************************/
