code: public static <E> Distribution<E> simpleGoodTuring(Counter<E> counter, int numberOfKeys) {
  
      // check arguments
      validateCounter(counter);
      int numUnseen = numberOfKeys - counter.size();
      if (numUnseen < 1)
        throw new IllegalArgumentException(String.format("ERROR: numberOfKeys %d must be > size of counter %d!", numberOfKeys, counter.size()));
  
      // do smoothing
      int[][] cc = countCounts2IntArrays(collectCountCounts(counter));
      int[] r = cc[0];                    // counts
      int[] n = cc[1];                    // counts of counts
      SimpleGoodTuring sgt = new SimpleGoodTuring(r, n);
  
      // collate results
      Counter<Integer> probsByCount = new ClassicCounter<Integer>();
      double[] probs = sgt.getProbabilities();
      for (int i = 0; i < probs.length; i++) {
        probsByCount.setCount(r[i], probs[i]);
      }
  
      // make smoothed distribution
      Distribution<E> dist = new Distribution<E>();
      dist.counter = new ClassicCounter<E>();
      for (Map.Entry<E, Double> entry : counter.entrySet()) {
        E item = entry.getKey();
        Integer count = (int) Math.round(entry.getValue());
        dist.counter.setCount(item, probsByCount.getCount(count));
      }
      dist.numberOfKeys = numberOfKeys;
      dist.reservedMass = sgt.getProbabilityForUnseen();
      return dist;
  
    }
docs: Creates a Distribution from the given counter using Gale &amp; Sampsons'
  "simple Good-Turing" smoothing.
  
  @return a new simple Good-Turing smoothed Distribution.