/* abni.java - 23/01/03 - 07/05/04
 * indépendance
 * modification et ajout de main() le 19/02/18
 */

import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.util.*;

public class abni extends java.applet.Applet {
  static final long serialVersionUID = 180219L;
  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 i;
  }

 private double gparmd (String s, double d) {
    try {
      s = getParameter (s);
      d = Double.parseDouble (s);
    }
    catch (NumberFormatException e) {}
    catch (NullPointerException e) {}
    return d;
  }


  public void init ()
  { int nsimul = gparmi ("nsimul", 100);
    int nkhi2 = gparmi ("nkhi2", 1000);
    int ndec = gparmi ("ndec", 3);
    double min = gparmd ("min", 0.0);
    double max = gparmd ("max", 0.04);
    int nclasses = gparmi ("nclasses", 20);
    double a = gparmd ("a", 0.4);
    double b = gparmd ("b", 0.2);
    double c = gparmd ("c", 0.3);
    double d = gparmd ("d", 0.1);
    T = new tableau (nsimul, nkhi2, a, b, c, d, ndec);
    TBL = new table (min, max, nclasses);
    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 = 180219L;
  double a, b, c, d;
  double [] [] theo;
  Font f;
  int nsimul, nkhi2, arrond;
  double darrond;
  TextField tnsimul, tnkhi2, tarrondi, ta, tb, tc, td;
  Button simule;
  GridBagLayout gbl;
  GridBagConstraints gbc;
  Label [] [] lbl;

  private void ajoutlbl (String s)
  { Label lbl = new Label (s);
    lbl.setBackground (Color.lightGray);
    lbl.setFont (f);
    gbl.setConstraints(lbl, gbc);
    add (lbl);
  }

  private TextField ajouttf (int n)
  { TextField tf = new TextField (Integer.toString (n));
    tf.setFont (f);
    gbl.setConstraints(tf, gbc);
    add (tf);
    return tf;
  }

  private TextField ajouttfd (double d)
  { TextField tf = new TextField (Double.toString (d));
    tf.setFont (f);
    gbl.setConstraints(tf, gbc);
    add (tf);
    return tf;
  }

  private Button ajoutb (String s)
  { Button b = new Button (s);
    b.setFont (f);
    b.addActionListener (this);
    gbl.setConstraints(b, gbc);
    add (b);
    return b;
  }

  public tableau (int nsimul, int nkhi2, double a, double b, double c, double d, int arrond)
  { this.nsimul = nsimul;
    this.nkhi2 = nkhi2;
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;
    this.arrond = arrond;
    f = new Font ("Helvetica", Font.PLAIN, 10);
    gbl = new GridBagLayout ();
    gbc = new GridBagConstraints ();
    setLayout (gbl);
    gbc.fill = GridBagConstraints.BOTH;
    tnsimul = ajouttf (nsimul);
    ajoutlbl (" simul. eff. ");
    tnkhi2 = ajouttf (nkhi2);
    ajoutlbl (" fois; a = ");
    ta = ajouttfd (a);
    ajoutlbl (" b = ");
    tb = ajouttfd (b);
    ajoutlbl (" c = ");
    tc = ajouttfd (c);
    ajoutlbl (" d = ");
    td = ajouttfd (d);
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    simule = ajoutb ("simule");
    lbl = new Label [4] [4];
    gbc.weightx = 1.0;
    gbc.gridwidth = 1;
    lbl [0] [0] = tajoutlbl ("", Color.yellow);
    gbc.gridwidth = 2;
    lbl [0] [1] = tajoutlbl ("A", Color.yellow);
    lbl [0] [2] = tajoutlbl ("NA", Color.yellow);
    gbc.gridwidth = 3;
    lbl [0] [3] = tajoutlbl ("Total", Color.yellow);

    gbc.gridwidth = 2;
    ajoutlbl (" nbr. déc. = ");
//    darrond = 1;
    if (arrond >= 0) darrond = Math.pow (10.0, arrond);
    gbc.gridwidth = 2;
    tarrondi = ajouttf (arrond);

    gbc.gridwidth = GridBagConstraints.REMAINDER;
    Label lbl = tajoutlbl ("                    ", Color.lightGray);

    talb (1, "B");
    talb (2, "NB");
    gbc.gridheight = GridBagConstraints.REMAINDER;
    talb (3, "Total");

    theo = new double [3] [3];
    theomaj ();
  }

  private void talb (int i, String s)
  { gbc.gridwidth = 1;
    lbl [i] [0] = tajoutlbl (s, Color.yellow);
    gbc.gridwidth = 2;
    lbl [i] [1] = tajoutlbl ("                    ", Color.white);
    lbl [i] [2] = tajoutlbl ("                    ", Color.white);
    gbc.gridwidth = 3;
    lbl [i] [3] = tajoutlbl ("                    ", Color.white);
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    Label lbl = tajoutlbl ("                    ", Color.lightGray);
  }

  private Label tajoutlbl (String s, Color clr)
  { Label lbl = new Label (s);
    lbl.setBackground (clr);
    lbl.setFont (f);
    gbl.setConstraints(lbl, gbc);
    add (lbl);
    return lbl;
  }

  private void theomaj ()
  { double s = a + b + c + d;
    theo [0] [0] = a / s;
    theo [0] [1] = b / s;
    theo [0] [2] = (a + b) / s;
    theo [1] [0] = c / s;
    theo [1] [1] = d / s;
    theo [1] [2] = (c + d) / s;
    theo [2] [0] = (a + c) / s;
    theo [2] [1] = (b + d) / s;
    theo [2] [2] = 1.0;
    for (int i = 0; i < 3;  i ++)
     for (int j = 0; j < 3;  j ++)
       lbl [i + 1] [j + 1] . setText (sarrondi (theo [i] [j]));
  }

  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);
      TBL.mult = getvald (TBL.tmult, TBL.mult);
      double d1 = getvald (TBL.tseuil, TBL.seuil);
      if ((d1 > 0.0) && (d1 < 100.0)) TBL.seuil = d1;
      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);
      nsimul = getvali (tnsimul, nsimul);
      nkhi2 = getvali (tnkhi2, nkhi2);
      arrond = getvali (tarrondi, arrond);
      a = getvald (ta, a);
      a = Math.max (a, 0.0);
      ta.setText (Double.toString (a));
      b = getvald (tb, b);
      b = Math.max (b, 0.0);
      tb.setText (Double.toString (b));
      c = getvald (tc, c);
      c = Math.max (c, 0.0);
      tc.setText (Double.toString (c));
      d = getvald (td, d);
      d = Math.max (d, 0.0);
      td.setText (Double.toString (d));
      theomaj ();
      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 "abni.java par j.-p. Quelen"; }
}
////////////////////////////////////////////////////////////////////////////////
protected class dessin extends Canvas {
  static final long serialVersionUID = 180219L;
  Image img;
  Graphics g;
  int w, h, hmax, nclasses, nvaleurs, nval;
  int [] histogramme;
  double [] ref;
  double min, max, mult, 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;
    mult = TBL.mult;
    seuil = TBL.seuil;
    nvaleurs = T.nkhi2;
    nval = T.nsimul;
  }

  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);
      TBL.retrace = true;
	  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];
        dv *= mult;
        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);
          g.drawString ("d2 x " + mult + " = " + dv, 50, 10);
            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 * mult), 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 * mult - 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] * mult;
      double dvm1 = don.valeurs [idvl1] * mult;

      double lim = T.a * T.d - T.b * T.c;
      lim *= lim;
      lim /= ((T.a + T.b) * (T.c + T.d) * (T.a + T.c) * (T.b + T.d));
      lim *= mult;
      g.setColor (Color.green);
      xi = (int) ((lim - min) * w / (max - min));
      g.drawLine (xi, 0, xi, h);

      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);
      g.setColor (Color.black);
      g.drawString ("seuils à " + seuil + "% : " + dvm + "; " + dvm1, 50, 30);
      g.drawString ("limite x " + mult + " = " + lim, 50, 40);
      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 = 180219L;
  TextArea ta;
  boolean retrace;
  int nclasses;
  double min, max, mult, seuil;
  TextField tnclasses, tmax, tmult, tseuil;
  Button ok;
  Font f;
  GridBagLayout gbl;
  GridBagConstraints gbc;

  private Label ajoutlbl (String s)
  { Label lbl = new Label (s);
    lbl.setBackground (Color.lightGray);
    lbl.setFont (f);
    gbl.setConstraints(lbl, gbc);
    return lbl;
  }

  private TextField ajouttf (int n)
  { TextField tf = new TextField (Integer.toString (n));
    tf.setFont (f);
    gbl.setConstraints(tf, gbc);
    add (tf);
    return tf;
  }

  private TextField ajoutd (double d)
  { TextField tf = new TextField (Double.toString(d));
    tf.setFont (f);
    gbl.setConstraints(tf, gbc);
    return tf;
  }

  public table (double min, double max, int nclasses)
  { this.min = min;
    this.max = max;
    this.nclasses = nclasses;
    mult = 1.0;
    seuil = 10.0;
    f = new Font ("Arial", Font.PLAIN, 10);
    gbl = new GridBagLayout ();
    gbc = new GridBagConstraints ();
    setLayout (gbl);
    setBackground (Color.lightGray);
    gbc.fill = GridBagConstraints.BOTH;
    gbc.weightx = 1.0;
    add (tnclasses = ajouttf (nclasses));
    gbl.setConstraints(tnclasses, gbc);
    add (ajoutlbl (" classes; max "));
    add (tmax = ajoutd (max));
    add (ajoutlbl ("; d2 multiplié par "));
    add (tmult = ajoutd (mult));
    add (ajoutlbl (" seuil "));
    add (tseuil = ajoutd (seuil));
    add (ajoutlbl (" % "));
    add (ok = new Button ("Ok"));
    ok.setFont (f);
    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);
    mult = getvald (tmult, mult);
    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 = 180219L;
  double [] valeurs;
  double [] [] histo;

  public donnees ()
  { int n = T.nkhi2; // n : nombre de khi2 simulés
    int nval = T.nsimul; // nval : taille de l'échantillon
    Random rnd = new Random ();
    valeurs = new double [n];
    histo = new double [2] [2];
    double ptheo = T.theo [0] [2] * T.theo [1] [2] * T.theo [2] [0] * T.theo [2] [1];
    double dn1 = T.theo [0] [0];
    double dn2 = dn1 + T.theo [0] [1];
    double dn3 = dn2 + T.theo [1] [0];
    for (int j = 0; j < n; j ++)
    { histo [0] [0] = histo [0] [1] = histo [1] [0] = histo [1] [1] = 0.0;
      for (int i = 0; i < nval; i ++)
      { double d = rnd.nextDouble ();
        int ii = 0; int jj = 0;
        if (d > dn1)
        { jj ++;
          if (d > dn2)
          { ii ++; jj --;
            if (d > dn3) jj ++;
          }
        }
        histo [ii] [jj] ++;
      }

// fAB*fNANB-fANB*fNAB
      double d = histo [0] [0] * histo [1] [1] - histo [0] [1] * histo [1] [0];
// fAB - fA*fB
//      double d = (histo [0] [0] * nval - (histo [0] [0] + histo [0] [1]) * (histo [0] [0] + histo [1] [0]));
      valeurs [j] = d * d / ((histo [0] [0] + histo [0] [1])*(histo [1] [0] + histo [1] [1])*(histo [0] [0] + histo [1] [0])*(histo [0] [1] + histo [1] [1]));
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

    public static void main (String [] args) {

        abni a = new abni();
        a.init ();
        a.start ();

        Frame f = new Frame ("abni");
        f.addWindowListener (new fermer ());
        f.add (a);
        f.setSize (500, 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);
        }
    }

}
