/* Loi de Hardy-Weinberg khi2 - 12/06/04
 * modification et ajout de main() le 06/02/18
 */

import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.util.*;

public class hwkhi2 extends java.applet.Applet {
  static final long serialVersionUID = 180206L;
  dessin D;
  tableau T;
  table TBL;

  private int gparmi (String s, int i) {
    try {
	  s = getParameter (s);
	  i = Integer.parseInt (s);
	}
    catch (NumberFormatException e) {}
	catch (NullPointerException e) {}
    return Math.max (i, 1);
  }

  private double gparmd (String s, double d) {
    try {
	  s = getParameter (s);
	  d = Double.parseDouble (s);
	}
    catch (NumberFormatException e) {}
	catch (NullPointerException e) {}
    return Math.max (d, 0.0);
  }

  public void init ()
  { setFont (new Font ("Arial", Font.PLAIN, 10));
    int NAA = gparmi ("NGAGA", 64);
    int NAa = gparmi ("NGAPA", 82);
    int Naa = gparmi ("NPAPA", 47);
    int ndec = gparmi ("ndec", 4);
    int nq2pr = gparmi ("nq2pr", 500);
    double min = gparmd ("min", 0.0);
    double max = gparmd ("max", 15.0);
    double seuil = gparmd ("seuil", 10.0);
    int nclasses = gparmi ("nclasses", 20);

    T = new tableau (nq2pr, NAA, NAa, Naa, ndec);
    TBL = new table (min, max, nclasses, seuil);
    D = new dessin ();
    setLayout (new BorderLayout ());
    add (T, BorderLayout.NORTH);
    add (D, BorderLayout.CENTER);
    add (TBL, BorderLayout.SOUTH);
  }

////////////////////////////////////////////////////////////////////////////////
protected class tableau extends Panel implements ActionListener {
  static final long serialVersionUID = 180206L;
  double p, q, r;
  int nq2pr, N, NAA, NAa, Naa, arrond;
  double darrond;
  TextField tnq2pr, tNAA, tNAa, tNaa, tarrondi;
  Label lN, lpqr;
  Button simule;
  GridBagLayout gbl;
  GridBagConstraints gbc;

  private Label ajoutlbl (String s)
  { Label lbl = new Label (s);
    lbl.setBackground (Color.lightGray);
    gbl.setConstraints(lbl, gbc);
    add (lbl);
    return lbl;
  }

  private TextField ajouttf (int n)
  { TextField tf = new TextField (Integer.toString (n));
    gbl.setConstraints(tf, gbc);
    add (tf);
    return tf;
  }

  private TextField ajouttfd (double d)
  { TextField tf = new TextField (Double.toString (d));
    gbl.setConstraints(tf, gbc);
    add (tf);
    return tf;
  }

  private Button ajoutb (String s)
  { Button b = new Button (s);
    b.addActionListener (this);
    gbl.setConstraints(b, gbc);
    add (b);
    return b;
  }

  public tableau (int nq2pr, int NAA, int NAa, int Naa, int arrond)
  { this.nq2pr = nq2pr;
    this.NAA = NAA;
    this.NAa = NAa;
    this.Naa = Naa;
    N = NAA + NAa + Naa;
    double d = (double) (2 * N);
    p = (double) (2 * NAA + NAa) / d;
    p *= p;
    r = (double) (2 * Naa + NAa) / d;
    r *= r;
    this.arrond = arrond;
    gbl = new GridBagLayout ();
    gbc = new GridBagConstraints ();
    setLayout (gbl);
    gbc.fill = GridBagConstraints.BOTH;
    ajoutlbl ("nbre de simulations = ");
    tnq2pr = ajouttf (nq2pr);
    ajoutlbl ("; NAA = ");
    tNAA = ajouttf (NAA);
    ajoutlbl (" NAa = ");
    tNAa = ajouttf (NAa);
    ajoutlbl (" Naa = ");
    tNaa = ajouttf (Naa);
    ajoutlbl (" N = ");
    lN = ajoutlbl (Integer.toString(N));
    lN.setBackground (Color.white);
    if (arrond >= 0) darrond = Math.pow (10.0, arrond);
    ajoutlbl ("; p, q, r : ");
    lpqr = ajoutlbl (sarrondi(p) + ", " + sarrondi(1.0 - p - r) + ", " + sarrondi(r));
    lpqr.setBackground (Color.white);
    ajoutlbl ("; déc ");
    tarrondi = ajouttf (arrond);
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    simule = ajoutb ("simule");
  }

