public double[] getPrecisions(double[] predictions, Sample sample, TieBreaker tieBreaker)
      throws Exception {
    RankingSample rankingSample = (RankingSample) sample;

    int chunkSize = 1 + (rankingSample.numQueries / mapWorkers.getSize());
    int offset = 0;
    int workerCount = 0;
    for (int i = 0; i < mapWorkers.getSize() && offset < rankingSample.numQueries; i++) {
      int endOffset = offset + Math.min(rankingSample.numQueries - offset, chunkSize);
      PrecisionWorker worker = mapWorkers.getTask(i);
      workerCount++;
      worker.init(rankingSample, predictions, offset, endOffset, tieBreaker);
      BlockingThreadPoolExecutor.getInstance().execute(worker);
      offset += chunkSize;
    }
    BlockingThreadPoolExecutor.getInstance().await();

    double[] result = new double[maxLevels];
    for (int i = 0; i < workerCount; i++) {
      double[] localResult = mapWorkers.getTask(i).getResult();
      for (int p = 0; p < maxLevels; p++) {
        result[p] += localResult[p];
      }
    }

    for (int p = 0; p < maxLevels; p++) {
      result[p] /= rankingSample.numQueries;
    }
    return result;
  }
 public PrecisionEval(int maxDocsPerQuery, int maxLevels, double relevancyThreshold)
     throws Exception {
   super(true);
   this.maxDocsPerQuery = maxDocsPerQuery;
   this.maxLevels = maxLevels;
   this.relevancyThreshold = relevancyThreshold;
   int numWorkers = BlockingThreadPoolExecutor.getInstance().getMaximumPoolSize();
   mapWorkers = new TaskCollection<PrecisionEval.PrecisionWorker>();
   for (int i = 0; i < numWorkers; i++) {
     mapWorkers.addTask(new PrecisionWorker());
   }
 }