/*  Chirikov.java                                               */ 
/*  Oliver Knill, June, 2 1999                                  */

/*  A simple but fast Chirikov Standard map Java applet.        */
/*  The orbits are colored according                            */
/*  to Lyapunov exponent. The color range is determined by a    */ 
/*  a rule of thumb formula for the Kolmogorov-Sinai entropy of */
/*  the Standard map.                                           */

/*  javac might complain about deprecated API,  ignore          */
/*  or run javac -nowarn                (;-))                   */

/*  We had as example code a 1996 Java code by Tina Tshen       */
/*  http://blackcat.brynmawr.edu:80/~tshen/                     */


import java.applet.Applet;
import java.awt.Graphics;                  /* Loead only the    */
import java.awt.Color;                     /* abstract window   */
import java.awt.Event;                     /* toolkit tools     */
import java.awt.Rectangle;                 /* which are needed. */
import java.awt.Button;
import java.awt.Choice;

public class chirikov extends Applet {
   double      x,y;                        /*  points on torus  */
   int         u,v;                        /*  to display them  */
   double      z;                          /*  memory variable  */

   double      x1, y1;                     /*  to test lyapun   */
   double      z1;                         /*  to test lyapun   */
   double      lya;                        /*  lyapunov expon   */
   double      max_lya;                    /*  maximal lya for k*/
   int         nlya;                       /*  mormalized color */
   int         red;                        /*  red  channel     */
   int         green;                      /*  green channel    */
   int         blue;                       /*  blue channel     */
   
   double      beta;                       /*  random angle     */
   double      A11,A12,A21,A22;            /*  jacobean matrix  */
   double      B11,B12,B21,B22;            /*  copy for iterat. */
   double      abs_a11;                    /*  abs(A11)         */
   double      pot;                        /*  in matrix entry  */
   double      t;                          /*  to sum up        */

   double      k=0.0;                      /*  parameter in map */
   double      two_pi = 2*Math.PI;         /*  a well known num */ 
   double      pi_inv = 1/(2*Math.PI);     /*  to speed up      */
   int         i;                          /*  time             */
   int         m=10;                       /*  orb lenght lyap  */
   int         l=2000;                     /*  orb length plot  */
   int         w, h;                       /*  width and height */

   Choice      k_menu;                     /*  to choose paramet*/
   Button      clear_button;               /*  to clear screen  */
   Rectangle   r;                          /*  drawing plane    */
   Graphics    g;                          /*  graphics (? else)*/
   String      arg;                        /*  argument for k   */

   public void init() {                    /*  menu of parameter*/
      k_menu = new Choice();
      k_menu.addItem("0.0"); k_menu.addItem("0.3"); 
      k_menu.addItem("0.5"); k_menu.addItem("0.8");
      k_menu.addItem("0.9"); k_menu.addItem("1.0");
      k_menu.addItem("1.1"); k_menu.addItem("1.3");
      k_menu.addItem("1.5"); k_menu.addItem("2.0"); 
      k_menu.addItem("2.5"); k_menu.addItem("3.0"); 
      k_menu.addItem("3.5"); k_menu.addItem("4.0");
      k_menu.addItem("5.0"); k_menu.addItem("6.0");
      k_menu.setForeground(new Color(255, 0, 0));
      k_menu.setBackground(new Color(0,   0, 0));
      this.add(k_menu);                                

      clear_button = new Button("Clear");
      clear_button.setForeground(new Color(255, 0, 0));
      this.add(clear_button);
    }

    public boolean action(Event event, Object obj) {
       g    = this.getGraphics();
       arg  = (String) obj;

       if (event.target==k_menu) {                 /* Take parameter    */
               if (arg.equals("0.0")) k=0.0;       /* from scrolling    */
          else if (arg.equals("0.3")) k=0.3;       /* list              */
          else if (arg.equals("0.5")) k=0.5;
          else if (arg.equals("0.8")) k=0.8;       /* You can add here  */
          else if (arg.equals("0.9")) k=0.9;       /* your favorate     */
          else if (arg.equals("1.0")) k=1.0;       /* parameter.        */
          else if (arg.equals("1.1")) k=1.1;       /* Make sure to add  */
          else if (arg.equals("1.3")) k=1.3;       /* it also above.    */
          else if (arg.equals("1.5")) k=1.5;
          else if (arg.equals("2.0")) k=2.0;
          else if (arg.equals("2.5")) k=2.5;
          else if (arg.equals("3.0")) k=3.0;
          else if (arg.equals("3.5")) k=3.5;
          else if (arg.equals("4.0")) k=4.0;
          else if (arg.equals("5.0")) k=5.0;
          else if (arg.equals("6.0")) k=6.0;
          r = this.bounds();
          g.fillRect(r.x,r.y,r.width,r.height);   /* Clear screen      */  
          return true;                             /* when changing k   */
       }

       if (event.target==clear_button) {
         r = this.bounds();
         g.fillRect(r.x,r.y,r.width,r.height);    /* Clear screen      */
         repaint();
         return true;                              /* when pushing      */
       }                                           /* clear button      */
       else return true;
    }

