package yi.ai.cluster;

/**
 * Clusters is a data structure to store the resultant clusters
 */
public class Clusters implements java.io.Serializable{
  /**
   * The length of data (how many vectors to be clustered)
   */
  int m_dataLength;

  /**
   * The number of data dimension (how long a vector)
   */
  int m_dataDimension;

  /**
   * The resultant cluster number.
   */
  int m_clusterNum;

  /**
   * The means of resultant clusters.  The first dimension is the cluster index;
   * the second one is the data dimension index.  Use m_clusterNum as the
   * resultant cluster number instead using the length of m_clusterMeans because
   * the latter may be longer than the former.
   */
  double[][] m_clusterMeans;

  /**
   * The data->cluster_index map
   */
  int[] m_clusterMap;

  public Clusters(int cluster_num, int data_dim, double[][] means, int data_len, int[] map) {
    m_clusterNum = cluster_num;
    m_dataDimension = data_dim;
    m_clusterMeans = means;

    m_dataLength = data_len;
    m_clusterMap = map;
  }

  public Clusters(int cluster_num, int data_dim, int[][] means, int data_len, int[] map) {
    m_clusterNum = cluster_num;
    m_dataDimension = data_dim;
    m_clusterMeans = new double[cluster_num][data_dim];
    for (int i=0; i<cluster_num; i++) {
      for (int j=0; j<data_dim; j++) {
        m_clusterMeans[i][j] = means[i][j];
      }
    }

    m_dataLength = data_len;
    m_clusterMap = map;
  }

  public int getClusterNum() {
    return m_clusterNum;
  }

  public double[][] getClusterMeans() {
    return m_clusterMeans;
  }

  public int[] getClusterMap() {
    return m_clusterMap;
  }

  public int getDataLength() {
    return m_dataLength;
  }

  public int getDataDimension() {
    return m_dataDimension;
  }

//  public double getScore(int[][] data) {
//    double sum1 = 0, sum2=0;
//    int count1=0, count2=0;
//    for (int i=0; i<m_dataLength; i++) {
//      double deviation = distance(m_clusterMeans[m_clusterMap[i]], data[i]);
//      if (m_clusterMap[i]==0)  {
//        sum1 += deviation * deviation;
//        count1++;
//      }
//      else {
//        sum2 += deviation * deviation;
//        count2++;
//      }
//    }
//    double distance = distance(m_clusterMeans[0], m_clusterMeans[1]);
//    return (Math.sqrt(sum1/count1)+Math.sqrt(sum2/count2))/distance;
//  }
//
//
//  public double getScore(int[][] data) {
//    double sum = 0;
//    for (int i=0; i<m_dataLength; i++) {
//      double deviation = distance(m_clusterMeans[m_clusterMap[i]], data[i]);
//      sum += deviation * deviation;
//    }
//
//    // mean of data
//    double[] mean = new double[m_dataDimension];
//    for (int i=0; i<m_dataLength; i++) {
//      for (int j=0; j<m_dataDimension; j++) {
//        mean[j] += data[i][j];
//      }
//    }
//    for (int j=0; j<m_dataDimension; j++) {
//        mean[j] /= m_dataLength;
//    }
//    // variance
//    double variance = 0;
//    for (int i=0; i<m_dataLength; i++) {
//      double deviation = distance(mean, data[i]);
//      variance += deviation * deviation;
//    }
//    if (variance == 0) return 1;
//    return (sum/variance);
//  }
  public double getScore(int[][] data) {
    double sum = 0;
    for (int i=0; i<m_dataLength; i++) {
      double d1 = distance(m_clusterMeans[0], data[i]);
      double d2 = distance(m_clusterMeans[1], data[i]);
      if (m_clusterMap[i]==0)  {
        sum += d1 / d2;
      }
      else {
        sum += d2 / d1;
      }
    }
    sum /= m_dataLength;
    return sum;
  }

  /**
   * Return the distance of two vectors
   * @param v1
   * @param v2
   * @return the distance
   */
  double distance(double[] v1, int[] v2) {
    double dist = 0;
    for (int i=0; i<v1.length; i++) {
      double d= v1[i] - v2[i];
      dist += d*d;
    }
    return Math.sqrt(dist);
  }

  double distance(double[] v1, double[] v2) {
    double dist = 0;
    for (int i=0; i<v1.length; i++) {
      double d= v1[i] - v2[i];
      dist += d*d;
    }
    return Math.sqrt(dist);
  }
}