/* histokhi2n.java
 * 14/05/2000 - jpq
 * modification et ajout de main() le 07/02/18
 */

import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.util.*;

public class histokhi2n extends java.applet.Applet {
  static final long serialVersionUID = 180207L;
  controles C;
  dessin D;
  table T;

  private int gpi (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 gpd (String s, double d) {
    try {
	  s = getParameter (s);
	  d = Double.parseDouble (s);
	}
    catch (NumberFormatException e) {}
	catch (NullPointerException e) {}
    return d;
  }

  public void init ()
  { double min = gpd ("min", 0.0);
    double max = gpd ("max", 50.0);
    int nclasses = gpi ("nclasses", 10);
    int nvaleurs = gpi ("nvaleurs", 100);
    double nmin = gpd ("nmin", -2.0);
    double nmax = gpd ("nmax", 2.0);
    int nval = gpi ("nval", 100);
    int nch = gpi ("nch", 10);
    setLayout (new BorderLayout ());
    D = new dessin (min, max, nclasses, nvaleurs, nmin, nmax, nval, nch);
    T = new table (D);
    C = new controles (D, 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 "histogrammes par j.-p. Quelen"; }

////////////////////////////////////////////////////////////////////////////////
protected class dessin extends Canvas {
  static final long serialVersionUID = 180207L;
  Image img;
  Graphics g;
  int w, h, hmax, nclasses, nvaleurs, nval, nch;
  int [] histogramme;
  double [] ref;
  int arrond = 2;
  double min, max, nmin, nmax;
  double darrond = 100.0;
  boolean retrace;
  String surl, message;
  donnees don;

  public dessin (double min, double max, int nclasses, int nvaleurs, double nmin, double nmax, int nval, int nch)
  { this.min = min;
    this.max = max;
    this.nclasses = nclasses;
    this.nvaleurs = nvaleurs;
    this.nmin = nmin;
    this.nmax = nmax;
    this.nval = nval;
    this.nch = nch;
    don = new donnees (nvaleurs, nmin, nmax, nval, nch);
    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 = true;
  }

  private String sarrondi (double d)
  { if (arrond > 0) d = Math.floor (d * darrond) / darrond;
    return Double.toString (d);
  }

  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 ();
      if (T != null) T.retrace = true;
    }

    if (retrace)
    { retrace = false;
      hmax = 0;
      for (int i = 0; i < don.valeurs.length; i ++)
      { double dv = don.valeurs [i];
        if ((dv >= min) && (dv < max))
        { int k = 0;
          while ((k < nclasses) && (dv > ref [k])) k ++;
          histogramme [-- k] ++;
          if (histogramme [k] > hmax) hmax = histogramme [k];
        }
        g.setColor (Color.white);
        g.fillRect (0, 0, w, h);
        painthisto (g);
        g1.drawImage (img, 0, 0, this);
      }
    }
    g.setColor (Color.yellow);
    int wpas = w / nclasses;
    double hpas = (double)(h - 100) / hmax;
    for (int i = 0; i < nclasses; i ++)
    { int dy = (int)(hpas * don.valeurs.length * don.proba (min + (max - min) * i / nclasses, min + (max - min) * (i + 1) / nclasses));
      g.drawRect (wpas * i, h - dy - 1, wpas, dy + 1);
    }
    painthisto (g);
    g1.drawImage (img, 0, 0, this);
  }
}
////////////////////////////////////////////////////////////////////////////////
protected class controles extends Panel implements ActionListener {
  static final long serialVersionUID = 180207L;
  dessin D;
  table T;
  Font f;
  Button ok, nv;
  TextField tnclasses, tnvaleurs, tnch, tnval, tmax, tnmin, tnmax;

  private Label ajoutlbl (String s)
  { Label lbl = new Label (s);
    lbl.setBackground (Color.lightGray);
    lbl.setFont (f);
    return lbl;
  }

  private TextField ajouttf (int n)
  { TextField tf = new TextField (Integer.toString (n));
    tf.setFont (f);
    return tf;
  }

  private TextField ajouttfd (double d)
  { TextField tf = new TextField (new Double (d).toString ());
    tf.setFont (f);
    return tf;
  }

  private Button ajoutb (String s)
  { Button b = new Button (s);
    b.setFont (f);
    b.addActionListener (this);
    return b;
  }

