package yi.stat.hist;

/**
 * Class WeightedHistogram implements the histograms that each data may
 * contribute a weight (the weight is always 1 in the SimpleHistogram).  For
 * example, data {1, 1, 2} with weight {1.5, 2.5, 4.5} makes a histogram of
 * range {4, 4.5}
 */
public class WeightedHistogram extends SimpleHistogram {
  /**
   * The weighted histogram
   */
  double [] m_weightedHistogram;

  /**
   * Weighted total
   */
  double m_weightedTotal = 0;

  /**
   * Create a histogram with bin_num empty bins.
   * @param bin_num
   */
  public WeightedHistogram(int bin_num) {
    this(0, bin_num - 1);
  }

  /**
   * Create a histogram with the min value and max value specified
   * @param min the minimum value of the data (inclusive)
   * @param max the maximum value of the data (inclusive)
   */
  public WeightedHistogram(int min, int max) {
    super(min, max);
    m_weightedTotal  = 0;
    m_weightedHistogram = new double[m_binNum];
  }

  /**
   * Create a histogram with the data specified.  The minimum and maximum data
   * value is decided by the current data.  If later, bigger or smaller data
   * is added to the histogram, they will be put into the last or the first bin.
   * So this constructor is suitable for the situation that you have all the
   * data already
   * @param data the data to be put into the histogram
   */
  public WeightedHistogram(int[] data, double[] weights) {
    this(yi.stat.StatUtil.minimum(data), yi.stat.StatUtil.maximum(data));
    putToBin(data, weights);
  }

  /**
   * Put data value into the histogram.  If the value is greater than the
   * pre-defined maximum, it will be put into the last bin; if it is less than
   * the pre-define minmum, it will be put into bin #0.
   * @param value the value of the data to be put into the histogram
   */
  public void putToBin(int value, double weight){
    int bin_index = value - m_minValue;
    if (bin_index >= m_binNum) bin_index = m_binNum - 1;
    else if (bin_index < 0) bin_index = 0;
    m_histogram[bin_index] ++;
    m_total ++;
    m_weightedHistogram[bin_index] += weight;
    m_weightedTotal += weight;
  }

  /**
   * Put data values into the histogram.  If a value is greater than the
   * pre-defined maximum, it will be put into the last bin; if it is less than
   * the pre-define minmum, it will be put into bin #0.
   * @param values the values of the data to be put into the histogram
   */
  public void putToBin(int[] values, double[] weights){
    for (int i=0; i<values.length; i++) putToBin(values[i], weights[i]);
  }

  /**
   * Get the weighted value in the bin specified by bin_index.
   * @param bin_index the bin index
   * @return the value in the bin
   * @throws OutOfHistRangeException
  */
  public double getWeightBin(int bin_index) throws OutOfHistRangeException{
    if (bin_index >= m_binNum) {
      throw new OutOfHistRangeException("Out of Histogram Range: "
        + "bin# = " + m_binNum + "; requested = " + bin_index);
    }
    return m_weightedHistogram[bin_index];
  }

  /**
   * Return the weighted total in the histogram
   * @return the number
   */
  public double getWeightedTotal() {
    return m_weightedTotal;
  }

  public String toString() {
    StringBuffer str = new StringBuffer();
    int[] tags = getBinTags();
    for (int i=0;i<tags.length; i++) {
      if (i != 0) str.append(",");
      str.append("[" + tags[i] + "]=" + m_weightedHistogram[i]);
    }
    return str.toString();
  }

  /**
   * Return the weighted histogram
   * @return the weighted histogram
   */
  public double[] getWeightedHistogram() {
    return m_weightedHistogram;
  }

  public void output(String file_name_root) {

    double[][] hist_data = new double[][] {
        yi.util.data.Conversion.toDouble(getBinTags()),
        m_weightedHistogram,
        yi.util.data.Conversion.toDouble(m_histogram)
    };

    hist_data = yi.util.data.Conversion.swapXandY(hist_data, m_binNum, 3);
    yi.util.io.OutputResult.print(file_name_root, hist_data, 3, m_binNum);
  }
}