  private int getvali (TextField tf, int i)
  { int ii = i;
    try { i = Integer.parseInt (tf.getText ()); }
    catch (NumberFormatException nfe) { }
    if (i <= 0) i = ii;
    tf.setText (Integer.toString (i));
    return i;
  }

  private double getvald (TextField tf, double d)
  { double dd = d;
    try { d = new Double (tf.getText ()) . doubleValue (); }
    catch (NumberFormatException nfe) { }
    tf.setText (Double.toString (d));
    return d;
  }

  private String sarrondi (double d)
  { if (arrond >= 0) d = Math.floor (d * darrond + 0.5) / darrond;
    return Double.toString (d);
  }

  public void actionPerformed (ActionEvent e)
  { if (e.getSource () == simule)
    { TBL.nclasses = getvali (TBL.tnclasses, TBL.nclasses);
      TBL.max = getvald (TBL.tmax, TBL.max);
      double d = getvald (TBL.tseuil, TBL.seuil);
      if ((d > 0.0) && (d < 100.0)) TBL.seuil = d;
      else TBL.tseuil.setText (Double.toString (TBL.seuil));
      try { arrond = Integer.parseInt (tarrondi.getText ()); }
      catch (NumberFormatException nfe) { }
      if (arrond >= 0) darrond = Math.pow (10.0, arrond);
      NAA = getvali (tNAA, NAA);
      NAa = getvali (tNAa, NAa);
      Naa = getvali (tNaa, Naa);
      N = NAA + NAa + Naa;
      arrond = getvali (tarrondi, arrond);
      tNAA.setText (Integer.toString (NAA));
      tNAa.setText (Integer.toString (NAa));
      tNaa.setText (Integer.toString (Naa));
      lN.setText (Integer.toString(N));
      d = (double) (2 * N);
      p = (double) (2 * NAA + NAa) / d;
      p *= p;
      r = (double) (2 * Naa + NAa) / d;
      r *= r;
      lpqr.setText (sarrondi(p) + ", " + sarrondi(1.0 - p - r) + ", " + sarrondi(r));
      TBL.ta.setText ("SIMULATION EN COURS ...");
      D.don = new donnees (); // simulation
      D.init ();
      D.retrace = TBL.retrace = true;
      D.repaint ();
      TBL.repaint ();
    }
  }

  public String getAppletInfo ()
  { return "hwkhi2.java par j.-p. Quelen"; }
}
////////////////////////////////////////////////////////////////////////////////
protected class dessin extends Canvas {
  static final long serialVersionUID = 180206L;
  Image img;
  Graphics g;
  int w, h, hmax, nclasses, nvaleurs, nval;
  int [] histogramme;
  double [] ref;
  double min, max, dv, med, seuil, moy, variance;
  boolean retrace, tok;
  String surl, message;
  donnees don;

  private void majvar ()
  { min = TBL.min;
    max = TBL.max;
    nclasses = TBL.nclasses;
    seuil = TBL.seuil;
    nvaleurs = T.nq2pr;
    nval = T.N;
  }

  public dessin ()
  { don = new donnees ();
    for (int i = 0; i < don.valeurs.length; i ++) don.valeurs [i] = -1.0;
    tok = true;
//    dv = med = moy = variance = 0.0;
    init ();
  }

  private void init ()
  { majvar ();
    if ((histogramme == null) || (nclasses > histogramme.length))
    { histogramme = new int [nclasses];
      ref = new double [nclasses];
    }
    for (int i = 0; i < nclasses; i ++)
    { histogramme [i] = 0;
      ref [i] = min + (max - min) * i / nclasses;
    }
    hmax = 0;
//    retrace = false;
  }

  public void update (Graphics g)
  { paint (g); }

  public void message (Graphics g, String s)
  { g.setColor (Color.black);
    g.drawString (s, 10, 10);
  }

