/**
   * Method that combines the fingerprint evidence across all the read groups for the same sample
   * and then produces a matrix of LOD scores for comparing every sample with every other sample.
   */
  private void crossCheckSamples(final List<Fingerprint> fingerprints, final PrintStream out) {
    final SortedMap<String, Fingerprint> sampleFps =
        FingerprintChecker.mergeFingerprintsBySample(fingerprints);
    final SortedSet<String> samples = (SortedSet<String>) sampleFps.keySet();

    // Print header row
    out.print("\t");
    for (final String sample : samples) {
      out.print(sample);
      out.print("\t");
    }
    out.println();

    // Print results rows
    for (final String sample : samples) {
      out.print(sample);
      final Fingerprint fp = sampleFps.get(sample);

      for (final String otherSample : samples) {
        final MatchResults results =
            FingerprintChecker.calculateMatchResults(
                fp, sampleFps.get(otherSample), GENOTYPING_ERROR_RATE, LOSS_OF_HET_RATE);
        out.print("\t");
        out.print(formatUtil.format(results.getLOD()));
      }

      out.println();
    }
  }
  /**
   * Method that pairwise checks every pair of read groups and reports a LOD score for the two read
   * groups coming from the same sample.
   */
  private int crossCheckReadGroups(
      final Map<SAMReadGroupRecord, Fingerprint> fingerprints, final PrintStream out) {
    int mismatches = 0;
    int unexpectedMatches = 0;

    final List<SAMReadGroupRecord> readGroupRecords = new ArrayList<>(fingerprints.keySet());
    final List<String> output = new ArrayList<>();

    for (int i = 0; i < readGroupRecords.size(); i++) {
      final SAMReadGroupRecord lhsRg = readGroupRecords.get(i);
      for (int j = i + 1; j < readGroupRecords.size(); j++) {
        final SAMReadGroupRecord rhsRg = readGroupRecords.get(j);
        final boolean expectedToMatch =
            EXPECT_ALL_READ_GROUPS_TO_MATCH || lhsRg.getSample().equals(rhsRg.getSample());

        final MatchResults results =
            FingerprintChecker.calculateMatchResults(
                fingerprints.get(lhsRg),
                fingerprints.get(rhsRg),
                GENOTYPING_ERROR_RATE,
                LOSS_OF_HET_RATE);
        if (expectedToMatch) {
          if (results.getLOD() < LOD_THRESHOLD) {
            mismatches++;
            output.add(getMatchDetails(UNEXPECTED_MISMATCH, results, lhsRg, rhsRg));
          } else {
            if (!OUTPUT_ERRORS_ONLY) {
              output.add(getMatchDetails(EXPECTED_MATCH, results, lhsRg, rhsRg));
            }
          }
        } else {
          if (results.getLOD() > -LOD_THRESHOLD) {
            unexpectedMatches++;
            output.add(getMatchDetails(UNEXPECTED_MATCH, results, lhsRg, rhsRg));
          } else {
            if (!OUTPUT_ERRORS_ONLY) {
              output.add(getMatchDetails(EXPECTED_MISMATCH, results, lhsRg, rhsRg));
            }
          }
        }
      }
    }

    if (!output.isEmpty()) {
      out.println(
          "RESULT\tLOD_SCORE\tLOD_SCORE_TUMOR_NORMAL\tLOD_SCORE_NORMAL_TUMOR\tLEFT_RUN_BARCODE\tLEFT_LANE\tLEFT_MOLECULAR_BARCODE_SEQUENCE\tLEFT_LIBRARY\tLEFT_SAMPLE\t"
              + "RIGHT_RUN_BARCODE\tRIGHT_LANE\tRIGHT_MOLECULAR_BARCODE_SEQUENCE\tRIGHT_LIBRARY\tRIGHT_SAMPLE");
      out.println(String.join("\n", output));
    }

    if (mismatches + unexpectedMatches > 0) {
      log.info("WARNING: At least two read groups did not relate as expected.");
      return EXIT_CODE_WHEN_MISMATCH;
    } else {
      log.info("All read groups related as expected.");
      return 0;
    }
  }