/**
  * Skips to the first match (including the current) whose entity, tuple and cell numbers are
  * greater than or equal to the given targets. <br>
  * When this method is used the {@link #explain(int)} method should not be used. <br>
  * The implementation uses the skipTo() method on the subscorers.
  *
  * @param entityID The target entity number.
  * @param tupleID The target tuple number.
  * @param cellID The target cell number.
  * @return true iff there is such a match.
  */
 @Override
 public int advance(final int entityID, final int tupleID, final int cellID) throws IOException {
   if (scorerCellQueue == null) {
     this.initScorerCellQueue();
   }
   if (entityID < entity
       || (entityID == entity && tupleID <= tuple)
       || (entityID == entity && tupleID == tuple && cellID <= cell)) {
     return entity;
   }
   while (queueSize > 0) {
     if (scorerCellQueue.topEntity() > entityID
         || (scorerCellQueue.topEntity() == entityID
             && (scorerCellQueue.topTuple() != Integer.MAX_VALUE
                 && scorerCellQueue.topTuple() >= tupleID))
         || (scorerCellQueue.topEntity() == entityID
             && scorerCellQueue.topTuple() == tupleID
             && (scorerCellQueue.topCell() != Integer.MAX_VALUE
                 && scorerCellQueue.topCell() >= cellID))) {
       entity = scorerCellQueue.topEntity();
       this.nextPosition();
       return entity;
     } else if (!scorerCellQueue.topSkipToAndAdjustElsePop(entityID, tupleID, cellID)) {
       if (--queueSize == 0) {
         return NO_MORE_DOCS;
       }
     }
   }
   return NO_MORE_DOCS;
 }
 /**
  * Advance to the next position.<br>
  * Set the cell and tuple information.<br>
  * Iterate over the queue, and count how many matchers there are. Increment the score
  * consequently.
  */
 @Override
 public int nextPosition() throws IOException {
   // if tuple or cell have been set to sentinel value, there is no more position
   if (scorerCellQueue.topTuple() == Integer.MAX_VALUE
       || scorerCellQueue.topCell() == Integer.MAX_VALUE) {
     return NO_MORE_POS;
   }
   tuple = scorerCellQueue.topTuple();
   cell = scorerCellQueue.topCell();
   currentScore = 0;
   nrMatchers = 0;
   // Count how many matchers there are, and increment current score
   while (scorerCellQueue.topEntity() == entity
       && scorerCellQueue.topTuple() == tuple
       && scorerCellQueue.topCell() == cell) { // while top is a match, advance
     currentScore += scorerCellQueue.topScore();
     if (scorerCellQueue.topIncMatchers()) nrMatchers++;
     if (!scorerCellQueue.topNextPositionAndAdjust()) {
       return 0; // stop, no more position. position is invalid in this scorer,
       // return 0.
       // All positions in the queue are consumed. If nextPosition
       // is called another time, we will return NO_MORE_POS.
     }
   }
   return 0; // position is invalid in this scorer, return 0.
 }
  /**
   * Advance all subscorers after the current document determined by the top of the <code>
   * scorerCellQueue</code> if all the subscorers are not exhausted. <br>
   * At least the top scorer with the minimum entity number will be advanced.
   *
   * @return true iff there is a match. <br>
   *     In case there is a match, </code>entity</code>, </code>currentScore</code>, and </code>
   *     nrMatchers</code> describe the match.
   */
  protected int advanceAfterCurrent() throws IOException {
    if (scorerCellQueue.size() > 0) {
      if ((queueSize -= scorerCellQueue.nextAndAdjustElsePop()) == 0) {
        return NO_MORE_DOCS;
      }

      entity = scorerCellQueue.topEntity();
      this.nextPosition(); // advance to the first position [SRN-24]
      return entity;
    }
    return NO_MORE_DOCS;
  }
 @Override
 public int nextDoc() throws IOException {
   if (scorerCellQueue == null) {
     this.initScorerCellQueue();
     if ((nrMatchers = scorerCellQueue.nrMatches()) > 0) {
       entity = scorerCellQueue.topEntity();
       this.nextPosition(); // advance to the first position [SRN-24]
       return entity;
     }
     return NO_MORE_DOCS;
   }
   return this.advanceAfterCurrent();
 }
 /**
  * Skips to the first match (including the current) whose document number is greater than or equal
  * to a given target. <br>
  * When this method is used the {@link #explain(int)} method should not be used. <br>
  * The implementation uses the skipTo() method on the subscorers.
  *
  * @param entityID The target entity number.
  * @return true iff there is such a match.
  */
 @Override
 public int advance(final int entityID) throws IOException {
   if (scorerCellQueue == null) {
     this.initScorerCellQueue();
   }
   if (entityID <= entity) {
     return entity;
   }
   while (queueSize > 0) {
     if (scorerCellQueue.topEntity() >= entityID) {
       entity = scorerCellQueue.topEntity();
       this.nextPosition(); // advance to the first position [SRN-24]
       return entity;
     } else if (!scorerCellQueue.topSkipToAndAdjustElsePop(entityID)) {
       if (--queueSize == 0) {
         return NO_MORE_DOCS;
       }
     }
   }
   return NO_MORE_DOCS;
 }
 /**
  * Called the first time next() or skipTo() is called to initialize <code>scorerCellQueue</code>.
  */
 private void initScorerCellQueue() throws IOException {
   final Iterator<SirenPrimitiveScorer> si = scorers.iterator();
   scorerCellQueue = new ScorerCellQueue(nrScorers);
   queueSize = 0;
   while (si.hasNext()) {
     final SirenPrimitiveScorer se = si.next();
     if (se.nextDoc()
         != NO_MORE_DOCS) { // entity(), tuple() and cell () method will be used in scorerDocQueue.
       if (scorerCellQueue.insert(se)) {
         queueSize++;
       }
     }
   }
 }