//Schrodinger wave equation simulation
Source code files are:

Schrodinger.java

SwingWorker.java
ThreadsSchrodinger.java
package schrodingerappletapplication;

/*
 * @Copyright (c) 2005, Lazar Kovacevic
 * www.inverudio.com
 * 
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies.
 * 
 * Numerical solution taken from: http://electron6.phys.utk.edu/qm1/numerical/program1.htm
 */

//najmanji npts koji ce da radi posao; odnos dx-L-dt!?! - normalizacija parametara.
//Known bugs: repaint when webpage is scrolled up/down
//TODO  rescaling of the graph for different functions and for different window sizes (to make current fixed width flexible, and rescale the graph)
//TODO  to detach applet from the webpage
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
//import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
//import java.util.ArrayList;
import java.util.LinkedList;

import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * @author Lazar Kovacevic
 */

public class Schrodinger extends JApplet implements Runnable {
	private static final long serialVersionUID = 1L;

	// Calculation related fields

	static final int MAX_NPTS = 1000; // Number of points in space (npts) cannot be larger than this.

	int npts = MAX_NPTS / 2; // Initial value for npts

	int nReIm = 1000; // Dimension of the phase plot vector - determines how many time steps will be plotted.

	static final int xoff = 10; // Offset for simulation plot

	static final int yoff = 10; // Offset for simulation plot

	boolean p = false; // Determines if the simulation is paused

	boolean draw = false; // Determines if the function is drown or generated

	double max = 0; // Used for normalizing y-axis of the plot

	int xpos; // Used for mouse draging of the npts line

	int reimPos = MAX_NPTS / 2;

	int delay = 100; // Determines number of frames (10 frames/second)

	long T = 0; // Current time step

	double time = 0; // Current time

	double norm;

	double phi2[] = new double[MAX_NPTS]; // Square of the wave function

	double phir[] = new double[MAX_NPTS]; // Real part

	double phii[] = new double[MAX_NPTS]; // Imaginary part

	LinkedList reim = new LinkedList(); // Array for the Re-Im plot

	LinkedList entropy = new LinkedList(); // Array for the Entropy in time plot
	//  arraylist -> linked list (possible speed improvement)

	/*    ArrayList phaseplot = new ArrayList();*///Array for the phase plot
	//  ArrayList var = new ArrayList();  - Tried to generate all textFields with one loop, but did not succeed. Need to learn more! 
	double Cnt = (double) npts;

	double Minimum;

	double Maximum;

	int MaxBins;

	double MinMax[];

	// User defined parameters
	// npts is density of points, L is width of region, dx = L/npts.
	boolean automax = true;

	int plotTimeJump = 10; // How many time steps are calculated before each plotting

	int sampling = 1; // Sampling rate - by increasing this beyond 1, normalization will be broken

	double energy = 5; // Energy 

	double dt = 0.1; // Time step

	double L = 100; // Length of the bounded area.

	double dx = L / npts; // Space step

	double a = 25 * dx; // Width of the Gaussian wave posket - standard deviation related constant

	String[] functions = { "Gauss", "Square Envelope", "Square Re Im", "Dirac",
			"Sine" }; //To think about boundary conditions...

	// GUI related fields and components

	// URL for the refference - did not know how to put url, so I put it in a textfield
	JLabel refl = new JLabel("Numerical solution taken from: ");

	JTextField ref = new JTextField(
			"http://electron6.phys.utk.edu/qm1/numerical/program1.htm");

	// A few instruction lines
	JLabel instruction1 = new JLabel(
			"To change parameters (enter real numbers not text), you have to press return/enter in text box! Move mouse over text fields and buttons to see the tip.");

	JLabel instruction2 = new JLabel(
			"You will receive message Change Acknowledged! while simulation is running.");

	//JLabel instruction3 = new JLabel("Be careful! There are no exceptions at this point so be sure to enter real numbers and not text.");
	JLabel instruction4 = new JLabel(
			"To see phase plot of the whole spatial series, use this trick: ");

	JLabel instruction5 = new JLabel(
			"Decrease dt to be small enough so that series do not evolve, and slide slowly slider from left to right.");

	JTextArea explanation = new JTextArea(
			"This program solves the time-dependent Schroedinger equation for "
					+ "\n"
					+ "a wavepacket representing an electron of average energy E(eV) confined to "
					+ "\n"
					+ "a region from x=0 to x=L(1E-10m).  Time is stepped in units of dt(1E-16s).");

	// Box that groups instructions together
	JComponent instructionBox = new JPanel();

	// JTextFields accepting input from the user. 
	// Pressing 'Enter' is required for the change to be acknowledged.
	// It is much easier than testing for exceptions, and doing logic for real time change
	JTextField txtSampling = new JTextField(3);

	JTextField txtPlotTimeJump = new JTextField(3);

	JTextField txtEnergy = new JTextField(3);

	JTextField txtdt = new JTextField(3);

	JTextField txtdx = new JTextField(3);

	JTextField txta = new JTextField(3);

	JTextField txtL = new JTextField(3);

	JTextField txtnReIm = new JTextField(4);

	JCheckBox cbAutoMax = new JCheckBox("max dt?");

	JButton startButton;

	JButton interruptButton; // Stop button

	JButton printButton;

	JButton drawButton;

	JComboBox functionBox;

	// npts related components and fields
	JTextField txtNpts = new JTextField(8);

	JSlider sldNpts = new JSlider(1, 50, MAX_NPTS / npts);

	String str = "# of points: " + npts; // Current number of points in space that is being calculated

	boolean lineMove = false; // Registers if user is moving boundary (npts line)with a mouse

