// SimulateWindow.java

// Written by Julian Devlin, 8/97, for the text book
// "Introduction to Probability," by Charles M. Grinstead & J. Laurie Snell

import java.applet.Applet;
import java.awt.*;

public class SimulateWindow
	extends java.awt.Frame
{
	Panel dispArea;
	Panel controls;		// Panel for user controls
	Panel pPanel;
	
	Label numl;			// Controls
	TextField num;
	
	TextField[][] P;
	TextArea output;
	Matrix pMat, wMat, xMat, zMat, cMat;
	float[][] rls;
	float[][] cls;
	String[] names;
	int[] count;
	float[] fraction;
	Matrix rows, columns1, columns2;
	Label pLab, wLab;
	
	Checkbox print;
	Button go;
	
	GridBagLayout gbl, pgbl, dgbl;
	GridBagConstraints cc, pcc, dcc;
	
	JRandom myRand;
	
	int size;
	
	Color labC;
	
	public SimulateWindow(int s) {
		super("SimulateChain");
		size = s;	
	}
		
	// Initialize applet
	public void init()
	{	
		go = new Button("Go");
		output = new TextArea(15, 30);
		numl = new Label("No.");			// Create controls
		num = new TextField("250", 4);
		
		count = new int[size];
		fraction = new float[size];
		
		rls = new float[size][1];
		for (int i = 0; i < size; i++) {
			rls[i][0] = i + 1;	
		}
		
		cls = new float[1][size];
		for (int i = 0; i < size; i++) {
			cls[0][i] = i + 1;	
		}
		
		if (size == 3) {
			P = new TextField[3][3];
			P[0][0] = new TextField(".5", 4);
			P[0][1] = new TextField(".25", 4);
			P[0][2] = new TextField(".25", 4);
			P[1][0] = new TextField(".5", 4);
			P[1][1] = new TextField("0", 4);
			P[1][2] = new TextField(".5", 4);
			P[2][0] = new TextField(".25", 4);
			P[2][1] = new TextField(".25", 4);
			P[2][2] = new TextField(".5", 4);
		
			names = new String[3];
			names[0] = new String("R");
			names[1] = new String("N");
			names[2] = new String("S");
		}
		else {
			P = new TextField[size][size];
			for (int i = 0; i < size; i++) {
				for (int j = 0; j < size; j++) {
					P[i][j] = new TextField("0", 4);
				}
			}
			names = new String[size];
			for (int i = 0; i < size; i++) {
				names[i] = new String(String.valueOf(i + 1));
			}
		}
			
		pLab = new Label("P = ");
		pLab.setAlignment(Label.RIGHT);
		wLab = new Label("w = ");
		wLab.setAlignment(Label.RIGHT);
		
		labC = new Color(100, 0, 0);
		
		rows = new Matrix(rls);
		rows.setColor(labC);
		columns1 = new Matrix(cls);
		columns1.setColor(labC);
		columns2 = new Matrix(cls);
		columns2.setColor(labC);
		
		dispArea = new Panel();				// Set up window
		controls = new Panel();
		pPanel = new Panel();
		setLayout(new BorderLayout(5, 5));
		
		add("South", controls);
		add("Center", dispArea);
		
		pgbl = new GridBagLayout();
		pcc = new GridBagConstraints();
		pPanel.setLayout(pgbl);
		for (int r = 0; r < size; r++) {
			for (int c = 0; c < size; c++) {
				pcc.gridx = c;
				pcc.gridy = r;
				pgbl.setConstraints(P[r][c], pcc);
				pPanel.add(P[r][c]);
			}	
		}
		
		dgbl = new GridBagLayout();
		dcc = new GridBagConstraints();
		dispArea.setLayout(dgbl);
		//dcc.fill = GridBagConstraints.BOTH;
		dcc.gridx = 0;
		dcc.gridy = size / 2;
		dcc.gridwidth = 2;
		dcc.gridheight = 1;
		dgbl.setConstraints(pLab, dcc);
		dispArea.add(pLab);
		
		dcc.gridx = 2;
		dcc.gridy = 1;
		dcc.gridwidth = 1;
		dcc.gridheight = size;
		dgbl.setConstraints(rows, dcc);
		dispArea.add(rows);
		
		dcc.gridx = 3;
		dcc.gridy = 0;
		dcc.gridwidth = size;
		dcc.gridheight = 1;
		dgbl.setConstraints(columns1, dcc);
		dispArea.add(columns1);
		
		dcc.gridx = 3;
		dcc.gridy = 1;
		dcc.gridwidth = size;
		dcc.gridheight = size;
		dgbl.setConstraints(pPanel, dcc);
		dispArea.add(pPanel);
		
		dcc.gridx = 0;
		dcc.gridy = size + 2;
		dcc.gridwidth = 2;	
		dcc.gridheight = 1;
		dgbl.setConstraints(wLab, dcc);
		dispArea.add(wLab);
		
		dcc.gridx = 3;
		dcc.gridy = 1 + size;
		dcc.gridwidth = size;
		dcc.gridheight = 1;
		dgbl.setConstraints(columns2, dcc);
		dispArea.add(columns2);
		
		pMat = new Matrix(P);
		
		xMat = Matrix.add(Matrix.subtract(Matrix.identity(size), pMat),
			Matrix.con(size, size));
		zMat = xMat.inverse();
		cMat = Matrix.con(1, size);
		wMat = Matrix.multiply(cMat, zMat);
		wMat.round(.0001f);
		
		dcc.gridx = 3;
		dcc.gridy = 2 + size;
		dcc.gridwidth = size;
		dcc.gridheight = 1;
		dgbl.setConstraints(wMat, dcc);
		dispArea.add(wMat);
		
		gbl = new GridBagLayout();
		controls.setLayout(gbl);
		
		cc = new GridBagConstraints();
		
		cc.gridx = 0;
		cc.gridy = 0;
		cc.gridwidth = 2;
		cc.gridheight = 10;
		gbl.setConstraints(output, cc);
		controls.add(output);
		
		cc.gridx = 0;
		cc.gridy = 10;
		cc.gridwidth = 1;
		cc.gridheight = 1;
		gbl.setConstraints(numl, cc);
		controls.add(numl);
		
		cc.gridx = 1;
		cc.gridy = 10;
		cc.gridwidth = 1;
		cc.gridheight = 1;
		gbl.setConstraints(num, cc);
		controls.add(num);
		
		print = new Checkbox("Print");
		print.setState(true);
		
		cc.gridx = 0;
		cc.gridy = 11;
		cc.gridwidth = 2;
		gbl.setConstraints(print, cc);
		controls.add(print);
		
		cc.gridx = 0;
		cc.gridy = 12;
		cc.gridwidth = 2;
		gbl.setConstraints(go, cc);
		controls.add(go);
		
		myRand = new JRandom();
		
		validate();
	}
	
	// Handle events
	public boolean handleEvent(Event evt)
	{
		String minStr, maxStr;
		if (evt.target instanceof Button)
		{
			if (evt.target == go && evt.id == Event.ACTION_EVENT)	// When button is clicked
			{
        		simulate(Integer.valueOf(num.getText()).intValue());
        		return true;					// Generate correct number of tosses
			}
		}
		if (evt.id == Event.WINDOW_DESTROY) {
			hide();
			dispose();	
		}
		return super.handleEvent(evt);	// Handle other events as usual
	}
	
	
	// Calculate probabilities
    public void simulate(int num)
    {	
    	dispArea.remove(wMat);
    	
    	pMat = new Matrix(P);
		float[][] pArr = pMat.toArray();
		float total;
		for (int r = 0; r < pArr.length; r++) {
			total = 0;
			for (int c = 0; c < pArr[0].length; c++) {
				total += pArr[r][c];
			}	
			for (int c = 0; c < pArr[0].length; c++) {
				pArr[r][c] = round(pArr[r][c] / total, .001f);
			}		
		}
		
		for (int r = 0; r < pArr.length; r++) {
			for (int c = 0; c < pArr[0].length; c++) {
				P[r][c].setText(String.valueOf(pArr[r][c]));
			}	
		}
		
		pMat = new Matrix(pArr);
		
		xMat = Matrix.add(Matrix.subtract(Matrix.identity(size), pMat),
			Matrix.con(size, size));
		zMat = xMat.inverse();
		cMat = Matrix.con(1, size);
		wMat = Matrix.multiply(cMat, zMat);
		wMat.round(.0001f);
		
		dcc.gridx = 3;
		dcc.gridy = 2 + size;
		dcc.gridwidth = size;
		dcc.gridheight = 1;
		dgbl.setConstraints(wMat, dcc);
		dispArea.add(wMat);
		
		output.setText("");
		
		for (int i = 0; i < size; i++) {
			count[i] = 0;
			fraction[i] = 0;
		}
		
		float r;
		int state = 0;
		for (int i = 1; i < num; i++) {
			r = myRand.nextFloat(0, 1);
			for (int j = 0; j < size; j++) {
				r -= pMat.toArray()[state][j];
				if (r < 0) {
					state = j;
					if (print.getState() == true) {
						output.appendText(names[state]);
						if (i % 30 == 29)
							output.appendText("\n");
					}
					j = size;	
				}
			}	
			count[state]++;
		}
    	
    	output.appendText("\n\nState     Times     Fraction\n");
    	
    	for (int i = 0; i < size; i++) {
    		fraction[i] = round((float) count[i] / (float) num, .001f);
    		output.appendText(names[i] + "               " + 
    			String.valueOf(count[i]) + "               " +
    			String.valueOf(fraction[i]) + "\n");	
    	}
    	
   	 	validate();
	}
	
	public float round(float num, float accuracy) {
		return accuracy * Math.round(num / accuracy);
	}
	
}




