package edu.tamu.tcat.dia.morphological;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.logging.Logger;

import edu.tamu.tcat.dia.binarization.BinaryImage;

public final class RunLengthRatioGenerator
{
   private final static Logger logger = Logger.getLogger("edu.tamu.tcat.dia.morph.rlgenerator");

//   public static <T> List<T> mode(List<? extends T> coll)
//   {
//      Map<T, Integer> seen = new HashMap<T, Integer>();
//      int max = 0;
//      List<T> maxElems = new ArrayList<T>();
//      for (T value : coll)
//      {
//         if (seen.containsKey(value))
//            seen.put(value, seen.get(value) + 1);
//         else
//            seen.put(value, 1);
//         if (seen.get(value) > max)
//         {
//            max = seen.get(value);
//            maxElems.clear();
//            maxElems.add(value);
//         }
//         else if (seen.get(value) == max)
//         {
//            maxElems.add(value);
//         }
//      }
//
//      for (T e : maxElems)
//      {
//         logger.info("Value " + e + " occurs " + seen.get(e) + " times");
//      }
//      return maxElems;
//   }
//
//   public RunLengthRatioGenerator()
//   {
//   }
   
   /**
    * Calculates 
    * @param input
    * @param numberOfRuns
    * @return
    */
   public static Map<Integer, Double> findRunLengthRatios(BinaryImage input, int numberOfRuns)
   {
      Map<Integer, Double> outMap = new HashMap<>(numberOfRuns);
      
      Random randomGen = new Random();
      int numCols = input.getWidth();

      // for each run
      for (int i = 0; i < numberOfRuns; i++)
      {
         int colIx = selectColumn(randomGen, numCols, outMap.keySet());

         List<PixelRun> runs = findRuns(input, colIx);
         double foregroundRatio = computeForegroundRatio(runs);
         
         outMap.put(Integer.valueOf(colIx), Double.valueOf(foregroundRatio));
      }

      return outMap;
   }

   private static double computeForegroundRatio(List<PixelRun> runs)
   {
      // compute the average ratio of background (white) to foreground pixels)
      
      List<Double> ratios = new ArrayList<>();
      int startIx = runs.get(0).foreground ? 1 : 0;
      for (int i = startIx; i < runs.size() - 1; i += 2)
      {
         double ratio = runs.get(i).ct / (double) runs.get(i + 1).ct;
         ratios.add(Double.valueOf(ratio));
      }
      
      // TODO for now, just use the average, eventually should through out abnormal values 
      //      and/or find groupings of related values
      double sum =  ratios.parallelStream()
                     .reduce(Double::sum)
                     .orElse(Double.valueOf(0)); 
      return sum / ratios.size();
   }

   private static List<PixelRun> findRuns(BinaryImage input, int colIx)
   {
      int h = input.getHeight();
      List<PixelRun> runs = new ArrayList<>();
      boolean previous = input.isForeground(colIx, 0);
      PixelRun run = new PixelRun(previous);
      run.ct++;
      for (int j = 1; j < h; j++)
      {
         boolean current = input.isForeground(colIx, j);
         if (previous != current) {
            runs.add(run);
            run = new PixelRun(current);
         }
         
         run.ct++;
      }
      return runs;
   }
   
   private static int selectColumn(Random randomGen, int max, Set<Integer> previous)
   {
      int colIx = -1;
      do {
         colIx = randomGen.nextInt(max);
         
      } while (previous.contains(Integer.valueOf(colIx)));
         
      logger.finest("Processing col " + colIx);
      return colIx;
   }

   private static class PixelRun
   {
      /** true if this is a run of foreground pixels. */
      private final boolean foreground;
      
      /** the number of pixels in the run. */
      int ct = 0;
      
      public PixelRun(boolean foreground)
      {
         this.foreground = foreground;
      }
   }

}

@@ -23,21 +23,34 @@
 dia.
-morphological
+classifier.music.runlength
 ;%0A%0Ai
@@ -72,24 +72,54 @@
 .ArrayList;%0A
+import java.util.Collections;%0A
 import java.
@@ -308,16 +308,80 @@
 ryImage;
+%0Aimport edu.tamu.tcat.dia.classifier.music.runlength.EM.Cluster;
 %0A%0Apublic
@@ -521,824 +521,9 @@
 );%0A%0A
-//   public static %3CT%3E List%3CT%3E mode(List%3C? extends T%3E coll)%0A//   %7B%0A//      Map%3CT, Integer%3E seen = new HashMap%3CT, Integer%3E();%0A//      int max = 0;%0A//      List%3CT%3E maxElems = new ArrayList%3CT%3E();%0A//      for (T value : coll)%0A//      %7B%0A//         if (seen.containsKey(value))%0A//            seen.put(value, seen.get(value) + 1);%0A//         else%0A//            seen.put(value, 1);%0A//         if (seen.get(value) %3E max)%0A//         %7B%0A//            max = seen.get(value);%0A//            maxElems.clear();%0A//            maxElems.add(value);%0A//         %7D%0A//         else if (seen.get(value) == max)%0A//         %7B%0A//            maxElems.add(value);%0A//         %7D%0A//      %7D%0A//%0A//      for (T e : maxElems)%0A//      %7B%0A//         logger.info(%22Value %22 + e + %22 occurs %22 + seen.get(e) + %22 times%22);%0A//      %7D%0A//      return maxElems;%0A//   %7D
+ 
 %0A//%0A
@@ -699,22 +699,28 @@
 nteger, 
-Double
+Set%3CCluster%3E
 %3E findRu
@@ -794,22 +794,28 @@
 nteger, 
-Double
+Set%3CCluster%3E
 %3E outMap
@@ -1147,30 +1147,36 @@
     
-double foregroun
+Set%3CCluster%3E clustere
 dRatio
+s
  = c
@@ -1262,39 +1262,23 @@
 x), 
-Double.valueOf(foregroun
+clustere
 dRatio
-)
+s
 );%0A 
@@ -1330,22 +1330,28 @@
  static 
-double
+Set%3CCluster%3E
  compute
@@ -1521,24 +1521,97 @@
 ayList%3C%3E();%0A
+      if (runs.isEmpty())%0A         return Collections.emptySet();%0A      %0A
       int st
@@ -1854,283 +1854,41 @@
     
-// TODO for now, just use the average, eventually should through out abnormal values %0A      //      and/or find groupings of related values%0A      double sum =  ratios.parallelStream()%0A                     .reduce(Double::sum)%0A                     .orElse(Double.valueOf(0)
+EM estimator = new EM(ratios, 3
 );
- 
 %0A   
@@ -1901,24 +1901,25 @@
 urn 
-sum / ratios.siz
+estimator.estimat
 e();
@@ -2401,16 +2401,48 @@
 rrent);%0A
+            previous = current;%0A
         

Use EM based clustering of run lengths