package ChaosDemos;
import java.lang.*;
import java.awt.*;
/**
* Calculates dimesion of 2D maps by box counting algorithm, uisng a fast
* sort algorithm to arrange array of finest boxes and then successively
* coarsening and weeding the array.<br>
* Stores number of boxes to cover points in divIndex[nDiv+1]
* @version 2 August 1997
* @author Michael Cross
*/
public class boxCalculate extends Panel implements Runnable {

/* private variables */
    private int[] index;         // array of current boxes in index form
    private int[] x;             // array of current boxes in x,y form
    private int number;          // numebr of current boxes
    private int divisions;       // Number of current boxes across axis
    private int nDiv=6;            // Highest number of subdivisions
    private int[] copy;          // Working array
    private int[] copy1;
    private int nOld;            // Position to write in output array
    private boolean stopRequested;
    private Button b1;
    private TextField status;
    private int[] occupancy;
    private int q=0;
    private TextField t1;
    private TextField t2;
    private alertDialog alert;
    
/* Public varibales */
/**
* Array of size nDiv+1 containing number of boxes covering attractor at each division scale
*/
    public int[] divIndex;
/**
* Total nuber of points
*/    
    public int total;      
/**
* Array contining box locations: 
* <UL><LI>0 to divIndex[0]-1 for finest subdivision
*     <LI>divIndex[0] to divIndex[1]-1 for next etc.
* </UL>
* Position (i,j) is stored as i+j*N where there are N boxes across one direction of map at
* this scale, and i and j run from 0 to N-1.
*/        
    public int[] output;
/**
* Array contining number of points in boxes: 
* <UL><LI>0 to divIndex[0]-1 for finest subdivision
*     <LI>divIndex[0] to divIndex[1]-1 for next etc.
* </UL>
* stored as in output.
*/    
    public int[] boxCount;
/**
* True if calculation of box coverage is complete
*/    
    public boolean completed=false;
//    public int updateCount=10;            

/** Default constructor
* lays out button and textBox controls    
*/
    public boxCalculate() {

        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints constraints = new GridBagConstraints();
        setLayout(gridbag);
        constraints.weightx = 1.0;
        constraints.weighty = 1.0;
        constraints.gridwidth=1;
        constraints.insets = new Insets(5,5,5,5);         
        b1 = new Button(" Stop ");
        gridbag.setConstraints(b1, constraints);
        add(b1);
        b1.disable();
        t1 = new TextField("6",3);
        Label l1 = new Label("Div",Label.RIGHT);
        Panel p1 = new Panel();
        p1.setLayout(new FlowLayout(FlowLayout.RIGHT));
        gridbag.setConstraints(p1, constraints);
        add(p1);
        p1.add(l1);
        p1.add(t1);  
        t2 = new TextField("0",3);
        Label l2 = new Label(" q ",Label.RIGHT);
        Panel p2 = new Panel();
        p2.setLayout(new FlowLayout(FlowLayout.RIGHT));
        constraints.gridwidth=GridBagConstraints.REMAINDER;        
        gridbag.setConstraints(p2, constraints);
        add(p2);
        p2.add(l2);
        p2.add(t2);                
        status = new TextField(30);
        constraints.fill=GridBagConstraints.HORIZONTAL;
        gridbag.setConstraints(status, constraints);
        add(status);
     }
/**
* Sets up calculation. Must be called before calculation thread is started.
* @param in_x Array of data in x,y pairs of ints given by box number on finest (2^nDiv)
* scale
* @param in_nDiv Number of subdivisions to make (from 0 to nDiv)
*/      
     public void setup (int[] in_x) {
        q=parseTextField(t2,q);
        nDiv=parseTextField(t1,nDiv,true);
        stopRequested=false;
        number = in_x.length/2;
        total = number;
        x= new int[2*number];
        System.arraycopy(in_x,0,x,0,2*number);        
        divIndex = new int[nDiv+1];
        nOld=0;
        index = new int[number];
        output = new int[1];
        divisions=(int) (Math.pow(2.,(double)nDiv));
        divIndex[nDiv]=0;
        if(q!=0) {
            occupancy = new int[number];     
            for(int i=0;i<number;i++) occupancy[i]=1;
        }
     }
     
/**
* Thread to perform calculation
*/     
     public void run() {
        if(q!=0) boxCount= new int[1];
        stopRequested=false;
        completed=false;
        b1.enable(); 
        status.setText("Calculating dimension....");        
        int i;
//      System.out.println("nDiv= "+nDiv+" divisions= "+divisions);
        for(int iDiv=nDiv-1; iDiv>=0; iDiv--) {
//          System.out.println("iDiv= "+iDiv+" Divisions= "+divisions);
            toIndex();
//            System.out.println("Unsorted");
//            for(int j=0;j<number;j++) {
//                System.out.println( index[j]+" "+occupancy[j]);
//            }            
            if(q==0) sort(index,number);
            else sort(index, occupancy, number);
//            System.out.println("Sorted");
//            for(int j=0;j<number;j++) {
//                System.out.println( index[j]+" "+occupancy[j]);
//            }
            if(stopRequested) return;
            if(q==0) weed(index);
            else weed(index, occupancy);
//            System.out.println("Weeded");
//            for(int j=0;j<number;j++) {
//                System.out.println( index[j]+" "+occupancy[j]);
//            }
            fromIndex();
//            for(int j=0;j<number;j++) {
//                System.out.println("x= "+x[2*j]+" y= "+x[2*j+1]);
//            }
            divIndex[iDiv]=divIndex[iDiv+1]+number;
            copy = new int[2*nOld+2*number];
            System.arraycopy(output,0,copy,0,2*nOld);
            System.arraycopy(x,0,copy,2*nOld,2*number);

            if(q!=0) {
                  copy1 = new int[nOld+number];
                  System.arraycopy(boxCount,0,copy1,0,nOld);
                  System.arraycopy(occupancy,0,copy1,nOld,number);
            }
                              
            nOld=nOld+number;
            
            output = new int[2*nOld];
            System.arraycopy(copy,0,output,0,2*nOld);
            
            if(q!=0) {
                  boxCount = new int[nOld];
                  System.arraycopy(copy1,0,boxCount,0,nOld);
            }      
            
            for(i=0;i<number;i++) {
                x[2*i]=x[2*i]/2;
                x[2*i+1]=x[2*i+1]/2;
            }
            divisions = divisions/2;
        }
        completed=true;
        status.setText("Dimension calculation done!");        
        b1.disable();
     }
/**
* Converts x,y pair to index
*/     
     private void toIndex() {
        for(int i=0;i<number;i++) 
            index[i]=x[2*i]+divisions*x[2*i+1];
     
     }
/**
* Converts index to x,y pair
*/     
     private void fromIndex() {
        for(int i=0;i<number;i++) {
            x[2*i+1]=index[i]/divisions;
            x[2*i]=index[i]-x[2*i+1]*divisions;
        }
     }
/**
* Sorts index array ra and associated array rb using method from Numerical Recipes
@ param ra index array
@ param rb second array
@ n number of entries in arrays to be sorted
*/
     private void sort(int ra[], int rb[], int n)  {
      int i,ir,j,l;
      int rra;
      int rrb;
      if (n<2) return;
      l=n/2;
      ir=n-1;
      while(true) {
//          System.out.println("l= "+l+" ir= "+ir);
            if(l > 0) {
                  l=l-1;
                  rra=ra[l];
                  rrb=rb[l];    
            }      
            else {
                  rra=ra[ir];
                  rrb=rb[ir];
                  ra[ir]=ra[0];
                  rb[ir]=rb[0];
                  ir=ir-1;
                  if(ir == 0) {
                        ra[0]=rra;
                        rb[0]=rrb;
                        return;
                  }
            }
            i=l;
            j=l+l+1;
            while(j <=ir) {
                  if(j < ir && ra[j] < ra[j+1]) j++;
                  if(rra <ra[j]) {
                        ra[i]=ra[j];
                        rb[i]=rb[j];
                        i=j;
                        j=j+j+1;
                  }
                  else
                        j=ir+1;
            }
            ra[i]=rra;
            rb[i]=rrb;
      }
     }
     
/**
* Sorts index array using method from Numerical Recipes
@ param ra index array
@ n number of entries in arrays to be sorted
*/
     private void sort(int ra[], int n)  {
      int i,ir,j,l;
      int rra;
      if (n<2) return;
      l=n/2;
      ir=n-1;
      while(true) {
//          System.out.println("l= "+l+" ir= "+ir);
            if(l > 0) {
                  l=l-1;
                  rra=ra[l];
            }      
            else {
                  rra=ra[ir];
                  ra[ir]=ra[0];
                  ir=ir-1;
                  if(ir == 0) {
                        ra[0]=rra;
                        return;
                  }
            }
            i=l;
            j=l+l+1;
            while(j <=ir) {
                  if(j < ir && ra[j] < ra[j+1]) j++;
                  if(rra <ra[j]) {
                        ra[i]=ra[j];
                        i=j;
                        j=j+j+1;
                  }
                  else
                        j=ir+1;
            }
            ra[i]=rra;
      }
     }                          
     


/**
* Weeds out duplicate values and adds to count number
@ param a array to be weeded
@ param b array of counts
*/
    private void weed(int a[], int b[]) {
        int n=0;
        int count=b[0];
        for(int i=1;i<number;i++) {
             if(a[i] != a[n]) {
                b[n]=count;
                n++;
                count=b[i];
                a[n]=a[i];                
             }
             else count=count+b[i];
        }
        b[n]=count;
        number=n+1;
    }
    
/**
* Weeds out duplicate values
@ param a array to be weeded
*/
    private void weed(int a[]) {
        int n=0;
        for(int i=1;i<number;i++) {
             if(a[i] != a[n]) {
                n++;
                a[n]=a[i];                
             }
        }
        number=n+1;
    }    
/**
* stops calculation
*/    
    public void stopRequest() {
        stopRequested=true;
    }     
/**
* Handles button event to stop calculation
*/    
    public boolean handleEvent(Event evt) {
        if(evt.id==Event.ACTION_EVENT && evt.target == b1) {
            status.setText("Calculation aborted!");            
            b1.disable();
            stopRequested=true;
        }
        return super.handleEvent(evt);         
    }  
      
/**
* Sets textBox contents
* @param text contents
*/    
    public void setText(String text) {
        status.setText(text);
    }
    
//*********************************************************************
/**
* Parses text field known to be integer of known sign.  
* Resets old value of corresponding variable if input format
* is incorrect or wrong sign and brings up alertDialog warning box.
* @param t field to be parsed
* @param i old value of variable
* @param positive true if value should be positive, false if should be negative
* @return new value of parameter if textbox format is correct,
* and value of correct sign, otherwise old value.
* @see alertDialog
*/
//*********************************************************************

