/***********************************************************
  Applet Displayrand1 : shows a random dynamical system
  from a data base of around 50000.
  Copyright 1998 : Zito Giuseppe
***********************************************************/

import java.awt.*;
import java.awt.image.*;
import java.io.DataInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.util.StringTokenizer;
import java.applet.*;

public class Displayrand1 extends Applet implements Runnable {

  URL theURL;
  Thread runner;
  TextArea ta = new TextArea("Getting text...",30,20);
  Formula form = new Formula();
  int maxcol = 160;
  int ncolr = 30;
  byte redm[] = new byte[maxcol];
  byte greenm[] = new byte[maxcol];
  byte bluem[] = new byte[maxcol];
  byte b255 = (byte)255;
  IndexColorModel icm;
  boolean debug = false;
  boolean test = false;
  Dr1basin basin;
  Dr1attr attr;
  Dr1global global;
  Panel imagePanel;
  Panel controlPanel;
  Button tasto1;
  TextField f1;

  public void init() {
    int i; 
       
    UserParameters();
    if (debug) System.out.println ("Starting init...");
    redm[0] = 0; greenm[0] = 0; bluem[0] = 0;
    redm[1] = b255; greenm[1] = b255; bluem[1] = b255;
    redm[2] = b255; greenm[2] = 0; bluem[2] = 0;
    redm[3] = 0; greenm[3] = b255; bluem[3] = 0;
    redm[4] = 0; greenm[4] = 0; bluem[4] = b255;
    redm[5] = b255; greenm[5] = b255; bluem[5] = 0;
    redm[6] = b255; greenm[6] = 0; bluem[6] = b255;
    redm[7] = 0; greenm[7] = b255; bluem[7] = b255;
    redm[8] = 0; greenm[8] = (byte)254; bluem[8] = (byte)254;
    redm[9] = 0; greenm[9] = (byte)253; bluem[9] = (byte)253;
    for (i=0;i<ncolr;i++) {
      redm[i+10] = interp(255,255,i/(double)(ncolr-1.));
      greenm[i+10] = interp(0,255,i/(double)(ncolr-1.));
      bluem[i+10] = interp(0,0,i/(double)(ncolr-1.));
    }
    for (i=0;i<ncolr;i++) {
      redm[i+ncolr+10] = interp(255,0,i/(double)(ncolr-1.));
      greenm[i+ncolr+10] = interp(255,255,i/(double)(ncolr-1.));
      bluem[i+ncolr+10] = interp(0,0,i/(double)(ncolr-1.));
    }
    for (i=0;i<ncolr;i++) {
      redm[i+2*ncolr+10] = interp(0,0,i/(double)(ncolr-1.));
      greenm[i+2*ncolr+10] = interp(255,255,i/(double)(ncolr-1.));
      bluem[i+2*ncolr+10] = interp(0,255,i/(double)(ncolr-1.));
    }
    for (i=0;i<ncolr;i++) {
      redm[i+3*ncolr+10] = interp(0,0,i/(double)(ncolr-1.));
      greenm[i+3*ncolr+10] = interp(255,0,i/(double)(ncolr-1.));
      bluem[i+3*ncolr+10] = interp(255,255,i/(double)(ncolr-1.));
    }
    for (i=0;i<ncolr;i++) {
      redm[i+4*ncolr+10] = interp(0,255,i/(double)(ncolr-1.));
      greenm[i+4*ncolr+10] = interp(0,0,i/(double)(ncolr-1.));
      bluem[i+4*ncolr+10] = interp(255,255,i/(double)(ncolr-1.));
    }
    icm = new IndexColorModel(8,maxcol,redm,greenm,bluem);
    String url = "http://www.ba.infn.it/zitobin/nph-rformula";
    try { this.theURL = new URL(url); 
    }catch ( MalformedURLException e) {
      System.out.println("Bad URL: " + theURL);
    }
    setLayout(new BorderLayout());
    imagePanel = new Panel();
    imagePanel.setLayout(new GridLayout(2,1));
    add("Center", imagePanel);
    basin = new Dr1basin();
    imagePanel.add(basin);
    global = new Dr1global();
    imagePanel.add(global);
    attr = new Dr1attr("strange attractor");
    attr.resize(500,500);
    attr.move(50,50);
    attr.show();
    add("East",ta);
    form.setvar(debug);
    controlPanel = new Panel();
    controlPanel.setLayout(new GridLayout(2,1));
    add("South", controlPanel);
    tasto1 = new Button("Another random formula");
    controlPanel.add  (tasto1);
    f1 = new TextField("Starting",10);
    controlPanel.add(f1);  
    if (debug) System.out.println ("Exiting init...");
  }

