// 2D Ising with thermostat algorithm,  Evgeny Demidov  24 Oct 2006
// based on Harvey Gould and Jan Tobochnik sources   http://sip.clarku.edu
import java.awt.*;
import java.awt.event.*;
public class IsingT1 extends java.applet.Applet implements MouseListener,
 MouseMotionListener, ItemListener, KeyListener, ActionListener, Runnable {
  Thread thCycle;
  int L = 50,L1, M, E, d, w, hd, hBut = 30, border = 0, nInit = 0,
    it, Mi[], Ei[], spin[][];
  double T = 2.269, avM, avMM, avE, avEE,  wi[];
  boolean painted = false, bCycle = false;
  long generTime;
  float flM, flE;
  Image buffImage;     Graphics buffGraphics;
  Choice chL,chBorder,chInit;
  Label  lbL,lbT,lbBorder,lbInit;    TextField tfT;
  Button   btRun, btClear, btPrint;
public void init(){
  w = getSize().width - 200 - 10;
  spin = new int[w+2][w+2];
  Mi = new int[w];  Ei = new int[w];
  String s=getParameter("L"); if (s != null) L = Integer.parseInt(s);
  d = w/L;  hd = w-d; L1 = L+1;
  s=getParameter("T"); if (s != null) T = Double.valueOf(s).doubleValue();
  s=getParameter("Init"); if (s != null) nInit = Integer.parseInt(s);
  wi = new double[9];
  for (int i = 0; i < 9; i++) wi[i] = 1./(1. + Math.exp(2*(i-4)/T));
  setup();
  lbT = new Label("T", Label.RIGHT);  add(lbT);
  tfT = new TextField( "" + (float)T, 5);  add(tfT);
  tfT.addKeyListener(this);
  lbL = new Label("L", Label.RIGHT);  add(lbL);
  chL = new Choice();
  if (w == 400){
   chL.addItem("20");
   for (int i = 0, l = 50; i < 4; i++){
    chL.addItem(Integer.toString(l));  l *= 2;}}
  else
   for (int i = 0, l = 20; i < 6; i++){
    chL.addItem(Integer.toString(l));  l *= 2;};
  chL.select(""+L);  chL.addItemListener(this);  add(chL);
  lbBorder = new Label("border", Label.RIGHT);  add(lbBorder);
  chBorder = new Choice();
  chBorder.addItem("free"); chBorder.addItem(" +1 ");
  chBorder.select(border);
  chBorder.addItemListener(this);  add(chBorder);
  lbInit = new Label("init", Label.RIGHT);  add(lbInit);
  chInit = new Choice();
  chInit.addItem(" -1 "); chInit.addItem("rand"); chInit.addItem(" +1 ");
  chInit.select(nInit + 1);
  chInit.addItemListener(this);  add(chInit);
  btRun = new Button("Run ");  btRun.addActionListener(this);  add(btRun);
  btClear = new Button("Clear");   btClear.addActionListener(this);
  add(btClear);
  btPrint = new Button("Print");   btPrint.addActionListener(this);
  add(btPrint);
  buffImage = createImage(w+210, w);   buffGraphics = buffImage.getGraphics();
  addMouseListener(this);  addMouseMotionListener(this);
}
public void setup(){
  it = 0;
  for (int i = 0; i < w; i++) Mi[i] = Ei[i] = w;
  for (int i = 0; i <= L1; i++)
    spin[0][i] = spin[L1][i] = spin[i][0] = spin[i][L1] = border;
  M = E = it = 0;
  for (int i = 1; i < L1; i++)
   for (int j = 1; j < L1; j++){
    int s;
    if (nInit != 0) s = spin[i][j] = nInit;
    else if (Math.random() > .5) s = spin[i][j] = 1; else s = spin[i][j] = -1;
    M += s; E -= s*(spin[i-1][j] + spin[i][j-1]);}
  for (int i = 1; i < L1; i++){
    E -= spin[i][L]*spin[i][L1];  E -= spin[L][i]*spin[L1][i];}
  M /= 2;  E /= 2;
}
public void destroy(){
  removeMouseListener(this);  removeMouseMotionListener(this);
}
public void run() {
  while(true) {
    if ( Thread.currentThread() != thCycle ) return;
    MC();
    painted = false;
    repaint();
    try {  Thread.sleep( 20 );
    } catch (InterruptedException e) {}
  }
}
public void stop(){  thCycle = null; }
public void actionPerformed(ActionEvent e){
  if ( e.getActionCommand().equals("Clear") ){
    for (int i = 0; i < w; i++) Mi[i] = Ei[i] = w;
    it = 0;  avM = avMM = avE = avEE = 0.;
    showStatus( "it="+ it);}
  else if ( e.getActionCommand().equals("Print") ){
    double Mt = avM/it, Et = avE/it, n = L*L;
    showStatus( "T="+ (float)T +"  M="+ (float)Mt +
     "  x="+ (float)(n*(avMM/it - Mt*Mt)/T) +"  E="+ (float)Et +
     "  C="+ (float)(n*(avEE/it - Et*Et)/(T*T)) +"  it="+ it );
    System.out.println( ""+ (float)T +"  "+ (float)Mt +
     "  "+ (float)(n*(avMM/it - Mt*Mt)/T) +"  "+ (float)Et +
     "  "+ (float)(n*(avEE/it - Et*Et)/(T*T)) );}
  else{
    if (bCycle){
      bCycle = false;  btRun.setLabel("Run "); thCycle = null;}
    else { bCycle = true;  btRun.setLabel("Stop");
      thCycle = new Thread(this); thCycle.start();}}
}
public void mouseClicked(MouseEvent e){}       // event handling
public void mousePressed(MouseEvent e) {
  if( e.getX() < w){
    MC();
    painted = false;
    repaint();}
  e.consume();
}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e)  {}
public void mouseMoved(MouseEvent e) {}
public void mouseDragged(MouseEvent e) {
  if( e.getX() > w){
    T = 6. - (e.getY() - 30)*6./w;
    for (int i = 0; i < 9; i++) wi[i] = 1./(1. + Math.exp(2*(i-4)/T));
    tfT.setText(""+(float)T);
    painted = false;
    repaint();}
  e.consume();
}
public void keyTyped(KeyEvent e){}
public void keyPressed(KeyEvent e){}
public void keyReleased(KeyEvent e){
  final int keyEnter = 10;
  if (e.getKeyCode() == keyEnter){
    try{ T = Double.valueOf(tfT.getText()).doubleValue();
    }catch(NumberFormatException ne){}
    for (int i = 0; i < 9; i++) wi[i] = 1./(1. + Math.exp(2*(i-4)/T));
    painted = false;
    repaint();
  }
  e.consume();
}
public void itemStateChanged(ItemEvent e){
  Object src = e.getSource();
  if (src == chL){
   L = Integer.parseInt(chL.getSelectedItem());
   d = w/L;  hd = w-d; L1 = L+1;}
  if (src == chBorder) border = chBorder.getSelectedIndex();
  if (src == chInit) nInit = chInit.getSelectedIndex() - 1;
  setup();
  painted = false;
  repaint();
}
public void MC(){
  generTime = System.currentTimeMillis();
  for (int i = 1; i < L1; i++)
   for (int j = 1; j < L1; j++){
     int sum = spin[i][j]*
       (spin[i][j+1] + spin[i][j-1] + spin[i+1][j] + spin[i-1][j]);
     if (Math.random() < wi[sum + 4]){
       int s = spin[i][j] = -spin[i][j];
       M += s;  E += sum;}}
  flM = 2*(float)M/(L*L);  flE = 2*(float)E/(L1*L1);
  avM += flM; avMM += flM*flM;  avE += flE; avEE += flE*flE;
  Mi[it % w] = w + (int)(100*(flM + 1));
  Ei[it % w] = w + (int)(50*(flE + 2));   it++;
  generTime = System.currentTimeMillis() - generTime;
}
public void paint(Graphics g){
 if ( !painted ){
  buffGraphics.setColor(Color.white);
  buffGraphics.fillRect(0, 0, w+200, w);
  buffGraphics.setColor(Color.black);
  for (int i = 0; i < L; i++)
   for (int j = 0; j < L; j++)
     if (spin[i+1][j+1] < 0) buffGraphics.fillRect(j*d,hd-i*d, d,d);
  buffGraphics.setColor(Color.lightGray);
  for (int i = 0; i < 200; i += 20)
    buffGraphics.drawLine(w+i, 0, w+i, w);
  buffGraphics.setColor(Color.gray);
  buffGraphics.drawLine(w+100, 0, w+100, w);
  buffGraphics.setColor(Color.red);
  for (int i = 0; i < w; i++)
   buffGraphics.drawLine(Mi[i], i, Mi[i], i);
  buffGraphics.setColor(Color.blue);
  for (int i = 0; i < w; i++)
   buffGraphics.drawLine(Ei[i], i, Ei[i], i);
  int Ti = (int)((1. - T/6.)*w);
  buffGraphics.setColor(Color.red);
  buffGraphics.fillRect(w+200, Ti, 10, w-Ti);
  buffGraphics.setColor(Color.white);
  buffGraphics.fillRect(w+200, 0, 10, Ti);
  buffGraphics.setColor(Color.black);
  buffGraphics.fillRect(w+200, (int)((1. - 2.269/6.)*w)-1, 10, 3);
  painted = true;}
 g.drawImage(buffImage, 0, hBut, this);
 showStatus( "T="+ (float)T +"  it="+ it +"  M="+ flM +
  "  E="+ flE +"  t="+ generTime +"ms ");
}
public void update(Graphics g){ paint(g); }
}