	// If memory true, remembers values right of npts after restarting simulation
	JCheckBox mem = new JCheckBox("Memory on right");

	boolean memory = false; // used with 

	// Box that groups text fields and buttons togethers
	JComponent buttonBox = new JPanel();

	// Label showing current time step
	String ds = "      "; // Used for right allingment of the current time value 

	JLabel timeStep = new JLabel("Time step: " + "0");

	// Label showing status of the simulation
	JLabel statusField = new JLabel("Click Start to begin", JLabel.CENTER);

	// Reports if the user has changed the parameters
	JLabel changeacknowledged = new JLabel("");//("Change Acknowledged!"); // something is messed up, and relevant code is not working properly. 

	int cnt = 0; // counts 15 time steps, then erases changeacknowledged from the screen

	// Box that groups current status related fields
	JComponent statusBox = new JPanel();

	//  Box that holds changeAcknowleged note
	JComponent changeBox = new JPanel();

	JPanel panel; // Panel for plotting simulation

	SwingWorker worker; // Uses 3rd version of SwingWorker abstract class

	SwingWorker drawing; // Uses 3rd version of SwingWorker abstract class

	//  Called when this applet is loaded into the browser.
	public void init() {

		//Execute a job on the event-dispatching thread:
		//creating this applet's GUI.
		try {
			javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
				public void run() {
					createGUI();
				}
			});
		} catch (Exception e) {
			System.err.println("createGUI didn't successfully complete");
		}
	}

	/**
	 * And now for a little assembly. Put together the buttons, progress bar and
	 * status text field.
	 */
	public void createGUI() {
		//new Schrodinger();
		setLayout(null);
		// Putting all components together
		String tt = "";
		// Refference
		add(refl);
		add(ref);
		//refl.setBounds(10,1,200,20);
		//ref.setBounds(191,1,330,20);
		refl.setBounds(270, 430, 440, 20);
		ref.setBounds(451, 430, 350, 20);

		add(explanation);
		explanation.setBounds(270, 460, 500, 50);
		explanation.setEnabled(false);

		// Initializing names for labels
		String lbls[] = new String[8];
		lbls[0] = "Sampling";
		lbls[1] = "Plot every n-th step";
		lbls[2] = "Energy";
		lbls[3] = "dt";
		lbls[4] = "dx";
		lbls[5] = "a";
		lbls[6] = "L";
		lbls[7] = "nReIm";

		// Initializing text fields for user changable variables
		txtSampling.setText("" + sampling);
		txtSampling.setToolTipText("Integers only!");
		txtPlotTimeJump.setText("" + plotTimeJump);
		txtEnergy.setText("" + energy);
		txtdt.setText("" + dt);
		txtdx.setText("" + L / npts);
		// txtdx.setEnabled(false);
		txta.setText("" + a / dx); // refresh all related variables & textfields when some component is changed!
		txtL.setText("" + L);
		txtnReIm.setText("" + nReIm);

		//for (int i=0;i<6;i++) var.add(new JTextField(4));
		/*var.add(txtSampling);
		 var.get(0).setText("1");
		 var.add(txtPlotTimeJump);
		 var.get(1).setText("1");
		 var.add(txtEnergy);
		 var.get(2).setText("5");
		 var.add(txtdt);
		 var.get(3).setText(".1");
		 var.add(txtdx);
		 var.get(4).setText("" + L / npts);
		 var.get(4).setEnabled(false);
		 var.add(txta);
		 var.get(5).setText("25");*/

		/*addJLabel(buttonBox, lbls[0]);
		 buttonBox.add(txtSampling);
		 txtSampling.addActionListener(new ActionListener()
		 {  
		 public void actionPerformed(ActionEvent e)
		 {
		 sampling = Integer.parseInt(txtSampling.getText());
		 changeBox.add(changeacknowledged);
		 cnt = 15;
		 }
		 }
		 );*/
		tt = "Integers only!";
		addJLabel(buttonBox, lbls[1], tt);
		buttonBox.add(txtPlotTimeJump);
		txtPlotTimeJump.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				plotTimeJump = Integer.parseInt(txtPlotTimeJump.getText());
				changeBox.add(changeacknowledged);
				cnt = 15;
			}
		});
		txtPlotTimeJump.setToolTipText(tt);

		tt = "See description below.";
		addJLabel(buttonBox, lbls[2], tt);
		buttonBox.add(txtEnergy);
		txtEnergy.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				energy = Double.parseDouble(txtEnergy.getText());
				changeBox.add(changeacknowledged);
				cnt = 15;
			}
		});
		txtEnergy.setToolTipText(tt);

		cbAutoMax.setSelected(automax);
		cbAutoMax
				.setToolTipText("If checked, program automatically finds almost maximum dt for an accurate simulation within .001 in Phi units.");
		cbAutoMax.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				automax = !automax;
				changeBox.add(changeacknowledged);
				cnt = 15;
			}
		});
		buttonBox.add(cbAutoMax);

		tt = "Time step increment";
		addJLabel(buttonBox, lbls[3], tt);
		buttonBox.add(txtdt);
		txtdt.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				dt = Double.parseDouble(txtdt.getText());
				changeBox.add(changeacknowledged);
				cnt = 15;
			}
		});
		txtdt.setToolTipText(tt);

		tt = "See description below.";
		addJLabel(buttonBox, lbls[4], tt);
		buttonBox.add(txtdx);
		/*txtdx.addActionListener(new ActionListener()
		 {  
		 public void actionPerformed(ActionEvent e)
		 {
		 dx = Double.parseDouble(txtdx.getText());
		 //L = dx * npts; This is wrong! L is constant!
		 //npts = (int)(L/dx);
		 changeBox.add(changeacknowledged);
		 cnt = 15;
		 a = Double.parseDouble(txta.getText())*dx;
		 }
		 }
		 );*/
		txtdx.setEnabled(false); // not user changable!
		txtdx.setToolTipText(tt);

		tt = "Wave width parameter.";
		addJLabel(buttonBox, lbls[5], tt);
		buttonBox.add(txta);
		txta.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				a = Double.parseDouble(txta.getText()) * dx;
				changeBox.add(changeacknowledged);
				cnt = 15;
			}
		});
		txta.setToolTipText(tt);

		tt = "See description below.";
		addJLabel(buttonBox, lbls[6], tt);
		buttonBox.add(txtL);
		txtL.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				L = Double.parseDouble(txtL.getText());
				dx = L / npts;
				txtdx.setText("" + dx);
				a = Double.parseDouble(txta.getText()) * dx;
				changeBox.add(changeacknowledged);
				cnt = 15;
			}
		});
		txtL.setToolTipText(tt);

		tt = "Number of points that (blue) time series can contain. If too many, calculation will slow down.";
		addJLabel(buttonBox, lbls[7], tt);
		buttonBox.add(txtnReIm);
		txtnReIm.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				nReIm = Integer.parseInt(txtnReIm.getText());
				changeBox.add(changeacknowledged);
				cnt = 15;
			}
		});
		txtnReIm.setToolTipText(tt);

		// See above defining of 'ArrayList var' for comment
		/*for (int i = 0; i < lbls.length; i++)
		 {         addJLabel(buttonBox, lbls[i]);
		 buttonBox.add(var.get(i));
		 var.get(i).addActionListener(new ActionListener()
		 {         public void actionPerformed(ActionEvent e)
		 {         if (var.get(0).equals(e.getActionCommand())) {
		 source = (JTextField)e.getSource();
		 sampling = Integer.parseInt(txtSampling.getText());
		 } else if (var.get(1).equals(e.getActionCommand())) {
		 source = (JTextField)e.getSource();
		 plotTimeJump = Integer.parseInt(txtPlotTimeJump.getText());
		 }}});}*/

		startButton = new JButton("Start");
		startButton.addActionListener(startListener);
		startButton.setEnabled(true);
		startButton
				.setToolTipText("Starts animation. You can pause/unpause animation by clicking at the plot.");

		interruptButton = new JButton("Stop");
		interruptButton.addActionListener(interruptListener);
		interruptButton.setEnabled(false);
		interruptButton
				.setToolTipText("Stops animation and reinitializes parameters. Enables change of density and other parameters.");

		printButton = new JButton("PrintToFile");
		printButton.addActionListener(printListener);
		printButton.setEnabled(true);
		printButton
				.setToolTipText("Creates on Desktop 'txt' file of the current plots with current parameters encoded in the filename.");

		drawButton = new JButton("Gauss Wave");
		drawButton.addActionListener(drawListener);
		drawButton.setEnabled(true);
		drawButton
				.setToolTipText("Allows you to draw a function with a mouse.");

		functionBox = new JComboBox(functions);
		//functionBox.addActionListener(functionListener);
		functionBox.setEnabled(true);
		functionBox.setToolTipText("Choose a function.");
		functionBox.setSelectedIndex(0);

		txtNpts.setText(str);
		txtNpts.setEnabled(false);
		txtNpts.setToolTipText("NPTS: Number of PoinTS in space.");

		mem.setSelected(memory);
		mem
				.setToolTipText("If checked, you can start animation with high NPTS, and then restart with smaller NPTS, and keep the old wave there!");
		mem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				memory = !memory;
				changeBox.add(changeacknowledged);
				cnt = 15;
			}
		});

		buttonBox.add(startButton);
		buttonBox.add(interruptButton);
		buttonBox.add(printButton);
		//buttonBox.add(drawButton);
		buttonBox.add(functionBox);
		drawButton.setEnabled(false);
		//buttonBox.add(txtNpts);
		//buttonBox.add(mem);
		add(buttonBox);
		Dimension size = buttonBox.getPreferredSize();
		buttonBox.setBounds(1, 25, size.width + 30, size.height);

		add(timeStep);
		add(statusField);
		timeStep.setBounds(25, 55, 350, 50);
		statusField.setBounds(250, 55, 750, 50);
		statusField.setForeground(Color.blue);
		
		add(changeBox);
		changeBox.setBounds(550, 65, 150, 50);

		// Panel with plot
		panel = new JPanel();
		panel.addMouseListener(pauseListener);
		//translate(3,3);
		add(panel);
		panel.setBounds(0, 100, 1011, 311);

		sldNpts.addChangeListener(nptsListener);
		sldNpts
				.setToolTipText("Changes the number of points (NPTS) that is currently being calculated for animation. (dx=L/NPTS)");
		add(sldNpts);
		sldNpts.setBounds(10, 430, 50, 20);
		JLabel sl = new JLabel("Change dx");
		add(sl);
		sl.setBounds(61, 430, 90, 20);
		add(txtNpts);
		txtNpts.setBounds(151, 430, 100, 20);

		instructionBox.add(instruction1);
		//instructionBox.add(instruction2);
		//instructionBox.add(instruction4);
		//instructionBox.add(instruction5);
		add(instructionBox);
		//instructionBox.setBounds(250 , 427 , 600, 100);
		instructionBox.setBounds(10, 1, 900, 100);
	}

	// Used for double buffering of the picture, so that animation would be smooth
	Dimension offDimension;

	Image offImage;

	Graphics2D offGraphics;

	public void addJLabel(Container c, String title, String tooltip) {
		JLabel label = new JLabel(title);
		label.setToolTipText(tooltip);
		c.add(label);
	}

	// Main class ThreadsSchrodinger is calling this method
	JButton getStartButton() {
		return startButton;
	}

	/**
	 * This method represents the application code that we'd like to run on a
	 * separate thread. It simulates Schrodinger wave equation. 
	 */
	Object doWork() {
		try {
			double azr[] = new double[MAX_NPTS];
			double gamr[] = new double[MAX_NPTS];
			double gami[] = new double[MAX_NPTS];
			double betr[] = new double[MAX_NPTS];
			double beti[] = new double[MAX_NPTS];
			double phi2old[] = new double[MAX_NPTS];
			double phi2current[] = new double[MAX_NPTS];
			int i;

			//for (i=reim.size()-1; i>0; i--) reim.remove(i);
			reim.clear();
			entropy.clear();

			//if(drawButton.getText()=="Gauss Wave")
			{
				initialize(dx, a, phir, phii);
				for (i = 0; i < npts; i++) {
					azr[i] = -2;
				}
				// Finding maximum dt for accurate calculation (within .001 error tolerance)
				if (automax) {
					boolean maxdt;
					int I = 1;
					double maxt = 0;
					boolean maxdtP = false;
					int start = 0;
					while (I < 5 && dt < 1000000) {
						dt = dt / Math.pow(2, I);
						timeStep(1 + (int) Math.pow(2, I), azr, gamr, gami,
								betr, beti, phir, phii, phi2old);
						for (i = 0; i < npts; i++) {
							phi2current[(Math.round(i * MAX_NPTS / npts))] = phi2[(Math
									.round(i * MAX_NPTS / npts))];
						}
						dt = dt * (1 + Math.pow(2, I));
						timeStep(1, azr, gamr, gami, betr, beti, phir, phii,
								phi2old);
						maxdt = true;
						for (i = 0; i < npts; i++) {
							if (Math.abs(phi2current[(Math.round(i * MAX_NPTS
									/ npts))]
									- phi2[(Math.round(i * MAX_NPTS / npts))]) > .001) {
								maxdt = false;
							}
						}//dt proportional to error!
						if (maxdt && (maxt < dt))
							maxt = dt;
						if (!maxdt)
							dt = dt - dt * 2 / (1 + Math.pow(2, I));
						if (!maxdt && maxdtP & start > 1)
							I++;
						maxdtP = maxdt;
						start++;
					}
					dt = maxt;
					if (maxt == 0) {
						txtdt.setText("INF");
						txtdt.setForeground(Color.red);//WHY IS THIS NOT WORKING????????????????????
					} else
						txtdt.setText("" + dt);
						txtdt.setForeground(Color.black);
				}
			}

			//Plotting initial graf
			updateStatus(0);
			long startTime = System.currentTimeMillis();
			//Calculating and plotting simulation
			for (int inf = 1; inf < 10;) {
				if (!p) {
					timeStep(plotTimeJump, azr, gamr, gami, betr, beti, phir,
							phii, phi2old);
					T = T + plotTimeJump;
					time = time + dt * plotTimeJump;
					updateStatus(T);
				}

				if (Thread.interrupted()) {
					throw new InterruptedException();
				}

				try {
					startTime += delay;
					if ((startTime - System.currentTimeMillis()) < 0) {
						p = true;
						statusField
								.setText("Paused... Please decrease 'Plot every n-th step'. Calculation is too demanding, and animation is not smooth. Click on plot to resume.");
					}
					Thread.sleep(Math.max(0, startTime
							- System.currentTimeMillis()));
				} catch (InterruptedException e) {
					break;
				}
			}
		} catch (InterruptedException e) {
			updateStatus(0);
			return "Interrupted"; // SwingWorker.get() returns this
		}
		return "Done?"; // or this
	}

	// Initializing the wave function
	private void initialize(double dx, double a, double[] phir, double[] phii) {
		double x;
		double GAUSS; // this is envelope for the function
		norm = 0;
		double k0 = Math.sqrt(0.263 * energy);
		double x0 = dx * npts / 2;
		int i;
		// Start with a Gaussian wavepacket of width a*dx*sqr(2).
		T = 0;
		time = 0;
		phir[0] = 0;
		phii[0] = 0;
		phir[npts - 1] = 0;
		phii[npts - 1] = 0;
		phi2[(Math.round((npts - 1) * MAX_NPTS / npts))] = 0;
		phi2[(Math.round((npts - 1) * MAX_NPTS / npts))] = 0;

		switch (functionBox.getSelectedIndex()) {
		case 1: //"Square Envelope"

			for (i = 1; i < npts; i++) {
				x = i * dx;
				GAUSS = 1 / (2 * a * dx);

				if (Math.abs(x - x0) < a) {
					phir[i] = Math.cos(k0 * x) * GAUSS;//Math.cos(k0 * x)* GAUSS;
					phii[i] = Math.cos(k0 * x) * GAUSS;//Math.sin(k0 * x)* GAUSS;
					phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
							+ phii[i] * phii[i];
				} else {
					phir[i] = 0;
					phii[i] = 0;
					phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
				}
			}
			break;

		case 2: //"Square Re Im"

			for (i = 1; i < npts; i++) {
				x = i * dx;
				GAUSS = 1 / (2 * a * dx);

				if (Math.abs(x - x0) < a) {
					phir[i] = GAUSS;//Math.cos(k0 * x)* GAUSS;
					phii[i] = GAUSS;//Math.sin(k0 * x)* GAUSS;
					phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
							+ phii[i] * phii[i];
				} else {
					phir[i] = 0;
					phii[i] = 0;
					phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
				}
			}
			break;

		case 3: //"Dirac"

			for (i = 1; i < npts; i++) {
				x = i * dx;
				GAUSS = 1 / dx;

				if (x == x0) {
					phir[i] = Math.cos(k0 * x) * GAUSS;
					phii[i] = Math.sin(k0 * x) * GAUSS;
					phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
							+ phii[i] * phii[i];
				} else {
					phir[i] = 0;
					phii[i] = 0;
					phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
				}
			}
			break;

		case 4: //"Sine"

			for (i = 1; i < npts; i++) {
				x = i * dx;
				GAUSS = Math.sin(x / (a / dx) / (2 * Math.PI) * 360 / L);
				if (i % sampling == 0 && i < npts - 1) {
					phir[i] = Math.cos(k0 * x) * GAUSS;
					phii[i] = Math.sin(k0 * x) * GAUSS;
					phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
							+ phii[i] * phii[i];

				} else if (i < npts - 1 || (i > npts - 1 && !memory)) {
					phir[i] = 0;
					phii[i] = 0;
					phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
				}
			}
			break;

		default: // "Gauss"

			for (i = 1; i < npts; i++) {
				x = i * dx;
				GAUSS = (1 / a) * (1 / Math.sqrt(Math.PI))
						* Math.exp(-Math.pow((x - x0), 2) / (a * a));//I added: (1/a)*(1/Math.sqrt(Math.PI))*
				if (i % sampling == 0 && i < npts - 1) {
					phir[i] = Math.cos(k0 * x) * GAUSS;
					phii[i] = Math.sin(k0 * x) * GAUSS;
					phi2[(Math.round(i * MAX_NPTS / npts))] = Math
							.pow(GAUSS, 2);

				} else if (i < npts - 1 || (i > npts - 1 && !memory)) {
					phir[i] = 0;
					phii[i] = 0;
					phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
				}
			}
		}

		for (i = 0; i < npts; i++)
			norm = norm + phi2[(Math.round(i * MAX_NPTS / npts))] * dx;
		double sqn;
		sqn = Math.sqrt(norm);
		double phi2max = 0;
		max = 0;
		for (i = 0; i < npts; i++) {
			phir[i] = phir[i] / sqn;
			phii[i] = phii[i] / sqn;
			phi2[(Math.round(i * MAX_NPTS / npts))] = phi2[(Math.round(i
					* MAX_NPTS / npts))]
					/ norm;
			if (phi2[(Math.round(i * MAX_NPTS / npts))] > phi2max) {
				phi2max = phi2[(Math.round(i * MAX_NPTS / npts))];
				max = phi2max;
			}
		}
	}

	// Calculates plotTimeJump number of time steps
	private void timeStep(int plotTimeJump, double[] azr, double[] gamr,
			double[] gami, double[] betr, double[] beti, double[] phir,
			double[] phii, double[] phi2old) {
		double azi;
		double azi2;
		double denom;
		double chir;
		double temp;
		double chii;
		double xdt = dx * dx / dt;
		int i;
		int timeJump = plotTimeJump;

		if (cnt > 0) {
			cnt--;
		} else if (cnt == 0) {
			changeBox.remove(changeacknowledged);
		}
		for (int tj = 0; tj < timeJump; tj++) {
			// 'Calculate Gamma.     
			azi = 3.457 * xdt * 100;
			azi2 = 2 * azi;
			denom = azr[npts - 1] * azr[npts - 1] + azi * azi;
			gamr[npts - 1] = -azr[npts - 1] / denom;
			gami[npts - 1] = azi / denom;
			for (i = npts - 2; i > 0; i--) {
				denom = Math.pow((gamr[i] + azr[i - 1]), 2)
						+ Math.pow((gami[i] + azi), 2);
				gamr[i - 1] = -(gamr[i] + azr[i - 1]) / denom;
				gami[i - 1] = (gami[i] + azi) / denom;
			}
			// 'Calculate beta.
			betr[npts - 1] = 0;
			beti[npts - 1] = 0;
			for (i = npts - 2; i > 0; i--) {
				betr[i - 1] = gamr[i] * (betr[i] + azi2 * phii[i]);
				betr[i - 1] = betr[i - 1] - gami[i]
						* (beti[i] - azi2 * phir[i]);
				beti[i - 1] = gamr[i] * (beti[i] - azi2 * phir[i]);
				beti[i - 1] = beti[i - 1] + gami[i]
						* (betr[i] + azi2 * phii[i]);
			}
			// 'CALCULATE PHI
			chir = 0;
			chii = 0;
			//double sum=0, sum2=0;
			for (i = 1; i < npts - 1; i++) {
				temp = chir;
				chir = gamr[i] * chir - gami[i] * chii + betr[i - 1];
				chii = gamr[i] * chii + gami[i] * temp + beti[i - 1];
				phir[i] = chir - phir[i];
				phii[i] = chii - phii[i];
				phi2old[(Math.round(i * MAX_NPTS / npts))] = phi2[(Math.round(i
						* MAX_NPTS / npts))];
				phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
						+ phii[i] * phii[i];
				//sum = Math.sqrt(phi2[Math.round(i*MAX_NPTS/npts)])+sum; //checks the integral value of the wave - should be constant with small variations due to rounding
				//sum2 = phi2[Math.round(i*MAX_NPTS/npts)]+sum2; //checks the integral value of the wave - should be constant with small variations due to rounding
			}

			//System.out.println(sum+" "+sum2); //same as above
		}
	}

	/**
	 * When the worker needs to update the GUI we do so by queuing a Runnable
	 * for the event dispatching thread with SwingUtilities.invokeLater(). 
	 * Here, buffered immage is created, and then finished picture is plotted on the screen.
	 */
	void updateStatus(final long i) {
		Runnable doSetProgressBarValue = new Runnable() {
			public void run() {
				Dimension d = getSize();
				if ((offGraphics == null) || (d.width != offDimension.width)
						|| (d.height != offDimension.height)) {
					offDimension = d;
					offImage = createImage(d.width, d.height);
					offGraphics = (Graphics2D) offImage.getGraphics();
				}
				//              offGraphics.setBackground(Color.WHITE);
				//              offGraphics.setColor(getBackground());
				offGraphics.setColor(Color.white);
				offGraphics.fillRect(0, 0, d.width, d.height);
				offGraphics.setColor(Color.black);

				// Allignment to the right of the time step
				/*if (i<10000000){ds = "";}
				 if (i<1000000){ds = " ";}
				 if (i<100000){ds = "  ";}
				 if (i<10000){ds = "   ";}
				 if (i<1000){ds = "    ";}
				 if (i<100){ds = "     ";}
				 if (i<10){ds = "      ";}*/

				if (i != 0)
					timeStep.setText("Time step: " + i + "  Time: "	+ Math.round(time));// + "     dx*dx/dt = " + dx*dx*dt

				plotting(); 
				//TODO here somewhere is repaint method bug
				panel.getGraphics().drawImage(offImage, xoff, yoff, panel);
			}
		};
		SwingUtilities.invokeLater(doSetProgressBarValue);
	}

	// Plotting
	 public void plotting()//unfinished version of the above function with entropy 
	 {
	 entropy.addLast(new bins(CalcEntropy(phi2, 0, max, 2),CalcEntropy(phi2, 0, max, 8),CalcEntropy(phi2, 0, max, 32),CalcEntropy(phi2, 0, max, 128)));
	 offGraphics.draw(new Line2D.Double(0, 0, MAX_NPTS, 0));
	 offGraphics.draw(new Line2D.Double(0, 0, 0, 300));
	 offGraphics.draw(new Line2D.Double(MAX_NPTS, 0, MAX_NPTS,
	 300));
	 //offGraphics.setColor(Color.blue);
	 offGraphics.draw(new Line2D.Double(sldNpts.getValue(), 0, sldNpts.getValue(),
	 300));
	 offGraphics.setColor(Color.blue);
	 offGraphics.draw(new Line2D.Double(reimPos, 0, reimPos,
	 300));
	 offGraphics.setColor(Color.black);
	 offGraphics.draw(new Line2D.Double(0, 300,
	 MAX_NPTS, 300));
	 for (int i = 0; i < npts - 1; i++)
	 {
	 offGraphics.draw(new Line2D.Double((Math.round(i*MAX_NPTS/npts)), 300 * (1 - phi2[(Math.round(i*MAX_NPTS/npts))] / max/1.5),
	 Math.round((i+1)*MAX_NPTS/npts), 300 * (1 - phi2[(Math.round((i+1)*MAX_NPTS/npts))] / max/1.5)));
	 }
	 reim.addLast(new ReIm(phir[reimPos/sldNpts.getValue()],phii[reimPos/sldNpts.getValue()]));

	 while (reim.size()>nReIm)reim.removeFirst();
	 offGraphics.setColor(Color.blue);
	 offGraphics.drawString("Real-Imaginary plane plot at position = " + reimPos/sldNpts.getValue() + " (drag blue line with mouse)",600,20);
	 for (ReIm cords : reim)
	 {
	 offGraphics.fillOval((int)(50*cords.getX()/max)+800,(int)(1-50*cords.getY()/max) + 150,1,1);
	 }
	 offGraphics.setColor(Color.black);
	 }

	/**
	 * This action listener, called by the "Start" button, effectively forks the
	 * thread that does the work.
	 */
	ActionListener startListener = new ActionListener() {
		public void actionPerformed(ActionEvent event) {
			startButton.setEnabled(false);
			interruptButton.setEnabled(true);
			drawButton.setEnabled(false);
			functionBox.setEnabled(false);
			txtNpts.setEnabled(false);
			txtSampling.setEnabled(false);
			txtL.setEnabled(false);
			sldNpts.setEnabled(false);
			//txtdx.setEnabled(false);
			txta.setEnabled(false);
			txtEnergy.setEnabled(false);
			statusField.setText("Working...");
			cbAutoMax.setEnabled(false);

			/*
			 * Invoking start() on the SwingWorker causes a new Thread to be
			 * created that will call construct(), and then finished(). Note
			 * that finished() is called even if the worker is interrupted
			 * because we catch the InterruptedException in doWork().
			 */
			worker = new SwingWorker() {
				public Object construct() {
					return doWork();
				}

				public void finished() {
					startButton.setEnabled(true);
					interruptButton.setEnabled(false);
					drawButton.setEnabled(true);
					functionBox.setEnabled(true);
					// txtnpts.setEnabled(true);
					statusField.setText(get().toString());
				}
			};
			worker.start();
		}
	};

	/**
	 * This action listener, called by the "Stop" button, interrupts the
	 * worker thread which is running this.doWork(). Note that the doWork()
	 * method handles InterruptedExceptions cleanly.
	 */
	ActionListener interruptListener = new ActionListener() {
		public void actionPerformed(ActionEvent event) {
			worker.interrupt();

			interruptButton.setEnabled(false);
			startButton.setEnabled(true);
			drawButton.setEnabled(true);
			functionBox.setEnabled(true);
			txtSampling.setEnabled(true);
			txtL.setEnabled(true);
			sldNpts.setEnabled(true);
			//txtdx.setEnabled(true);
			txta.setEnabled(true);
			txtEnergy.setEnabled(true);
			cbAutoMax.setEnabled(true);
			/*offGraphics.setColor(getBackground());
			 offGraphics.fillRect(0, 0, MAX_NPTS + 1, MAX_NPTS + 1);
			 offGraphics.setColor(Color.black);
			 panel.getGraphics().drawImage(offImage, xoff, yoff, panel);*/
			if (p) {
				p = false;
			}
			// drawing method needs to be modified, not working properly at this moment.
			/*drawing = new SwingWorker()
			 {
			 public Object construct()
			 {
			 return progres();
			 }
			 
			 public void finished()
			 {
			 startButton.setEnabled(true);
			 interruptButton.setEnabled(false);
			 drawButton.setEnabled(true);
			 // txtnpts.setEnabled(true);
			 statusField.setText(get().toString());
			 }
			 };
			 drawing.start();*/
		}
	};

	// allows drawing - not finished/used
	ActionListener drawListener = new ActionListener() {
		public void actionPerformed(ActionEvent event) {
			if (drawButton.getText() == "Gauss Wave") {
				drawButton.setText("User Drown");
				// - disejblujes parametre vezane za gausovu funkciju - 
				txtSampling.setEnabled(false);
				//txtL.setEnabled(false);
				sldNpts.setEnabled(true);
				//txtdx.setEnabled(true);
				txta.setEnabled(false);
				txtEnergy.setEnabled(false);
			} else if (drawButton.getText() == "User Drown") {
				//- enablujes parametre vezane za gausovu funkciju - 
				drawButton.setText("Gauss Wave");
				txtSampling.setEnabled(true);
				//txtL.setEnabled(true);
				sldNpts.setEnabled(true);
				//txtdx.setEnabled(true);
				txta.setEnabled(true);
				txtEnergy.setEnabled(true);
				/*offGraphics.setColor(getBackground());
				 offGraphics.fillRect(0, 0, MAX_NPTS + 1, MAX_NPTS + 1);
				 offGraphics.setColor(Color.black);
				 panel.getGraphics().drawImage(offImage, xoff, yoff, panel);*/
			}
		}
	};

	/*// exporting plot to datafile
	 ActionListener functionListener = new ActionListener()
	 {
	 public void actionPerformed(ActionEvent event)
	 {
	 }
	 };*/

	// exporting plot to datafile
	ActionListener printListener = new ActionListener() {
		public void actionPerformed(ActionEvent event) {
			dataFile();
		}
	};

	// Pausing simulation or draging npts line
	MouseAdapter pauseListener = new MouseAdapter() {
		// Pause
		public void mouseClicked(MouseEvent arg0) {
			if (statusField.getText() != "Done?") {
				if (!p) {
					p = true;
					statusField.setText("Paused... Click on plot to resume.");
				} else {
					p = false;
					statusField.setText("Working...");
				}
			}
			/*else if (drawButton.getText()=="User Drown") //not used
			 {
			 if(xpos!=(getMousePosition().x-xoff))
			 {
			 xpos = getMousePosition().x-xoff;
			 phi2[Math.round(getMousePosition().x)]=-(getMousePosition().y/411-1)*max*norm;
			 //300 * (1 - phi2[(Math.round((i+1)*MAX_NPTS/npts))] / max)=-(xpos/300-1)*max
			 phir[xpos/sldNpts.getValue()] = Math.cos(getMousePosition().x) * Math.sqrt(phi2[xpos]);
			 phii[xpos/sldNpts.getValue()] = Math.sin(getMousePosition().x) * Math.sqrt(phi2[xpos]);
			 updateStatus(0);
			 }
			 }*/
		}

		// npts line pressed
		public void mousePressed(MouseEvent arg0) {
			if ((reimPos - 10 + xoff) < getMousePosition().x
					&& getMousePosition().x < (reimPos + 10 + xoff)) {
				lineMove = true;
				xpos = getMousePosition().x;
			}
		}

		// npts line released
		public void mouseReleased(MouseEvent arg0) {
			if (lineMove && (xpos != getMousePosition().x)) {
				//for (int i=reim.size()-1; i>0; i--) reim.remove(i);
				reim.clear();
				entropy.clear();
				reimPos = getMousePosition().x - xoff;
				if (reimPos < 1)
					reimPos = 1;
				if (reimPos > MAX_NPTS)
					reimPos = MAX_NPTS;
				reimPos = (reimPos / sldNpts.getValue()) * MAX_NPTS / npts;
				//sldNpts.setValue(npts - xoff);
				//str = "# of points: " + npts;
				//txtNpts.setText(str);
				updateStatus(0);
			}
			lineMove = false;
		}

		public void mouseEntered(MouseEvent arg0) {
			if (drawButton.getText() == "User Drown")
				draw = true;

		}

		public void mouseExited(MouseEvent arg0) {
			if (drawButton.getText() == "User Drown")
				draw = false;

		}
	};

	// listener for slider - changes npts
	ChangeListener nptsListener = new ChangeListener() {
		public void stateChanged(ChangeEvent arg0) {
			npts = MAX_NPTS / sldNpts.getValue();
			str = "# of points: " + npts;
			txtNpts.setText(str);
			dx = L / npts;
			txtdx.setText("" + dx);
			updateStatus(0);
		}
	};

	// listener for slider - changes l
	/*ChangeListener lListener = new ChangeListener()
	 {
	 public void stateChanged(ChangeEvent arg0)
	 {
	 L = sldL.getValue();
	 txtL.setText(""+L);
	 dx = L/npts;
	 txtdx.setText(""+dx);
	 a = Double.parseDouble(txta.getText())*dx;
	 changeBox.add(changeacknowledged);
	 updateStatus(0);
	 }
	 };*/

	// Opens text file to export plot values
	public void dataFile() {
		try {
			boolean wp = true;
			if (!p) {
				p = true;
				wp = false;
			} // pauses simulation
			PrintWriter out = new PrintWriter(new FileWriter("Function-"
					+ functionBox.getSelectedItem() + " npts-" + npts
					+ " time-" + Math.round(time) + " T-" + T + " En-" + energy
					+ " dt-" + Math.round(dt * 100) / 100 + " dx-"
					+ Math.round(dx * 100) / 100 + " L-" + L + " a-" + a / dx
					+ " .txt")); // encodes file name with current simulation parameters' values
			writeData(out);
			out.close();
			if (!wp)
				p = false; // unpauses simulation
		} catch (IOException exception) {
			exception.printStackTrace();
		}
	}

	// Writes to text file
	void writeData(PrintWriter out) throws IOException {
		// spatial series at current time
		out.println("Spatial series at itteration T = " + T + "  and time = "
				+ time);
		out.println("Function: " + functionBox.getSelectedItem() + " ; npts = "
				+ npts + " ; time = " + time + " ; T = " + T + " ; Energy = "
				+ energy + " ; dt = " + dt + " ; dx = " + dx + " ; L = " + L
				+ " ; a = " + a / dx);
		for (int i = 0; i < npts; i++) {
			out.println(phi2[(Math.round(i * MAX_NPTS / npts))]);
		}
		// time series for the last nreim time steps that were plotted
		out.println("Time series in positions " + reimPos / sldNpts.getValue());
		out.println("Function: " + functionBox.getSelectedItem()
				+ "Number of points = " + reim.size()
				+ " ; First T (scaled for current 'Plot every n-th step')= "
				+ (T - plotTimeJump * (reim.size() - 1)) + " ; Last T = " + T
				+ " ; time = " + time + " ; npts = " + npts + " ; Energy = "
				+ energy + " ; dt = " + dt + " ; dx = " + dx + " ; L = " + L
				+ " ; a = " + a / dx);
		out
				.println("Re                     Im                     Add their squares to calculate amplitude.");
		for (int i = 0; i < reim.size(); i++) {
			out.println(reim.get(i).getX() + "       " + reim.get(i).getY());
		}
	}

	// Class used for storing time series in reim array
	class ReIm {
		double re = 0;

		double im = 0;

		public ReIm(double x, double y) {
			super();
			re = x;
			im = y;
		}

		public double getX() {
			return re;
		}

		public double getY() {
			return im;
		}
	}

	void updateDrawing()//not used
	{
		Runnable drawing = new Runnable() {
			public void run() {
				/*if(draw)
				 {
				 phi2[Math.round(getMousePosition().x)]=-(getMousePosition().y/300-1)*max;
				 //300 * (1 - phi2[(Math.round((i+1)*MAX_NPTS/npts))] / max)=-(xpos/300-1)*max
				 phir[getMousePosition().x/sldNpts.getValue()] = Math.cos(getMousePosition().x) * Math.sqrt(phi2[getMousePosition().x]);
				 phii[getMousePosition().x/sldNpts.getValue()] = Math.sin(getMousePosition().x) * Math.sqrt(phi2[getMousePosition().x]);
				 }*/
			}
		};
		SwingUtilities.invokeLater(drawing);
	}

	/**
	 * This method represents the application code that we'd like to run on a
	 * separate thread. It allows drawing from the user. 
	 */
	// not being used at this version
	Object progres() {
		try {
			while (draw) {
				updateDrawing();
				updateStatus(0);
				if (Thread.interrupted()) {
					throw new InterruptedException();
				}
				Thread.sleep(10);
			}
		} catch (InterruptedException e) {
			updateStatus(0);
			return "Interrupted"; // SwingWorker.get() returns this
		}
		return "Done?"; // or this
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		worker = new SwingWorker() {
			public Object construct() {
				return doWork();
			}

			public void finished() {
				startButton.setEnabled(true);
				interruptButton.setEnabled(false);
				drawButton.setEnabled(true);
				functionBox.setEnabled(true);
				// txtnpts.setEnabled(true);
				statusField.setText(get().toString());
			}
		};
		worker.start();
	}

	public String getAppletInfo() {
		return "Title: Schrodinger, 23 Jul 2005\n"
				+ "Author: Lazar Kovacevic\n"
				+ "Simulation of the Schrodinger wave equation.";
	}

	public Schrodinger() {
	}

	public static void main(String args[]) {
		Schrodinger applet = new Schrodinger();
		JFrame frame = new JFrame(
				"Scrodinger wave-equation simulation for a particle confined to a region 0 to L.");
		// To close the application:
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().add(applet);
		frame.setSize(1050, 550);//TODO detach applet from webpage, make it rescalable, and rescale graph
		applet.init();
		applet.start();
		frame.setVisible(true);
	}

	public void start() {
		if (!p) {
		} else {
			p = false;
		}
	}

	public void stop() {
		if (p) {
		} else {
			p = true;
		}
	}
}
//