  public byte interp(int a,int b,double f) {
    return (byte)(a*(1-f)+b*f);
  }
  
  private void UserParameters () {
    String parameter;

    // Get user parameters
    parameter = getParameter ("debug");
    if (parameter != null) debug = parameter.equals("true");
    parameter = getParameter ("test");
    if (parameter != null) test = parameter.equals("true");
  }

  public Insets insets() {
    return new Insets(10,10,10,10);
  }

  public void start() {
    if (runner == null) {
      runner = new Thread(this);
      runner.start();
      basin.setvar(debug,maxcol,form, icm,runner,f1);
      attr.setvar(debug,form,f1);
      global.setvar(debug,maxcol,form, icm,runner,f1);
    }
  }
  
  public void stop() {
    if (runner != null) {
      runner.stop();
      runner = null;
    }
  }

  public void update(Graphics g) {
    paint (g);
  }

  public void run() {
    if(!test)form.read(theURL);
    form.print(ta);
    form.init();
    attr.drawimage();
    global.drawimage();
    global.repaint();
    basin.drawimage();
  }

}

/*******************************************************
            Formula
*******************************************************/
class Formula {
  int nsel[]=new int[2];
  int isel[][]=new int [2][50];
  double c[][]=new double [2][50];
  double stack[]=new double[50];
  int iexe[][]=new int[2][50];
  double p[]=new double[2];
  int istack,k,numpull;
  boolean debug;
  double xin,yin;
  StringBuffer buf;
  Fpoint fp1 = new Fpoint(0.,0.);

  public Formula(){
    nsel[0] = 6; nsel[1] = 2;
    isel[0][0]=14;
    isel[0][1]=3;
    isel[0][2]=16;
    isel[0][3]=7;
    isel[0][4]=9;
    isel[0][5]=19;
    isel[1][0]=13;
    isel[1][1]=9;

    for(int j=0;j<2;j++){
      for (int i=0;i<nsel[j];i++)
        {c[j][i]=0.;
      }
    }
    c[0][1]=1.;c[0][4]=-1.4;c[1][1]=.3;
    xin=0;yin=0;
  }

  public void  setvar(boolean debug1){
    debug = debug1;
  }

  public void print(TextArea ta){
    ta.setText(buf.toString()); 
  }