    public 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("Try an integer");
                return i;
            }
            if(((iNew < 0) && positive) || ((iNew>0) && !positive)) {
                t.setText(String.valueOf(i));
                if(positive) alert = new alertDialog("Must be positive");
                else alert = new alertDialog("Must be negative");
                return i;
            }
            return iNew;
   }    

//*********************************************************************
/**
* Parses text field known to be integer.  
* Resets old value of corresponding variable if input format
* is incorrect and brings up alertDialog warning box.
* @param t field to be parsed
* @param i old value of variable
* @return new value of parameter if textbox format is correct,
* otherwise old value.
* @see alertDialog
*/
//*********************************************************************

    public int parseTextField(TextField t, int i) {
            int iNew;
            try {
                iNew=(new Integer(t.getText())).intValue();
            }
            catch (NumberFormatException e) {
                t.setText(String.valueOf(i));
                alert = new alertDialog("Try an integer");
                return i;
            }
            return iNew;
   }   
//*********************************************************************
/** returns value of dimension order
* @return dimension order q
*/
   public int getQ() {
        q=parseTextField(t2,q);
        return q;
   }

//*********************************************************************
/** sets value of dimension order
* @param in_q dimension order q
*/   
   public void setQ(int in_q) {
        t2.setText(String.valueOf(in_q));
        q=in_q;
   }   

//*********************************************************************
/** returns value of number of subdivisions
* @return number of subdivisions
*/    
   public int getNDiv() {
        nDiv=parseTextField(t1,nDiv,true);   
        return nDiv;
   }

//*********************************************************************
/** sets value of number of subdivisions
* @param in_nDiv number of subdivisions
*/    
   public void setNDiv(int in_nDiv) {
        t1.setText(String.valueOf(in_nDiv));   
        nDiv=in_nDiv;
   }

//*********************************************************************
/** enables TextFields */    
   public void textBoxEnable() {
      t1.enable();
      t2.enable();
   }

//*********************************************************************
/** disables TextFields */       
   public void textBoxDisable() {
      t1.disable();
      t2.disable();
   }   
    
}         
