/* histosomme.java
 * 19/06/01 - 04/02/02 - 03/03/02 - 30/11/02 - 04/02/03
 * ajout de main() le 31/01/18
 */

import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.util.*;

public class histosomme extends java.applet.Applet {
  static final long serialVersionUID = 180131L;
  controles C;
  dessin D;
  table T;

  private double gparmd (String s, double d) {
    try {
	  s = getParameter (s);
      d = Double.parseDouble (s);
	}
    catch (NumberFormatException e) {}
	catch (NullPointerException e) {}
    return d;
  }

  private int gparmi (String s, int i) {
    try {
	  s = getParameter (s);
	  i = Integer.parseInt (s);
	}
    catch (NumberFormatException e) {}
	catch (NullPointerException e) {}
    if (i <= 0)
	  i = 1;
    return i;
  }

  public void init ()
  { double min = gparmd ("min", 0.0);				//min et max
    double max = gparmd ("max", 0.05);
    int nclasses = gparmi ("nclasses", 20);			// nombre de classes de l'histogramme
    int nvaleurs = gparmi ("nvaleurs", 200);		// nombre de d^2
    int nval = gparmi ("nval", 100);				//taille de l'échantillon
    int nch = gparmi ("nch", 6);					// nombre de résultats possibles
    if (nch <= 1)
	  nch = 2;
    setLayout (new BorderLayout ());
    D = new dessin (min, max, nclasses, nvaleurs, nval, nch);
    C = new controles (D);
    T = new table (D, C);
    C.T = T;
    add (C, BorderLayout.NORTH);
    add (D, BorderLayout.CENTER);
    add (T, BorderLayout.SOUTH);
  }

  public void destroy ()
  { remove (D); remove (C); remove (T); }

  public String getAppletInfo ()
  { return "histosomme par j.-p. Quelen"; }

////////////////////////////////////////////////////////////////////////////////
protected class dessin extends Canvas {
  static final long serialVersionUID = 180131L;
  Image img;
  Graphics g;
  int w, h, hmax, nclasses, nvaleurs, nval, nch;
  int [] histogramme;
  double [] ref;
  double min, max, mult, dv, med, seuil;
  boolean retrace, tok;
  String surl, message;
  donnees don;

  public dessin (double min, double max, int nclasses, int nvaleurs, int nval, int nch)
  { this.min = min;
    this.max = max;
    this.nclasses = nclasses;
    this.nvaleurs = nvaleurs;
    this.nval = nval;
    this.nch = nch;
    don = new donnees (nvaleurs, nval, nch);
    for (int i = 0; i < don.valeurs.length; i ++) don.valeurs [i] = -1.0;
    tok = true;
    mult = 1.0;
//    dv = med = 0.0;
    seuil = 10.0;
    init ();
  }

  private void init ()
  { 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;
      img = createImage (w, h);
      g = img.getGraphics ();
      g.setColor (Color.white);
      g.fillRect (0, 0, w, h);
      if (T != null) T.retrace = true;
    }
    if (retrace)
    { retrace = false;
      hmax = 0;
      g.setColor (Color.white);
      g.fillRect (0, 0, w, h);
      dv = med = 0.0;
      
      for (int i = 0; i < don.valeurs.length; i ++)
      { dv = don.valeurs [i];
        dv *= mult;
        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 ("\u03A3 (fi-pi)^2 = " + dv, 50, 10);
// 03A3 unicode de SIGMA; non reconnu par IE
          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, 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;
      g.setColor (Color.red);
//      xi = (int) ((dvm - min) * w / (max - min));
//      g.drawLine (xi, 0, xi, h);
      xi = (int) ((dvm1 - min) * w / (max - min));
      g.drawLine (xi, 0, xi, h);
      g.setColor (Color.black);
      g.drawString ("seuils : " + dvm + "; " + dvm1, 50, 20);
      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 controles extends Panel implements ActionListener {
  static final long serialVersionUID = 180131L;
  dessin D;
  table T;
  Font f;
  Button nv;
  TextField tnch, tnval, tnvaleurs;
  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);
    return tf;
  }

  public controles (dessin D)
  { this.D = D;
    setBackground (Color.lightGray);
    gbl = new GridBagLayout ();
    gbc = new GridBagConstraints ();
    setLayout (gbl);
    gbc.fill = GridBagConstraints.BOTH;
    gbc.weightx = 1.0;
    f = new Font ("Arial", Font.PLAIN, 10);
    add (tnval = ajouttf (D.nval));
    add (ajoutlbl (" nombres au hasard dans {1, ..., "));
    add (tnch = ajouttf (D.nch));
    add (ajoutlbl (" }; expérience répétée : "));
    add (tnvaleurs = ajouttf (D.nvaleurs));
    add (ajoutlbl (" fois "));
    add (nv = new Button ("simule"));
    nv.setFont (f);
    nv.addActionListener (this);
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    gbl.setConstraints(nv, gbc);
  }

  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) { }
    if (d <= 0.0) d = dd;
    tf.setText (Double.toString(d));
    return d;
  }