  public void read(URL theURL) {
    URLConnection conn;
    DataInputStream data;
    String line;
    int numlin,j;
    
    buf = new StringBuffer();
    try { 
      conn = theURL.openConnection();
      conn.connect();
      data = new DataInputStream(new BufferedInputStream(
                     conn.getInputStream()));
      numlin=0;
      line = data.readLine();
      line = data.readLine();
      while ((line = data.readLine()) != null) {
        buf.append(line + "\n");
        numlin = numlin + 1;
        if (debug) System.out.println(numlin+" "+line);
        StringTokenizer st = new StringTokenizer(line);
        if(numlin == 1) {
          st.nextToken();
          nsel[0] = Integer.valueOf(st.nextToken()).intValue();
          if(debug)System.out.println("nsel[0] ="+nsel[0]);
          nsel[1] = Integer.valueOf(st.nextToken()).intValue();
          if(debug)System.out.println("nsel[1] ="+nsel[1]); 
        }   
        if(numlin > 1 && numlin < 7){
          j = (numlin-2)*10;
          for(int i =0;i<10;i++){
            isel[0][j]=Integer.valueOf(st.nextToken()).intValue();
            if(debug)System.out.println("isel ="+isel[0][j]);j++;
          }
        }
        if(numlin > 6 && numlin < 12){
          j = (numlin-7)*10;
          for(int i =0;i<10;i++){
            isel[1][j]=Integer.valueOf(st.nextToken()).intValue();
            if(debug)System.out.println("isel ="+isel[1][j]);j++;
          }
        }
        if(numlin > 11 && numlin < 17){
          j = (numlin-12)*10;
          for(int i =0;i<10;i++){
            c[0][j]=Double.valueOf(st.nextToken()).doubleValue();
            if(debug)System.out.println("c ="+c[0][j]);j++;
          }
        }
        if(numlin > 16 && numlin < 22){
          j = (numlin-17)*10;
          for(int i =0;i<10;i++){
            c[1][j]=Double.valueOf(st.nextToken()).doubleValue();
            if(debug)System.out.println("c ="+c[1][j]);j++;
          }
        }
        if(numlin == 22){
          xin = Double.valueOf(st.nextToken()).doubleValue();
          yin = Double.valueOf(st.nextToken()).doubleValue();
          if(debug)System.out.println("x0,y0= "+xin+yin);
        }
      }
    }
    catch (IOException e) {
      System.out.println("IO Error:" + e.getMessage());
    }
  }

  public void init(){
  if (debug) System.out.println("Computing iexe");
  /* This is to speed up formula computation */
  for(int i=0; i < 2; i++)for(int j=0; j < nsel[i]; j++) iexe[i][j] = 1;
  for (int j=0; j < 2; j++) {
    for (int i=0; i < nsel[j]; i++) {
      if(isel[j][i] > 15 && isel[j][i] < 19) {
        numpull = 0;
        if((i+1) < nsel[j]) {
          for(k = i+1; k < nsel[j]; k++) {
            if(isel[j][k] > 15 && isel[j][k] < 19)numpull = numpull - 1;
            if(isel[j][k] > 18 && isel[j][k] < 23)numpull = numpull + 1;
            if(numpull == 1) break;
          }
        }
        if(numpull != 1) iexe[j][i] = 0;
      }
    }
  }
}

public Fpoint compute(Fpoint fp0){
  double x0,y0;

  x0 = fp0.x; y0 = fp0.y;
  p[0] = 0.; p[1] = 0.;
  for(int i=0; i< 2; i++) {
    istack = 0;
    for (int k=0; k < nsel[i]; k++) {
      switch (isel[i][k]) {
        case  1: p[i] = p[i] + x0;break;
        case  2: p[i] = p[i] + y0;break;
        case  3: p[i] = p[i] + c[i][k];break;
        case  4: p[i] = p[i] - x0;break;
        case  5: p[i] = p[i] - y0;break;
        case  6: p[i] = p[i] - c[i][k];break;
        case  7: p[i] = p[i] * x0;break;
        case  8: p[i] = p[i] * y0;break;
        case  9: p[i] = p[i] * c[i][k];break;
        case 10: if(x0 == 0.) p[i] = 0.;else  p[i] = p[i] / x0;break;
        case 11: if(y0 == 0.) p[i] = 0.;else  p[i] = p[i] / y0;break;
        case 12: if(c[i][k] == 0.) p[i] = 0.;else  p[i] = p[i] / c[i][k];break;
        case 13: if(k == 0)p[i] = x0;break;
        case 14: if(k == 0)p[i] = y0;break;
        case 15: if(k == 0)p[i] = c[i][k];break;
        case 16: if(iexe[i][k] == 1){
                   istack = istack + 1; 
                   stack[istack] = p[i];
                   p[i] = x0;
                 }
                 break;
        case 17: if(iexe[i][k] == 1){
                   istack = istack + 1; 
                   stack[istack] = p[i];
                   p[i] = y0;
                 }
                 break;
        case 18: if(iexe[i][k] == 1){
                   istack = istack + 1; 
                   stack[istack] = p[i];
                   p[i] = c[i][k];
                 }
                 break;
        case 19: if(istack > 0) {
                   p[i] = p[i] + stack[istack];
                   istack = istack - 1;
                 }
                 break;
        case 20: if(istack > 0) {
                   p[i] = p[i] - stack[istack];
                   istack = istack - 1;
                 }
                 break;
        case 21: if(istack > 0) {
                   p[i] = p[i] * stack[istack];
                   istack = istack - 1;
                 }
                 break;
        case 22: if(istack > 0) {
                    if(stack[istack] == 0.)  p[i] = 0.;
                    else {
                      p[i] = p[i] / stack[istack];
                      istack = istack - 1;
                    }
                 }
                 break;
      }
      if(debug)System.out.println("p[i]= "+p[i]);
    }
    if(debug)System.out.println("i,p[i]= "+i+" "+p[i]);
  }
  fp1.x=p[0];fp1.y=p[1];
  if(debug)System.out.println("fp1= "+fp1.x+" "+fp1.y);
  return(fp1);
  }
}