  public controles (dessin D, table T)
  { this.D = D;
    this.T = T;
    setLayout (new GridLayout(2,1));
    Panel ph, pb;
    add (ph = new Panel ());
    add (pb = new Panel ());
    setBackground (Color.lightGray);
    f = new Font ("Arial", Font.PLAIN, 10);
    ph.add (ajoutlbl ("Nbr. de classes :")); // à ajouter nmin, nmax
    ph.add (tnclasses = ajouttf (D.nclasses));
    ph.add (ajoutlbl ("max :"));
    tmax = new TextField (new Double(D.max).toString());
    tmax.setFont (f);
    ph.add (tmax);
    ph.add (ok = ajoutb ("Ok"));
    ph.add (ajoutlbl ("Nbr. de simulations :"));
    ph.add (tnvaleurs = ajouttf (D.nvaleurs));
    ph.add (nv = ajoutb ("Nouv. Simul."));
    pb.add (tnval = ajouttf (D.nval));
    pb.add (ajoutlbl ("données de loi N [0, 1] réparties dans"));
    pb.add (tnch = ajouttf (D.nch));
    pb.add (ajoutlbl ("classes. de"));
    pb.add (tnmin = ajouttfd (D.nmin));
    pb.add (ajoutlbl ("à"));
    pb.add (tnmax = ajouttfd (D.nmax));
  }

  public void actionPerformed (ActionEvent e)
  { if ((e.getSource () == ok) || (e.getSource () == nv))
    { if (e.getSource () == nv)
      { try { D.nvaleurs = Integer.parseInt (tnvaleurs.getText ()); }
        catch (NumberFormatException nfe) { }
        if (D.nvaleurs <= 0) D.nvaleurs = 1;
        tnvaleurs.setText (Integer.toString (D.nvaleurs));
        try { D.nval = Integer.parseInt (tnval.getText ()); }
        catch (NumberFormatException nfe) { }
        if (D.nval <= 0) D.nval = 1;
        tnval.setText (Integer.toString (D.nval));
        try { D.nch = Integer.parseInt (tnch.getText ()); }
        catch (NumberFormatException nfe) { }
        if (D.nch <= 1) D.nch = 2;
        tnch.setText (Integer.toString (D.nch));
        try { D.nmin = new Double(tnmin.getText()).doubleValue(); }
        catch (NumberFormatException nfe) { }
        try { D.nmax = new Double(tnmax.getText()).doubleValue(); }
        catch (NumberFormatException nfe) { }
        if (D.nmin > D.nmax)
        { double d = D.nmin;
          D.nmin = D.nmax;
          D.nmax = d;
        }
        tnmin.setText (new Double(D.nmin).toString());
        tnmax.setText (new Double(D.nmax).toString());
        D.don = new donnees (D.nvaleurs, D.nmin, D.nmax, D.nval, D.nch);
      }
      try { D.nclasses = Integer.parseInt (tnclasses.getText ()); }
      catch (NumberFormatException nfe) { }
      if (D.nclasses <= 0) D.nclasses = 1;
      tnclasses.setText (Integer.toString (D.nclasses));
      try { D.max = new Double(tmax.getText()).doubleValue(); }
      catch (NumberFormatException nfe) { }
      if (D.max <= 1.0) D.max = 1.0;
      tmax.setText (new Double(D.max).toString());
      D.init ();
    }
    D.retrace = T.retrace = true;
    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 ("", 3, 50, TextArea.SCROLLBARS_HORIZONTAL_ONLY));
    retrace = false;
  }

  public void paint (Graphics g)
  { if (retrace)
    { retrace = false;
      String s = "";
      for (int i = 0; i < D.nclasses; i ++) s = s + "[" + D.sarrondi (D.min + (D.max - D.min) * i / D.nclasses) + ";" + D.sarrondi (D.min + (D.max - D.min) * (i + 1) / D.nclasses) + "[\t";
      s = s + "\n";
      for (int i = 0; i < D.nclasses; i ++) s = s + Integer.toString (D.histogramme [i]) + "\t";
      ta.setText (s);
    }
  }
}
////////////////////////////////////////////////////////////////////////////////
protected class donnees {
  static final long serialVersionUID = 180207L;
  double [] valeurs;
  int [] histo;
  Random rnd;
  int nch, nval, nchm1, histomin, histomax;
  double nmin, nmax;
  boolean b;

