/**
   * Constructor. The Gaussian mixture model will be learned from the given data with the EM
   * algorithm. The number of components will be selected by BIC.
   *
   * @param data the training data.
   */
  @SuppressWarnings("unchecked")
  public GaussianMixture(double[] data) {
    if (data.length < 20) throw new IllegalArgumentException("Too few samples.");

    ArrayList<Component> mixture = new ArrayList<Component>();
    Component c = new Component();
    c.priori = 1.0;
    c.distribution = new GaussianDistribution(data);
    mixture.add(c);

    int freedom = 0;
    for (int i = 0; i < mixture.size(); i++) freedom += mixture.get(i).distribution.npara();

    double bic = 0.0;
    for (double x : data) {
      double p = c.distribution.p(x);
      if (p > 0) bic += Math.log(p);
    }
    bic -= 0.5 * freedom * Math.log(data.length);

    double b = Double.NEGATIVE_INFINITY;
    while (bic > b) {
      b = bic;
      components = (ArrayList<Component>) mixture.clone();

      split(mixture);
      bic = EM(mixture, data);

      freedom = 0;
      for (int i = 0; i < mixture.size(); i++) freedom += mixture.get(i).distribution.npara();

      bic -= 0.5 * freedom * Math.log(data.length);
    }
  }