  private void painthisto (Graphics g)
  { g.setColor (Color.black);
    g.drawRect (0, 0, w -1, h - 1);
    g.setColor (Color.blue);
    int wpas = w / nclasses;
    double hpas = (double)(h - 100) / hmax;
    for (int i = 0; i < nclasses; i ++)
    { int dy = (int)(hpas * histogramme [i]);
      g.drawRect (wpas * i, h - dy - 1, wpas, dy + 1);
    }
    g.setColor (Color.black);
    int Y = h - (int)(hpas * hmax) - 1;
    g.drawString (Integer.toString (hmax), 0, Y); 
    g.drawLine (20, Y, 40, Y);
  }

  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.white);
      g.fillRect (0, 0, w, h);
      if (TBL != null) TBL.retrace = true;
    }
    if (retrace)
    { retrace = false;
      hmax = 0;
      g.setColor (Color.white);
      g.fillRect (0, 0, w, h);
      dv = med = moy = variance = 0.0;
      
      for (int i = 0; i < don.valeurs.length; i ++)
      { dv = don.valeurs [i];
        moy += dv;
        variance += dv * dv;
        if ((dv >= min) && (dv < max))
        { int k = 0;
          while ((k < nclasses) && (dv >= ref [k])) k ++;
          histogramme [-- k] ++;
          if (histogramme [k] > hmax) hmax = histogramme [k];
        }
         if (tok)
        { g.setColor (Color.white);
          g.fillRect (0, 0, w, h);
          painthisto (g);
            g1.drawImage (img, 0, 0, this);
        }
      }
      tok = true;
    }
    g.setColor (Color.black);
    g.drawRect (0, 0, w -1, h - 1);
    if (don.valeurs [0] != -1)
    { g.setColor (Color.white);
      g.fillRect (0, 0, w, h);
      painthisto (g);

      qsort (0, don.valeurs.length - 1);
      int idvl = don.valeurs.length / 2 - 1;
      if (don.valeurs.length % 2 == 0) med = (don.valeurs [idvl] + don.valeurs [idvl + 1]) / 2.0; 
      else med = don.valeurs [idvl + 1];
      g.drawString ("médiane = " + med, 50, 20);
      moy /= (double)(don.valeurs.length);
      variance = variance / don.valeurs.length - moy * moy;
      g.drawString ("moyenne = " + T.sarrondi (moy) + " variance = " + variance, 50, 10);
      g.setColor (Color.yellow);
      int xi = (int)((med - min) * w / (max - min));
      g.drawLine (xi, 0, xi, h); 
      double dvl = (double)(don.valeurs.length) * seuil / 100.0;
      idvl = (int)(dvl) - 1;
      int idvl1 = don.valeurs.length - idvl - 2;
      if (idvl < 0) idvl = 0;
      if (idvl1 >= don.valeurs.length) idvl1 = don.valeurs.length - 1;
      double dvm = don.valeurs [idvl];
      double dvm1 = don.valeurs [idvl1];
      g.setColor (Color.red);
// premier seuil
//      xi = (int) ((dvm - min) * w / (max - min));
//      g.drawLine (xi, 0, xi, h);
// second seuil
      xi = (int) ((dvm1 - min) * w / (max - min));
      g.drawLine (xi, 0, xi, h);
// Nd^2obs en vert
      double d = (double) (4 * T.NAA * T.Naa - T.NAa * T.NAa) / (double) ((2 * T.NAA + T.NAa) * (2 * T.Naa + T.NAa));
      d *= d * T.N;
      xi = (int) ((d - min) * w / (max - min));
      g.setColor (Color.green);
      g.drawLine (xi, 0, xi, h);
      g.setColor (Color.black);
      g.drawString ("Nd^2obs = " + d, 50, 40);
      g.drawString ("seuil à " + seuil + "% : " + dvm1, 50, 30);
      g.drawString (Double.toString (min), 5, h - 2);
      String s = Double.toString (max);
      g.drawString (s, w - s.length () * 8, h - 2);
    }
    g1.drawImage (img, 0, 0, this);
  }

  private void qsort (int lo0, int hi0)
  { int lo = lo0;
    int hi = hi0;
    double mid;
    if ( hi0 > lo0)
    { mid = don.valeurs [(lo0 + hi0) / 2];
      while (lo <= hi)
      { while ((lo < hi0) && (don.valeurs [lo] < mid)) ++ lo;
        while ((hi > lo0) && (don.valeurs [hi] > mid )) -- hi;
        if( lo <= hi )
        { double d = don.valeurs [lo];
          don.valeurs [lo] = don.valeurs [hi];
          don.valeurs [hi] = d;
          ++ lo; -- hi;
        }
      }
      if (lo0 < hi) qsort (lo0, hi);
      if (lo < hi0) qsort (lo, hi0);
    }
  }
}
////////////////////////////////////////////////////////////////////////////////
protected class table extends Panel implements ActionListener {
  static final long serialVersionUID = 180206L;
  TextArea ta;
  boolean retrace;
  int nclasses;
  double min, max, seuil;
  TextField tnclasses, tmax, tseuil;
  Button ok;
  GridBagLayout gbl;
  GridBagConstraints gbc;

