// Law.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 Law
	extends java.applet.Applet
{
	//TextArea disp;		// Area to display HT
	
	Panel dispArea;
	Panel controls;		// Panel for user controls
	
	Label[] size;
	Label[] real;
	Label[] estimate;
	
	Label numl1, numl2;			// Controls
	TextField num1, num2;
	Button go;
	
	GridBagLayout gbl;
	GridBagConstraints cc;
	
	JRandom myRand;
		
	// Initialize applet
	public void init()
	{	
		numl1 = new Label("probability =");			// Create controls
		num1 = new TextField(".5", 4);
		numl2 = new Label("epsilon =");
		num2 = new TextField(".1", 4);
		
		go = new Button("Go");
		
		//disp = new TextArea(15, 20);		// Create display area
		
		dispArea = new Panel();				// Set up window
		controls = new Panel();
		setLayout(new BorderLayout(5, 5));
		
		add("South", controls);
		add("Center", dispArea);
		
		dispArea.setLayout(new GridLayout(9, 3));
		//dispArea.add(disp);
		
		size = new Label[9];
		real = new Label[9];
		estimate = new Label[9];
		
		for (int r = 0; r < 9; r++) {
			size[r] = new Label("");
			size[r].setAlignment(Label.CENTER);
			real[r] = new Label("");
			real[r].setAlignment(Label.CENTER);
			estimate[r] = new Label("");
			estimate[r].setAlignment(Label.CENTER);
		}
		size[0].setText("n");
		real[0].setText("P()");
		estimate[0].setText("Chebyshev bound");
		
		for (int r = 0; r < 9; r++) {
			dispArea.add(size[r]);
			dispArea.add(real[r]);
			dispArea.add(estimate[r]);
		}
		
		gbl = new GridBagLayout();
		controls.setLayout(gbl);
		
		cc = new GridBagConstraints();
		
		cc.gridx = 0;
		cc.gridy = 0;
		gbl.setConstraints(numl1, cc);
		controls.add(numl1);
		
		cc.gridx = 1;
		gbl.setConstraints(num1, cc);
		controls.add(num1);
		
		cc.gridx = 0;
		cc.gridy = 1;
		gbl.setConstraints(numl2, cc);
		controls.add(numl2);
		
		cc.gridx = 1;
		gbl.setConstraints(num2, cc);
		controls.add(num2);
		
		cc.gridx = 0;
		cc.gridy = 2;
		cc.gridwidth = 2;
		gbl.setConstraints(go, cc);
		controls.add(go);
		
		validate();
		
		myRand = new JRandom();
	}
	
	// Handle events
	public boolean handleEvent(Event evt)
	{
		if (evt.target instanceof Button)
		{
			if (evt.target == go && evt.id == Event.ACTION_EVENT)	// When button is clicked
			{
				//disp.setText("");			// Reset output window
        		simulate(Float.valueOf(num1.getText()).floatValue(),
        			Float.valueOf(num2.getText()).floatValue());
        		return true;					// Generate correct number of tosses
			}
		}
		return super.handleEvent(evt);	// Handle other events as usual
	}
	
	// Calculate probabilities
    public void simulate(float p, float e)
    {	
    	
    	int total = 0;
    	int temp;
    	float cheb;
    	float actual;
    	int lb, ub;
    	real[0].setText("P(|An - " + String.valueOf(p) + "| >= " + String.valueOf(e) + ")");
    	//disp.appendText("n            P(|An - " + String.valueOf(p) +
    		//"| >= " + String.valueOf(e) + "          Chebyshev bound\n\n");
    	
    
    	for (int n = 50; n <= 400; n+= 50) {
    		actual = 0;
    		cheb = p * (1 - p) / ((float) n * (float) Math.pow((double) e, 2));
    		// Ask about bounds - true basic seems to be doing strange things.
    		lb = (int) Math.ceil(Math.max(0, (double) n * (double) p - (double) n
    			* (double) e));
    		ub = (int) Math.floor(Math.min((double) n, (double) n * (double) p + (double) n
    			* (double) e));
    		for (int k = lb; k <= ub; k++)
    			actual += Combinatorics.bernoulli(n, p, k);
    		//disp.appendText(String.valueOf(n) + "                    " 
    			//+ String.valueOf(1 - actual) + "                    " 
    			//+ String.valueOf(cheb) + "\n");
    		actual = round(1 - actual, .0001f);
    		if (actual < 0)
    			actual = 0;
    		if (cheb < 0)
    			cheb = 0;
    		cheb = round(cheb, .0001f);
    		size[n / 50].setText(String.valueOf(n));
    		real[n / 50].setText(String.valueOf(actual));
    		estimate[n / 50].setText(String.valueOf(cheb));
	    }
	    validate();
    }
    
    public float round(float num, float accuracy) {
		return accuracy * Math.round(num / accuracy);
	}

}