package yi.iu.imgdb.learn;

import java.io.*;
import java.text.*;
import java.util.*;

import java.awt.*;

import org.jmat.gui.*;
import yi.ds.*;
import yi.ds.fold.*;
import yi.ds.hash.*;
import yi.ds.sort.*;
import yi.stat.*;
import yi.stat.em.*;
import yi.stat.nn.*;
import yi.util.config.*;
import yi.util.data.*;
import yi.util.io.*;
import yi.util.io.input.*;
import yi.iu.processor.resample.*;
import yi.ds.hd.*;
import yi.iu.imgdb.desc.*;
import yi.util.*;
import yi.iu.processor.clc.*;
import yi.graph.polygon.*;

/**
 * Modelize each object as a Gaussian Mixture Model.
 */
public class GMMNNAdapter
    extends StatModelAdapter implements VerificationErrorProvider{

  /**
   * HashMap of GaussianMixtureModel.  Each entry corresponds to one (feature, object)
   */
  HashMapHashMap m_gmmModels = new HashMapHashMap();

  /**
   * To remember the smallest value on each component of a gmm.  Later in
   * testing, if there is a component got no entry, use this smallest value
   * instead.  This is to replace using a fixed small value (eg. -1000 for log
   * likelihood) which can be sth not good.
   * In each entry, the key is the object name and feature name and the value
   * is the uniformed distribution density in that feature space, which is
   * used as the minimum allowed probability on a distribution.
   */
  HashMapHashMap m_gmmUniformDensity = new HashMapHashMap();

  /**
   * HashMap of Neural Nets.  Each entry corresponds to one object
   */
  HashMap m_nnModels = new HashMap();

  /**
   * Save the NN training verification error for each object;
   */
  DoubleScoreHashMap m_nnVerifyErrors = new DoubleScoreHashMap();

  /**
   * When draw the distribution, how many of points will be sampled.
   */
  public int DISTRIBUTION_DRAW_SLICE_NUM = 100;

  /**
   * Whether to show the compaison of data histogram and model distribution
   * one plot
   */
  public boolean SHOW_DATA_MODEL_PLOT = false;

  /**
   * output the NN training and testing data for NN tuning outside
   */
  public boolean OUTPUT_NN_DATA = false;

  /**
   * output the gmm results in the format to use svmlib
   */
  public boolean OUTPUT_NN_FOR_SVM = false;

  /**
   * How many Gaussians per GMM
   */
  public int M = 8;
  /**
   * Try how many times of GMM to pick up the best
   */
  public int GMM_MODEL_TRIES = 1;
  /**
       * When construct a GMM model, the EM will be restarted several times and this
   * is the retry number
   */
  public int GMM_CONSTR_RETRIES = 5;

  /**
   * GMM training break threshold
   */
  public double GMM_BREAK_THRESHOLD = 1e-4;


  /**
   * hidden variable number for the nn
   */
  public int NN_HIDDEN_VAR_NUM = 8;

  /**
   * NN converge rate
   */
  public double NN_RATE = 0.02;

  /**
   * NN training iterations (currently use a fixed iteration numbers, later on,
   * this can be changed)
   */
  public int NN_ITERATIONS = 200;

  /**
   * Maximum NN training iteration
   */
  public int MAX_NN_ITERATION = 2500;

  /**
   * GMM EM will be initialized by k-means.  k-means restarts several times and
   * the best initial will be used.
   */
  public int GMM_KMEAN_INIT_RESTARTS = 40;
  /**
   * GMM EM will be initialized by k-means.  k-means will only run several
   * iterations before the intermediate results will be used to init EM
   */
  public int GMM_KMEAN_INIT_ITERATIONS = 5;
//  /**
//   * When a score on a Gaussian component is close to 0, its log likelihood
//   * will be set to this value
//   */
//  public static double SMALLEST_LOG_P = -1000; // max double 2^1023 ~= 9e307

  public final static int MEAN_COMBINING_FUNC = 0;
  public final static int MAX_COMBINING_FUNC = 1;
  public final static int PROB_COMBINING_FUNC = 2;

  /**
   * Combining function for
   */
  public int COMBINING_FUNC = MEAN_COMBINING_FUNC;
  public boolean COMBINING_USING_LOG = true;

  public final static int LOCATING_LEAVE_ONE_OUT = 0;
  public final static int LOCATING_ADD_ONE_IN = 1;
  public final static int LOCATING_ADD_ONE_MORE = 2;

  public int LOCATING = LOCATING_LEAVE_ONE_OUT;

  public double VERIFICATION_PORTION = 1.0/3.0;

  static final long serialVersionUID = 5095849325873648238L;

  public String[] getParameterNames() {
     String[] parameters = new String[] {
         // io paarameters
         "OUTPUT_NN_DATA",
         "OUTPUT_NN_FOR_SVM",

         // for construct GMM
         "M",
         "GMM_MODEL_TRIES",
         "GMM_CONSTR_RETRIES",
         "GMM_BREAK_THRESHOLD",
         "GMM_KMEAN_INIT_RESTARTS",
         "GMM_KMEAN_INIT_ITERATIONS",

         // for construct NN
         "NN_HIDDEN_VAR_NUM",
         "NN_RATE",
         "NN_ITERATIONS",
         "MAX_NN_ITERATION",
         "VERIFICATION_PORTION",

         // combining function
         "COMBINING_FUNC",
         "COMBINING_USING_LOG",

         "OUTPUT_NN_DATA"
     };
     String[] parent_param = super.getParameterNames();
     return Configuration.combineParameterNames(parameters, parent_param);
  }

  /**
   * @param data_center
   * @param features
   * @param imgs
   */
  public GMMNNAdapter(ImgDataCenter data_center, LinkedList img_list,
                      LinkedList ft_list, LinkedList obj_list) {
    super(data_center, img_list, ft_list, obj_list);
    Configuration.configParameters(this);

    System.out.println("M = " + M);
    System.out.println("GMM_BREAK_THRESHOLD = " + GMM_BREAK_THRESHOLD);
    System.out.println("GMM_MODEL_TRIES = " + GMM_MODEL_TRIES);
    System.out.println("GMM_CONSTR_RETRIES = " + GMM_CONSTR_RETRIES);
    System.out.println("GMM_KMEAN_INIT_RESTARTS = " + GMM_KMEAN_INIT_RESTARTS);
    System.out.println("GMM_KMEAN_INIT_ITERATIONS = " + GMM_KMEAN_INIT_ITERATIONS);
    System.out.println("COMBINING_FUNC = " + ((COMBINING_FUNC==0)?"MEAN_COMBINING_FUNC":((COMBINING_FUNC==1)?"MAX_COMBINING_FUNC":"PROB_COMBINING_FUNC")));
    System.out.println("COMBINING_USING_LOG = " + COMBINING_USING_LOG);

    learn(data_center, img_list, ft_list, obj_list);
  }

  /**
   * Learn the GMM-NN models
   * @param data_center
   * @param img_list
   * @param ft_list
   * @param obj_list
   */
  public void learn(ImgDataCenter data_center, LinkedList img_list,
                      LinkedList ft_list, LinkedList obj_list) {

    // remove "bg" since it is not useful here
    for (Iterator o = m_objectList.iterator(); o.hasNext(); ) {
      String object = (String) o.next();
      if (object.compareTo("bg") == 0) {
        o.remove();
      }
    }
    System.out.println("objects to learn: ");
    OutputResult.print(System.out, m_objectList);

    // learn the objects one by one
    for (Iterator o = m_objectList.iterator(); o.hasNext(); ) {
      String object = (String) o.next();

      System.out.println();
      System.out.println("learning on \"" + object + "\"" + "\n");

      // seperate training set and verifying set
      LinkedList posTrainImgs = new LinkedList();
      LinkedList negTrainImgs = new LinkedList();
      seperatePosImgsFromNegImgs(img_list, object, posTrainImgs, negTrainImgs);

      // for each feature, learn a gmm
      for (Iterator f = m_featureList.iterator(); f.hasNext(); ) {
        String feature = (String)f.next();
        System.out.println("using feature \"" + feature + "\"" + "\n");
        try {
          learnGaussianMixtureModelOnFeature(object, feature, posTrainImgs, M);
          GaussianMixtureModel gmm = (GaussianMixtureModel) m_gmmModels.get(
              object, feature);
//          double[][] pos_scores_on_components = scoresOnComponents(gmm, posTrainImgs, feature, object, true);
//          double[] pos_scores_mean = yi.stat.StatUtil.meanOnCol(pos_scores_on_components);
//          System.out.println("pos score on components (mean)");
//          OutputResult.print(System.out, pos_scores_mean);
//          double[][] neg_scores_on_components = scoresOnComponents(gmm, negTrainImgs, feature, object, true);
//          double[] neg_scores_mean = yi.stat.StatUtil.meanOnCol(neg_scores_on_components);
//          System.out.println("neg score on components (mean)");
//          OutputResult.print(System.out, neg_scores_mean);
//          boolean removed = false;
//          for (int i=0; i<pos_scores_mean.length; i++) {
//            if (pos_scores_mean[i] < neg_scores_mean[i]) {
//              System.out.println("component " + i + " removed");
//              gmm.removeComponent(i);
//              removed = true;
//            }
//          }
//          if (removed) {
//            gmm.removeEmptyClusterIndex();
//          }


//          int comp_num = gmm.getRstNumComponents();
//          double[][] bi_prob_sum = new double[comp_num][comp_num];
//          double[] prob_sum = new double[comp_num];
//          for (int img = 0; img<pos_scores_on_components.length; img++) {
//            double[] log_probs = pos_scores_on_components[img];
//            for (int i = 0; i < comp_num - 1; i++) {
//              for (int j = i + 1; j < comp_num; j++) {
//                bi_prob_sum[i][j] += Math.exp(log_probs[i] + log_probs[j]);
//              }
//            }
//            for (int i = 0; i < comp_num; i++) {
//              prob_sum[i] += Math.exp(log_probs[i]);
//            }
//          }
//          // average
//          for (int i = 0; i < comp_num - 1; i++) {
//            for (int j = i + 1; j < comp_num; j++) {
//              bi_prob_sum[i][j] = bi_prob_sum[i][j]/posTrainImgs.size();
//            }
//          }
//          for (int i = 0; i < comp_num; i++) {
//            prob_sum[i] = prob_sum[i]/posTrainImgs.size();
//          }
//          // print the ratio
//          // average
//          for (int i = 0; i < comp_num - 1; i++) {
//            for (int j = i + 1; j < comp_num; j++) {
//              System.out.println("(" + i + "," + j + ")" + bi_prob_sum[i][j] + "/" +
//                                 prob_sum[i] + "/" + prob_sum[j] + "=" +
//                                 (bi_prob_sum[i][j]/prob_sum[i]/prob_sum[j]));
//            }
//          }
        }
        catch (ModelSingularException e) {
          e.printStackTrace();
        }

      }

      constructAbstractRegionModel(object, img_list);
      double[][] pos_scores_concat = getFeatureVectors(posTrainImgs, object, null);
      double[][] neg_scores_concat = getFeatureVectors(negTrainImgs, object, null);

      if (OUTPUT_NN_DATA) {
        System.out.println("score on pos writen to " + object + ".pos.dat");
        yi.util.io.OutputResult.print(object.toString() + ".pos.dat",
                                      pos_scores_concat,
                                      pos_scores_concat[0].length,
                                      pos_scores_concat.length);
        System.out.println("score on neg writen to " + object + ".neg.dat");
        yi.util.io.OutputResult.print(object.toString() + ".neg.dat",
                                      neg_scores_concat,
                                      pos_scores_concat[0].length,
                                      neg_scores_concat.length);
      }
      if (OUTPUT_NN_FOR_SVM) {
        PrintStream out = OutputResult.getPrintStream(object + ".train");
        if (out != null) {
          for (int i = 0; i < pos_scores_concat.length; i++) {
            out.print("+1");
            for (int j = 0; j < pos_scores_concat[i].length; j++) {
              out.print(" " + (j + 1) + ":" + pos_scores_concat[i][j]);
            }
            out.println();
          }
          for (int i = 0; i < neg_scores_concat.length; i++) {
            out.print("-1");
            for (int j = 0; j < neg_scores_concat[i].length; j++) {
              out.print(" " + (j + 1) + ":" + neg_scores_concat[i][j]);
            }
            out.println();
          }
          out.close();
        }
      }

      // for experiemnt
      LinkedList test_img = StatModelFoldExperiment.testing;
      double[][] pos_test_scores = null;
      double[][] neg_test_scores = null;
      if (test_img != null) {
        constructAbstractRegionFeatures(object, test_img);
        LinkedList posTestImgs = new LinkedList();
        LinkedList negTestImgs = new LinkedList();
        seperatePosImgsFromNegImgs(test_img, object, posTestImgs, negTestImgs);
        pos_test_scores = getFeatureVectors(posTestImgs, object, null);
        neg_test_scores = getFeatureVectors(negTestImgs, object, null);
      }

      NeuralNet nn = trainNN(pos_scores_concat, neg_scores_concat, pos_test_scores, neg_test_scores);
      m_nnModels.put(object, nn);
      m_nnVerifyErrors.putScore(object, nn.getVerifyError());

      if (FIND_THRESHOLD) {
        m_thresholds.put(object, new Double(m_foundThreshold));
      }

    } // end loop of objects
  }


  /**
   * Construct abstract region features
   * @param object
   * @param img_list
   */
  public void constructAbstractRegionFeatures(String object, LinkedList img_list) {

  }

  /**
   * Construct abstract region feature mixture models
   * @param data_center
   * @param object
   * @param img_list
   */
  public void constructAbstractRegionModel(String object, LinkedList img_list) {

  }


  /**
   * return HashMap of GaussianMixtureModel.  Each entry corresponds to one
   * (feature, object)
   * @return
   */
  public HashMapHashMap getGMMModels() {
    return m_gmmModels;
  }

  /**
   * return HashMap of Neural Nets.  Each entry corresponds to one object
   */
  public HashMap getNNModels() {
    return m_nnModels;
  }

  public void putNNModel(String object, NeuralNet nn) {
    m_nnModels.put(object, nn);
  }

  public HashMapHashMap getMinComponentProbs() {
    return m_gmmUniformDensity;
  }

  /**
   * Return the log(max prob) on each component.  Basically, the mean should
   * get the highest prob.
   * @param obj
   * @param feature
   * @return
   */
  public double[] getLogMaxComponentProbs(String obj, String feature, boolean use_component_weight) throws GaussianModelSingularException{
    GaussianMixtureModel gmm = (GaussianMixtureModel) m_gmmModels.get(
        obj, feature);
    ArrayList keys = new ArrayList(gmm.getRstComponentKeys());
    java.util.Collections.sort(keys);

    double[] highest_log_p = new double[gmm.getRstNumComponents()];
    int j_index = 0;
    for (Iterator j = keys.iterator(); j.hasNext(); j_index++) {
      Object key = j.next();
      GaussianComponent gc = gmm.getGaussianComponent(key);
      double highest_p = gc.getMaxProb();
      if (use_component_weight)
        highest_p *= gmm.getWeight(key);
      highest_log_p[j_index] = Math.log(highest_p);
    }
    assert(j_index == highest_log_p.length);
    return highest_log_p;
  }

  /**
   * Concatenate two 2D double arrays
   * @param m1
   * @param m2
   * @return
   */
  protected double[][] concatenate(double[][] m1, double[][] m2, int rows) {
    int col1 = (m1 == null || m1[0] == null) ? 0 : m1[0].length;
    int col2 = (m2 == null || m2[0] == null) ? 0 : m2[0].length;
    double[][] concat = new double[rows][col1 + col2];
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < col1; j++) {
        concat[i][j] = m1[i][j];
      }
      for (int j = 0; j < col2; j++) {
        concat[i][col1 + j] = m2[i][j];
      }
    }
    return concat;
  }

  /**
   * add the distribution (each one is 1D) of current model to the passed in
   * linked list
   */
  void calculateDistributions(
      GaussianMixtureModel gmm, LinkedList[] ffs,
      double[] mins, double[] maxs, int slice_num) throws
      GaussianModelSingularException {
    for (int i = 0; i < ffs.length; i++) {
      double[] ff = gmm.getOneDFrequencyFunction(i, mins[i], maxs[i], slice_num);
      if (ffs[i] == null)
        ffs[i] = new LinkedList();
      ffs[i].add(ff);
    }
  }

  /**
   * Filter the training data.  This method can be overrided by abstract
   * region learner to erease some non-promising regions
   * @param data
   * @param object
   * @param feature
   * @param posTrainImgs
   * @return
   */
  double[][] filterTrainingData(double[][] data, String object, String feature, LinkedList posTrainImgs) {
    return data;
  }

  /**
   * Learn the Gaussian Mixture model for the given object and given feature
   * from the given images.
   * The learned gmm model will be added into m_gmmModels and uniformed
   * distribution will be added into m_gmmMinComponentProbs as the minimum
   * allowed density for each component
   * @param object
   * @param feature
   * @param posTrainImgs
   * @param m number of mixture component
   * @throws ModelSingularException
   */
  void learnGaussianMixtureModelOnFeature(String object, String feature,
                                          LinkedList posTrainImgs, int m) throws
      ModelSingularException {

    int feature_len = m_imgDataCenter.getFeatureLength(feature);
    System.out.println("feature length = " + feature_len);

    // group the feature vectors in the positive ones to construct positive model
    double[][] data = m_imgDataCenter.groupFeatureVectors(posTrainImgs,
        feature);
    System.out.println("total positive training feature vectors #= " +
                       data.length + "\n");

    // filter the data
    data = filterTrainingData(data, object, feature, posTrainImgs);
    if (data.length == 0) {
      System.err.println("no data to learn the Gaussian Mixture Model");
      return;
    }

    // the uniformed distribution density
    double uniform_dist = yi.stat.StatUtil.getUniformDistributionDensity(data);
    System.out.println("uniform distribution density = " + uniform_dist);

//      OutputResult.print(System.out, data);
//      Matrix matrix = new Matrix(data);
//      Plot3DPanel pp = new Plot3DPanel();
//      matrix.toPlot3DPanel(pp, "hsb", "SCATTER");
//      new ExistablePlotViewer("ROC", pp);

    /** @todo change the feature length to show plot */
    // for SHOW_DISTRIBUTION, save the frequency functions
    // each entry is for one dimension.  Each entry of the linked list
    // is the one for a specified m (as in m mixture models)
    LinkedList[] ffs = new LinkedList[feature_len];

    double[] max_data = yi.stat.StatUtil.maximumOnCol(data);
    double[] min_data = yi.stat.StatUtil.minimumOnCol(data);
    /** @todo see whether m can be fixed; or a big m is enough */
    GaussianMixtureModel best_gmm = learnGaussianMixtureModelOnData(
        m, data, min_data, max_data, GMM_MODEL_TRIES, ffs);

    m_gmmModels.put(object, feature, best_gmm);
    System.out.println("learned GMM has " + best_gmm.getRstNumComponents() +
                       " components");

//          // output the distributions
//          for (int i = 0; i < ffs.length; i++) {
//            double[][] ff_w_m = yi.util.data.Conversion.to2DDoubleArray(ffs[i]);
//            OutputResult.outputResult("distributions", ff_w_m);
//          }

    // draw the distributions fro each dimension of the feature
    if (SHOW_DATA_MODEL_PLOT) {
      System.out.println("object model: " + best_gmm);
      for (int i = 0; i < ffs.length; i++) {
        Plot2DPanel pp = best_gmm.showDataModelOnPanel(ffs[i], i,
            min_data[i],
            max_data[i],
            DISTRIBUTION_DRAW_SLICE_NUM);
        new FrameView("obj_D" + i, pp);
      }
    }

//    // use uniform distribution probability as the minimum possilbe prob
//    double[] minimums = new double[best_gmm.getRstNumComponents()];
//    Arrays.fill(minimums, uniform_dist);

//    double[] minimums = best_gmm.getRstComponentMinProb(true);
//    // relax the limitation a little bit

//    double[] minimums = best_gmm.getRstComponentMinProbToCoverPortionData(1, true);

    System.out.println("use uniformed distribution density as the minimum: " + uniform_dist);

    m_gmmUniformDensity.put(object, feature, new Double(uniform_dist));

  }

  /**
   * Learn the object model.  This method try create Gaussian Mixture Model
   * "tries" times and return the best one
   * @param m number of Gaussian components
   * @param data data to learn the model
   * @param min_data minimum value on each data dimension (used for scaling
   *                 the data when using k-means for initialization)
   * @param max_data maximum value on each data dimension (used for scaling
   *                 the data when using k-means for initialization)
   * @param tries how many time of tries to construct Gaussian Mixture Model.
   *              The best one will be returned.
   * @param ffs the constructed distribution for each dimension (frequency functions)
   * @return
   * @throws yi.stat.em.ModelSingularException
   */
  GaussianMixtureModel learnGaussianMixtureModelOnData(int m, double[][] data, double[] min_data, double[] max_data, int tries,
                                        LinkedList[] ffs) throws yi.stat.em.ModelSingularException{
    double last_score = -Double.MAX_VALUE;
    GaussianMixtureModel best_gmm = null;

    // create a sigular fixer, it use the whole data to generate the fake
    // covariance matrix entries (to replace zeros)
    double[] fake_cm = yi.stat.StatUtil.varianceOnCol(data);
    SingularGaussianModelFixable fixer = new SingularGaussianModelConstantFixer(fake_cm);

    // increase kernels
    for (int t = 0; t < tries; t++) {

      System.out.println("\ngmm try # = " + (t+1));
      System.out.println("kernel # = " + m + "\n");


//      // training
//      GaussianMixtureModel gmm = new GaussianMixtureModel(m, data, null);
//      // GaussianMixtureModel gmm = new AlignedGaussianMixtureModel(m, data, min_data, max_data); // suppose the data is within [0,1)
//      gmm.BREAK_THRESHOLD = GMM_BREAK_THRESHOLD;
//      gmm.learnTheModel();

      // construct the scales of each dimension.  The scales are made that each
      // dimension will contribute the same to the Euclidean distance between
      // data points
      double[] scale = new double[max_data.length];
      for (int i=0; i<scale.length; i++) {
        scale[i] = 1 / (max_data[i] - min_data[i]);
      }
      GaussianMixtureModel gmm = (new GaussianMixtureModelFactory()).createGaussianMixtureModel(
          m, data, scale, GMM_CONSTR_RETRIES, GMM_BREAK_THRESHOLD,
          GMM_KMEAN_INIT_RESTARTS, GMM_KMEAN_INIT_ITERATIONS, 1.2, fixer);
//      GaussianMixtureModel gmm = (new AlignedGaussianMixtureModelFactory()).createGaussianMixtureModel(
//          m, data, scale, GMM_CONSTR_RETRIES, GMM_BREAK_THRESHOLD,
//          GMM_KMEAN_INIT_RESTARTS, GMM_KMEAN_INIT_ITERATIONS, 1.2, fixer);
//      GaussianMixtureModel gmm = (new AlignedGaussianMixtureModelFactory()).createSplittingAndVanishingGaussianMixtureModel(
//          m, data, scale, GMM_CONSTR_RETRIES, GMM_BREAK_THRESHOLD,
//          GMM_KMEAN_INIT_RESTARTS, GMM_KMEAN_INIT_ITERATIONS, 1.2, fixer,
//          2, 2);

      // verify on positive cases
      double total_score =   gmm.getParameterLogLikelihood(data);

      System.out.println("\n" + "gmm likelihood = " + total_score);

      // get the distribution of current model
      try {
        /** @todo the min and max of plots */
        calculateDistributions(gmm, ffs, min_data, max_data, DISTRIBUTION_DRAW_SLICE_NUM);
      } catch (Exception e) {
        e.printStackTrace();
      }

      // compare the current score to the last one.  If not better, just quit
      if (total_score > last_score) {
        last_score = total_score;
        best_gmm = gmm;
      }
    } // end of loop of m (mixture model number)

    System.out.println("\n" + "use best gmm with likelihood = " + last_score);

    // remove empty cluster index (since some Gaussian component may be removed
    // in the training stage)
    best_gmm.removeEmptyClusterIndex();

    return best_gmm;
  }


