@Test
public void testFrequencyComparison() {

  final Random rand = RandomUtils.getRandom();
  // build a vector full of sample from exponential distribuiton
  // this will have lots of little positive values and a few big ones
  Vector p1 = new DenseVector(25)
    .assign(new DoubleFunction() {
      @Override
      public double apply(double arg1) {
        return -Math.log1p(-rand.nextDouble());
      }
    });
  // make a copy
  Vector p2 = p1.like().assign(p1);
  // nuke elements 0..4
  p1.viewPart(0, 5).assign(0);
  // and boost elements 5..7
  p1.viewPart(5, 3).assign(Functions.mult(4));
  // then normalize to turn it into a probability distribution
  p1.assign(Functions.div(p1.norm(1)));
  // likewise normalize p2
  p2.assign(Functions.div(p2.norm(1)));
  // sample 100 times from p1
  Multiset<Integer> w1 = HashMultiset.create();
  for (int i = 0; i < 100; i++) {
    w1.add(sample(p1, rand));
  }
  // and 1000 times from p2
  Multiset<Integer> w2 = HashMultiset.create();
  for (int i = 0; i < 1000; i++) {
    w2.add(sample(p2, rand));
  }
  // comparing frequencies, we should be able to find 8 items with score > 0
  List<LogLikelihood.ScoredItem<Integer>> r = LogLikelihood.compareFrequencies(w1, w2, 8, 0);
  assertTrue(r.size() <= 8);
  assertFalse(r.isEmpty());
  for (LogLikelihood.ScoredItem<Integer> item : r) {
    assertTrue(item.getScore() >= 0);
  }
  // the most impressive should be 7
  assertEquals(7, (int) r.get(0).getItem());
  // make sure scores are descending
  double lastScore = r.get(0).getScore();
  for (LogLikelihood.ScoredItem<Integer> item : r) {
    assertTrue(item.getScore() <= lastScore);
    lastScore = item.getScore();
  }
  // now as many as have score >= 1
  r = LogLikelihood.compareFrequencies(w1, w2, 40, 1);
  // only the boosted items should make the cut
  assertEquals(2, r.size());
  assertEquals(7, (int) r.get(0).getItem());
  assertEquals(6, (int) r.get(1).getItem());
  r = LogLikelihood.compareFrequencies(w1, w2, 1000, -100);
  Multiset<Integer> k = HashMultiset.create();
  for (LogLikelihood.ScoredItem<Integer> item : r) {
    k.add(item.getItem());
  }
  for (int i = 0; i < 25; i++) {
    assertTrue("i = " + i, k.count(i) == 1 || w2.count(i) == 0);
  }
  // all values that had non-zero counts in larger set should have result scores
  assertEquals(w2.elementSet().size(), r.size());
  assertEquals(7, (int) r.get(0).getItem());
  assertEquals(6, (int) r.get(1).getItem());
  
  // the last item should definitely have negative score
  assertTrue(r.get(r.size() - 1).getScore() < 0);
  // make sure scores are descending
  lastScore = r.get(0).getScore();
  for (LogLikelihood.ScoredItem<Integer> item : r) {
    assertTrue(item.getScore() <= lastScore);
    lastScore = item.getScore();
  }
}