     public void paint(Graphics g) {               /* Black background  */
        this.setBackground(new Color(0,0,0));      /* is more beautyful */
     }

     public boolean mouseUp(Event e, int x0, int y0) {
         g = this.getGraphics();
         x=x0;    y=y0;                            /* Grab init. condit.*/
         run_orbit(g);                             /* and run the orbit */
         return true;
     }

     public double mod(double z)
     {                                             /* Because z appears */
       z=z-Math.floor(z);                          /* twice here,writing*/
       return(z);                                  /* a routine pays off*/
     }                                             /* (mod is used in   */
                                                   /* loop where speed  */
                                                   /* is important).    */

     public void run_orbit(Graphics g) {
        w = this.size().width;
        h = this.size().height;
        x = x/w; y = h-(y/h);
        t = 0.0; 

        x1=x; y1=y;
        beta = 0.234123512351234;                 /* Tangent direction   */
                                                  /* Don't bother to make*/
                                                  /* this random.        */
                                                  /* By Oseledec, we     */
                                                  /* sure hit good direct*/
                                                  /* don't make it ration*/
                                                  /* however because of  */
                                                  /* symmetry.           */

        A11= Math.cos(beta); A12=Math.sin(beta);
        A21=-Math.sin(beta); A22=Math.cos(beta);
                                               
                                                  /* Use Aubry duality   */ 
                                                  /* rule of thumb for   */
                                                  /* Lyapunov estimation */
                                                  /* Could be refined but*/
                                                  /* suffices            */

        if (k==0) {max_lya = 0.0; }                     else 
        if (k==2) {max_lya = 0.2; }                     else 
        if (k>2)  {max_lya = Math.abs(Math.log(k/2)) 
                           + Math.abs(Math.log(2/k)); } else 
        if (k<2)  {max_lya = Math.abs(Math.log(2/k)); };
        t=0.0; 

        for (i=1; i<=m; i++) {
          z1=x1; x1=mod(2*x1-y1+k*pi_inv*Math.sin(two_pi*x1));y1=mod(z1);

          pot=2+k*Math.cos(two_pi*x1);            /* Don't want to comp. */
                                                  /* this twice          */
                                                  /* in the next lines   */
          B11 = pot*A11 - A21;                    /* multiplication with */    
          B12 = pot*A12 - A22;                    /* jacobean  B=dT*B    */
          B21 =           A11;
          B22 =           A12;
          A11=B11; A12=B12;
          A21=B21; A22=B22;                       /* Prevent blow up     */
          if (Math.abs(A11)>1.0) {                /* from entries        */
            abs_a11=Math.abs(A11);
            t=t+Math.log(abs_a11);                /* Normalize and       */
            A12=A12/abs_a11;                      /* keep track of       */
            A21=A21/abs_a11;                      /* factor exp(t)       */
            A22=A22/abs_a11;
            A11=A11/abs_a11;
          }
          lya=t/m;                                /* Local lyap. exponent*/
          nlya = (int) Math.floor(255*(lya/max_lya));
          if (nlya>255) {nlya=255;}               /* in case, out of     */   
          if (nlya<0)   {nlya=0;}                 /* range               */
        }

        red=0; green=0; blue=0;                   /* following colors    */
                                                  /* are good for web    */

        if ((nlya>-1 ) && (nlya<= 40)) {red=255; green=0;   blue=0;  }
        if ((nlya>40 ) && (nlya<=100)) {red=255; green=255; blue=0;  }
        if ((nlya>100) && (nlya<=160)) {red=0;   green=255; blue=0;  }
        if ((nlya>160) && (nlya<=180)) {red=0;   green=255; blue=255;}
        if ((nlya>180) && (nlya<=260)) {red=0;   green=0;   blue=255;}

                                                  /* Having determined   */ 
                                                  /* the color, we can   */      
        g.setColor(new Color(red,green,blue));    /* set the orbit color */

                                                  /* Now fasten seat belt*/
        for (i=1; i<=l; i++) {                    /* this has to be fast */
           z=x; x=mod(2*x-y+k*pi_inv*Math.sin(two_pi*x)); y=z;
           u = (int) (x*w);  v=(int) (h-y*h);
           g.drawLine(u,v,u,v);
        }
     }
}