//  /**
//   * Get the log likelihood
//   * @param gmm
//   * @param img
//   * @param feature
//   * @return
//   */
//  double score(GaussianMixtureModel gmm, String img, String feature) {
//    // get the feature vectors
//    FeatureRepresentationList features = m_imgDataCenter.getImgFeatureList(
//        img, // image name
//        feature);
//    double[][] data = features.as2DDoubleArray();
//
//    try {
//      return gmm.getParameterLogLikelihood(data);
//    } catch (ModelSingularException e) {
//      return 0;
//    }
//  }


//  /**
//   * Return the score or probability of having those mixture components for
//   * the given image for the given feature
//   * @param gmm
//   * @param images
//   * @param feature
//   * @param fix_low_prob change low prob (like prob(0) to the smallest
//   *        valid probability finded in the training stage
//   * @return
//   */
//  double[] scoreOnComponents(GaussianMixtureModel gmm, String img, String feature, String obj, boolean fix_low_prob) {
//    // get the feature vectors
//    FeatureRepresentationList features = m_imgDataCenter.getImgFeatureList(
//        img, // image name
//        feature);
//
//    // if the image doesn't has any feature vector for the feature, then the
//    // score for the components should be all 0
//    if (features.size() == 0) {
//      double[] scores = new double[gmm.getRstNumComponents()];
//      java.util.Arrays.fill(scores, Double.NEGATIVE_INFINITY);
//      return scores;
//    }
//
//    double[][] data = features.as2DDoubleArray();
//    double[] log_mins = (double[])m_gmmMinComponentProbs.get(obj, feature);
//
//    yi.ds.hash.DoubleScoreHashMap score_map = new yi.ds.hash.DoubleScoreHashMap();
//    for (int i=0; i<data.length; i++) {
//      double[] d = data[i];
//      HashMap scores = gmm.getDensitiesUnderComponents(d, false, false);
//      score_map.putScoreWhenHigher(scores);
//    }
//
//    // print out the probabilities under different components
//    double[] scores = new double[score_map.size()];
//    ArrayList keys = new ArrayList(score_map.keySet());
//    java.util.Collections.sort(keys);
//    int i_index = 0;
//    for (Iterator i = keys.iterator(); i.hasNext(); i_index++) {
//      Object key = i.next();
//      double p = score_map.getScore(key);
//      scores[i_index] = (p==0)?Double.NEGATIVE_INFINITY:Math.log(p);
//    }
//    if (i_index != scores.length) {
//      System.err.println("log score length = " + i_index + ", less than " + scores.length);
//    }
//
//
//    // fixing the low probability
//    for (int k=0; k<scores.length; k++) {
//      if (scores[k] >= log_mins[k]) {
//        scores[k] -= log_mins[k];
//      } else {
//        scores[k] = 0;
//      }
//    }
//
//    return scores;
//
//  }


  /**
   * Return the score or probability of having those mixture components for
   * the given image for the given feature.  Flag COMBINING_FUNC determines
   * whether to use the average probability of all the data on a Gaussian
   * component as the probability of the whole image
   * p(I|component)=(\prod(p(r|component)))^(1/|r|), in which
   * I is the image, r is the regions and |r| is the number of all the regions
   * or use max probability of all the data ...
   * It will fix the low probability anyway even the pass-in parameter
   * "fix_low_prob" is false.
   * @param gmm
   * @param images
   * @param feature
   * @param fix_low_prob deprecated.
   * @return
   */
  double[] scoreOnComponents(GaussianMixtureModel gmm, String img, String feature, String obj) {

    int n_comp = gmm.getRstNumComponents();
    double[] scores = new double[n_comp];
    double[][] scores_data = null;


    if (COMBINING_FUNC == PROB_COMBINING_FUNC) {
      scores_data = featureScoresOnComponents(gmm, img, feature, obj, true);
      // get the 1-\Prod_{p_i}(1-p_i)
      for (int k = 0; k < scores.length; k++) {
        if (scores_data.length == 0) {
          scores[k] = 0;
        } else {
          double p = 1;
          for (int i = 0; i < scores_data.length; i++) {
            p *= (1 - scores_data[i][k]);
          }
          scores[k] = 1 - p;
        }
      }
      return scores;
    }

    double minimum = ((Double)m_gmmUniformDensity.get(obj, feature)).doubleValue();
    scores_data = featureScoresOnComponents(gmm, img, feature, obj, false);

    if (COMBINING_FUNC == MEAN_COMBINING_FUNC) {
      // get the mean score on each Gaussian component
      for (int i = 0; i < scores_data.length; i++) {
        for (int k = 0; k < scores_data[i].length; k++) {
          scores[k] += scores_data[i][k];
        }
      }
      for (int k = 0; k < scores.length; k++) {
        if (scores_data.length == 0) {
          scores[k] = minimum;
        } else {
          scores[k] /= scores_data.length;
        }
      }
    } else { // COMBINING_FUNC == MAX_COMBINING_FUNC
      if (scores_data.length == 0) {
        for (int k = 0; k < scores.length; k++) {
          scores[k] = minimum;
        }
      } else {
        // scores are 0 at the beginning (probability can't be < 0)
        for (int i = 0; i < scores_data.length; i++) {
          for (int k = 0; k < scores_data[i].length; k++) {
            if (scores[k] < scores_data[i][k]) {
              scores[k] = scores_data[i][k];
            }
          }
        }
      }
    }
//    OutputResult.print("score_comp.dat", scores_data);
//    OutputResult.print("score_comb.dat", scores);
    // log(mean)
    if (COMBINING_USING_LOG) {
      for (int k = 0; k < scores.length; k++) {
        scores[k] = Math.log(scores[k]);
      }
    }
//    OutputResult.print("score_comb_log.dat", scores);

    return scores;

  }

  /**
   * Return the scores on each component for each feature
   * @param gmm
   * @param img
   * @param feature
   * @param obj
   * @return
   */
  double[][] featureScoresOnComponents(GaussianMixtureModel gmm, String img, String feature, String obj, boolean norm_to_comp_highest) {
    // get the feature vectors
    FeatureRepresentationList features = m_imgDataCenter.getImgFeatureList(
        img, // image name
        feature);

    int n_comp = gmm.getRstNumComponents();

    // scores are set to 0 first to summarize
    if (features.size() == 0) {
      return new double[0][n_comp];
    }

    double[][] data = features.as2DDoubleArray();
//    OutputResult.print("fv.dat", data);
    double[][] scores_data = new double[data.length][];

    double minimum = ((Double)m_gmmUniformDensity.get(obj, feature)).doubleValue();

    for (int i = 0; i < data.length; i++) {
      scores_data[i] = featureScoresOnComponents(gmm, data[i], minimum, norm_to_comp_highest);
    }

    return scores_data;

  }

  double[] featureScoresOnComponents(GaussianMixtureModel gmm, double[] data, double minimum, boolean norm_to_comp_highest) {
    int n_comp = gmm.getRstNumComponents();
    double[] scores_data = new double[n_comp];

    HashMap scores_map = null;
    if (norm_to_comp_highest) {
      gmm.getProbUnderComponents(data);
    }
    else {
      scores_map = gmm.getDensitiesUnderComponents(data, false, false);
    }

    // print out the probabilities under different components
    // since some components may be deleted due to singularity.  its
    // key doesn't continuous any more.  So I sorted them and use that as
    // index
    ArrayList keys = new ArrayList(scores_map.keySet());
    java.util.Collections.sort(keys);
    int j_index = 0;
    for (Iterator j = keys.iterator(); j.hasNext(); j_index++) {
      Object key = j.next();
      double p = ( (Double) scores_map.get(key)).doubleValue();
      // fix small values
      if (COMBINING_USING_LOG) {
        if (p < minimum)
          p = minimum;
      }
      scores_data[j_index] = p;
    }
    if (j_index != n_comp) {
      System.err.println("score length = " + j_index + ", less than " +
                         n_comp);
    }

    return scores_data;

  }

  /**
   * Return the score or probability of having those mixture components for
   * each image for the given feature
   * @param gmm
   * @param images
   * @param feature
   * @param fix_low_prob change low prob (like prob(0) to the smallest
   *        valid probability finded in the training stage
   * @return
   */
  double[][] scoresOnComponents(GaussianMixtureModel gmm, Collection images, String feature, String obj) {
    double[][] scores = new double[images.size()][];
    int i_index = 0;
    for (Iterator i = images.iterator(); i.hasNext(); i_index++) {
      String img = (String) i.next();
      scores[i_index] = scoreOnComponents(gmm, img, feature, obj);
    }
    return scores;
  }

  /**
   * If try to analize the performance ...
   */
  public static final boolean PERFORMANCE_ANALYSIS = true;

  /**
   * Construct the feature vectors (to feed in N.N.) for the images
   * @param imgs
   * @return
   */
  protected double[][] getFeatureVectors(LinkedList imgs, String object, LinkedList features) {
    double[][] fv_concat = null;
    if (features==null) features = getFeaturesForConstructVector();
    for (Iterator f = features.iterator(); f.hasNext(); ) {
      String feature = (String)f.next();
      GaussianMixtureModel gmm = (GaussianMixtureModel) m_gmmModels.get(
          object, feature);
      if (gmm == null) continue;
      double[][] scores_on_components = scoresOnComponents(gmm, imgs, feature, object);
      // concatenate the feature vectors for each image
      fv_concat = concatenate(fv_concat, scores_on_components, imgs.size());
    }
    return fv_concat;
  }

  /**
   * Get the list of features used to construct feature vectors
   * @return
   */
  protected LinkedList getFeaturesForConstructVector() {
    return m_featureList;
  }

  /**
   * Given score for each images in the given image list for each object.
   * The score here is after log.
   * @param imgs
   * @return StatTestRst
   */
  public StatTestRst test(java.util.LinkedList imgs) {
    StatTestRst rst = new StatTestRst();
    if (FIND_THRESHOLD) {
       rst = new StatTestRstWithVerErr();
       ((StatTestRstWithVerErr)rst).setThresholds(m_thresholds);
    }

    for (Iterator o = m_objectList.iterator(); o.hasNext(); ) {
      String object = (String)o.next();
      if (object.compareToIgnoreCase("bg")==0) continue;
      System.out.println(object);

//      constructAbstractRegionFeatures(object, imgs);

      double[][] fv_concat = getFeatureVectors(imgs, object, null);

      NeuralNet nn = (NeuralNet)m_nnModels.get(object);

      int i_index = 0;
      double[][] scores = new double[imgs.size()][];
      // for return the normalized data in NN
      double[][] normalized_data = new double[imgs.size()][];
      double[][] hidden_nodes = new double[imgs.size()][];
      double[] final_scores = new double[imgs.size()];
      int[] cat = new int[imgs.size()];
      for (Iterator j = imgs.iterator(); j.hasNext(); i_index++) {
        String img = (String)j.next();
        scores[i_index] = fv_concat[i_index];
        normalized_data[i_index] = new double[fv_concat[0].length];
        double[] nn_scores = nn.test(scores[i_index], false, normalized_data[i_index]);
        double nn_score = nn_scores[0];
        final_scores[i_index] = nn_score;
        rst.putScore(object, img, nn_score);
        cat[i_index] = m_imgDataCenter.getImgDescriptions(img).contains(object) ? 1 : 0;
        hidden_nodes[i_index] = (double[])nn.getHiddenNodes().clone();
      }

      if (VERBOSE_IN_TEST) {
        String img_path = Configuration.getParameter("IMAGE_PATH", "");
        String ft_path = Configuration.getParameter("FEATURE_PATH", "");
        String rep_path = Configuration.getParameter("REPRESENTATION_PATH", "");
        String learn_path = Configuration.getParameter("LEARNING_PATH", "");

        int[] sorted_index = yi.ds.sort.SortUtil.sort(final_scores);
        double[][] nn_beta = nn.getBeta();
        String generator = (String)m_featureList.getFirst();
        FeatureDescriptor ft_desc = FeatureDescriptor.getDefaultFeatureDescriptor(generator);
        RepresentationDescriptor rpt_desc = RepresentationDescriptor.getDefaultRepresentationDescriptor(generator);
        for (int i=0; i<sorted_index.length; i++) {
          int sorted_i = sorted_index[i];
          String image = (String)imgs.get(sorted_i);
          int disp_width = 0, disp_height = 0;
          try {
            String rpt_file_name = RepresentationDescriptor.getRepresentationOutputName(rpt_desc, image, true);
            FeatureRepresentationList fr = (FeatureRepresentationList) DIU_Ser.input(
                rpt_file_name);
            if (fr instanceof RgnFeatureRepresentationList) {
              RgnFeatureRepresentationList rft = (RgnFeatureRepresentationList) fr;
              disp_width = rft.imgWidth;
              disp_height = rft.imgHeight;
            }
          } catch (Exception e) {
            e.printStackTrace();
          }
          String ft_gdemo_name = FeatureDescriptor.getFeatureGraphicDemoName(ft_desc, image, true);
          String ft_cdemo_name = FeatureDescriptor.getFeatureColorDemoName(ft_desc, image, true);
          String rpt_gdemo_name = RepresentationDescriptor.getRepresentationGraphicDemoName(rpt_desc, image, true);
          String rpt_cdemo_name = RepresentationDescriptor.getRepresentationColorDemoName(rpt_desc, image, true);

          System.out.print("<img src=\"" +
                           img_path  + imgs.get(sorted_i) + "\"");
          if (disp_width != 0) {
            System.out.print(" width=" + disp_width + " height=" + disp_height);
          }
          System.out.print(">\t");
          if (ft_gdemo_name != null) {
            System.out.print("<img src=\"" + ft_gdemo_name + "\">\t");
          }
          if (ft_cdemo_name != null) {
            System.out.print("<img src=\"" + ft_cdemo_name + "\">\t");
          }
          if (rpt_gdemo_name != null) {
            System.out.print("<img src=\"" + rpt_gdemo_name + "\">\t");
          }
          if (rpt_cdemo_name != null) {
            System.out.print("<img src=\"" + rpt_cdemo_name + "\">\t");
          }

          System.out.print("<br>");
          if (cat[sorted_i] == 1) {
            System.out.print("<font color=red>");
          }
          else {
            System.out.print("<font color=black>");
          }
          System.out.print("\t");
          for (int k = 0; k < scores[sorted_i].length; k++) {
            System.out.print( (int) (scores[sorted_i][k] * 100) / 100.0);
            System.out.print("\t");
          }
          System.out.print("<br>");
          for (int k = 0; k < normalized_data[sorted_i].length; k++) {
            System.out.print( (int) (normalized_data[sorted_i][k] * 100) / 100.0);
            System.out.print("\t");
          }
          System.out.print("<br><i>");
          for (int k = 0; k < hidden_nodes[sorted_i].length; k++) {
            System.out.print( (int) (hidden_nodes[sorted_i][k] * 100) / 100.0);
            System.out.print("\t");
          }
          System.out.print("<br>");
          for (int k = 0; k < hidden_nodes[sorted_i].length; k++) {
            System.out.print( (int) (hidden_nodes[sorted_i][k] * nn_beta[k][0] * 100) / 100.0);
            System.out.print("\t");
          }
          System.out.print("</i><b>");
          System.out.print(final_scores[sorted_i]);
          System.out.println("</b></font><br>");
        }
      }

      if (OUTPUT_NN_DATA) {
        System.out.println("scores on test writen to " + object + ".test.dat");
        yi.util.io.OutputResult.print(object.toString() + ".test.dat", scores,
                                      scores[0].length, scores.length);

        System.out.println("categories on test writen to " + object +
                           ".test.cat");
        yi.util.io.OutputResult.print(object.toString() + ".test.cat", cat, 1,
                                      cat.length);

//        i_index = 0;
//        for (Iterator j = imgs.iterator(); j.hasNext(); i_index++) {
//          String img = (String)j.next();
//          System.out.print(img);
//          System.out.print("\t");
//          for (int k=0; k<scores[i_index].length; k++) {
//            System.out.print(scores[i_index][k]);
//            System.out.print("\t");
//          }
//          System.out.println(cat[i_index]);
//        }
      }
      if (OUTPUT_NN_FOR_SVM) {
        PrintStream out = OutputResult.getPrintStream(object + ".test");
        if (out != null) {
          for (int i = 0; i < scores.length; i++) {
            if (cat[i] == 1) {
              out.print("+1");
            }
            else {
              out.print("-1");
            }
            for (int j = 0; j < scores[i].length; j++) {
              out.print(" " + (j + 1) + ":" + scores[i][j]);
            }
            out.println();
          }
          out.close();
        }
      }
    }
    return rst;
  }

  /**
   * Locate the object in each of the image.  Probably some demo images are
   * written to the disk and a html index file is generated
   * @param imgs
   * @return StatTestRst
   * @deprecated. this one only support one feature and it was replaced by
   *  the function having the same name but different parameters.
   */
  public boolean locateObject(LinkedList imgs, LinkedList obj_list,
                              FeatureDescriptor ft_desc, RepresentationDescriptor rp_desc,
                              String exp_name) throws Exception{

    // draw the demo image
    String represent_path = Configuration.getParameter(
        "REPRESENTATION_PATH", "");
    String learn_path = Configuration.getParameter("LEARNING_PATH", "");
    String image_path = Configuration.getParameter("IMAGE_PATH", "");

    NumberFormat nf = NumberFormat.getNumberInstance();
    nf.setMaximumFractionDigits(1);

    // currently, only can consider one feature case
    String feature = StringUtil.getClassShortName(ft_desc.generator);

    double[] final_scores = new double[imgs.size()];
    int i_index = 0;
    for (Iterator i = imgs.iterator(); i.hasNext(); i_index++) {
      String img = (String) i.next();

      // get the feature vectors
      FeatureRepresentationList features = m_imgDataCenter.
          getImgFeatureList(
          img, // image name
          feature);

      FeatureRepresentable data = FeatureUtil.readFeature(img, ft_desc);

      if (data instanceof LabeledRgnFtRepresentable) {
        LabeledRgnFtRepList labeld_rep = ( (LabeledRgnFtRepresentable) data).
            representLabeledRgnFts(
            feature, rp_desc.mode);
        String cdemo = FeatureDescriptor.getFeatureColorDemoName(ft_desc, img, true);
        String gdemo = FeatureDescriptor.getFeatureGraphicDemoName(ft_desc,
            img, true);
        System.out.println("<a href=\"" + image_path + img + "\">" +
                           " <img src=" + image_path + img + "></a>" +
                           "<img src=" + cdemo + " >" +
                           "<img src=" + gdemo + " >" +
                           "<br>");
      }

      LinkedList descriptions = m_imgDataCenter.getImgDescriptions(img);

      for (Iterator j = obj_list.iterator(); j.hasNext(); ) {
        String obj = (String) j.next();


        GaussianMixtureModel gmm = (GaussianMixtureModel) m_gmmModels.get(
            obj, feature);
        // minimum allowed score on each component
        double minimum = ( (Double) m_gmmUniformDensity.get(obj, feature)).
            doubleValue();

        LinkedList temp = new LinkedList();
        temp.add(img);
        double[][] fv_concat = getFeatureVectors(temp, obj, null);
        double[] scores = fv_concat[0];

        NeuralNet nn = (NeuralNet) m_nnModels.get(obj);

        // the classification of feature to component has a prob.
        // is the normalized prob.  prob/max_prob, so [0,1]
        double[] ft_score = new double[features.size()];
        int f_index = 0;
        for (Iterator f = features.iterator(); f.hasNext(); f_index++) {
          FeatureRepresentation fr = (FeatureRepresentation) f.next();
          double[] d = fr.data;
          Object classify_to = (Object) gmm.classify(d);
          int index = gmm.objectToRstComponentIndex(classify_to);
          GaussianComponent gc = gmm.getGaussianComponent(classify_to);
          double highest_score = gc.getDensity(d);

          if (highest_score < minimum)
            highest_score = minimum;
//        // simulate the combing function
//        if (COMBINING_FUNC == MEAN_COMBINING_FUNC) {
//          highest_score = (highest_score + (features.size() - 1) * minimum) / features.size();
//        }
          highest_score = Math.log(highest_score);

          // save the combining score on the component
          double score_after_comb = scores[index];
          // the prob gotten if the score on that component is 0
          scores[index] = Math.log(minimum);
          double[] nn_scores = nn.test(scores, false, null);
          double score_with_0 = nn_scores[0];
          // the prob gotten if the score on that component is from this feature
          scores[index] = highest_score;
          nn_scores = nn.test(scores, false, null);
          double score_with_this_ft = nn_scores[0];

          // restore the scores
          scores[index] = score_after_comb;

          ft_score[f_index] = score_with_this_ft - score_with_0;
        }

        System.err.println("scores on regions: ");
        OutputResult.print(System.err, ft_score);
        // normalize the scores
//        double min = StatUtil.minimum(ft_score);
        double min = 0;
        double max = StatUtil.maximum(ft_score);
        double[] nn_scores = nn.test(scores, false, null);
        double prob_orig = nn_scores[0];
        for (int f = 0; f < ft_score.length; f++) {
          ft_score[f] = (max == min)? 1 :(ft_score[f] - min) / (max - min);
          ft_score[f] *= prob_orig;
          if (ft_score[f] < 0)
            ft_score[f] = 0;
          if (ft_score[f] > 1)
            ft_score[f] = 1;
        }

        if (data instanceof LabeledRgnFtRepresentable) {
          LabeledRgnFtRepList labeld_rep = ( (LabeledRgnFtRepresentable) data).
              representLabeledRgnFts(
              feature, rp_desc.mode);
          int[] prob_demo = labeld_rep.drawProbabilityDemo(null, ft_score, null);
          String prob_demo_name = learn_path + img +
              "." + exp_name +
              "." + obj + ".loc" +
              ".png";
          yi.util.io.File.makeDirectory(prob_demo_name, true);
          ImageIOUtil.write(prob_demo_name,
                            "png", prob_demo, labeld_rep.imgWidth,
                            labeld_rep.imgHeight);
          if (descriptions.contains(obj)) {
            System.out.print("<b>" + obj + "</b>");
          } else {
            System.out.print(obj);
          }
          System.out.println(" (" + nf.format(prob_orig * 100) + ")" + "<br>");
          System.out.println(
              "<a href=\"" + prob_demo_name + "\">" +
              "<img src=\"" + prob_demo_name + "\" border=1>" +
              "</a><br>");

        }
      }

    }

    return false;
  }

  /**
   * Locate the objects in each of the image using features specified in the
   * "ft_descs".  Since different feature may use different rescaled image,
   * the size of the result is passed in too.
   * @param imgs
   * @param obj_list
   * @param ft_descs
   * @param rp_descs
   * @param exp_name
   * @param size
   * @return
   * @throws Exception
   */
  public boolean locateObject(LinkedList imgs, LinkedList obj_list,
                              FeatureDescriptor[] ft_descs, RepresentationDescriptor[] rp_descs,
                              String exp_name, int rst_size) throws Exception{
    // draw the demo image
    String represent_path = Configuration.getParameter(
        "REPRESENTATION_PATH", "");
    String learn_path = Configuration.getParameter("LEARNING_PATH", "");
    String image_path = Configuration.getParameter("IMAGE_PATH", "");

    NumberFormat nf = NumberFormat.getNumberInstance();
    nf.setMaximumFractionDigits(1);

    int n_ft = ft_descs.length;
    // output the links to original image and image regions
    int i_index = 0;
    for (Iterator i = imgs.iterator(); i.hasNext(); i_index++) {
      String img = (String) i.next();
      // line cluster features will be saved and to be dealt with seperately
      LineClusterSets[] lcs = new LineClusterSets[n_ft];
      LabeledRgnFtRepList[] reps = new LabeledRgnFtRepList[n_ft];
      for (int f=0; f<ft_descs.length; f++) {
        FeatureDescriptor ft_desc = ft_descs[f];
        String feature = StringUtil.getClassShortName(ft_desc.generator);
        RepresentationDescriptor rp_desc =rp_descs[f];
        FeatureRepresentable data = FeatureUtil.readFeature(img, ft_desc);
        if (data instanceof LabeledRgnFtRepresentable) {
          reps[f] = ( (LabeledRgnFtRepresentable) data).
              representLabeledRgnFts(feature, rp_desc.mode);
          Dimension new_dim = Resampler.getDestSize(reps[0].imgWidth, reps[0].imgHeight, rst_size, rst_size);
          String cdemo = FeatureDescriptor.getFeatureColorDemoName(ft_desc, img, true);
          String gdemo = FeatureDescriptor.getFeatureGraphicDemoName(ft_desc,
              img, true);
          String thumb_img = ImgDescManager.toThumbImageName(img);
          System.out.println(feature + "<br>");
          System.out.println("<a href=\"" + image_path + img + "\">" +
                             " <img src=" + image_path + thumb_img + "></a>" +
                             "<img src=" + cdemo + " width=" + new_dim.width + " height=" + new_dim.height + ">" +
                             "<img src=" + gdemo + " width=" + new_dim.width + " height=" + new_dim.height + ">" +
                             "<br>");
        }
        if (data instanceof LineClusterSets) {
          lcs[f] = (LineClusterSets)data;
        }
      }

      LinkedList descriptions = m_imgDataCenter.getImgDescriptions(img);

      for (Iterator j = obj_list.iterator(); j.hasNext(); ) {
        String obj = (String) j.next();

        // get the gmm and minimum allowed log likelihood for each feature
        GaussianMixtureModel[] gmms = new GaussianMixtureModel[n_ft];
        double[] minimums = new double[n_ft];
        for (int f=0; f<n_ft; f++) {
          FeatureDescriptor ft_desc = ft_descs[f];
          String feature = StringUtil.getClassShortName(ft_desc.generator);
          gmms[f] = (GaussianMixtureModel) m_gmmModels.get(
              obj, feature);
          // minimum allowed score on each component
          if (COMBINING_USING_LOG) {
            minimums[f] = ( (Double) m_gmmUniformDensity.get(obj, feature)).
                doubleValue();
          } else {
            minimums[f] = 0;
          }
        }

        // get combined features
        LinkedList temp = new LinkedList();
        temp.add(img);
        double[][] fv_concat = getFeatureVectors(temp, obj, null);
        double[] nn_input = fv_concat[0];

        // get the NN output on the combined feature
        NeuralNet nn = (NeuralNet) m_nnModels.get(obj);
        double[] nn_scores = nn.test(nn_input, false, null);
        double score_normal = nn_scores[0];

        // get the mapping between nn input index to feature index
        int[] nn_index_2_ft_index = new int[nn_input.length];
        int nn_index = 0;
        for (int f=0; f<n_ft; f++) {
          for (int c=0; c<gmms[f].getRstNumComponents(); c++) {
            nn_index_2_ft_index[nn_index++] = f;
          }
        }

        // get the importance of each component
        double[]  nn_comp_zero_scores = new double[nn_input.length];

        // use (LOCATING == LOCATING_LEAVE_ONE_OUT) {
        for (int c = 0; c < nn_input.length; c++) {
          double[] test = (double[]) nn_input.clone();
          test[c] = Math.log(minimums[nn_index_2_ft_index[c]]);
          double[] test_scores = nn.test(test, false, null);
          double score0 = test_scores[0];
          nn_comp_zero_scores[c] = score0;
        }


        // get the maximum value of each component
        // double[][] comp_highest = new double[ft_descs.length][];
        double[][] comp_average = new double[ft_descs.length][];
        for (int f=0; f<ft_descs.length; f++) {
          String feature = StringUtil.getClassShortName(ft_descs[f].generator);
          double[][] ft_comp_scores = featureScoresOnComponents(gmms[f], img, feature, obj, false);
          if (ft_comp_scores == null || ft_comp_scores.length == 0) {
            comp_average[f] = new double[gmms[f].getRstNumComponents()];
            Arrays.fill(comp_average[f], minimums[f]);
            continue;
          }
          // comp_highest[f] = StatUtil.maximumOnCol(ft_comp_scores);
          comp_average[f] = StatUtil.meanOnCol(ft_comp_scores);
        }

        // to save the mapping from labels (for different features) to the
        // score of having the object.  For example, if a pixel is classified
        // as color region #3, texture region #5, then we calculate its
        // score of having a object "o".  We save it in "labels2score" so that
        // we can just use the results directly (since there will be other
        // pixels having the same labels)
        HashMap labels2score = new HashMap();

        // rescale the labels first
        Dimension new_dim = Resampler.getDestSize(reps[0].imgWidth, reps[0].imgHeight, rst_size, rst_size);
        SimpleResampler resampler = new SimpleResampler(new_dim.width, new_dim.height);
        int[][] ft_labels = new int[ft_descs.length][];
        for (int f=0; f<ft_descs.length; f++) {
          LabeledRgnFtRepList rep = reps[f];
          ft_labels[f] = resampler.getProcessed(rep.getLabels(), new Dimension(rep.imgWidth, rep.imgHeight));
        }

        // consider pixel by pixel
        double[][] prob_img = new double[new_dim.width * new_dim.height][n_ft];
        for (int p=0; p<prob_img.length; p++) {
          int[] p_labels = new int[ft_descs.length]; // store the labels for the pixel
          for (int f=0; f<n_ft; f++) {
            p_labels[f] = ft_labels[f][p];
          }

          // find out whether a similar pixel is calculated yet
          IntArray labels_key = new IntArray(p_labels);
          if (!labels2score.containsKey(labels_key)) {
            double[] ft_scores = new double[n_ft];
            int comp_base_index = 0;
            for (int f=0; f<ft_descs.length; f++) {
              if (lcs[f] == null) {  // line cluster sets will be dealt with differently
                String feature = StringUtil.getClassShortName(ft_descs[f].
                    generator);
                double[] unnorm_score = new double[gmms[f].getRstNumComponents()];
                double[] norm_score = new double[gmms[f].getRstNumComponents()];
                double[] contribute = new double[gmms[f].getRstNumComponents()];

                if (p_labels[f] > 0) { // label 0 is backgound
                  double[] d = ( (FeatureRepresentation) reps[f].get(p_labels[f] -
                      1)).data;
                  double[] d_scores = featureScoresOnComponents(gmms[f], d,
                      minimums[f], false);
                  for (int c = 0; c < d_scores.length; c++) {
                    double score_on_component = d_scores[c];
                    if (score_on_component > comp_average[f][c])
                      score_on_component = comp_average[f][c];
                    double[] score_with_this_comp = (double[]) nn_input.clone();
                    score_with_this_comp[c +
                        comp_base_index] = Math.log(score_on_component);
                    nn_scores = nn.test(score_with_this_comp, false, null);
                    double score_with = nn_scores[0];
                    contribute[c] = score_with - nn_comp_zero_scores[c +
                        comp_base_index];
                  }
                  ft_scores[f] = StatUtil.sum(contribute);
                }
              }
              comp_base_index += gmms[f].getRstNumComponents();
//              OutputResult.print(System.out, unnorm_score);
//              OutputResult.print(System.out, norm_score);
//              OutputResult.print(System.out, contribute);
//              System.out.println("" + (p_labels[f] - 1) + "\t" + ft_scores[f]);
            }

            labels2score.put(labels_key, ft_scores);
          }
          prob_img[p] = (double[])labels2score.get(labels_key);
        }

        // consider Line Structure feature
        // for every feature vector, get its contribution to all the components
        // the region under the region (represented by the feature vector)
        // will take the
        int comp_base_index = 0;
        for (int f=0; f<ft_descs.length; f++) {
          if (lcs[f] != null) {
            LineClusterSets struct = lcs[f];
            String generator = ft_descs[f].generator;
            PolygonFeatureRepresentationList rep_list = struct.representPolygonFeatures(generator, RepresentationDescriptor.getDefaultRepresentationDescriptor(generator).mode);
            int struct_width = struct.getImageWidth();
            int struct_height = struct.getImageHeight();
            double[] struct_score = new double[struct_width * struct_height];

            String feature = StringUtil.getClassShortName(ft_descs[f].
                generator);
            double[] unnorm_score = new double[gmms[f].getRstNumComponents()];
            double[] norm_score = new double[gmms[f].getRstNumComponents()];
            double[] contribute = new double[gmms[f].getRstNumComponents()];

            double[] lc_contributes = new double[rep_list.size()];
            for (int lc = 0; lc < rep_list.size(); lc++) { // for each line cluster
              double[] d = ( (FeatureRepresentation) rep_list.get(lc)).data;
              double[] d_scores = featureScoresOnComponents(gmms[f], d,
                  minimums[f], false);
              for (int c = 0; c < d_scores.length; c++) {
                double score_on_component = d_scores[c];
                if (score_on_component > comp_average[f][c])
                  score_on_component = comp_average[f][c];
                double[] score_with_this_comp = (double[]) nn_input.clone();
                score_with_this_comp[c +
                    comp_base_index] = Math.log(score_on_component);
                nn_scores = nn.test(score_with_this_comp, false, null);
                double score_with = nn_scores[0];
                contribute[c] = score_with - nn_comp_zero_scores[c +
                    comp_base_index];
              }
              lc_contributes[lc] = StatUtil.sum(contribute);
            }
            // get the maximum score from the regions (can be overlapped for struct) to which a pixel belongs
            for (int lc = 0; lc < rep_list.size(); lc++) { // for each line cluster
              PolygonFeatureRepresentation rep = (PolygonFeatureRepresentation)rep_list.get(lc);
              rep.region.filledDraw(struct_score, struct_width, struct_height, lc_contributes[lc], GenericPolygon.FILL_MAX);
            }
            // resize the score (rescale the image)
            double[] struct_rst = resampler.getProcessed(struct_score, new Dimension(struct_width, struct_height));

            // fill it to the position of prob_img
            assert(struct_rst.length == prob_img.length);
            for (int p=0; p<prob_img.length; p++) {
              prob_img[p][f] = struct_rst[p];
            }
            break; // there is only one struct feature, usually.
          }
          comp_base_index += gmms[f].getRstNumComponents();
        }

        // combine scores from different features
        // use max() or mean() for now.
        double[] prob_img_comb = new double[new_dim.width * new_dim.height];
        if (n_ft > 1) {
          // find the max
          double max = 0;
          for (int k = 0; k < prob_img_comb.length; k++) {
            max = Math.max(StatUtil.maximum(prob_img[k]), max);
          }
          for (int f=0; f<n_ft; f++) {
            for (int k=0; k<prob_img_comb.length; k++) {
              prob_img_comb[k] = prob_img[k][f];
            }
            double min = 0;
            prob_img_comb = StatUtil.normalize(prob_img_comb, min, max, score_normal);
            int[] prob_demo = ImageUtil.makeNewImageFromNormalizedGray(prob_img_comb);
            String prob_demo_name = learn_path + img +
                "." + exp_name +
                "." + obj + ".f" + f + ".loc" +
                ".png";
            yi.util.io.File.makeDirectory(prob_demo_name, true);
            ImageIOUtil.write(prob_demo_name,
                              "png", prob_demo, new_dim.width, new_dim.height);
            String feature = StringUtil.getClassShortName(ft_descs[f].generator);
            System.out.println(feature + "<br>");
            System.out.println(
                "<a href=\"" + prob_demo_name + "\">" +
                "<img src=\"" + prob_demo_name + "\" border=1>" +
                "</a><br>");
          }
        }
        for (int k=0; k<prob_img_comb.length; k++) {
//          // use max to combine
//          prob_img_comb[k] = StatUtil.maximum(prob_img[k]);
          // use mean to combine
          prob_img_comb[k] = StatUtil.mean(prob_img[k]);
        }

        // normalize the scores
        double min = 0;
        double max = StatUtil.maximum(prob_img_comb);
        prob_img_comb = StatUtil.normalize(prob_img_comb, min, max, score_normal);

        int[] prob_demo = ImageUtil.makeNewImageFromNormalizedGray(prob_img_comb);

        String prob_demo_name = learn_path + img +
            "." + exp_name +
            "." + obj + ".loc" +
            ".png";
        yi.util.io.File.makeDirectory(prob_demo_name, true);
        ImageIOUtil.write(prob_demo_name,
                          "png", prob_demo, new_dim.width, new_dim.height);
        if (descriptions.contains(obj)) {
          System.out.print("<b>" + obj + "</b>");
        }
        else {
          System.out.print(obj);
        }
        System.out.println(" (" + nf.format(score_normal * 100) + ")" + "<br>");
        System.out.println(
            "<a href=\"" + prob_demo_name + "\">" +
            "<img src=\"" + prob_demo_name + "\" border=1>" +
            "</a><br>");

      }

    }

    return false;
  }

  /**
   * Locate the objects in each of the image using features specified in the
   * "ft_descs".  Since different feature may use different rescaled image,
   * the size of the result is passed in too.
   * @param imgs
   * @param obj_list
   * @param ft_descs
   * @param rp_descs
   * @param exp_name
   * @param size
   * @return
   * @throws Exception
   */
  public boolean locateObject_old(LinkedList imgs, LinkedList obj_list,
                              FeatureDescriptor[] ft_descs, RepresentationDescriptor[] rp_descs,
                              String exp_name, int rst_size) throws Exception{
    // draw the demo image
    String represent_path = Configuration.getParameter(
        "REPRESENTATION_PATH", "");
    String learn_path = Configuration.getParameter("LEARNING_PATH", "");
    String image_path = Configuration.getParameter("IMAGE_PATH", "");

    NumberFormat nf = NumberFormat.getNumberInstance();
    nf.setMaximumFractionDigits(1);

    // output the links to original image and image regions
    int i_index = 0;
    for (Iterator i = imgs.iterator(); i.hasNext(); i_index++) {
      String img = (String) i.next();
      LabeledRgnFtRepList[] reps = new LabeledRgnFtRepList[ft_descs.length];
      for (int f=0; f<ft_descs.length; f++) {
        FeatureDescriptor ft_desc = ft_descs[f];
        String feature = StringUtil.getClassShortName(ft_desc.generator);
        RepresentationDescriptor rp_desc =rp_descs[f];
        FeatureRepresentable data = FeatureUtil.readFeature(img, ft_desc);
        if (data instanceof LabeledRgnFtRepresentable) {
          reps[f] = ( (LabeledRgnFtRepresentable) data).
              representLabeledRgnFts(feature, rp_desc.mode);
          String cdemo = FeatureDescriptor.getFeatureColorDemoName(ft_desc, img, true);
          String gdemo = FeatureDescriptor.getFeatureGraphicDemoName(ft_desc,
              img, true);
          String thumb_img = ImgDescManager.toThumbImageName(img);
          System.out.println("<a href=\"" + image_path + img + "\">" +
                             " <img src=" + image_path + thumb_img + "></a>" +
                             "<img src=" + cdemo + " >" +
                             "<img src=" + gdemo + " >" +
                             "<br>");
        }
      }

      LinkedList descriptions = m_imgDataCenter.getImgDescriptions(img);
      int n_ft = ft_descs.length;

      for (Iterator j = obj_list.iterator(); j.hasNext(); ) {
        String obj = (String) j.next();

        // get the gmm and minimum allowed log likelihood for each feature
        GaussianMixtureModel[] gmms = new GaussianMixtureModel[n_ft];
        double[] minimums = new double[n_ft];
        for (int f=0; f<n_ft; f++) {
          FeatureDescriptor ft_desc = ft_descs[f];
          String feature = StringUtil.getClassShortName(ft_desc.generator);
          gmms[f] = (GaussianMixtureModel) m_gmmModels.get(
              obj, feature);
          // minimum allowed score on each component
          if (COMBINING_USING_LOG) {
            minimums[f] = ( (Double) m_gmmUniformDensity.get(obj, feature)).
                doubleValue();
          } else {
            minimums[f] = 0;
          }
        }

        // get combined features
        LinkedList temp = new LinkedList();
        temp.add(img);
        double[][] fv_concat = getFeatureVectors(temp, obj, null);
        double[] nn_input = fv_concat[0];

        // get the NN output on the combined feature
        NeuralNet nn = (NeuralNet) m_nnModels.get(obj);
        double[] nn_scores = nn.test(nn_input, false, null);
        double score_normal = nn_scores[0];

        // get the mapping between nn input index to feature index
        int[] nn_index_2_ft_index = new int[nn_input.length];
        int nn_index = 0;
        for (int f=0; f<n_ft; f++) {
          for (int c=0; c<gmms[f].getRstNumComponents(); c++) {
            nn_index_2_ft_index[nn_index++] = f;
          }
        }

        // get the importance of each component
        double[]  nn_comp_important_scores = new double[nn_input.length];

        if (LOCATING == LOCATING_ADD_ONE_MORE) {

          double[] nn_config_scores = new double[1 << nn_input.length];
          for (int t = 0; t < (1 << nn_input.length); t++) {
            double[] test = (double[]) nn_input.clone();
            int ones = 0;
            for (int c = 0; c < nn_input.length; c++) {
              if ( ( (1 << c) & t) > 0) {
                test[c] = Math.log(minimums[nn_index_2_ft_index[c]]);
//              System.out.print(1 + "\t");
              }
              else {
                ones++;
//              System.out.print(0 + "\t");
              }
            }
            double[] test_scores = nn.test(test, false, null);
            double score0 = test_scores[0];
            nn_config_scores[t] = score0;
//          if (ones == 1) {
//            for (int c = 0; c < nn_input.length; c++) {
//              System.out.print(test[c] + "\t");
//            }
//            System.out.println(score0);
//          }
          }
          int[] nn_comp_important_index = new int[nn_input.length];
          for (int c = 0; c < nn_input.length; c++) {
            for (int t = 0; t < (1 << nn_input.length); t++) {
              double score_with = nn_config_scores[t];
              double score_without = nn_config_scores[t | (1 << c)];
              double important = score_with - score_without;
              if (important > nn_comp_important_scores[c]) {
                nn_comp_important_scores[c] = important;
                nn_comp_important_index[c] = t;
              }
            }
          }
//        OutputResult.print(System.out, nn_comp_important_scores);
          for (int c = 0; c < nn_input.length; c++) {
            int t = nn_comp_important_index[c];
            for (int c2 = 0; c2 < nn_input.length; c2++) {
              if ( ( (1 << c2) & t) > 0) {
//              System.out.print(1 + "\t");
//              System.out.print(Math.log(minimums[0]) + "\t");
              }
              else {
//              System.out.print(0 + "\t");
//              System.out.print(nn_input[c2] + "\t");
              }
            }
//          System.out.print(nn_config_scores[t] + "\t");
//          System.out.print(nn_config_scores[t| (1<<c)] + "\t");
//          System.out.println(nn_comp_important_scores[c]);
          }
        } else if (LOCATING == LOCATING_LEAVE_ONE_OUT) {
          for (int c = 0; c < nn_input.length; c++) {
            double[] test = (double[]) nn_input.clone();
            test[c] = Math.log(minimums[nn_index_2_ft_index[c]]);
            double[] test_scores = nn.test(test, false, null);
            double score0 = test_scores[0];
            nn_comp_important_scores[c] = score_normal - score0;
          }
        }else { // LOCATING_ADD_ONE_IN
          double score0 = 0;
          double[] test = (double[]) nn_input.clone();
          for (int c = 0; c < nn_input.length; c++) {
            test[c] = Math.log(minimums[nn_index_2_ft_index[c]]);
            double[] test_scores = nn.test(test, false, null);
            score0 = test_scores[0];
          }
          for (int c = 0; c < nn_input.length; c++) {
            test = (double[]) nn_input.clone();
            test[c] = Math.log(minimums[0]);
            double[] test_scores = nn.test(test, false, null);
            double score1 = test_scores[0];
            nn_comp_important_scores[c] = score1 - score0;
          }
        }


        nn_comp_important_scores = StatUtil.normalize(nn_comp_important_scores, 0, StatUtil.maximum(nn_comp_important_scores), 1);
//        OutputResult.print(System.out, nn_comp_important_scores);

        // get the maximum value of each component
//        double[][] comp_highest = new double[ft_descs.length][];
        double[][] comp_average = new double[ft_descs.length][];
        for (int f=0; f<ft_descs.length; f++) {
          String feature = StringUtil.getClassShortName(ft_descs[f].generator);
          double[][] ft_comp_scores = featureScoresOnComponents(gmms[f], img, feature, obj, false);
//          comp_highest[f] = StatUtil.maximumOnCol(ft_comp_scores);
          comp_average[f] = StatUtil.meanOnCol(ft_comp_scores);
        }

        // to save the mapping from labels (for different features) to the
        // score of having the object.  For example, if a pixel is classified
        // as color region #3, texture region #5, then we calculate its
        // score of having a object "o".  We save it in "labels2score" so that
        // we can just use the results directly (since there will be other
        // pixels having the same labels)
        HashMap labels2score = new HashMap();

        // rescale the labels first
        Dimension new_dim = Resampler.getDestSize(reps[0].imgWidth, reps[0].imgHeight, rst_size, rst_size);
        Resampler resampler = new SimpleResampler(new_dim.width, new_dim.height);
        int[][] ft_labels = new int[ft_descs.length][];
        for (int f=0; f<ft_descs.length; f++) {
          LabeledRgnFtRepList rep = reps[f];
          ft_labels[f] = resampler.getProcessed(rep.getLabels(), new Dimension(rep.imgWidth, rep.imgHeight));
        }

        // consider pixel by pixel
        double[][] prob_img = new double[new_dim.width * new_dim.height][n_ft];
        for (int p=0; p<prob_img.length; p++) {
          int[] p_labels = new int[ft_descs.length]; // store the labels for the pixel
          for (int f=0; f<n_ft; f++) {
            p_labels[f] = ft_labels[f][p];
          }

          // find out whether a similar pixel is calculated yet
          IntArray labels_key = new IntArray(p_labels);
          if (labels2score.containsKey(labels_key)) {
            prob_img[p] = (double[])labels2score.get(labels_key);
          } else {
            double[] ft_scores = new double[n_ft];
            int comp_base_index = 0;
            for (int f=0; f<ft_descs.length; f++) {
              String feature = StringUtil.getClassShortName(ft_descs[f].generator);
              double[] unnorm_score = new double[gmms[f].getRstNumComponents()];
              double[] norm_score = new double[gmms[f].getRstNumComponents()];
              double[] contribute = new double[gmms[f].getRstNumComponents()];

              if (p_labels[f] > 0) { // label 0 is backgound
                double[] d = ((FeatureRepresentation) reps[f].get(p_labels[f] - 1)).data;
                double[] d_scores = featureScoresOnComponents(gmms[f], d, minimums[f], false);
                for (int c=0; c<d_scores.length; c++) {
                  double score_on_component = d_scores[c];
                  unnorm_score[c] = score_on_component;
                  score_on_component = score_on_component / comp_average[f][c];
                  if (score_on_component > 1) score_on_component = 1;
                  norm_score[c] = score_on_component;
                  contribute[c] = nn_comp_important_scores[c + comp_base_index] * score_on_component;
                }
                ft_scores[f] = StatUtil.sum(contribute);
//                ft_scores[f] = StatUtil.maximum(contribute);
              }
              comp_base_index += gmms[f].getRstNumComponents();
//              OutputResult.print(System.out, unnorm_score);
//              OutputResult.print(System.out, norm_score);
//              OutputResult.print(System.out, contribute);
//              System.out.println("" + (p_labels[f] - 1) + "\t" + ft_scores[f]);
            }

            labels2score.put(labels_key, ft_scores);
          }
        }

        // combine scores from different features
        // use max() for now.
        double[] prob_img_comb = new double[new_dim.width * new_dim.height];
        if (n_ft > 1) {
          // find the max
          double max = 0;
          for (int k = 0; k < prob_img_comb.length; k++) {
            max = Math.max(StatUtil.maximum(prob_img[k]), max);
          }
          for (int f=0; f<n_ft; f++) {
            for (int k=0; k<prob_img_comb.length; k++) {
              prob_img_comb[k] = prob_img[k][f];
            }
            double min = 0;
            prob_img_comb = StatUtil.normalize(prob_img_comb, min, max, score_normal);
            int[] prob_demo = ImageUtil.makeNewImageFromNormalizedGray(prob_img_comb);
            String prob_demo_name = learn_path + img +
                "." + exp_name +
                "." + obj + ".f" + f + ".loc" +
                ".png";
            yi.util.io.File.makeDirectory(prob_demo_name, true);
            ImageIOUtil.write(prob_demo_name,
                              "png", prob_demo, new_dim.width, new_dim.height);
            System.out.println(
                "<a href=\"" + prob_demo_name + "\">" +
                "<img src=\"" + prob_demo_name + "\" border=1>" +
                "</a><br>");
          }
        }
        for (int k=0; k<prob_img_comb.length; k++) {
          prob_img_comb[k] = StatUtil.maximum(prob_img[k]);
        }

        // normalize the scores
        double min = 0;
        double max = StatUtil.maximum(prob_img_comb);
        prob_img_comb = StatUtil.normalize(prob_img_comb, min, max, score_normal);

        int[] prob_demo = ImageUtil.makeNewImageFromNormalizedGray(prob_img_comb);

        String prob_demo_name = learn_path + img +
            "." + exp_name +
            "." + obj + ".loc" +
            ".png";
        yi.util.io.File.makeDirectory(prob_demo_name, true);
        ImageIOUtil.write(prob_demo_name,
                          "png", prob_demo, new_dim.width, new_dim.height);
        if (descriptions.contains(obj)) {
          System.out.print("<b>" + obj + "</b>");
        }
        else {
          System.out.print(obj);
        }
        System.out.println(" (" + nf.format(score_normal * 100) + ")" + "<br>");
        System.out.println(
            "<a href=\"" + prob_demo_name + "\">" +
            "<img src=\"" + prob_demo_name + "\" border=1>" +
            "</a><br>");

      }

    }

    return false;
  }

  /**
   * Given the learning images, seperate them to positive images and negative
   * images.
   * @param img_list
   * @param object
   * @param posImgs
   * @param negImgs
   */
  void seperatePosImgsFromNegImgs(LinkedList img_list, Object object,
                                       LinkedList positive,
                                       LinkedList negative) {
    for (Iterator i = img_list.iterator(); i.hasNext(); ) {
      String img = (String) i.next();
      LinkedList desc_list = m_imgDataCenter.getImgDescriptions(img);
      if (desc_list !=null && desc_list.contains(object)) {
        positive.add(img);
      }
      else {
        negative.add(img);
      }
    }

    System.out.println("Positive Training set: " + positive.size());
    System.out.println("Negative Training set: " + negative.size());
  }
