/* hypergeo.java - jpq - 19/10/99 - 14/12/99
 * modification et ajout de main() le 07/02/18
 */

import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.util.*;

public class hypergeo extends java.applet.Applet {
  static final long serialVersionUID = 180207L;
  controles C;
  dessin D;
  table T;

  private int gparmi (String s, int i) {
    try {
	  s = getParameter (s);
	  i = Integer.parseInt (s);
	}
    catch (NumberFormatException e) {}
	catch (NullPointerException e) {}
    return i;
  }


  public void init ()
  { setLayout (new BorderLayout ());
    int Nsimul = gparmi ("Nsimul", 10);
    int NR = gparmi ("NR", 5);
    int NV = gparmi ("NV", 3);
    int n = gparmi ("n", 2);
    int arrondi = gparmi ("arrondi", 3);
	boolean visible = true;
	try {
	  String s = getParameter ("visible");
	  visible = ((s == null) || ! (s.equalsIgnoreCase ("non")));
	}
    catch (NullPointerException e) {}    
    D = new dessin (Nsimul, NR, NV, n, arrondi);
    T = new table (D);
    C = new controles (D);
    add (C, BorderLayout.NORTH);
    if (visible) add (T, BorderLayout.SOUTH);
    add (D, BorderLayout.CENTER);
  }

  public void destroy ()
  { remove (D); remove (C); }

  public String getAppletInfo ()
  { return "hypergeo par j.-p. Quelen"; }

////////////////////////////////////////////////////////////////////////////////
protected class dessin extends Canvas {
  static final long serialVersionUID = 180207L;
  Image img;
  Graphics g;
  int w, h;
  Random rnd;
  int Nsimul, NR, NV, n, bsup, loi;
  double p, total;
  int [] histogramme = new int [21];
  double [] [] cnk = new double [2] [21];
  int arrondi = 2;
  double darrondi;
  double unsr2pi = 1.0 / Math.sqrt (2.0 * Math.PI);
  static final int HYPERG = 1;
  static final int BINOMIALE = 2;
  static final int POISSON = 3;
  static final int NORMALE = 4;
  boolean retrace;

  public dessin (int Nsimul, int NR, int NV, int n, int arrondi)
  { this.Nsimul = Nsimul;
    this.NR = NR;
    this.NV = NV;
    this.n = n;
    this.arrondi = arrondi;
    darrondi = Math.pow (10.0, arrondi);
// loi = 0;
    init ();
  }

  private void init ()
  { p = (double)(NR) / (double)(NR + NV);
    bsup = n + 1;
    if (bsup > histogramme.length)
    { bsup += 20;
      histogramme = new int [bsup];
      cnk = new double [2] [bsup];
      bsup = n + 1;
    }
    total = c ( n , NR + NV);
    rnd = new Random ();
    int k = (n + 1) % 2;
    for (int i = 0; i < n; i ++)
    { cnk [k] [0] = cnk [k] [i + 1] = 1.0;
      int k1 = (k + 1) % 2;
      for (int j = 1; j <= i; j ++) cnk [k] [j] = cnk [k1] [j - 1] + cnk [k1] [j];
      k = k1;
    }
  }

  private double c (int k, int n)
  { if (k > n ) return 0.0;
    if (k > n / 2) k = n - k;
    double r = 1.0;
    for (int i = 1; i <= k; i ++) r = r * n -- / i;
    return r;
  }

  private String sarrondi (double d)
  { if (arrondi > 0) d = Math.floor (d * darrondi) / darrondi;
    return Double.toString (d);
  }

  private void h (Graphics g, int i)
  { g.setColor (Color.white);
    g.fillRect (1, 1, getSize().width - 2, getSize().height - 2);
    g.setColor (Color.yellow);
    g.drawLine (20, getSize().height - 40, getSize().width, getSize().height - 40);
    g.drawLine (20, 40, 20, getSize().height - 40);
    g.setColor (Color.blue);
    g.drawString ("0", 5, getSize().height - 40);
    g.drawString (Integer.toString (i + 1), 5, 40);

    for (int j = 0; j < bsup; j ++)
    { int x = j * (getSize().width - 50) / bsup + 50;
      int y = getSize().height - 40 - histogramme [j] * (getSize().height - 80) / (i + 1);
      g.setColor (Color.black);
      g.drawLine (x, y, x, getSize().height - 40);
      g.setColor (Color.blue);
      g.drawString (Integer.toString (j), x, getSize().height - 10);
    }
      g.setColor (Color.black);
      g.drawRect (0, 0, getSize().width - 1, getSize().height - 1);
  }