/*******************************************************
            Fpoint
*******************************************************/

class Fpoint{
  public double x;
  public double y;

  public Fpoint(double x0,double y0){
    x=x0; y=y0;
  }
}

/*******************************************************
            Dr1basin
*******************************************************/

class Dr1basin extends Canvas {
  boolean debug;
  int hh=100;
  int width=100;
  int maxcol;
  Thread runner;
  Formula form;
  byte data[] ;
  Image img;
  IndexColorModel icm;
  TextField f1;

  public void  setvar(boolean debug1,int maxcol1,
               Formula form1, IndexColorModel icm1, 
               Thread runner1, TextField f){
    debug = debug1;
    maxcol=maxcol1;
    runner=runner1;
    form = form1;
    data = new byte[hh*width];
    icm=icm1;
    img = createImage(new  MemoryImageSource(width,hh,icm,data,0,width));
    f1 = f;
  }

  public  void drawimage() {
    int Xscreen =hh ,Yscreen=width;
    int Nxpixel=hh,Nypixel=width;
    double Left= -4.,Right= 4.,Bottom= -4.,Top= 4.;
    int numcol = maxcol;
    int maxiter = 160;
    double x,y,x0,y0,xp,yp,Deltax,Deltay;
    Fpoint fp0 =new Fpoint(0.,0.);
    Fpoint fp1 =new Fpoint(0.,0.);
    int i,j,iter,ic,l,j1;

    /* Main loop on image pixels */
    Deltax = (Right - Left) / Xscreen;
    Deltay = (Top - Bottom) / Yscreen;
    for (j=0; j < Yscreen; j++) {
      f1.setText("Iteration "+j);
      try { runner.sleep(100);}
      catch (InterruptedException e) { }
      for (l=0;l < Xscreen; l++) {
        xp = l*Deltax + Left;
        yp = j*Deltay + Bottom;
        x0 = xp ;
        y0 = yp;
        x = xp;
        y = yp;
        iter = 1;
        while(iter <= maxiter){
          fp0.x = x0;
          fp0.y = y0;
          fp1 = form.compute(fp0);
          x = fp1.x;
          y = fp1.y;
          if(debug)System.out.println("x,y,x0,y0 = "+x+" "+y+" "+x0+" "+y0);
          if(Math.abs(x) > 10000f || Math.abs(y) > 10000f)break;
          iter = iter + 1;
          x0 = x;
          y0 = y;
        }
        ic = (iter % numcol) + 1;
        if(debug) System.out.println("pixel "+l+" "+j+" has color "+ic);
        data[l+(Yscreen-1-j) *width] = (byte)ic;
      }
      img.flush();
      img = null;
      img = createImage( new MemoryImageSource(width,hh,icm,data,0,width));
      repaint();
    }
  }