/*
  public NeuralNet trainNN(double[][] pos_features, double[][] neg_features){

    System.out.println("pos data = " + pos_features.length);
    System.out.println("neg data = " + neg_features.length);
    int scale = neg_features.length / pos_features.length;
    if (scale < 1) scale = 1; // in case positive cases are more than negative cases
    System.out.println("scale = " + scale);

    // construct the data
    double[][] d = new double[pos_features.length * scale + neg_features.length][];
    double[][] target = new double[pos_features.length * scale +
        neg_features.length][1];
    int index = 0;
    for (int s = 0; s < scale; s++) {
      for (int i = 0; i < pos_features.length; i++) {
        target[index][0] = 1;
        d[index++] = pos_features[i];
      }
    }
    System.out.println("pos instance = " + index);
    for (int i = 0; i < neg_features.length; i++) {
      target[index][0] = 0;
      d[index++] = neg_features[i];
    }

    System.out.println("total data length = " + d.length);

    NeuralNet nn = new NeuralNet(d[0].length, NN_HIDDEN_VAR_NUM, 1, NN_RATE);
//    nn.train(d, target, NN_ITERATIONS);
    nn.trainWithVerification(d, target, NN_ITERATIONS, 1000, 0.33);
    return nn;
  }
*/

  void prepareNNData(double[][] pos_features, double[][] neg_features, double[][] d, double[][] target) {
    // construct the data
    int index = 0;
    for (int i = 0; i < pos_features.length; i++) {
      target[index][0] = 1;
      d[index++] = pos_features[i];
    }
    for (int i = 0; i < neg_features.length; i++) {
      target[index][0] = 0;
      d[index++] = neg_features[i];
    }
  }

  public NeuralNet trainNN(double[][] pos_features, double[][] neg_features, double[][] pos_test, double[][] neg_test){

    System.out.println();
    System.out.println("train NN");
    System.out.println("pos data = " + pos_features.length);
    System.out.println("neg data = " + neg_features.length);

    // construct the data
    double[][] d = new double[pos_features.length + neg_features.length][];
    double[][] target = new double[pos_features.length +
        neg_features.length][1];
    prepareNNData(pos_features, neg_features, d, target);

    System.out.println("total data length = " + d.length);
    System.out.println("data dim = " + d[0].length);

    // construct the test data
    double[][] test_d = null;
    double[][] test_target = null;
    if (pos_test != null) {
      System.out.println("pos test data = " + pos_test.length);
      System.out.println("neg test data = " + neg_test.length);
      test_d = new double[pos_test.length + neg_test.length][];
      test_target = new double[pos_test.length + neg_test.length][1];
      prepareNNData(pos_test, neg_test, test_d, test_target);
    }

    NeuralNet nn = new WeightedBinaryNN(d[0].length, NN_HIDDEN_VAR_NUM, 1, NN_RATE);
//    NeuralNet nn = new NeuralNet(d[0].length, NN_HIDDEN_VAR_NUM, 1, NN_RATE);
//    nn.train(d, target, NN_ITERATIONS);
    nn.trainWithVerification(d, target, NN_ITERATIONS, MAX_NN_ITERATION, VERIFICATION_PORTION, test_d, test_target);

    if (FIND_THRESHOLD) {
      DoubleScoreHashMap one_obj_score = new DoubleScoreHashMap();
      for (int i=0; i<d.length; i++) {
        double[] nn_out = nn.test(d[i], false);
        one_obj_score.putScore(new Integer(i), nn_out[0]);
      }
      HashSet pos_set = new HashSet();
      for (int i=0; i<pos_features.length; i++) {
        pos_set.add(new Integer(i));
      }
      m_foundThreshold = StatTestRst.rpcEquallPoint(one_obj_score,
                                pos_set, true);

    }

    return nn;
  }
  boolean FIND_THRESHOLD = true;
  DoubleScoreHashMap m_thresholds = new DoubleScoreHashMap();
  double  m_foundThreshold;

  public String toString() {
    StringWriter out = new StringWriter();

    out.write("feature: ");
    for (Iterator f = m_featureList.iterator(); f.hasNext();)  {
      out.write(f.next() + " ");
    }
    out.write("\n");

    // learned the objects one by one
    for (Iterator o = m_objectList.iterator(); o.hasNext(); ) {
      String obj = (String)o.next();
      out.write(obj + "(object)" + "\n\n");
      ArrayList features = new ArrayList(((HashMap)m_gmmModels.get(obj)).keySet());
      Collections.sort(features);
      for (Iterator f=features.iterator(); f.hasNext(); ) {
        String feature = (String)f.next();
        // the GaussianMixtureModel model
        GaussianMixtureModel gmm = (GaussianMixtureModel) m_gmmModels.get(obj, feature);
        out.write(feature + "\n\n");
        out.write(gmm.toString());
      }
      // the NN model
      NeuralNet nn = (NeuralNet) m_nnModels.get(obj);
      out.write(nn.toString());
    }
    return out.toString();
  }

  /**
   * Get the total component number for recognizing one object.  Each feature has
   * some component (clusters), so totally, ...
   * @param obj
   * @param features
   * @return
   */
  public int getTotalComponentNum(String obj, LinkedList features) {
    // total length of the features
    int total_components = 0;
    for (Iterator i = features.iterator(); i.hasNext(); ) {
      Object feature = i.next();
      // finds out how many Gaussian components
      GaussianMixtureModel gmm = (GaussianMixtureModel) m_gmmModels.get(
          obj, feature);
      int n_components = gmm.getRstNumComponents();
      total_components += n_components;
    }
    return total_components;
  }


  public int[] getGMMComponentMeanInColor(String obj, String generator) {
    // finds out how many Gaussian components
    GaussianMixtureModel gmm = (GaussianMixtureModel)m_gmmModels.get(obj, generator);
    int n_components = gmm.getRstNumComponents();
    System.out.println("for " + obj + "," + generator + " component# =" + n_components);
    System.out.println();
    NumberFormat nf = NumberFormat.getNumberInstance();
    nf.setMaximumFractionDigits(2);


    // each component corresponding to one color
    int[] color_maps = yi.util.ImageUtil.labels2Colors(n_components - 1, false);
    // if the feature is color feature, then use the color to represent
    // the clusters
    if (generator.indexOf("BlobworldSegmenter") >= 0) {
      HashMap means = gmm.getComponentMeans();
      HashMap covs = gmm.getComponentCovariance();
      HashMap probs = gmm.getWeights();
      ArrayList sorted_key = new ArrayList(means.keySet());
      Collections.sort(sorted_key);
      for (Iterator i = sorted_key.iterator(); i.hasNext(); ) {
        Integer key = (Integer)i.next();
        double[] lab = (double[])means.get(key);
        // label 0 is background.  region 0 -> label 1
        int c = yi.iu.processor.color.CIELab.lab2rgb(lab);
        color_maps[key.intValue()] = c;
        int red = (c >> 16)&0xff, green = (c >> 8)&0xff, blue = c&0xff;
        double prob = ((Double)probs.get(key)).doubleValue();
        System.out.println("component " + key + " : " + nf.format(prob * 100) + "%");
        System.out.println("rgb (" + red + ", " + green + ", " + blue + ")");
        System.out.println();
        double[][] cov = (double[][])covs.get(key);
        OutputResult.print(System.out, cov);
        System.out.println();
      }
    } else {
      FeatureDescriptor ft_desc = FeatureDescriptor.getDefaultFeatureDescriptor(generator);
      String feature = (ft_desc == null)? generator: ft_desc.feature;
      if (feature.indexOf("CIELabLabeledRgnFts") >= 0) {
        HashMap means = gmm.getComponentMeans();
        HashMap covs = gmm.getComponentCovariance();
        HashMap probs = gmm.getWeights();
        ArrayList sorted_key = new ArrayList(means.keySet());
        Collections.sort(sorted_key);
        int i_index = 0;
        for (Iterator i = sorted_key.iterator(); i.hasNext(); i_index++) {
          Integer key = (Integer) i.next();
          double[] cie = (double[]) means.get(key);
//          // label 0 is background.  region 0 -> label 1
//          int c = java.awt.Color.HSBtoRGB( (float) hsi[0], (float) hsi[1],
//                                          (float) hsi[2]);
          int c = yi.iu.processor.color.CIELab.lab2rgb(cie);

          color_maps[i_index] = c;
          int red = (c >> 16) & 0xff, green = (c >> 8) & 0xff, blue = c & 0xff;
          double prob = ( (Double) probs.get(key)).doubleValue();
          System.out.println("component " + key + " : " + nf.format(prob * 100) +
                             "%");
          System.out.println("CIELab (" + cie[0] + ", " + cie[1] + ", " + cie[2] + ")");
          System.out.println("rgb (" + red + ", " + green + ", " + blue + ")");
          System.out.println();
          double[][] cov = (double[][]) covs.get(key);
          for (int k=0; k<cov.length; k++) {
            for (int j=0; j<cov[k].length; j++) {
              cov[k][j] = Math.sqrt(cov[k][j]);
            }
          }
          OutputResult.print(System.out, cov);
          System.out.println();
        }
      } else if (feature.indexOf("LabeledRgnFtsWithPxlValue") >= 0) {
        HashMap means = gmm.getComponentMeans();
        HashMap covs = gmm.getComponentCovariance();
        HashMap probs = gmm.getWeights();
        ArrayList sorted_key = new ArrayList(means.keySet());
        Collections.sort(sorted_key);
        int i_index = 0;
        for (Iterator i = sorted_key.iterator(); i.hasNext(); i_index++) {
          Integer key = (Integer) i.next();
          double[] hsi = (double[]) means.get(key);
          int c = java.awt.Color.HSBtoRGB( (float) hsi[0], (float) hsi[1],
                                          (float) hsi[2]);
//          int c = yi.iu.processor.color.CIELab.lab2rgb(cie);
//
          color_maps[i_index] = c;
          int red = (c >> 16) & 0xff, green = (c >> 8) & 0xff, blue = c & 0xff;
          double prob = ( (Double) probs.get(key)).doubleValue();
          System.out.println("component " + key + " : " + nf.format(prob * 100) +
                             "%");
          System.out.println("hsi (" + hsi[0] + ", " + hsi[1] + ", " + hsi[2] +
                             ")");
          System.out.println("rgb (" + red + ", " + green + ", " + blue + ")");
          System.out.println();
          double[][] cov = (double[][]) covs.get(key);
          for (int k = 0; k < cov.length; k++) {
            for (int j = 0; j < cov[k].length; j++) {
              cov[k][j] = Math.sqrt(cov[k][j]);
            }
          }
          OutputResult.print(System.out, cov);
          System.out.println();
        }
      }

    }
    return color_maps;

  }


  /**
   * We need a way to demonstrate the Gaussian components (clusters) of the
   * features in the first-layer (EM step).  Since it is not always possible
   * to show the results from the clustered results (for example, we can use
   * the cluster center to demonstrate color clusters, but it is difficult to
   * demonstrate texture cluster using the cluster center).  Alternatively,
   * given some images, we can pick up the regions having high likelihood for
   * each Gaussian component.  Those region can give us the idea about the
   * clusters
   */
  public static class DemoGaussianClusterTops {

    public final static String USAGE = "Usage: [-d img_data_center_file] -e experiment_file [-c use_cross_valid_fold] [-i img_list_file] [-f feature] [-o object] [-n top_n] [-y(only use positive images)] [-l(use learing images)] [-t(use testing image)]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      String learn_path = Configuration.getParameter("LEARNING_PATH", "");

      try {

        // read in an ObjGMMAdapter object
        String exp_file = ReadArgu.getOption('e', args, null);
        if (exp_file == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        String cross_valid_fold_str = ReadArgu.getOption('c', args, "0");
        int cross_valid_fold = Integer.parseInt(cross_valid_fold_str);

        StatModelFoldExperiment exp = StatModelFoldExperiment.createFromFile(
            learn_path + exp_file);
        GMMNNAdapter gmmnn = (GMMNNAdapter)exp.m_models[cross_valid_fold];

        // init the datacenter
        String data_center_name = ReadArgu.getOption(
            'd', args);

        ImgDataCenter data_center = (data_center_name == null) ?
            gmmnn.m_imgDataCenter :
            ImgDataCenter.createFromFile(data_center_name);


        // image list
        LinkedList img_list = null;

        boolean use_learning_images = Boolean.valueOf(ReadArgu.getOption('l', args)).booleanValue();
        if (use_learning_images) {
          img_list = ((TwoLinkedListData)exp.m_folds.get(cross_valid_fold)).first;
        }
        else {
          boolean use_testing_images = Boolean.valueOf(ReadArgu.getOption('t', args)).booleanValue();
          if (use_testing_images) {
            img_list = ((TwoLinkedListData)exp.m_folds.get(cross_valid_fold)).second;
          } else {
            // get the image list file name
            String img_list_file = ReadArgu.getOption('i', args);

            if (img_list_file != null) {
              String[] images = yi.util.io.ReadArgu.readStrings(img_list_file);
              img_list = yi.util.data.Conversion.asLinkedList(images);
            }
            else {
              img_list = gmmnn.m_imageList;
            }
          }
        }


        String image_path = Configuration.getParameter("IMAGE_PATH", "");

        // object
        String obj_str = ReadArgu.getOption('o', args);
        if (obj_str == null) {
          obj_str = (String) gmmnn.m_objectList.getFirst();
        }
        String obj = obj_str.replace('_', ' ');
        System.out.println("object : " + obj);

        // feature
        FeatureDescriptor[] ft_descs = FeatureDescriptor.parse(
            ReadArgu.getOption('f', args), data_center.getFeatureList());

        // represent
        RepresentationDescriptor[] rep_descs = RepresentationDescriptor.parse(
            ReadArgu.getOption('r', args), data_center.getFeatureList());


        PrintStream out = new PrintStream(new FileOutputStream(
            yi.util.data.StringUtil.getFileSuffixNameRemoved(exp_file) +
            ".tops.html"));

        for (int i_ft = 0; i_ft < ft_descs.length; i_ft++) {
          String generator = ft_descs[i_ft].generator;
          generator = yi.util.data.StringUtil.getClassShortName(generator);
          System.out.println("feature : " + generator);

          String top_n = ReadArgu.getOption('n', args, "10");
          int n = Integer.parseInt(top_n);

          String only_use_pos_str = ReadArgu.getOption('y', args, "false");
          boolean only_use_pos = Boolean.valueOf(only_use_pos_str).booleanValue();

          // finds out how many Gaussian components
          GaussianMixtureModel gmm = (GaussianMixtureModel) gmmnn.m_gmmModels.
              get(obj, generator);
          int n_components = gmm.getRstNumComponents();
          System.out.println("component# " + n_components);

          // keep the top scores for each components
          HashMap top_score_map = new HashMap();
          for (Iterator i = img_list.iterator(); i.hasNext(); ) {
            String img = (String) i.next();

            // if only use positive and the image doesn't contain the object
            if (only_use_pos) {
              // see whether it is a positive image
              LinkedList objs = data_center.getImgDescriptions(img);
              if (objs == null || !objs.contains(obj)) { // right positive
                continue;
              }
            }

            // get the feature vectors
            FeatureRepresentationList features = data_center.
                getImgFeatureList(
                img, // image name
                generator);
            // if the image doesn't has any feature vector for the feature, then the
            // score for the components should be the minimum
            if (features.size() == 0)
              continue;

            for (Iterator f = features.iterator(); f.hasNext(); ) {
              double[] d = ( (FeatureRepresentation) f.next()).data;
              HashMap scores = gmm.getDensitiesUnderComponents(d, false, false);
              // consider the scores
              for (Iterator comp = scores.keySet().iterator(); comp.hasNext(); ) {
                Object comp_key = comp.next();
                double score = ( (Double) scores.get(comp_key)).doubleValue();
                if (!top_score_map.containsKey(comp_key)) {
                  top_score_map.put(comp_key, new TopKeeper(n));
                }
                TopKeeper top = (TopKeeper) top_score_map.get(comp_key);
                top.insert(new FeatureLikelihood(img, d, score), score);
              }
            }
          }

          out.println("<h3>" + generator + "</h3>");

          // print out the top ones different components
          ArrayList components = new ArrayList(top_score_map.keySet());
          java.util.Collections.sort(components);
          int i_index = 0;
          for (Iterator i = components.iterator(); i.hasNext(); i_index++) {
            Object component = i.next();
            out.println("<h3>" + component + "</h3>");
            GaussianComponent gc = (GaussianComponent)gmm.getGaussianComponent(component);
            double[] means = gc.getMeans();
            double p = gmm.getWeight(component);
            out.print(p);
            out.println("<br>");
            OutputResult.print(out, means);
            out.println("<table><tr>");

            // top scores
            TopKeeper top = (TopKeeper) top_score_map.get(component);
            Vector tops = top.getTopObjects();
            for (int t = 0; t < tops.size(); t++) {
              FeatureLikelihood fl = (FeatureLikelihood) tops.elementAt(t);
              // see whether it is a positive image
              LinkedList objs = data_center.getImgDescriptions(fl.imageName);
              boolean contains = (objs != null && objs.contains(obj)) ? true : false;
              out.print("<td><font size=\"-2\" color=" +
                        (contains ? "#FF0000" : "#00FF00") + ">" +
                        fl.likelihood + "</font><br>");

//            Dimension d = new Dimension();
//            int[] orig_img = ImageIOUtil.read(orig_img_path + fl.imageName, d);
//            Dimension old_d = (Dimension) d.clone();
//
//            // rescale the image if necessary
//            double the_scale = 1.0;
//            if (scale != 1 && scale != 0) {
//              ResampledDescriptor resampled = Resampler.scaleDown(orig_img, d,
//                  Resampler.NEAREST_RESAMPLER, scale);
//              if (resampled != null) {
//                orig_img = resampled.m_resampled;
//                d = resampled.m_newDimension;
//                the_scale = resampled.m_scale;
//              }
//            }

              // reset the input (serialiable) index
              yi.util.io.input.DIU_Ser.resetIndex();

              FeatureRepresentable data = FeatureUtil.readFeature(
                  fl.imageName, ft_descs[i_ft]);

              if (data instanceof FeaturePickDemoable) {
                // get the feature vectors
                FeatureRepresentationList features = data_center.
                    getImgFeatureList(
                    fl.imageName, // image name
                    generator);
                boolean[] picked = new boolean[features.size()];

                int f_index = 0;
                for (Iterator f = features.iterator(); f.hasNext(); f_index++) {
                  double[] d = ( (FeatureRepresentation) f.next()).data;
                  picked[f_index] = (d == fl.featureVector) ? true : false;
                  if (picked[f_index]) {
                    OutputResult.print(out, d);
                    out.print("<br>");
                  }
                }

                Dimension demo_d = new Dimension();
                int[] demo = ( (FeaturePickDemoable) data).
                    drawPickedFeatures(picked,
                                       image_path + fl.imageName,
                                       0xffffffff, demo_d, rep_descs[i_ft].mode);
                String demo_name = yi.util.data.StringUtil.
                    getFileSuffixNameRemoved(exp_file) +
                    "." + generator + "." + obj_str + "." + component + "." + t +
                    ".png";
                ImageIOUtil.write(demo_name,
                                  "png", demo, demo_d.width, demo_d.height);
                out.print("<a href=\"" + image_path + fl.imageName +
                                 "\"><img src=" + image_path + fl.imageName +
                                 "></a>" +
                                 "<a href=\"" + demo_name +
                                 "\"><img src=" + demo_name +
                                 " border=1>" +
                                 "</a><br>");
              }
              else {
                System.out.print(data.getClass().getName() +
                                 " is not FeaturePickDemoable");
              }
              out.println("</td>");
            }
            out.println("</tr></table>");
          }
        }
        out.close();

      }

      catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * Use mean color to demonstrate the color clusters
   */
  public static class DemoGaussianClusterMeans {

    public final static String USAGE = "Usage: [-d img_data_center_file] [-c use_cross_valid_fold] -e experiment_file [-f feature] [-o object]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      String represent_path = Configuration.getParameter("REPRESENTATION_PATH", "");
      String learn_path = Configuration.getParameter("LEARNING_PATH", "");

      try {

        // read in an ObjGMMAdapter object
        String exp_file = ReadArgu.getOption('e', args, null);
        if (exp_file == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        String cross_valid_fold_str = ReadArgu.getOption('c', args, "0");
        int cross_valid_fold = Integer.parseInt(cross_valid_fold_str);

        StatModelFoldExperiment exp = StatModelFoldExperiment.createFromFile(
            learn_path + exp_file);
        GMMNNAdapter gmmnn = (GMMNNAdapter)exp.m_models[cross_valid_fold];

        // init the datacenter
        String data_center_name = ReadArgu.getOption(
            'd', args);

        ImgDataCenter data_center = (data_center_name == null) ?
            gmmnn.m_imgDataCenter :
            ImgDataCenter.createFromFile(represent_path + data_center_name);

        // feature
        String feature = ReadArgu.getOption('f', args);
        if (feature == null) {
          feature = (String) gmmnn.m_featureList.getFirst();
        }
        System.out.println("feature : " + feature);

        // object
        String obj = ReadArgu.getOption('o', args);
        if (obj == null) {
          obj = (String) gmmnn.m_objectList.getFirst();
        }
        obj = obj.replace('_', ' ');
        System.out.println("object : " + obj);
        String obj_str = obj.replace(' ' , '_');

        // each component corresponding to one color
        int[] color_maps = gmmnn.getGMMComponentMeanInColor(obj, feature);
        int width = 64, height = 64;
        int[] demo = yi.iu.processor.color.ColorUtil.createSpectrum(color_maps, width, height);
        String out_name = yi.util.data.StringUtil.getFileSuffixNameRemoved(exp_file) + "." +
                          yi.util.data.StringUtil.getClassShortName(feature) + "." +
                          obj_str + ".mc.png";

        ImageIOUtil.write(learn_path + out_name,
                          "png", demo, width * color_maps.length, height);
        System.out.print("mean color spectrum output to " + learn_path + out_name);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }


  /**
   * For each image region, classify it according to GMM results, and output
   * the resultant clustering image
   */
  public static class DemoGMMClassification {

    public final static String USAGE = "Usage: [-d img_data_center_file] -e experiment_file [-c use_cross_valid_fold] [-i img_list_file] [-f feature] [-o object] [-y(only use positive images)] [-l(use learing images)] [-t(use testing image)]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      String represent_path = Configuration.getParameter("REPRESENTATION_PATH", "");
      String learn_path = Configuration.getParameter("LEARNING_PATH", "");
      String image_path = Configuration.getParameter("IMAGE_PATH", "");

      try {

        // read in an ObjGMMAdapter object
        String exp_file = ReadArgu.getOption('e', args, null);
        if (exp_file == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        String cross_valid_fold_str = ReadArgu.getOption('c', args, "0");
        int cross_valid_fold = Integer.parseInt(cross_valid_fold_str);

        StatModelFoldExperiment exp = StatModelFoldExperiment.createFromFile(
            learn_path + exp_file);
        GMMNNAdapter gmmnn = (GMMNNAdapter)exp.m_models[cross_valid_fold];

        // init the datacenter
        String data_center_name = ReadArgu.getOption(
            'd', args);

        ImgDataCenter data_center = (data_center_name == null) ?
            gmmnn.m_imgDataCenter :
            ImgDataCenter.createFromFile(represent_path + data_center_name);

        // image list
        LinkedList img_list = null;

        boolean use_learning_images = Boolean.valueOf(ReadArgu.getOption('l', args)).booleanValue();
        if (use_learning_images) {
          img_list = ((TwoLinkedListData)exp.m_folds.get(cross_valid_fold)).first;
        }
        else {
          boolean use_testing_images = Boolean.valueOf(ReadArgu.getOption('t', args)).booleanValue();
          if (use_testing_images) {
            img_list = ((TwoLinkedListData)exp.m_folds.get(cross_valid_fold)).second;
          } else {
            // get the image list file name
            String img_list_file = ReadArgu.getOption('i', args);

            if (img_list_file != null) {
              String[] images = yi.util.io.ReadArgu.readStrings(img_list_file);
              img_list = yi.util.data.Conversion.asLinkedList(images);
            }
            else {
              img_list = gmmnn.m_imageList;
            }
          }
        }

        // feature
        FeatureDescriptor[] ft_descs = FeatureDescriptor.parse(
            ReadArgu.getOption('f', args), data_center.getFeatureList());
        String feature = yi.util.data.StringUtil.getClassShortName(ft_descs[0].generator);

        // represent
        RepresentationDescriptor[] rep_descs = RepresentationDescriptor.parse(
            ReadArgu.getOption('r', args), data_center.getFeatureList());

//        // get the feature space size
//        double[][] feature_data = data_center.groupFeatureVectors(img_list,
//            feature);
//        double[] mins = yi.stat.StatUtil.minimumOnCol(feature_data);
//        double[] maxs = yi.stat.StatUtil.maximumOnCol(feature_data);
//        for (int i = 0; i < mins.length; i++) {
//          System.out.println("dim #" + i + ", min = " + mins[i] + ", max = " +
//                             maxs[i]);
//        }
//        // find the probability for the uniform distribution
//        double uniform_dist = 1;
//        for (int i = 0; i < mins.length; i++) {
//          uniform_dist *= 1.0 / (maxs[i] - mins[i]);
//        }
//        System.out.println("uniform distribution density = " + uniform_dist);
//        uniform_dist *= 1;
//        System.out.println("use uniform distribution density = " + uniform_dist);

        // object
        String obj = ReadArgu.getOption('o', args);
        if (obj == null) {
          obj = (String) gmmnn.m_objectList.getFirst();
        }
        obj = obj.replace('_', ' ');
        System.out.println("object : " + obj);
        String obj_str = obj.replace(' ', '_');

        String only_use_pos_str = ReadArgu.getOption('y', args, "false");
        boolean only_use_pos = Boolean.valueOf(only_use_pos_str).booleanValue();

        // finds out how many Gaussian components
        GaussianMixtureModel gmm = (GaussianMixtureModel) gmmnn.m_gmmModels.get(
            obj, feature);
        int n_components = gmm.getRstNumComponents();
        System.out.println("component# " + n_components);

        // each component corresponding to one color
        int[] color_maps = gmmnn.getGMMComponentMeanInColor(obj, feature);
        // make label 0 for un-classified regions
        int[] color_maps_bg = new int[color_maps.length + 1];
        for (int i=0; i<color_maps.length; i++) color_maps_bg[i+1] = color_maps[i];
        color_maps_bg[0] = 0xffff0000;

        // minimum allowed score on each component
        double minimum = ((Double)gmmnn.m_gmmUniformDensity.get(obj, feature)).doubleValue();
        double log_min = Math.log(minimum);

        System.out.println("use minimums: " + minimum);
        double[] log_maxs = gmmnn.getLogMaxComponentProbs(obj, feature, true);

        for (Iterator i = img_list.iterator(); i.hasNext(); ) {
          String img = (String) i.next();

          // if only use positive and the image doesn't contain the object
          boolean contains = data_center.getImgDescriptions(img).contains(obj);
          if (only_use_pos && !contains) {
              continue;
          }

          // get the feature vectors
          FeatureRepresentationList features = data_center.
              getImgFeatureList(
              img, // image name
              feature);
//          // if the image doesn't has any feature vector for the feature, then the
//          // score for the components should be the minimum
//          if (features.size() == 0)
//            continue;

          // classify feature to one component
          int[] feature_component_map = new int[features.size()];
          // the classification of feature to component has a prob.
          // is the normalized prob.  prob/max_prob, so [0,1]
          double[] ft_comp_map_score = new double[features.size()];
          int f_index = 0;
          for (Iterator f = features.iterator(); f.hasNext(); f_index++) {
            FeatureRepresentation fr = (FeatureRepresentation) f.next();
            double[] d = fr.data;

            Object classify_to = (Object)gmm.classify(d);
            int index = gmm.objectToRstComponentIndex(classify_to);
            GaussianComponent gc = gmm.getGaussianComponent(classify_to);
            double highest_score = gc.getDensity(d);
            if (highest_score < minimum) {
              index = -1;
              highest_score = minimum;
            }

            feature_component_map[f_index] = index + 1;
            ft_comp_map_score[f_index] =
                (Math.log(highest_score) - log_min)/(log_maxs[index] - log_min);
          }

          FeatureRepresentable data = FeatureUtil.readFeature(
                  img, ft_descs[0]);

          if (data instanceof LabeledRgnFtRepresentable) {
            LabeledRgnFtRepList labeld_rep = ((LabeledRgnFtRepresentable)data).representLabeledRgnFts(
                feature, rep_descs[0].mode);
            int[] demo = labeld_rep.drawClassificationDemo(feature_component_map, ft_comp_map_score, color_maps_bg);
            String demo_name = learn_path + img +
                yi.util.data.StringUtil.getFileSuffixNameRemoved(exp_file) +
                "." + feature + "." + obj_str + ".cls" +
                ".png";
            int[] prob_demo = labeld_rep.drawProbabilityDemo(feature_component_map, ft_comp_map_score, color_maps_bg);
            String prob_demo_name = learn_path + img +
                yi.util.data.StringUtil.getFileSuffixNameRemoved(exp_file) +
                "." + feature + "." + obj_str + ".prb" +
                ".png";
            yi.util.io.File.makeDirectory(demo_name, true);
            ImageIOUtil.write(demo_name,
                              "png", demo, labeld_rep.imgWidth, labeld_rep.imgHeight);
            ImageIOUtil.write(prob_demo_name,
                              "png", prob_demo, labeld_rep.imgWidth, labeld_rep.imgHeight);
            String cdemo = FeatureDescriptor.getFeatureColorDemoName(ft_descs[0], img, true);
            String gdemo = FeatureDescriptor.getFeatureGraphicDemoName(ft_descs[0], img, true);
            System.out.println("<a href=\"" + image_path + img + "\">" +
                             " <img src=" + image_path + img + "></a>" +
                             "<a href=\"" + cdemo + "\">" +
                             "<img src=" + cdemo + " border=1>" + "</a>" +
                             "<a href=\"" + gdemo + "\">" +
                             "<img src=" + gdemo + " border=1>" + "</a>" +
                             "<a href=\"" + prob_demo_name + "\">" +
                             "<img src=" + prob_demo_name + " border=1>" + "</a>" +
                             "<a href=\"" + demo_name + "\">" +
                             "<img src=" + demo_name + " border=1>" + "</a><br>");

          }

//          // draw the demo image
//          if (features instanceof EllipseFeatureRepresentationList) {
//            int width = ( (EllipseFeatureRepresentationList) features).imgWidth;
//            int height = ( (EllipseFeatureRepresentationList) features).
//                imgHeight;
//            int[] demo = new int[width * height];
//            Arrays.fill(demo, 0);
//
//            for (Iterator f = features.iterator(); f.hasNext(); ) {
//              EllipseFeatureRepresentation fr = (EllipseFeatureRepresentation)
//                  f.next();
//              int label_index = ( (Integer) feature_component_map.get(fr)).
//                  intValue();
//              label_index++; // from -1 is bg, 0, .., n-1 is the component index
//              // to 0 is bg, 1,.., n is the component index
//              fr.region.drawApproximateContour(demo, width, height, label_index,
//                                               2, 8);
//
//            }
//            demo = ImageUtil.makeNewImageFromLabel(demo, color_maps);
//            String demo_img_name = data_path + img + "." +
//                              yi.util.io.output.DOU.shortName(feature) + "." +
//                              obj_str + ".cls.png";
//
//            ImageIOUtil.write(demo_img_name,
//                              "png", demo, width, height);
//
//            System.out.println("<font size=\"-2\" color=" +
//                               (contains ? "#FF0000" : "#00FF00") + ">" +
//                              img + "</font><br>");
//
//            System.out.print("<a href=\"" + orig_img_path + img +
//                             "\"><img src=" + orig_img_path + img +
//                             " width=256></a>");
//            System.out.print("<a href=\"" + data_path + img + ".s128.ms.rgn.gif" +
//                             "\"><img src=" + data_path + img + ".s128.ms.rgn.gif" +
//                             "></a>");
//            System.out.print("<a href=\"" + data_path + img + ".s128.ms.rgnc.gif" +
//                             "\"><img src=" + data_path + img + ".s128.ms.rgnc.gif" +
//                             "></a>");
//
//            System.out.print("<a href=\"" + demo_img_name +
//                             "\"><img src=" + demo_img_name +
//                             " border=1>" +
//                             "</a><br>");
//          }
//          else {
//            System.out.println(ft_ds.getClass().getName() +
//                               " is not EllipseFeatureRepresentationList");
//          }
        }
      }
      catch (Exception e) {
        e.printStackTrace();

      }
    }
  }

  /**
   * Use mean color to demonstrate the color clusters
   */
  public static class DemoNNParameters {

    public final static String USAGE = "Usage: [-d img_data_center_file] -e experiment_file [-f feature] [-o object]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      String represent_path = Configuration.getParameter("REPRESENTATION_PATH", "");
      String learn_path = Configuration.getParameter("LEARNING_PATH", "");

      try {

        // read in an ObjGMMAdapter object
        String exp_file = ReadArgu.getOption('e', args, null);
        if (exp_file == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        StatModelFoldExperiment exp = StatModelFoldExperiment.createFromFile(
            learn_path + exp_file);
        GMMNNAdapter gmmnn = (GMMNNAdapter)exp.m_models[0];

        // init the datacenter
        String data_center_name = ReadArgu.getOption(
            'd', args);

        ImgDataCenter data_center = (data_center_name == null) ?
            gmmnn.m_imgDataCenter :
            ImgDataCenter.createFromFile(represent_path + data_center_name);

        // feature
        String feature = ReadArgu.getOption('f', args);
        if (feature == null) {
          feature = (String) gmmnn.m_featureList.getFirst();
        }
        System.out.println("feature : " + feature);

        // object
        String obj = ReadArgu.getOption('o', args);
        if (obj == null) {
          obj = (String) gmmnn.m_objectList.getFirst();
        }
        obj = obj.replace('_', ' ');
        System.out.println("object : " + obj);
        String obj_str = obj.replace(' ' , '_');

        // finds out how many Gaussian components
        GaussianMixtureModel gmm = (GaussianMixtureModel)gmmnn.m_gmmModels.get(obj, feature);
        int n_components = gmm.getRstNumComponents();
        System.out.println("component# " + n_components);

        // print out neural net parameters
        NeuralNet nn = (NeuralNet)gmmnn.m_nnModels.get(obj);
        System.out.println("Neural Net parameters:");
        System.out.println(nn);
        System.out.println();

        // generate weight histogram (for demonstration)
        int[] colors = gmmnn.getGMMComponentMeanInColor(obj, feature);
        int chart_w = 256, chart_h = 256;
        int[][] alphas = nn.demoAlpha(chart_w, chart_h, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
                                      colors);

        String out_name = learn_path + yi.util.data.StringUtil.getFileSuffixNameRemoved(exp_file) + "." +
                          obj_str;

        for (int i=0; i<alphas.length; i++) {
          OutputResult.outputGIFImage(out_name + ".alpha." + i +".gif", alphas[i], chart_w, chart_h);
        }

        int[][] betas = nn.demoBeta(chart_w, chart_h, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
                                      null);
        for (int i=0; i<betas.length; i++) {
          OutputResult.outputGIFImage(out_name + ".beta." + i +".gif", betas[i], chart_w, chart_h);
        }

      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }



  public static class DemoScore {

    public final static String USAGE = "Usage: [-d img_data_center_file] [-m model_file] [-i img_list_file] [-f feature]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      try {

        // read in an ObjGMMAdapter object
        String model_file = ReadArgu.getOption(
            'm', args,
            "model0.ser"
            );

        ObjGMMAdapter gmm = (ObjGMMAdapter)ObjGMMAdapter.createFromFile(model_file);

        // init the datacenter
        String data_center_name = ReadArgu.getOption(
            'd', args,
            "groundtruth.rgn.ser"
            );

        ImgDataCenter data_center = ImgDataCenter.createFromFile(
            data_center_name);

        // get the image list file name
        String img_list_file = ReadArgu.getOption('i', args);

        // image list
        LinkedList img_list = null;
        if (img_list_file != null) {
          String[] images = yi.util.io.ReadArgu.readStrings(img_list_file);
          img_list = yi.util.data.Conversion.asLinkedList(images);
        }

        /** @todo custom the feature name */
        String feature = ReadArgu.getOption('f', args, "yi.iu.processor.texture.reg.DS_PolygonRegionTextureStat");

        HashMap rst_file_suffix_map = new HashMap();
        rst_file_suffix_map.put("yi.iu.processor.texture.reg.DS_PolygonRegionTextureStat", ".s256.text.reg.gif");
        rst_file_suffix_map.put("yi.iu.processor.color.reg.DS_PolygonRegionColorStat", ".s256.color.reg.gif");
        rst_file_suffix_map.put("yi.iu.processor.clc.LineClusterSets", ".CLC_LineStruct.bound.gif");

        String obj = ReadArgu.getOption(
            'd', args,
            "tree"
            );


        // loop through the image and their features
        for (Iterator i=img_list.iterator(); i.hasNext();) {
          String img = (String)i.next();
          PolygonFeatureRepresentationList rfrl =
              (PolygonFeatureRepresentationList)data_center.getImgFeatureList(img, feature);
          int[] score_img = rfrl.drawScoreDemo(obj, gmm, 128, 10);
          for (int j=0; j<score_img.length; j++) {
            if (score_img[j] < 0) score_img[j] = 0;
            if (score_img[j] > 255) score_img[j] = 255;
          }
          int[] pixels_img = yi.util.ImageUtil.makeNewImageFromGray(score_img);
          yi.util.io.OutputResult.outputGIFImage(img + ".score.gif", pixels_img,
                                                 rfrl.imgWidth, rfrl.imgHeight);
          System.out.println("<P>" +
                             "<IMG SRC=\"" + img + "\" width=256>" +
                             "<IMG SRC=\"" + img + (String)rst_file_suffix_map.get(feature) + "\" width=256>" +
                             "<IMG SRC=\"" + img + ".score.gif\" width=256>" +
                             "</P>");
        }

      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
  public DoubleScoreHashMap getVerificationErrors() {
    return m_nnVerifyErrors;
  }

//  public static class Test {
//
//    public final static String USAGE = "Usage: [-d img_data_center_file] [-m model_file] [-i img_list_file] [-f feature] [-r rst_file_name]";
//
//    public static void main(String[] args) {
//
//      // init configuration
//      Configuration.init(args);
//
//      try {
//        // read in an ObjGMMAdapter object
//        String model_file = ReadArgu.getOption(
//            'm', args,
//            "model0.ser"
//            );
//
//        GMMNNAdapter gmmnn = (GMMNNAdapter)GMMNNAdapter.createFromFile(model_file);
//
//        // init the datacenter
//        String data_center_name = ReadArgu.getOption(
//            'd', args,
//            "groundtruth.ser"
//            );
//
//        ImgDataCenter data_center = ImgDataCenter.createFromFile(
//            data_center_name);
//
//        // get the image list file name
//        String img_list_file = ReadArgu.getOption('i', args);
//
//        // image list
//        LinkedList img_list = null;
//        if (img_list_file != null) {
//          String[] images = yi.util.io.ReadArgu.readStrings(img_list_file);
//          img_list = yi.util.data.Conversion.asLinkedList(images);
//        }
//
//        // System.out.println(gmm.m_models);
//
//        gmmnn.VERBOSE_IN_TEST = true;
//        StatTestRst rst = gmmnn.test(img_list);
//
//        HashMap rpcs = rst.rpcAnalysis(data_center); // right positive curves
//
//
//        DoubleScoreHashMap roc_scores = rst.getROCScores(rpcs);
//        System.out.println("roc scores:");
//        System.out.println(roc_scores.toStringBykeyOrder());
//
//        Plot2DPanel pp = rst.drawROCs(rpcs);
//
//        String rst_output_file = ReadArgu.getOption('r', args);
//        if (rst_output_file != null) {
//          pp.setFixedBounds(0,0,1);
//          pp.setFixedBounds(1,0,1);
//          pp.invalidate();
//          pp.toGraphicFile(new java.io.File(rst_output_file + ".png"));
//        }
//
//      } catch (Exception e) {
//        e.printStackTrace();
//      }
//    }
//  }

//  public static class NNTest {
//    public static void main(String[] args) {
//
//      int correct = 0;
//      try {
//        String target_name = ReadArgu.getOption('i', args);
//        if (target_name == null) {
//          System.err.println("Usage: -i target_name");
//          System.exit(1);
//        }
//
//        String test_name = target_name + ".test.dat_3.rst";
//        String cat_name = target_name + ".test.cat_4.rst";
//
//        NeuralNet mtarget = (NeuralNet) NeuralNet.createFromFile(target_name + ".nn");
//
//        // read the data
//        double[][] data = yi.util.io.ReadArgu.loadMatlab(test_name);
//        double[][] categories = yi.util.io.ReadArgu.loadMatlab(cat_name);
//
//        for (int i=0; i<data.length; i++) {
//
//          // calculate the score on target
//          double[] score_on_target = mtarget.test(data[i], false);
//
//          // then the category
//          int c = (int) Math.round(categories[i][0]);
//          double c1 = score_on_target[0];
//          int c2 = (int)Math.round(c1);
//          if (c2 == c) correct ++;
//          System.out.println("" + c + ": " + c1);
//        }
//
//        System.out.println("correct rate = " + correct + "/" + data.length);
//
//      } catch (Exception e) {
//        e.printStackTrace();
//      }
//    }
//  }
//
//  public static class NNTrain {
//
//    protected static NeuralNet trainOneTarget(double[][] pos_features, double[][] neg_features) throws Exception{
//
//      System.out.println("pos data = " + pos_features.length);
//      System.out.println("neg data = " + neg_features.length);
//      int scale = neg_features.length/pos_features.length;
//      System.out.println("scale = " + scale);
//
//      // construct the data
//      double[][] d = new double[pos_features.length * scale + neg_features.length][];
//      double[][] target = new double[pos_features.length * scale + neg_features.length][1];
//      int index = 0;
//      for (int s=0; s<scale; s++) {
//        for (int i=0; i<pos_features.length; i++) {
//          target[index][0] = 1;
//          d[index++] = (double[])pos_features[i].clone();
//        }
//      }
//      System.out.println("pos instance = " + index);
//      for (int i=0; i<neg_features.length; i++) {
//        target[index][0] = 0;
//        d[index++] = neg_features[i];
//      }
//
//      System.out.println("total data length = " + d.length);
//
//      NeuralNet nn = new NeuralNet(d[0].length, 4, 1, 0.05);
//      nn.train(d, target, 200);
//      return nn;
//    }

//    public static void main(String[] args) {
//
//      String target_name = ReadArgu.getOption('i', args);
//      if (target_name == null) {
//        System.err.println("Usage: -i target_name");
//        System.exit(1);
//      }
//
//      try {
//        String pos_instances = target_name + ".pos.dat_1.rst";
//        String neg_instances = target_name + ".neg.dat_2.rst";
//
//        double[][] pos_features = yi.util.io.ReadArgu.loadMatlab(pos_instances);
//        double[][] neg_features = yi.util.io.ReadArgu.loadMatlab(neg_instances);
//
//        // train and compare the model
//        NeuralNet model = trainOneTarget(pos_features, neg_features);
//        model.writeToFile(target_name + ".nn");
//
//      } catch (Exception e) {
//        e.printStackTrace();
//      }
//    }
//
//  }
//
}

/**
 * Save the image feature (with its image name) and its likelihood
 */
class FeatureLikelihood {
  String imageName;
  double[] featureVector;
  double likelihood;
  FeatureLikelihood(String image_name, double[] feature_vector, double likelihood) {
    imageName = image_name;
    featureVector = feature_vector;
    this.likelihood = likelihood;
  }
}