  public void update (Graphics g)
  { paint (g); }

  public void paint (Graphics g1) {
    if (img == null || w != getSize().width || h != getSize().height) {
	  w = getSize().width;
      h = getSize().height;
      img = createImage (w, h);
      g = img.getGraphics ();
      g.setColor (Color.black);
      g.drawRect (0, 0, w - 1, h - 1);
    }

    g.setColor (Color.black);
    g.drawRect (0, 0, w - 1, h - 1);

    if (retrace)
    { retrace = false;
      for (int i = 0; i <= n; i ++) histogramme [i] = 0;
      for (int i = 0; i < Nsimul; i ++)
      { double somme = NR + NV;
        int v = 0; int r = 0;
        for (int j = 0; j < n; j ++) if ((int)(rnd.nextDouble () * somme --) + 1 <= NR - r) r ++; else v ++;
        histogramme [r] ++;
        h (g, i);
        g1.drawImage (img, 0, 0, this);
      }
    }
    else h (g, Nsimul - 1);

      g.setColor (Color.blue);
      switch (loi)
      { case HYPERG :
          for (int i = 0; i <= n; i ++)
          { int x = i * (getSize().width - 50) / bsup + 50;
            double proba = c (i, NR) * c (n - i, NV) / total;
            int y = getSize().height - 40 - (int)(proba * (getSize().height - 80));
            g.fillRect (x - 1, y - 1, 3, 3);
          }
          break;
        case BINOMIALE :
          for (int i = 0; i <= n; i ++)
          { int x = i * (getSize().width - 50) / bsup + 50;
            double proba = cnk [0] [i] * Math.pow (p, i) * Math.pow (1.0 - p, n - i);
            int y = getSize().height - 40 - (int)(proba * (getSize().height - 80));
            g.fillRect (x - 1, y - 1, 3, 3);
          }
          break;
        case POISSON :
          double lambda = p * n;
          double proba = Math.exp (- lambda);
          for (int i = 0; i <= n; i ++)
          { int x = i * (getSize().width - 50) / bsup + 50;
            int y = getSize().height - 40 - (int)(proba * (getSize().height - 80));
            g.fillRect (x - 1, y - 1, 3, 3);
            proba = proba * lambda / (i + 1);         
            g.fillRect (x - 1, y - 1, 3, 3);
          }
          break;
        case NORMALE :
          int Xp = -1; int Yp = -1;
          for (int X = 50; X < getSize().width; X ++)
          { double x2 = ((double)(X - 50) / (getSize().width - 50) * bsup / n - p);
            x2 = x2 * x2 * n / (p * (1 - p));
            double y = Math.exp (- x2 / 2.0) * unsr2pi / Math.sqrt (p * (1 - p) * n); // modif 11/10/99
            int Y = getSize().height - 40 - (int)(y * (getSize().height - 80));
            if (Xp != -1) g.drawLine (Xp, Yp, X, Y);
            Xp = X; Yp = Y;
          }
    }
      g1.drawImage (img, 0, 0, this);
  }
}
////////////////////////////////////////////////////////////////////////////////
protected class controles extends Panel implements ActionListener {
  static final long serialVersionUID = 180207L;
  dessin D;
  TextField tNsimul, tNR, tNV, tn;
  Button ok, hyperg, binomiale, normale, poisson;

  private Button ajoutbouton (String s)
  { Button b = new Button (s);
    b.addActionListener (this);
    add (b);
    return b;
  }