  public void paint(Graphics g){
    if(img != null)g.drawImage(img,0,10,this);
  }

  public void update (Graphics g) {
    paint(g);
  }
}

/*******************************************************
            Dr1global
*******************************************************/

class Dr1global extends Canvas{
  int Npix = 100;
  double Left = -4.;
  double Right = 4.;
  double Bottom =-4.;
  double Top = 4.;
  boolean debug;
  int maxcol;
  Thread runner;
  Formula form;
  byte data[] ;
  Image img;
  IndexColorModel icm;
  TextField f1;

  public void  setvar(boolean debug1,int maxcol1,
                Formula form1, IndexColorModel icm1, 
                Thread runner1, TextField f){
    debug = debug1;
    maxcol=maxcol1;
    runner=runner1;
    form = form1;
    data = new byte[Npix*Npix];
    icm=icm1;
    img = createImage(new  MemoryImageSource(Npix,Npix,icm,data,0,Npix));
    f1 = f;
  }

  public void drawimage() {
    double dx = (Right -Left)/Npix;
    double dy = (Top - Bottom)/Npix;
    int ine[][] = new int[Npix][Npix];
    int jne[][] = new int[Npix][Npix];
    int i,j,ncy,i0,j0,lcy,i1,j1,ntotcy1,ntotcy2,ncol;
    int icy[][] = new int[Npix][Npix];
    Fpoint fp0 =new Fpoint(0.,0.);
    Fpoint fp1 =new Fpoint(0.,0.);

    if(debug)System.out.println("First phase");
    f1.setText("First phase");
    try { runner.sleep(1000);}
    catch (InterruptedException e) { }
    for ( i=0;i<Npix;i++){
      double x0 = Left + i*dx+dx/2.;
      for ( j=0;j<Npix;j++){
        double y0 = Bottom + j*dy+dy/2.;
        double x1,y1; 
        icy[i][j] = 0;
        fp0.x = x0;
        fp0.y = y0;
        fp1 = form.compute(fp0);
        x1 = fp1.x;
        y1 = fp1.y;
        if ((y1>4)||(x1>4)||(x1<-4)||(y1<-4)) {
          icy[i][j]=-1000000;
        } else {
          ine[i][j]=(int)((x1-Left)/(Right-Left)*Npix);
          jne[i][j]=(int)((y1-Bottom)/(Top-Bottom)*Npix);
          if(ine[i][j] > Npix)ine[i][j]=Npix;
          if(jne[i][j] > Npix)jne[i][j]=Npix;
          if(ine[i][j] == i && jne[i][j] == j)icy[i][j]=-2;
        }
      }
    }
    if(debug)System.out.println("Second phase");
    f1.setText("Second phase");
    try { runner.sleep(1000);}
    catch (InterruptedException e) { }
    ncy =0;
    ntotcy1 = 0;
    ntotcy2 = 0; 
    for ( i=0;i<Npix;i++){
      for ( j=0;j<Npix;j++){
        if(icy[i][j] == 0) {
          i0 = i;
          j0 = j;
          ncy = ncy + 1;
          lcy = 0;
          lcy = lcy +1;
          icy[i0][j0] = ncy;
          i1 = ine[i0][j0];
          j1 = jne[i0][j0];
          while(icy[i1][j1] == 0) {
            i0 = i1;
            j0 = j1;
            lcy = lcy + 1;
            icy[i0][j0] = ncy;
            i1 = ine[i0][j0];
            j1 = jne[i0][j0];
          }
          if(icy[i1][j1] <= -1000000) {
            ntotcy2 = ntotcy2 + 1;
            i0 = i;
            j0 = j;
            while(icy[i0][j0] == ncy) {
              icy[i0][j0] = -1000000-lcy;
              i1 = ine[i0][j0];
              j1 = jne[i0][j0];
              i0 = i1;
              j0 = j1;
              lcy = lcy -1;
            }
            ncy = ncy -1;
            break;
          }
          if(icy[i1][j1]>0 ||(icy[i1][j1] < 0 && icy[i1][j1] > -1000000)) {
            if(icy[i1][j1] != ncy) {
              ntotcy1 = ntotcy1 + 1;
              i0 = i;
              j0 = j;
              while(icy[i0][j0] == ncy) {
                icy[i0][j0] = -ncy;
                i1 = ine[i0][j0];
                j1 = jne[i0][j0];
                i0 = i1;
                j0 = j1;
              }
            }
          }
        }
      }
    }
    if(debug)System.out.println("third phase");
    f1.setText("Last phase");
    try { Thread.sleep(1000);}
    catch (InterruptedException e) { }
    for ( i=0;i<Npix;i++){
      for ( j=0;j<Npix;j++){
        ncol = 1;
        if(icy[i][j] > 1) {
          ncol = icy[i][j]%(maxcol-10) + 10;
        }
        if(icy[i][j] < -1 && icy[i][j] > -1000000) {
          ncol = -icy[i][j];
          if(ncol >= maxcol) {
            ncol = (int)(ncol/(double)ntotcy1*maxcol);
          }
          ncol = ncol%(maxcol-10) + 10;
        }
        if (icy[i][j] <= -1000000){
          ncol = (-(icy[i][j]-1000000))%(maxcol-10)+ 1;
        }
        data[i+(Npix-1-j) *Npix] = (byte)ncol;
      }
    }
    img.flush();
    img = null;
    img = createImage( new MemoryImageSource(Npix,Npix,icm,data,0,Npix));
    f1.setText("Done");
    if (debug)System.out.println("Done!");
  }

