package yi.iu.imgdb.learn.absrgn;


import java.util.LinkedList;
import java.util.*;
import yi.util.io.*;
import yi.stat.em.*;
import org.jmat.data.*;
import org.jmat.gui.*;
import yi.ds.*;
import yi.util.io.output.*;
import yi.util.config.*;
import yi.ds.hash.*;
import yi.stat.nn.*;
import java.io.*;
import yi.ds.sort.*;
import java.awt.*;
import yi.iu.processor.resample.*;
import yi.util.*;
import yi.stat.*;
import yi.util.io.input.*;

import yi.iu.imgdb.learn.*;

/**
 * Abstract region relationship learner.
 */

public class AbsRgnRelationLearner {
  public AbsRgnRelationLearner() {
  }

  /**
   * Get the total component number for recognize one object.  Each feature has
   * some component (clusters), so totally, ...
   * @param gmmnn
   * @param obj
   * @param features
   * @return
   */
  public static int getTotalComponentNum(GMMNNAdapter gmmnn, 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) gmmnn.getGMMModels().get(
          obj, feature);
      int n_components = gmm.getRstNumComponents();
//      System.out.println("" + feature + " component# " + n_components);
      total_components += n_components;
    }
    return total_components;
  }

  /**
   * Use the training feature vector data (to get the maximum and minimum value
   * on each dimension), and calculate the uniform distribution
   * density.
   * @param feature_data
   * @return
   */
  public static double getUniformDistDensity(double[][] feature_data) {

    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);
    return uniform_dist;
  }

  public static ObjectWithScore classifiedToComponent(AbsRgnFeatureRepresentation fr,
                                             GaussianMixtureModel gmm,
                                             double uniform_dist) {
    double[] d = fr.data;
    HashMap scores = gmm.getDensitiesUnderComponents(d, true, false);
    // find out the highest score
    double highest_score = Double.NEGATIVE_INFINITY;
    Object obj_highest_score = null;
    ArrayList sorted_key = new ArrayList(scores.keySet());
    Collections.sort(sorted_key);
    for (Iterator comp = sorted_key.iterator(); comp.hasNext(); ) {
      Object comp_key = comp.next();
      double score = ( (Double) scores.get(comp_key)).doubleValue();
//              System.out.println("  " + comp_key + " " + score);
      if (score > highest_score) {
        highest_score = score;
        obj_highest_score = comp_key;
      }
    }

    // if the highest density is less than the uniform density, then
    // the feature is not from any of the components
    if (highest_score < uniform_dist) {
      obj_highest_score = null;
    }
    return new ObjectWithScore(obj_highest_score, highest_score);
  }


  /**
   * Classify each region in each image.  for each feature cluster, find out
   * which regions are classified to it.  The returned Linkedlist is indexed
   * by cluster (all the clusters from all the features).  Each element is a
   * AbsRgnProbMap array in which every entry is the region->score map for an
   * image
   *
   * @param data_center
   * @param features
   * @param img_list
   * @param obj
   * @param gmmnn
   * @param scales
   * @param min_scale
   * @param uniform_probs return the uniform distibution density
   * @return
   */
  public static LinkedList getAbstractRegionsForAllClusters(ImgDataCenter data_center,
      LinkedList features, LinkedList img_list, String obj, GMMNNAdapter gmmnn,
      int[] scales, int min_scale, double[] uniform_probs) {

    int img_n = img_list.size();
    int total_components = getTotalComponentNum(gmmnn, obj, features);

    // save the regions for each cluster for each image
    // each one is an instance of AbsRgnPRobMapList
    LinkedList abs_rgns = new LinkedList();
    for (int j=0; j<total_components; j++) {
      AbsRgnProbMap[] cand_relation = new AbsRgnProbMap[img_n];
      for (int i=0; i<img_n; i++) {
        cand_relation[i] = new AbsRgnProbMap();
      }
      abs_rgns.add(cand_relation);
    }

    int component_count_base = 0; // the first feature begins from 0
                                  // the next will begin from the total count of the previous
    int f_index = 0;
    for (Iterator f=features.iterator(); f.hasNext(); f_index++) {
      String feature = (String)f.next();
      System.out.println("consider " + feature);

      // get the feature space size
      double[][] feature_data = data_center.groupFeatureVectors(img_list,
          feature);

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

//      double uniform_dist = getUniformDistDensity(feature_data);
//      for (int i=0; i<n_components; i++) {
//        uniform_probs[component_count_base + i] = uniform_dist;
//      }
      double[] min_on_prob = (double[])gmmnn.getMinComponentProbs().get(feature);
      for (int i=0; i<n_components; i++) {
        uniform_probs[component_count_base + i] = Math.exp(min_on_prob[i]);
      }
      double uniform_dist = Math.exp(min_on_prob[0]);
      System.out.println("fix low prob lower than = " + uniform_dist);

      int i_index = 0;
      for (Iterator i = img_list.iterator(); i.hasNext(); i_index++) {
        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);

        // get the feature vectors
        FeatureRepresentationList feature_vects = 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;

        HashMap feature_component_map = new HashMap(feature_vects.size());
        for (Iterator fv = feature_vects.iterator(); fv.hasNext();) {
//              System.out.println("feature vector # "+ f_index);
          AbsRgnFeatureRepresentation fr = (AbsRgnFeatureRepresentation) fv.next();

          ObjectWithScore classify_to = classifiedToComponent(fr, gmm, uniform_dist);
          Object obj_highest_score = classify_to.object;
          double highest_score = classify_to.score;

          if (obj_highest_score != null) {
            int component_ind = ( (Integer) obj_highest_score).intValue();
            AbstractRegion ar = fr.getAbstractRegion();
            // scale the region if it is not the smallest scale
            if (min_scale != Integer.MAX_VALUE && scales[f_index] != min_scale) {
              if (scales[f_index] == 0) {
                int width = ((RgnFeatureRepresentationList)feature_vects).imgWidth;
                int height = ((RgnFeatureRepresentationList)feature_vects).imgHeight;
                ar.scale((double)min_scale/(double)Math.max(width, height));
              } else {
                ar.scale( (double) min_scale / (double) scales[f_index]);
              }
            }
            int c_index = component_ind + component_count_base;
            ((AbsRgnProbMap [])abs_rgns.get(c_index))[i_index].put(ar, highest_score);
          }
        }
      } // for each image
      // shift component count
      component_count_base += gmm.getRstNumComponents();
    } // for each feature
    return abs_rgns;
  }

  public static boolean[] getPositiveFlags(ImgDataCenter data_center,
                                           LinkedList img_list, String obj) {
    int img_n = img_list.size();
    // positive / negative images
    int[] positive = new int[img_n];
    boolean[] pos_flags = new boolean[img_n];
    int i_index = 0;
    for (Iterator i = img_list.iterator(); i.hasNext(); i_index++) {
      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);
      pos_flags[i_index] = contains;
      positive[i_index] = contains?1:0;
    }
    return pos_flags;
  }

  /**
   * Get the probability of all the bi-relationships.  return double[][].  The
   * first index is the bi-relationship candidates.  In the passed in "abs_rgns",
   * each entry is the image region->score maps for one feature cluster.  each
   * pair of those feature clusters will be considered, and a score will be given
   * to each candidate (first dimension) and each image (second dimension)
   * @param abs_rgns
   * @param img_n
   * @param pos_flags if specified, only positive images will be calculated.
   *        pass in null, if the calculation on negative images are also
   *        necessary
   * @return
   */
  public static double[][] getBiRelationshipProbs(LinkedList abs_rgns, int img_n, boolean[] pos_flags, String output_name) throws java.io.IOException{
    int total_components = abs_rgns.size();

    double[][] overlap_probs = new double[ (total_components * total_components -
                                            total_components) / 2][img_n];
    for (int r1 = 0, r_count = 0; r1 < total_components - 1; r1++) {
      for (int r2 = r1 + 1; r2 < total_components; r2++, r_count++) {
        System.out.println("relation #" + r_count + " (" + r1 + ", " + r2 + ")");
        AbsRgnProbMap[] rel_cand_1 = (AbsRgnProbMap[]) abs_rgns.get(r1);
        AbsRgnProbMap[] rel_cand_2 = (AbsRgnProbMap[]) abs_rgns.get(r2);

        AbsRgnProbMap[] rel_overlap = new AbsRgnProbMap[img_n];

        long startTime = System.currentTimeMillis();
        for (int i_index = 0; i_index < img_n; i_index++) {
          if (i_index % 10 == 0)
            System.out.println("  img " + i_index);
          if (pos_flags != null && !pos_flags[i_index])
            continue;
          AbsRgnProbMap arcc1 = rel_cand_1[i_index];
          AbsRgnProbMap arcc2 = rel_cand_2[i_index];

          AbsRgnProbMap arcc3 = arcc1.intersect(arcc2);
          rel_overlap[i_index ] = arcc3;

          if (arcc3 != null && arcc3.size() > 0) {
            overlap_probs[r_count][i_index] = arcc3.getMaxProbability();
//                overlap_cand[i_index][r_count] = arcc3;
//                int width = 128, height = 128;
//                int[] demo = new int[width * height];
//                Arrays.fill(demo, 0xffffffff);
//                arcc1.drawRegions(demo, width, height, 0xffff0000);
//                arcc2.drawRegions(demo, width, height, 0xff00ff00);
//                arcc3.drawRegions(demo, width, height, 0xff0000ff);
//                yi.util.io.ImageIOUtil.write("arcc" + r1 + "_" + r2 + ".png", "png", demo, width, height);
          }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("running time: " + (endTime - startTime));
        if (output_name != null) {
          DOU_Ser.output(output_name + ".br." + r1 + "." + r2, rel_overlap);
        }
      }
    }
    return overlap_probs;

  }

  public static double[][] readAndCalBiRelationshipProbs(int total_components, int img_n, boolean[] pos_flags, String input_name) throws Exception{

    double[][] overlap_probs = new double[ (total_components * total_components -
                                            total_components) / 2][img_n];
    for (int r1 = 0, r_count = 0; r1 < total_components - 1; r1++) {
      for (int r2 = r1 + 1; r2 < total_components; r2++, r_count++) {
        System.out.println("read relation #" + r_count + " (" + r1 + ", " + r2 + ")");

        AbsRgnProbMap[] rel_overlap = (AbsRgnProbMap[])DIU_Ser.input(input_name + ".br." + r1 + "." + r2);

        long startTime = System.currentTimeMillis();
        for (int i_index = 0; i_index < img_n; i_index++) {

          AbsRgnProbMap arcc3 = rel_overlap[i_index];

          if (arcc3 != null && arcc3.size() > 0) {
            overlap_probs[r_count][i_index] = arcc3.getMaxProbability();
//                overlap_cand[i_index][r_count] = arcc3;
//                int width = 128, height = 128;
//                int[] demo = new int[width * height];
//                Arrays.fill(demo, 0xffffffff);
//                arcc1.drawRegions(demo, width, height, 0xffff0000);
//                arcc2.drawRegions(demo, width, height, 0xff00ff00);
//                arcc3.drawRegions(demo, width, height, 0xff0000ff);
//                yi.util.io.ImageIOUtil.write("arcc" + r1 + "_" + r2 + ".png", "png", demo, width, height);
          }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("running time: " + (endTime - startTime));
      }
    }
    return overlap_probs;
  }

  /**
   * Generate bi-relationship features as specified
   * @param abs_rgns
   * @param bi_rels
   * @param img_n
   */
  public static double[] addBiRelationshipFeatures(
      LinkedList abs_rgns, AbsRgnOverlappingFtGen[] bi_rels, int img_n, double[] min_on_components) {

    double[] new_min = new double[min_on_components.length + bi_rels.length];
    for (int i=0; i<min_on_components.length; i++) new_min[i] = min_on_components[i];

    for (int i=0; i<bi_rels.length; i++) {
      int r1 = bi_rels[i].parent1;
      int r2 = bi_rels[i].parent2;

      System.out.println("relation #" + i + " (" + r1 + ", " + r2 + ")");
      AbsRgnProbMap[] rel_cand_1 = (AbsRgnProbMap[])abs_rgns.get(r1);
      AbsRgnProbMap[] rel_cand_2 = (AbsRgnProbMap[])abs_rgns.get(r2);
      AbsRgnProbMap[] bi_rel = new AbsRgnProbMap[img_n];

      long startTime = System.currentTimeMillis();
      for (int i_index = 0; i_index < img_n; i_index++) {
        if (i_index % 10 == 0) System.out.println("  img " + i_index);
        AbsRgnProbMap arcc1 = rel_cand_1[i_index];
        AbsRgnProbMap arcc2 = rel_cand_2[i_index];

        bi_rel[i_index] = arcc1.intersect(arcc2);
      }
      abs_rgns.add(bi_rel);
      new_min[min_on_components.length + i] = min_on_components[r1] * min_on_components[r2];
    }
    return new_min;

  }

  /**
   * Generate bi-relationship features as specified
   * @param abs_rgns
   * @param bi_rels
   * @param img_n
   */
  public static double[] addBiRelationshipFeatures(
      String input_name,
      LinkedList abs_rgns, AbsRgnOverlappingFtGen[] bi_rels, double[] min_on_components) throws Exception{

    double[] new_min = new double[min_on_components.length + bi_rels.length];
    for (int i=0; i<min_on_components.length; i++) new_min[i] = min_on_components[i];

    for (int i=0; i<bi_rels.length; i++) {
      int r1 = bi_rels[i].parent1;
      int r2 = bi_rels[i].parent2;

      System.out.println("read relation #" + i + " (" + r1 + ", " + r2 + ")");

      long startTime = System.currentTimeMillis();
      AbsRgnProbMap[] bi_rel = (AbsRgnProbMap[])DIU_Ser.input(input_name + ".br." + r1 + "." + r2);
      abs_rgns.add(bi_rel);
      new_min[min_on_components.length + i] = min_on_components[r1] * min_on_components[r2];
    }
    return new_min;

  }

  public static int getPostiveNumber(boolean[] pos_flags) {
    int pos_num = 0;
    for (int i=0; i<pos_flags.length; i++) {
      if (pos_flags[i]) pos_num ++;
    }
    return pos_num;
  }

  /**
   * Generate the scores on each component (for each image)
   * @param abs_rgns
   * @param pos_scores
   * @param neg_scores
   * @param img_n
   * @param pos_flags
   */
  public static void generateScoresOnComponents(LinkedList abs_rgns, double[][] pos_scores,
                              double[][] neg_scores, int img_n, boolean[] pos_flags,
                              double[] min_on_components) {

    double[] log_min = new double[min_on_components.length];
    for (int i=0; i<log_min.length; i++) log_min[i] = Math.log(min_on_components[i]);

    for (int i = 0; i < abs_rgns.size(); i++) {
      AbsRgnProbMap[] cluster = (AbsRgnProbMap[]) abs_rgns.get(i);
      int pos_count = 0, neg_count = 0;
      for (int j = 0; j < img_n; j++) {
        double score = (cluster[j] == null || cluster[j].size() == 0) ?
            log_min[i] : Math.log(cluster[j].getMaxProbability());
        if (score <  log_min[i]) score = log_min[i];

        if (pos_flags[j]) {
          pos_scores[pos_count++][i] = score;
        }
        else {
          neg_scores[neg_count++][i] = score;
        }
      }
    }
  }


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

    public final static String USAGE = "Usage: [-d img_data_center_file] [-m model_file] [-f feature_class_name[,feature_class_name]*] [-i img_list_file] [-o object] [-r orig_img_path] [-p data_path]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      try {

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

        GMMNNAdapter gmmnn = (GMMNNAdapter) GMMNNAdapter.createFromFile(
            model_file);

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

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

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

        String data_path = ReadArgu.getOption('p', args, "");
        String orig_img_path = ReadArgu.getOption('r', 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);
        }
        else {
          img_list = gmmnn.getImageList();
        }
        int img_n = img_list.size();

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

        // feature
        // generator,dataset,scale,mode list
        // get the feature data class names
        String ft_class_names = ReadArgu.getOption('f', args);
        if (ft_class_names == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        String[] feature_descs = ft_class_names.split(";");
        LinkedList ft_list = new LinkedList();
        LinkedList features = new LinkedList();
        int[] scales = new int[feature_descs.length];
        int min_scale = Integer.MAX_VALUE;
        for (int i=0; i<feature_descs.length; i++) {
          String[] ft_desc = feature_descs[i].split(",");
          ft_list.add(ft_desc);
          features.add(ft_desc[1]);
          scales[i] = Integer.parseInt(ft_desc[2]);
          // if scale is 0, then no resize
          if (scales[i] != 0 && scales[i] < min_scale) min_scale = scales[i];
        }
        System.out.println("minimum scale is " + min_scale);
        System.out.println("regions will be resized to the minimum scale");

        // total length of the features
        int total_components = getTotalComponentNum(gmmnn, obj, features);
        System.out.println("total component number : " + total_components);

        // first step: classify each image region to a cluster.  For each
        // cluster, there will be a list of regions attached to it, each has
        // its region (ellipse or polygon) and its probabilities of belonging to
        // the cluster.

        // save the regions for each cluster for each image
        // each one is an instance of AbsRgnPRobMapList
        double[] min_on_components = new double[total_components];
        LinkedList abs_rgns = getAbstractRegionsForAllClusters(data_center,
            features, img_list, obj, gmmnn,
            scales, min_scale, min_on_components);

        boolean[] pos_flags = getPositiveFlags(data_center,
                                               img_list, obj);
        double[][] overlap_probs = getBiRelationshipProbs(abs_rgns, img_n, null, model_file);

//        // normalize the score (using uniform distribution)
//        for (int i=0, index = 0; i<total_components - 1; i++) {
//          for (int j=i+1; j<total_components; j++, index++) {
//            double norm_unit = min_on_components[i] * min_on_components[j];
//            for (int k=0; k<overlap_probs[index].length; k++) {
//              overlap_probs[index][k] /= norm_unit;
//            }
//          }
//        }

        DOU_Ser.output(model_file + ".bi.probs.ser", overlap_probs);
        OutputResult.print(model_file + ".bi.rel", overlap_probs, overlap_probs[0].length, img_n);

        DOU_Ser.output(model_file + ".pos.ser", pos_flags);
      }
      catch (Exception e) {
        e.printStackTrace();

      }
    }
  }

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

    public final static String USAGE = "Usage: [-m model_file]";

    public static void main(String[] args) {
      // init configuration
      Configuration.init(args);

      try {

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

        GMMNNAdapter gmmnn = (GMMNNAdapter) GMMNNAdapter.createFromFile(
            model_file);

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

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

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

        String data_path = ReadArgu.getOption('p', args, "");
        String orig_img_path = ReadArgu.getOption('r', 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);
        }
        else {
          img_list = gmmnn.getImageList();
        }
        int img_n = img_list.size();

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

        // feature
        // generator,dataset,scale,mode list
        // get the feature data class names
        String ft_class_names = ReadArgu.getOption('f', args);
        if (ft_class_names == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        String[] feature_descs = ft_class_names.split(";");
        LinkedList ft_list = new LinkedList();
        LinkedList features = new LinkedList();
        int[] scales = new int[feature_descs.length];
        int min_scale = Integer.MAX_VALUE;
        for (int i=0; i<feature_descs.length; i++) {
          String[] ft_desc = feature_descs[i].split(",");
          ft_list.add(ft_desc);
          features.add(ft_desc[1]);
          scales[i] = Integer.parseInt(ft_desc[2]);
          // if scale is 0, then no resize
          if (scales[i] != 0 && scales[i] < min_scale) min_scale = scales[i];
        }
        System.out.println("minimum scale is " + min_scale);
        System.out.println("regions will be resized to the minimum scale");

        // total length of the features
        int total_components = getTotalComponentNum(gmmnn, obj, features);
        System.out.println("total component number : " + total_components);

        // first step: classify each image region to a cluster.  For each
        // cluster, there will be a list of regions attached to it, each has
        // its region (ellipse or polygon) and its probabilities of belonging to
        // the cluster.

        // save the regions for each cluster for each image
        // each one is an instance of AbsRgnPRobMapList
        double[] min_on_components = new double[total_components];

        int component_count_base = 0; // the first feature begins from 0
                                      // the next will begin from the total count of the previous
        int f_index = 0;
        for (Iterator f=features.iterator(); f.hasNext(); f_index++) {
          String feature = (String)f.next();
          System.out.println("consider " + feature);

          double[] min_on_prob = (double[]) gmmnn.getMinComponentProbs().get(
              feature);
          for (int i = 0; i < min_on_prob.length; i++) {
            min_on_components[component_count_base + i] = Math.exp(min_on_prob[i]);
          }
          component_count_base += min_on_prob.length;
        }

        boolean[] pos_flags = ( boolean[])DIU_Ser.input(model_file + ".pos.ser");

        double[][] overlap_probs = readAndCalBiRelationshipProbs(total_components, img_n, pos_flags, model_file);

        // normalize the score (using uniform distribution)
        for (int i=0, index = 0; i<total_components - 1; i++) {
          for (int j=i+1; j<total_components; j++, index++) {
            double norm_unit = min_on_components[i] * min_on_components[j];
            for (int k=0; k<overlap_probs[index].length; k++) {
              overlap_probs[index][k] /= norm_unit;
            }
          }
        }

        // pick up the best candidates
        AbsRgnPromisingBiRelSelector selector = new ARPSMostAppearances();

//        int img_n = overlap_probs[0].length;
        int cand_n = overlap_probs.length;
        int comp_n = (((int)Math.round(Math.sqrt(8 * cand_n - 1))) + 1 ) / 2;
        System.out.println("components # " + comp_n);
        int[] cand_parent1 = new int[cand_n];
        int[] cand_parent2 = new int[cand_n];
        for (int i=0, cand_index = 0; i<comp_n-1; i++) {
          for (int j=i+1; j<comp_n; j++, cand_index++) {
            cand_parent1[cand_index] = i;
            cand_parent2[cand_index] = j;
          }
        }

        // for each possible relationship, get a score
        final double[] scores = new double[cand_n];
        for (int i = 0; i < cand_n; i++) {
          scores[i] = selector.score(overlap_probs[i], pos_flags);
          System.out.println("score for relation candidate " + i +
                             "(" + cand_parent1[i] + "," + cand_parent2[i] + ")" +
                             " : " + scores[i]);
        }

        // sort the relation candidates according to their scores
        Integer[] cand_index = new Integer[cand_n];
        for (int i = 0; i < cand_n; i++)
          cand_index[i] = new Integer(i);
        Arrays.sort(cand_index, new Comparator() {
          /**
           * Get the one with highest score first
           * @param o1
           * @param o2
           * @return
           */
          public int compare(Object o1, Object o2) {
            double score1 = scores[ ((Integer) o1).intValue()];
            double score2 = scores[  ((Integer) o2).intValue()];
            if (score1 < score2) return 1;
            else if (score1 > score2) return -1;
            else return 0;
          }
        });

        // return the top n candidates
        int top_n = cand_n;
        for (int i = 0; i < top_n; i++) {
          int index = ((Integer)cand_index[i]).intValue();
          System.out.println("top " + i + ": index " + index +
                             "(" + cand_parent1[index] + "," + cand_parent2[index] + ")" +
                             ", score = " + scores[index]);
        }
      }
      catch (Exception e) {
        e.printStackTrace();

      }
    }
  }

  public static AbsRgnOverlappingFtGen[] parseBiRelationshipDesriptor(String bi_relationship_descriptor) {
    if (bi_relationship_descriptor == null) return null;
    String[] bi_relationships = bi_relationship_descriptor.split(";");

    AbsRgnOverlappingFtGen[] bi_rels = new AbsRgnOverlappingFtGen[bi_relationships.length];
    for (int i=0; i<bi_relationships.length; i++) {
      String[] br_desc = bi_relationships[i].split(",");
      char br_type = br_desc[0].charAt(0);
      int parent1 = Integer.parseInt(br_desc[1]);
      int parent2 = Integer.parseInt(br_desc[2]);
      switch (br_type) {
        case 'o' :
          bi_rels[i] = new AbsRgnOverlappingFtGen(parent1, parent2);
          System.out.println("  bi-relation #" + i + " : overlap " + parent1 + "+"  + parent2);
          break;
        default:
          return null;
      }
    }
    return bi_rels;

  }

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

    public final static String USAGE = "Usage: -b bi_relationship_descriptor [-d img_data_center_file] [-m model_file] [-f feature_class_name[,feature_class_name]*] [-i img_list_file] [-o object] [-r orig_img_path] [-p data_path]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      try {

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

        GMMNNAdapter gmmnn = (GMMNNAdapter) GMMNNAdapter.createFromFile(
            model_file);

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

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

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

        String data_path = ReadArgu.getOption('p', args, "");
        String orig_img_path = ReadArgu.getOption('r', 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);
        }
        else {
          img_list = gmmnn.getImageList();
        }
        int img_n = img_list.size();

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

        // feature
        // generator,dataset,scale,mode list
        // get the feature data class names
        String ft_class_names = ReadArgu.getOption('f', args);
        if (ft_class_names == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        String[] feature_descs = ft_class_names.split(";");
        LinkedList ft_list = new LinkedList();
        LinkedList features = new LinkedList();
        int[] scales = new int[feature_descs.length];
        int min_scale = Integer.MAX_VALUE;
        for (int i=0; i<feature_descs.length; i++) {
          String[] ft_desc = feature_descs[i].split(",");
          ft_list.add(ft_desc);
          features.add(ft_desc[1]);
          scales[i] = Integer.parseInt(ft_desc[2]);
          // if scale is 0, then no resize
          if (scales[i] != 0 && scales[i] < min_scale) min_scale = scales[i];
        }
        System.out.println("minimum scale is " + min_scale);
        System.out.println("regions will be resized to the minimum scale");

        // total length of the features
        int total_components = getTotalComponentNum(gmmnn, obj, features);
        System.out.println("total component number : " + total_components);

        // first step: classify each image region to a cluster.  For each
        // cluster, there will be a list of regions attached to it, each has
        // its region (ellipse or polygon) and its probabilities of belonging to
        // the cluster.

        // save the regions for each cluster for each image
        // each one is an instance of AbsRgnPRobMapList
        double[] min_on_components = new double[total_components];
        LinkedList abs_rgns = getAbstractRegionsForAllClusters(data_center,
            features, img_list, obj, gmmnn,
            scales, min_scale, min_on_components);


        boolean[] pos_flags = getPositiveFlags(data_center,
                                               img_list, obj);


        // parse bi-relationship descriptor
        String bi_relationship_descriptor = ReadArgu.getOption('b', args);
        AbsRgnOverlappingFtGen[] bi_rels = parseBiRelationshipDesriptor(bi_relationship_descriptor);
//        if (bi_rels == null) {
//          System.err.println(USAGE);
//          System.exit(1);
//        }

        if (bi_rels != null) {
//          min_on_components = addBiRelationshipFeatures(abs_rgns, bi_rels,
//              img_n, min_on_components);
          // read from file
          min_on_components = addBiRelationshipFeatures(
              model_file, abs_rgns, bi_rels, min_on_components);

        }

        int pos_num = getPostiveNumber(pos_flags);
        int neg_num = img_n - pos_num;

        double[][] pos_scores = new double[pos_num][abs_rgns.size()];
        double[][] neg_scores = new double[neg_num][abs_rgns.size()];

        generateScoresOnComponents(abs_rgns, pos_scores,
                              neg_scores, img_n, pos_flags, min_on_components);

        NeuralNet nn = gmmnn.trainNN(pos_scores, neg_scores, null, null);
        OutputResult.print("pos.txt", pos_scores, pos_scores[0].length, pos_scores.length);
        OutputResult.print("neg.txt", neg_scores, neg_scores[0].length, neg_scores.length);

        gmmnn.putNNModel(obj, nn);
        String new_file_name = (bi_relationship_descriptor == null)? "none" : bi_relationship_descriptor;
        DOU_Ser.output(model_file + "." + new_file_name, gmmnn);

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

      }
    }
  }

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

    public final static String USAGE = "Usage: -b bi_relationship_descriptor [-d img_data_center_file] [-m model_file] [-f feature_class_name[,feature_class_name]*] [-i img_list_file] [-o object] [-r orig_img_path] [-p data_path] [-t output_rst]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      try {

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

        // parse bi-relationship descriptor
        String bi_relationship_descriptor = ReadArgu.getOption('b', args);

        String new_file_name = (bi_relationship_descriptor == null)? "none" : bi_relationship_descriptor;

        GMMNNAdapter gmmnn = (GMMNNAdapter) GMMNNAdapter.createFromFile(
            model_file + "." + new_file_name);

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

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

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

        String data_path = ReadArgu.getOption('p', args, "");
        String orig_img_path = ReadArgu.getOption('r', 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);
        }
        else {
          img_list = gmmnn.getImageList();
        }
        int img_n = img_list.size();

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

        // feature
        // generator,dataset,scale,mode list
        // get the feature data class names
        String ft_class_names = ReadArgu.getOption('f', args);
        if (ft_class_names == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        String[] feature_descs = ft_class_names.split(";");
        LinkedList ft_list = new LinkedList();
        LinkedList features = new LinkedList();
        int[] scales = new int[feature_descs.length];
        int min_scale = Integer.MAX_VALUE;
        for (int i = 0; i < feature_descs.length; i++) {
          String[] ft_desc = feature_descs[i].split(",");
          ft_list.add(ft_desc);
          features.add(ft_desc[1]);
          scales[i] = Integer.parseInt(ft_desc[2]);
          // if scale is 0, then no resize
          if (scales[i] != 0 && scales[i] < min_scale)
            min_scale = scales[i];
        }
        System.out.println("minimum scale is " + min_scale);
        System.out.println("regions will be resized to the minimum scale");

        // total length of the features
        int total_components = getTotalComponentNum(gmmnn, obj, features);
        System.out.println("total component number : " + total_components);

        // first step: classify each image region to a cluster.  For each
        // cluster, there will be a list of regions attached to it, each has
        // its region (ellipse or polygon) and its probabilities of belonging to
        // the cluster.

        // save the regions for each cluster for each image
        // each one is an instance of AbsRgnPRobMapList
        double[] min_on_components = new double[total_components];
        LinkedList abs_rgns = getAbstractRegionsForAllClusters(data_center,
            features, img_list, obj, gmmnn,
            scales, min_scale, min_on_components);

        AbsRgnOverlappingFtGen[] bi_rels = parseBiRelationshipDesriptor(bi_relationship_descriptor);

        if (bi_rels != null) {
          min_on_components = addBiRelationshipFeatures(abs_rgns, bi_rels,
              img_n,
              min_on_components);
        }


        boolean[] pos_flags = new boolean[img_n];
        Arrays.fill(pos_flags, true);

        double[][] scores = new double[img_n][abs_rgns.size()];


        generateScoresOnComponents(abs_rgns, scores,
                                   null, img_n, pos_flags,
                                   min_on_components);

        OutputResult.print("test.txt", scores, scores[0].length, scores.length);

        NeuralNet nn = (NeuralNet)gmmnn.getNNModels().get(obj);

        StatTestRst rst = new StatTestRst();
        int i_index = 0;
        double[][] rst_scores = new double[img_n][];
        int[] cat = new int[img_n];
        for (Iterator j = img_list.iterator(); j.hasNext(); i_index++) {
          String img = (String)j.next();
          double[] nn_scores = nn.test(scores[i_index], false);
          double nn_score = nn_scores[0];
          rst.putScore(obj, img, nn_score);
          cat[i_index] = data_center.getImgDescriptions(img).contains(obj) ? 1 : 0;
        }


        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('t', args);
//        String rst_output_file = model_file + "." + new_file_name;
        if (rst_output_file != null) {
          System.out.println("output results to " + rst_output_file);
          rst.writeToFile(rst_output_file + ".rst.ser");
          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();

      }
    }
  }
}

class ObjectWithScore {
  Object object;
  double score;
  public ObjectWithScore(Object obj, double s) {
    object = obj;
    score = s;
  }
}