  public donnees (int nvaleurs, double nmin, double nmax, int nval, int nch)
  { this.nval = nval;
    this.nch = nch;
    this.nmin = nmin;
    this.nmax = nmax;
    nchm1 = nch - 1;
    rnd = new Random ();
    double d2 = 0.0;
    double pi2 = 2.0 * Math.PI;
    valeurs = new double [nvaleurs];
    histo = new int [nch];
    for (int j = 0; j < nvaleurs; j ++)
    { for (int i = 0; i < nch; i ++) histo [i] = 0;
      histomin = histomax = 0;
      for (int i = 0; i < nval; i ++)
      { double d = 0.0;
        if (b)
        { b = false;
          d = d2;
        }
        else
        { b = true;
          double d1 = Math.sqrt (- 2.0 * Math.log (rnd.nextDouble ()));
          d2 =  pi2 * rnd.nextDouble ();
          d = Math.sin (d2) * d1;
          d2 = Math.cos (d2) * d1;
        }
        if (d < nmin) histomin ++;
        else if (d >= nmax) histomax ++;
        else
        { int k = 0;
          double dmminxnch = (d - nmin) * nch;
          double maxmmin = nmax - nmin;
          while ((k < nch) && (dmminxnch > maxmmin * k)) k ++;
          histo [-- k] ++;
        }
      }
      double dpinval0 = normal (nmin) * nval;
      double d = histomin - dpinval0;
      double khi2 = d * d / dpinval0;
      double dpinval = (double)nval - normal (nmax) * nval;
      d = histomax - dpinval;
      khi2 += d * d / dpinval;
      for (int i = 0; i < nch; i ++)
      { double dpinval1 = normal (nmin + (nmax - nmin) * (i + 1) / nch) * nval;
        dpinval = dpinval1 - dpinval0;
        dpinval0 = dpinval1;
        d = histo [i] - dpinval;
        khi2 += d * d / dpinval;
      }
      valeurs [j] = khi2;
    }
  }

  public double proba (double a, double b)
  { return (a < b) ? fkhi2 (b, nchm1) - fkhi2 (a, nchm1) : 0.0;
  }
}
/**
 * Retourne l'intégrale entre - infini et x de 1 / racine(2 pi) exp (- t^2 / 2)
 * (fonction de répartition de la loi normale)
 */

  public static double normal (double x)
  { if (Math.abs (x) < 0.0000001) return 0.5;
    double h [] = { 0.319382, -0.356564, 1.781478, -1.821256, 1.330274 };
    double y = Math.exp (- x * x / 2.0) * 0.39894228;
    double q = 1.0 / (1.0 + 0.2316419 * Math.abs (x));
    double som = 0.0;
    for (int i = 0; i < 5; i ++)
    { double qi = q;
      for (int j = 0; j < i; j ++) qi *= q;
      som += h [i] * qi;
    }
    return (x >= 0.0) ? 1.0 - y * som : y * som;
  }

/**
 * Retourne l'image de x par la fonction de répartition du khi2 à n degrés de liberté.
 */

  public static double fkhi2 (double x, int n)
  { return fisher (x / n, n, 2147483647); }

/**
 * Retourne l'image de x par la fonction de répartition de la loi de fisher à n1 et n2 degrés de liberté.
 */

  public static double fisher (double x, int n1, int n2)
  { if (x < 0.0000001) return 0.0;
    double f = x;
    double fn1 = (double) n1;
    double fn2 = (double) n2;
    if (x < 1.0)
    { f = 1.0 / x;
      double piv = fn1;
      fn1 = fn2;
      fn2 = piv;
    }
    double a1 = 2.0 / (9.0 * fn1);
    double a2 = 2.0 / (9.0 * fn2);
    double z = (1.0 - a2) * Math.pow (f, 0.33333333) - (1.0 - a1);
    z = z / Math.sqrt (a1 + a2 * Math.pow (f, 0.666667));
    if (n2 <= 3) z = z * (1.0 + 0.08 * z * z * z * z / (fn2 * fn2 * fn2));
    if (Math.abs (z) > 6.0) return (z > 6.0) ? 1.0 : 0.0;
    return (x < 1.0) ? 1.0 - normal (z) : normal (z);
  }

////////////////////////////////////////////////////////////////////////////////

	public static void main (String [] args) {
		histokhi2n a = new histokhi2n();
		a.init ();
		a.start ();

		Frame f = new Frame ("histokhi2n");
		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);
		}
	}

}