  public void update (Graphics g) {
    paint(g);
  }

  public void paint(Graphics g){
    if(img != null)g.drawImage(img,0,10,this);
  }
}

/*******************************************************
            Dr1attr
*******************************************************/

class Dr1attr extends Frame {
  boolean debug;
  int Npix=500;
  Formula form;
  TextField f1;

  public Dr1attr(String s) {
    super(s);
    MenuBar mb = new MenuBar();
    setMenuBar(mb);
    Menu m1 = new Menu("File");
    mb.add(m1);
    m1.add(new MenuItem("Quit"));
  }

  public void  setvar(boolean debug1,Formula form1,TextField f){
    debug = debug1;
    form = form1;
    f1 = f;
  }

  public boolean action(Event e, Object o) {
    if (e.target instanceof MenuItem) {
      String label = (String)o;
      if (label.equals("Quit")) hide();
      return true;
    }
    else return false;
  }

  public  void drawimage() {
    double Left= -4.,Right= 4.,Bottom= -4.,Top= 4.;
    Fpoint fp0 =new Fpoint(0.,0.);Fpoint fp1 =new Fpoint(0.,0.);
    double x0,y0,x,y;
    int xpix,ypix;
    int Npix1,Npix2;
    Graphics g;

    g = this.getGraphics();
    g.setColor(Color.black);
    g.fillRect(0,0,this.size().width,this.size().height);
    f1.setText("Drawing attractor");
    x0 = form.xin; y0 =form.yin;
    Npix1 = this.size().width;
    Npix2 = this.size().height;
    g.setColor(Color.blue);
    for (int l=0;l < 10000; l++) {
      fp0.x = x0;
      fp0.y = y0;
      fp1=form.compute(fp0);
      x=fp1.x;y=fp1.y;
      if (l > 1000) {
        xpix = (int)((x-Left)/(Right-Left)*Npix1);
        ypix = Npix2-(int)((y-Bottom)/(Top-Bottom)*Npix2);
        g.drawLine(xpix,ypix,xpix,ypix);
      }
      if(debug)System.out.println("x,y,x0,y0 = "+x+" "+y+" "+x0+" "+y0);
      x0 =x;y0=y;
      if(x0 < Left || x0 > Right || y0 < Bottom || y0 > Top) break;
    }
  }

  public void update (Graphics g) {
    paint(g);
  }
}