  public void ap ()
  { D.nclasses = getvali (T.tnclasses, D.nclasses);
    D.max = getvald (T.tmax, D.max);
    D.mult = getvald (T.tmult, D.mult);
    double d = getvald (T.tseuil, D.seuil);
    if ((d > 0.0) && (d < 100.0)) D.seuil = d;
    else T.tseuil.setText (Double.toString (D.seuil));
    try { T.arrond = Integer.parseInt (T.tfa.getText ()); }
    catch (NumberFormatException nfe) { }
    if (T.arrond >= 0) T.darrond = Math.pow (10.0, T.arrond);
    D.init ();
  }

  public void actionPerformed (ActionEvent e)
  { if (e.getSource () == nv)
    { D.nvaleurs = getvali (tnvaleurs, D.nvaleurs);
      D.nval = getvali (tnval, D.nval);
      int i = getvali (tnch, D.nch);
      if (i > 1) D.nch = i;
      else tnch.setText (Integer.toString (D.nch));
      D.don = new donnees (D.nvaleurs, D.nval, D.nch); // simulation
      ap ();
    }
    D.retrace = T.retrace = true;
    D.repaint ();
    T.repaint ();
  }
}

////////////////////////////////////////////////////////////////////////////////
protected class table extends Panel implements ActionListener {
  static final long serialVersionUID = 180131L;
  TextArea ta;
  dessin D;
  controles C;
  boolean retrace;
  int arrond = 3;
  double darrond = 1000.0;
  TextField tfa, tnclasses, tnch, 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 ajoutd (double d)
  { TextField tf = new TextField (Double.toString(d));
    tf.setFont (f);
    gbl.setConstraints(tf, gbc);
    return tf;
  }

  public table (dessin D, controles C)
  { this.D = D;
    this.C = C;
    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 = C.ajouttf (D.nclasses));
    gbl.setConstraints(tnclasses, gbc);
    add (ajoutlbl (" classes; max "));
    add (tmax = ajoutd (D.max));
    add (ajoutlbl ("; "));
    add (tfa = new TextField ("3"));
    tfa.setFont (f);
    gbl.setConstraints(tfa, gbc);
    add (ajoutlbl (" décimales; d2 multiplié par "));
    add (tmult = ajoutd (D.mult));
    add (ajoutlbl (" seuil "));
    add (tseuil = ajoutd (D.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;
  }

  private String sarrondi (double d)
  { if (arrond > 0) d = Math.floor (d * darrond + 0.5) / darrond;
    return Double.toString (d);
  }

  public void paint (Graphics g)
  { if (retrace)
    { retrace = false;
      String s = "";
      for (int i = 0; i < D.nclasses; i ++) s = s + "[" + sarrondi (D.ref [i]) + ";" + sarrondi (D.min + (D.max - D.min) * (i + 1) / D.nclasses) + "[\t";
//for (int i = 0; i < D.don.valeurs.length; i ++) s += sarrondi (D.don.valeurs [i]) + "\t";
      s = s + "\n";
      for (int i = 0; i < D.nclasses; i ++) s = s + Integer.toString (D.histogramme [i]) + "\t";
      ta.setText (s);
    }
  }

  public void actionPerformed (ActionEvent e)
  { if (e.getSource () == ok)
    { C.ap ();
      D.tok = false;
      D.retrace = T.retrace = true;
      D.repaint ();
      repaint ();
    }
  }
 
}
////////////////////////////////////////////////////////////////////////////////
protected class donnees {
  static final long serialVersionUID = 180131L;
  double [] valeurs;

  public donnees (int n, int nval, int nch)
  { // n : nombre de khi2 simulés
    // nval : taille de l'échantillon
    // nch : loi équirépartie sur {1, ..., nch }
    Random rnd = new Random ();
    valeurs = new double [n];
    int [] histo = new int [nch];
    for (int j = 0; j < n; j ++)
    { for (int i = 0; i < nch; i ++) histo [i] = 0;
      for (int i = 0; i < nval; i ++)
      { double dnch = rnd.nextDouble () * nch;
        int k = 0;
        while ((k < nch) && (dnch > k)) k ++;
        histo [-- k] ++;
      }
      double khi2npi = 0.0;
      double nvalsnch = (double)(nval) / nch;
      for (int i = 0; i < nch; i ++)
      { double d = histo [i] - nvalsnch ;
        khi2npi += d * d;
      }
      valeurs [j] = khi2npi / (nval * nval);
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

  public static void main (String [] args) {
    int w = 600;
    int h = 400;

    histosomme hs = new histosomme ();
    hs.init();
    hs.start();

    Frame f = new Frame ("histosomme");
    f.addWindowListener (new fermer ());
    f.add (hs);
    f.setSize (w, h);
    f.setVisible (true);
  }

  protected static final class fermer extends WindowAdapter {
    public void windowClosing (WindowEvent e) {
      System.exit (0);
    }
  }
}