docs: void match(Input<RAW> rawInput, Input<BASE> baseInput, Tracking<RAW, BASE> tracking) {
      BlockHashSequence rawHashSequence = rawInput.getBlockHashSequence();
      BlockHashSequence baseHashSequence = baseInput.getBlockHashSequence();
  
      Multimap<Integer, RAW> rawsByLine = groupByLine(tracking.getUnmatchedRaws(), rawHashSequence);
      Multimap<Integer, BASE> basesByLine = groupByLine(tracking.getUnmatchedBases(), baseHashSequence);
      Map<Integer, HashOccurrence> occurrencesByHash = new HashMap<>();
  
      for (Integer line : basesByLine.keySet()) {
        int hash = baseHashSequence.getBlockHashForLine(line);
        HashOccurrence hashOccurrence = occurrencesByHash.get(hash);
        if (hashOccurrence == null) {
          // first occurrence in base
          hashOccurrence = new HashOccurrence();
          hashOccurrence.baseLine = line;
          hashOccurrence.baseCount = 1;
          occurrencesByHash.put(hash, hashOccurrence);
        } else {
          hashOccurrence.baseCount++;
        }
      }
  
      for (Integer line : rawsByLine.keySet()) {
        int hash = rawHashSequence.getBlockHashForLine(line);
        HashOccurrence hashOccurrence = occurrencesByHash.get(hash);
        if (hashOccurrence != null) {
          hashOccurrence.rawLine = line;
          hashOccurrence.rawCount++;
        }
      }
  
      for (HashOccurrence hashOccurrence : occurrencesByHash.values()) {
        if (hashOccurrence.baseCount == 1 && hashOccurrence.rawCount == 1) {
          // Guaranteed that baseLine has been moved to rawLine, so we can map all issues on baseLine to all issues on rawLine
          map(rawsByLine.get(hashOccurrence.rawLine), basesByLine.get(hashOccurrence.baseLine), tracking);
          basesByLine.removeAll(hashOccurrence.baseLine);
          rawsByLine.removeAll(hashOccurrence.rawLine);
        }
      }
  
      // Check if remaining number of lines exceeds threshold. It avoids processing too many combinations.
      if (basesByLine.keySet().size() * rawsByLine.keySet().size() >= 250_000) {
        return;
      }
  
      List<LinePair> possibleLinePairs = Lists.newArrayList();
      for (Integer baseLine : basesByLine.keySet()) {
        for (Integer rawLine : rawsByLine.keySet()) {
          int weight = lengthOfMaximalBlock(baseInput.getLineHashSequence(), baseLine, rawInput.getLineHashSequence(), rawLine);
          if (weight > 0) {
            possibleLinePairs.add(new LinePair(baseLine, rawLine, weight));
          }
        }
      }
      Collections.sort(possibleLinePairs, LinePairComparator.INSTANCE);
      for (LinePair linePair : possibleLinePairs) {
        // High probability that baseLine has been moved to rawLine, so we can map all issues on baseLine to all issues on rawLine
        map(rawsByLine.get(linePair.rawLine), basesByLine.get(linePair.baseLine), tracking);
      }
    }