  private TextField ajouttf (Font f, String s, int i)
  { Label l;
    add (l = new Label (s));
    l.setBackground (Color.lightGray);
    l.setFont (f);
    TextField T = new TextField (Integer.toString (i), 4);
    add (T);
    T.setFont (f);
    return T;
  }

  public controles (dessin D)
  { this.D = D;
    setLayout (new FlowLayout());
    setBackground (Color.lightGray);
    Font f = new Font ("Arial", Font.PLAIN, 10);
    tNsimul = ajouttf (f, "Nsimul", D.Nsimul);
    tNR = ajouttf (f, "b. rouges", D.NR);
    tNV = ajouttf (f, "B. vertes", D.NV);
    tn = ajouttf (f, "n", D.n);
    ok = ajoutbouton ("Ok");
    hyperg = ajoutbouton ("H");
    binomiale = ajoutbouton ("B");
    poisson = ajoutbouton ("P");
    normale = ajoutbouton ("N");
  }

  private int maj (TextField tf, int n)
  { try { n = Integer.parseInt (tf.getText ()); }
      catch (NumberFormatException nfe) { }
      if (n < 0) n = 0;
      tf.setText (Integer.toString (n));
      return n;
  }

  public void actionPerformed (ActionEvent e)
  { if (e.getSource () == ok)
    { D.Nsimul = maj (tNsimul, D.Nsimul);
      D.NR = maj (tNR, D.NR);
      D.NV = maj (tNV, D.NV);
      D.n = maj (tn, D.n);
	  if (D.n > D.NR + D.NV) {
        D.n = D.NR + D.NV;
		tn.setText (Integer.toString (D.n));
	  }
      if (D.n == 0)
        D.n = 1;
      D.retrace = T.retrace = true;
      D.init ();
    }
    else
    { tNsimul.setText (Integer.toString (D.Nsimul));
      tNR.setText (Integer.toString (D.NR));
      tNV.setText (Integer.toString (D.NV));
      if (e.getSource () == hyperg) D.loi = (D.loi == dessin.HYPERG) ? 0 : dessin.HYPERG;
      else if (e.getSource () == binomiale) D.loi = (D.loi == dessin.BINOMIALE) ? 0 : dessin.BINOMIALE;
      else if (e.getSource () == poisson) D.loi = (D.loi == dessin.POISSON) ? 0 : dessin.POISSON;
      else if (e.getSource () == normale) D.loi = (D.loi == dessin.NORMALE) ? 0 : dessin.NORMALE;
    }
    tn.setText (Integer.toString (D.n));
    D.repaint ();
    T.repaint ();
  }
}
////////////////////////////////////////////////////////////////////////////////
protected class table extends Panel {
  static final long serialVersionUID = 180207L;
  TextArea ta;
  dessin D;
  boolean retrace;

  public table (dessin D)
  { this.D = D;
    setLayout (new FlowLayout());
    setBackground (Color.lightGray);
    add (ta = new TextArea ("", 4, 50, TextArea.SCROLLBARS_HORIZONTAL_ONLY));
//    retrace = false;
  }

  public void paint (Graphics g)
  { if (retrace)
    { retrace = false;
      String s1 = "k\t";
      String s2 = "p\t";
      String s3 = "t\t";
      for (int i = 0; i < D.bsup; i ++)
      { s1 = s1 + Integer.toString (i) + "\t";
        s2 = s2 + D.sarrondi ((double)(D.histogramme [i]) / (double)(D.Nsimul)) + "\t";
        s3 = s3 + D.sarrondi (D.c (i, D.NR) * D.c (D.n - i, D.NV) / D.total) + "\t";
      }
      ta.setText (s1 + "\n" + s2 + "\n" + s3);
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

	public static void main (String [] args) {
		hypergeo a = new hypergeo();
		a.init ();
		a.start ();

		Frame f = new Frame ("hypergeo");
		f.addWindowListener (new fermer ());
		f.add (a);
		f.setSize (600, 400);
		f.setVisible (true);
    }

// Permet la fermeture de la fenêtre contenant l'applet
	protected static final class fermer extends WindowAdapter {
		public void windowClosing (WindowEvent e) {
			System.exit (0);
		}
	}

}