  private Label ajoutlbl (String s)
  { Label lbl = new Label (s);
    lbl.setBackground (Color.lightGray);
    gbl.setConstraints(lbl, gbc);
    return lbl;
  }

  private TextField ajouttf (int n)
  { TextField tf = new TextField (Integer.toString (n));
    gbl.setConstraints(tf, gbc);
    add (tf);
    return tf;
  }

  private TextField ajoutd (double d)
  { TextField tf = new TextField (Double.toString(d));
    gbl.setConstraints(tf, gbc);
    return tf;
  }

  public table (double min, double max, int nclasses, double seuil)
  { this.min = min;
    this.max = max;
    this.nclasses = nclasses;
    this.seuil = seuil;
    gbl = new GridBagLayout ();
    gbc = new GridBagConstraints ();
    setLayout (gbl);
    setBackground (Color.lightGray);
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.weightx = 1.0;
    add (tnclasses = ajouttf (nclasses));
    gbl.setConstraints(tnclasses, gbc);
    add (ajoutlbl (" classes; max = "));
    add (tmax = ajoutd (max));
    add (ajoutlbl (" seuil = "));
    add (tseuil = ajoutd (seuil));
    add (ajoutlbl (" % "));
    add (ok = new Button ("Ok"));
    ok.addActionListener (this);
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    gbl.setConstraints(ok, gbc);
    add (ta = new TextArea ("", 3, 50, TextArea.SCROLLBARS_HORIZONTAL_ONLY));
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    gbc.gridheight = GridBagConstraints.REMAINDER;
    gbl.setConstraints(ta, gbc);
    retrace = false;
  }

  public void paint (Graphics g)
  { if (retrace)
    { retrace = false;
      String s = "";
      for (int i = 0; i < nclasses; i ++) s = s + "[" + T.sarrondi (D.ref [i]) + ";" + T.sarrondi (D.min + (D.max - D.min) * (i + 1) / D.nclasses) + "[\t";
//for (int i = 0; i < D.don.valeurs.length; i ++) s += T.sarrondi (D.don.valeurs [i]) + "\t";
      s += "\n";
      for (int i = 0; i < nclasses; i ++) s += Integer.toString (D.histogramme [i]) + "\t";
      ta.setText (s);
    }
  }

  private int getvali (TextField tf, int i)
  { int ii = i;
    try { i = Integer.parseInt (tf.getText ()); }
    catch (NumberFormatException nfe) { }
    if (i <= 0) i = ii;
    tf.setText (Integer.toString (i));
    return i;
  }

  private double getvald (TextField tf, double d)
  { double dd = d;
    try { d = new Double (tf.getText ()) . doubleValue (); }
    catch (NumberFormatException nfe) { }
    tf.setText (Double.toString (d));
    return d;
  }

  public void actionPerformed (ActionEvent e)
  { if (e.getSource () == ok)
    { nclasses = getvali (tnclasses, nclasses);
    max = getvald (tmax, max);
    double d = getvald (tseuil, seuil);
    if ((d > 0.0) && (d < 100.0)) seuil = d;
    else tseuil.setText (Double.toString (seuil));
    D.init ();
    D.tok = false;
    D.retrace = retrace = true;
    D.repaint ();
    repaint ();
    }
  }
 
}
////////////////////////////////////////////////////////////////////////////////
protected class donnees {
  static final long serialVersionUID = 180206L;
  double [] valeurs;

  public donnees ()
  { int nq2pr = T.nq2pr;
    int N = T.N; // nval : taille de l'échantillon
    Random rnd = new Random ();
    valeurs = new double [nq2pr];
    double p = (double) (2 * T.NAA + T.NAa) / (double) (2 * N);
    int Np, Nr;
    for (int j = 0; j < nq2pr; j ++)
    { Np = Nr = 0;
      for (int i = 0; i < N; i ++)
      { double d = rnd.nextDouble ();
        double d1 = rnd.nextDouble ();
        if ((d < p) && (d1 < p)) Np ++;
        else if ((d > p) && (d1 > p)) Nr ++;
      }
      int Nq = N - Np - Nr;
      double d = (double)(4 * Np * Nr - Nq * Nq) / (double)((2 * Np + Nq)* (2 * Nr + Nq));
      d *= d * N;
      valeurs [j] = d;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

	public static void main (String [] args) {
		hwkhi2 a = new hwkhi2();
		a.init ();
		a.start ();

		Frame f = new Frame ("hwkhi2");
		f.addWindowListener (new fermer ());
		f.add (a);
		f.setSize (800, 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);
		}
	}

}
