Alignment QC, MultiQC, and Count Matrix

In this module, we will learn:

  • about the MultiQC tool and its capabilities
  • how to run multiQC on a remote system
  • how MultiQC gathers information for QC purposes
  • how MultiQC presents the results of cutadapt trimming and STAR alignment
  • how to combine gene-level results into a count matrix

Differential Expression Workflow

Here we will take the results from the previous module and operate on them a bit further. This will wrap up the preceding exercises, leaving us well-poised to begin differential expression, which we will discuss today and tomorrow.





Alignment Statistics with MultiQC

After aligning reads it is often helpful to know how many reads were uniquely aligned, mapped to multiple loci, or not mapped at all. The sample_NLog.final.out file which is output alongside the alignments in sample_N.temp/ folder (we used the --keep-intermediate-files flag), reports this information:

                                 Started job on |   Oct 02 13:09:23
                             Started mapping on |   Oct 02 13:09:51
                                    Finished on |   Oct 02 13:12:47
       Mapping speed, Million of reads per hour |   238.82

                          Number of input reads |   11675504
                      Average input read length |   146
                                    UNIQUE READS:
                   Uniquely mapped reads number |   10609591
                        Uniquely mapped reads % |   90.87%
                          Average mapped length |   146.37
                       Number of splices: Total |   2755543
            Number of splices: Annotated (sjdb) |   2734730
                       Number of splices: GT/AG |   2739697
                       Number of splices: GC/AG |   13204
                       Number of splices: AT/AC |   1744
               Number of splices: Non-canonical |   898
                      Mismatch rate per base, % |   0.18%
                         Deletion rate per base |   0.01%
                        Deletion average length |   1.60
                        Insertion rate per base |   0.01%
                       Insertion average length |   1.29
                             MULTI-MAPPING READS:
        Number of reads mapped to multiple loci |   599915
             % of reads mapped to multiple loci |   5.14%
        Number of reads mapped to too many loci |   12786
             % of reads mapped to too many loci |   0.11%
                                  UNMAPPED READS:
  Number of reads unmapped: too many mismatches |   20381
       % of reads unmapped: too many mismatches |   0.17%
            Number of reads unmapped: too short |   389960
                 % of reads unmapped: too short |   3.34%
                Number of reads unmapped: other |   42871
                     % of reads unmapped: other |   0.37%
                                  CHIMERIC READS:
                       Number of chimeric reads |   0
                            % of chimeric reads |   0.00%

MultiQC

While the information is incredibly useful, it can be tedious to look at the report for each sample separately, while keeping track of what trends emerge. It would be much easier to look at all the data compiled into a single report. MultiQC is a tool that does exactly this.

MultiQC is designed to interpret and aggregate reports from various tools and output a single report as an HTML document.

MultiQC Details

MultiQC’s main output is the report file in HTML format. This can be viewed in a web browser. Additionally, it creates a data directory with text files containing the data that MultiQC gathered during its execution - this same data is what is shown in the report.

Given an output directory out_multiqc, we should see something like the following:

# directory of multiqc data files
out_multiqc/multiqc_data/multiqc.log
out_multiqc/multiqc_data/multiqc_data.json
out_multiqc/multiqc_data/multiqc_general_stats.txt
out_multiqc/multiqc_data/multiqc_rsem.txt
out_multiqc/multiqc_data/multiqc_sources.txt
# multiqc report
out_multiqc/multiqc_report.html

In a moment we will run multiqc, and it will detect these reports from STAR and include them in the report.

If we then open the MultiQC report (HTML), the newly included STAR section will look something like the following:

Example of STAR alignment statistics in MultiQC. Example of STAR alignment statistics in MultiQC.

Source: MultiQC example report

MultiQC With STAR Exercise:

  1. Note the contents of our analysis directory, including the STAR contents
  2. Construct a MultiQC command and execute it on this directory
  3. View the MultiQC report
# Ensure that we're in our analysis directory, note the contents
cd ~/RSD_Shell/analysis
ls -l
# View MultiQC help page
multiqc --help
# Construct a MultiQC command and execute it
multiqc --outdir out_multiqc_rsem out_rsem/
# Verify that the output files are present

We just learned how to view all of our alignment results in one report with the help of MultiQC. MultiQC is also useful because it can utilize multiple separate modules to create summary figures of different steps in our pipeline.

What would happen if we pointed multiQC at our trimmed FastQC directory? What about if we point it to the entire analysis directory? Let’s try it out and see:

# A MultiQC command to analyze the trimmed read results
multiqc --outdir out_multiqc_cutadapt out_fastqc_trimmed/
# A multiQC command for a report of the entire project
multiqc --outdir out_multiqc_all  .

Transfer MultiQC Reports With scp Exercise

Before starting our file transfer exercise, we should make sure that we are on the same page. Follow the link below:

Link to report transfer exercise



Creating the count matrix

We have viewed some of the gene expression quantification results individually. It can be useful to combine these expression values into a count matrix. This is helpful when gathering expression-level QC metrics, as well as for input into a differential gene expression program such as DESeq2.

There are many ways to combine these results into a count matrix. For this workshop, we’ll use a small python script to combine.py that we’ve made for this purpose. To understand the process a bit more, let’s review the .genes.results files that we want to combine and discuss some details of the script. Finally, we’ll end with an exercise creating a count matrix.

If we review the *.genes.results files, we can see various columns of data output from RSEM that we discussed in the last module.

# Review of *.genes.results file contents
gene_id                 transcript_id(s)                        length  effective_length        expected_count  TPM     FPKM
ENSMUSG00000000001      ENSMUST00000000001                      3262.00 3116.28                 601.00          45.50   36.70
ENSMUSG00000000003      ENSMUST00000000003,ENSMUST00000114041   799.50  653.78                  0.00            0.00    0.00

We’ll take the expected_count column from each sample’s data, and combine these so that we have an aggregated data matrix with a row for each gene and a column for each sample.

The input for this step will be the directory of *.genes.results files from RSEM, and the output will be a tab-separated count matrix file which we can use for count-level QC and differential expression analysis.

Contents of combine.py script Here are the contents of the python script we’ll use, combine.py:

Count Matrix Exercise:

  1. View the help file of combine.py
  2. Construct / execute a command to combine our results into a count matrix
  3. View the resulting count matrix
# View the help file of combine.py
combine.py --help
# Construct and execute the command to combine.py
combine.py --input_path "out_rsem/*.genes.results" --output_file combined_counts.txt -c expected_count --id_columns gene_id
# View the resulting count matrix
less -S combined_counts.txt




These materials have been adapted and extended from materials created by the Harvard Chan Bioinformatics Core (HBC). These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.

LS0tCnRpdGxlOiAiTW9kdWxlIDA0OiBBbGlnbm1lbnQgUUMgYW5kIFF1YW50aWZpY2F0aW9uIgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keXsgLyogTm9ybWFsICAqLwogICAgICBmb250LXNpemU6IDE0cHQ7CiAgfQpwcmUgewogIGZvbnQtc2l6ZTogMTJwdAp9Cjwvc3R5bGU+CgojIEFsaWdubWVudCBRQywgTXVsdGlRQywgYW5kIENvdW50IE1hdHJpeAoKSW4gdGhpcyBtb2R1bGUsIHdlIHdpbGwgbGVhcm46CgoqIGFib3V0IHRoZSBNdWx0aVFDIHRvb2wgYW5kIGl0cyBjYXBhYmlsaXRpZXMKKiBob3cgdG8gcnVuIG11bHRpUUMgb24gYSByZW1vdGUgc3lzdGVtCiogaG93IE11bHRpUUMgZ2F0aGVycyBpbmZvcm1hdGlvbiBmb3IgUUMgcHVycG9zZXMKKiBob3cgTXVsdGlRQyBwcmVzZW50cyB0aGUgcmVzdWx0cyBvZiBjdXRhZGFwdCB0cmltbWluZyBhbmQgU1RBUiBhbGlnbm1lbnQKKiBob3cgdG8gY29tYmluZSBnZW5lLWxldmVsIHJlc3VsdHMgaW50byBhIGNvdW50IG1hdHJpeAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBXb3JrZmxvdwoKSGVyZSB3ZSB3aWxsIHRha2UgdGhlIHJlc3VsdHMgZnJvbSB0aGUgcHJldmlvdXMgbW9kdWxlIGFuZCBvcGVyYXRlIG9uIHRoZW0gYSBiaXQgZnVydGhlci4gVGhpcyB3aWxsIHdyYXAgdXAgdGhlIHByZWNlZGluZyBleGVyY2lzZXMsIGxlYXZpbmcgdXMgd2VsbC1wb2lzZWQgdG8gYmVnaW4gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIHdoaWNoIHdlIHdpbGwgZGlzY3VzcyB0b2RheSBhbmQgdG9tb3Jyb3cuCgohW10oaW1hZ2VzL3dheWZpbmRlci93YXlmaW5kZXItQWxpZ25tZW50X1FDX2FuZF9RdWFudGlmaWNhdGlvbi5wbmcpCjxicj4KPGJyPgo8YnI+Cjxicj4KCiMjIEFsaWdubWVudCBTdGF0aXN0aWNzIHdpdGggTXVsdGlRQwoKQWZ0ZXIgYWxpZ25pbmcgcmVhZHMgaXQgaXMgb2Z0ZW4gaGVscGZ1bCB0byBrbm93IGhvdyBtYW55IHJlYWRzIHdlcmUgdW5pcXVlbHkgYWxpZ25lZCwgbWFwcGVkIHRvIG11bHRpcGxlIGxvY2ksIG9yIG5vdCBtYXBwZWQgYXQgYWxsLiBUaGUgYHNhbXBsZV9OTG9nLmZpbmFsLm91dGAgZmlsZSB3aGljaCBpcyBvdXRwdXQgYWxvbmdzaWRlIHRoZSBhbGlnbm1lbnRzIGluIGBzYW1wbGVfTi50ZW1wL2AgZm9sZGVyICh3ZSB1c2VkIHRoZSBgLS1rZWVwLWludGVybWVkaWF0ZS1maWxlc2AgZmxhZyksIHJlcG9ydHMgdGhpcyBpbmZvcm1hdGlvbjoKCmBgYAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydGVkIGpvYiBvbiB8CU9jdCAwMiAxMzowOToyMwogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXJ0ZWQgbWFwcGluZyBvbiB8CU9jdCAwMiAxMzowOTo1MQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGaW5pc2hlZCBvbiB8CU9jdCAwMiAxMzoxMjo0NwogICAgICAgTWFwcGluZyBzcGVlZCwgTWlsbGlvbiBvZiByZWFkcyBwZXIgaG91ciB8CTIzOC44MgoKICAgICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXIgb2YgaW5wdXQgcmVhZHMgfAkxMTY3NTUwNAogICAgICAgICAgICAgICAgICAgICAgQXZlcmFnZSBpbnB1dCByZWFkIGxlbmd0aCB8CTE0NgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVTklRVUUgUkVBRFM6CiAgICAgICAgICAgICAgICAgICBVbmlxdWVseSBtYXBwZWQgcmVhZHMgbnVtYmVyIHwJMTA2MDk1OTEKICAgICAgICAgICAgICAgICAgICAgICAgVW5pcXVlbHkgbWFwcGVkIHJlYWRzICUgfAk5MC44NyUKICAgICAgICAgICAgICAgICAgICAgICAgICBBdmVyYWdlIG1hcHBlZCBsZW5ndGggfAkxNDYuMzcKICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXIgb2Ygc3BsaWNlczogVG90YWwgfAkyNzU1NTQzCiAgICAgICAgICAgIE51bWJlciBvZiBzcGxpY2VzOiBBbm5vdGF0ZWQgKHNqZGIpIHwJMjczNDczMAogICAgICAgICAgICAgICAgICAgICAgIE51bWJlciBvZiBzcGxpY2VzOiBHVC9BRyB8CTI3Mzk2OTcKICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXIgb2Ygc3BsaWNlczogR0MvQUcgfAkxMzIwNAogICAgICAgICAgICAgICAgICAgICAgIE51bWJlciBvZiBzcGxpY2VzOiBBVC9BQyB8CTE3NDQKICAgICAgICAgICAgICAgTnVtYmVyIG9mIHNwbGljZXM6IE5vbi1jYW5vbmljYWwgfAk4OTgKICAgICAgICAgICAgICAgICAgICAgIE1pc21hdGNoIHJhdGUgcGVyIGJhc2UsICUgfAkwLjE4JQogICAgICAgICAgICAgICAgICAgICAgICAgRGVsZXRpb24gcmF0ZSBwZXIgYmFzZSB8CTAuMDElCiAgICAgICAgICAgICAgICAgICAgICAgIERlbGV0aW9uIGF2ZXJhZ2UgbGVuZ3RoIHwJMS42MAogICAgICAgICAgICAgICAgICAgICAgICBJbnNlcnRpb24gcmF0ZSBwZXIgYmFzZSB8CTAuMDElCiAgICAgICAgICAgICAgICAgICAgICAgSW5zZXJ0aW9uIGF2ZXJhZ2UgbGVuZ3RoIHwJMS4yOQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1VTFRJLU1BUFBJTkcgUkVBRFM6CiAgICAgICAgTnVtYmVyIG9mIHJlYWRzIG1hcHBlZCB0byBtdWx0aXBsZSBsb2NpIHwJNTk5OTE1CiAgICAgICAgICAgICAlIG9mIHJlYWRzIG1hcHBlZCB0byBtdWx0aXBsZSBsb2NpIHwJNS4xNCUKICAgICAgICBOdW1iZXIgb2YgcmVhZHMgbWFwcGVkIHRvIHRvbyBtYW55IGxvY2kgfAkxMjc4NgogICAgICAgICAgICAgJSBvZiByZWFkcyBtYXBwZWQgdG8gdG9vIG1hbnkgbG9jaSB8CTAuMTElCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVTk1BUFBFRCBSRUFEUzoKICBOdW1iZXIgb2YgcmVhZHMgdW5tYXBwZWQ6IHRvbyBtYW55IG1pc21hdGNoZXMgfAkyMDM4MQogICAgICAgJSBvZiByZWFkcyB1bm1hcHBlZDogdG9vIG1hbnkgbWlzbWF0Y2hlcyB8CTAuMTclCiAgICAgICAgICAgIE51bWJlciBvZiByZWFkcyB1bm1hcHBlZDogdG9vIHNob3J0IHwJMzg5OTYwCiAgICAgICAgICAgICAgICAgJSBvZiByZWFkcyB1bm1hcHBlZDogdG9vIHNob3J0IHwJMy4zNCUKICAgICAgICAgICAgICAgIE51bWJlciBvZiByZWFkcyB1bm1hcHBlZDogb3RoZXIgfAk0Mjg3MQogICAgICAgICAgICAgICAgICAgICAlIG9mIHJlYWRzIHVubWFwcGVkOiBvdGhlciB8CTAuMzclCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDSElNRVJJQyBSRUFEUzoKICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXIgb2YgY2hpbWVyaWMgcmVhZHMgfAkwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAlIG9mIGNoaW1lcmljIHJlYWRzIHwJMC4wMCUKYGBgCgojIE11bHRpUUMKCldoaWxlIHRoZSBpbmZvcm1hdGlvbiBpcyBpbmNyZWRpYmx5IHVzZWZ1bCwgaXQgY2FuIGJlIHRlZGlvdXMgdG8gbG9vayBhdCB0aGUgcmVwb3J0IGZvciBlYWNoIHNhbXBsZSBzZXBhcmF0ZWx5LCB3aGlsZSBrZWVwaW5nIHRyYWNrIG9mIHdoYXQgdHJlbmRzIGVtZXJnZS4gSXQgd291bGQgYmUgbXVjaCBlYXNpZXIgdG8gbG9vayBhdCBhbGwgdGhlIGRhdGEgY29tcGlsZWQgaW50byBhIHNpbmdsZSByZXBvcnQuIFtNdWx0aVFDXShodHRwczovL211bHRpcWMuaW5mby8pIGlzIGEgdG9vbCB0aGF0IGRvZXMgZXhhY3RseSB0aGlzLgoKTXVsdGlRQyBpcyBkZXNpZ25lZCB0byBpbnRlcnByZXQgYW5kIGFnZ3JlZ2F0ZSByZXBvcnRzIGZyb20gW3ZhcmlvdXMgdG9vbHNdKGh0dHBzOi8vbXVsdGlxYy5pbmZvLyNzdXBwb3J0ZWQtdG9vbHMpIGFuZCBvdXRwdXQgYSBzaW5nbGUgcmVwb3J0IGFzIGFuIEhUTUwgZG9jdW1lbnQuCgojIyBNdWx0aVFDIERldGFpbHMKCk11bHRpUUMncyBtYWluIG91dHB1dCBpcyB0aGUgcmVwb3J0IGZpbGUgaW4gSFRNTCBmb3JtYXQuIFRoaXMgY2FuIGJlIHZpZXdlZCBpbiBhIHdlYiBicm93c2VyLiBBZGRpdGlvbmFsbHksIGl0IGNyZWF0ZXMgYSBgZGF0YWAgZGlyZWN0b3J5IHdpdGggdGV4dCBmaWxlcyBjb250YWluaW5nIHRoZSBkYXRhIHRoYXQgTXVsdGlRQyBnYXRoZXJlZCBkdXJpbmcgaXRzIGV4ZWN1dGlvbiAtIHRoaXMgc2FtZSBkYXRhIGlzIHdoYXQgaXMgc2hvd24gaW4gdGhlIHJlcG9ydC4KCkdpdmVuIGFuIG91dHB1dCBkaXJlY3Rvcnkgb3V0X211bHRpcWMsIHdlIHNob3VsZCBzZWUgc29tZXRoaW5nIGxpa2UgdGhlIGZvbGxvd2luZzoKCiAgICAjIGRpcmVjdG9yeSBvZiBtdWx0aXFjIGRhdGEgZmlsZXMKICAgIG91dF9tdWx0aXFjL211bHRpcWNfZGF0YS9tdWx0aXFjLmxvZwogICAgb3V0X211bHRpcWMvbXVsdGlxY19kYXRhL211bHRpcWNfZGF0YS5qc29uCiAgICBvdXRfbXVsdGlxYy9tdWx0aXFjX2RhdGEvbXVsdGlxY19nZW5lcmFsX3N0YXRzLnR4dAogICAgb3V0X211bHRpcWMvbXVsdGlxY19kYXRhL211bHRpcWNfcnNlbS50eHQKICAgIG91dF9tdWx0aXFjL211bHRpcWNfZGF0YS9tdWx0aXFjX3NvdXJjZXMudHh0CiAgICAjIG11bHRpcWMgcmVwb3J0CiAgICBvdXRfbXVsdGlxYy9tdWx0aXFjX3JlcG9ydC5odG1sCgoKSW4gYSBtb21lbnQgd2Ugd2lsbCBydW4gYG11bHRpcWNgLCBhbmQgaXQgd2lsbCBkZXRlY3QgdGhlc2UgcmVwb3J0cyBmcm9tIFNUQVIgYW5kIGluY2x1ZGUgdGhlbSBpbiB0aGUgcmVwb3J0LgoKSWYgd2UgdGhlbiBvcGVuIHRoZSBNdWx0aVFDIHJlcG9ydCAoSFRNTCksIHRoZSBuZXdseSBpbmNsdWRlZCBTVEFSIHNlY3Rpb24gd2lsbCBsb29rIHNvbWV0aGluZyBsaWtlIHRoZSBmb2xsb3dpbmc6Cgo8Y2VudGVyPgoKIVtFeGFtcGxlIG9mIFNUQVIgYWxpZ25tZW50IHN0YXRpc3RpY3MgaW4gTXVsdGlRQy5dKGltYWdlcy9tdWx0aXFjX3N0YXIucG5nKQpFeGFtcGxlIG9mIFNUQVIgYWxpZ25tZW50IHN0YXRpc3RpY3MgaW4gTXVsdGlRQy4KClNvdXJjZTogW011bHRpUUMgZXhhbXBsZSByZXBvcnRdKGh0dHBzOi8vbXVsdGlxYy5pbmZvL2V4YW1wbGVzL3JuYS1zZXEvbXVsdGlxY19yZXBvcnQuaHRtbCNzdGFyKQoKPC9jZW50ZXI+CgojIyBNdWx0aVFDIFdpdGggU1RBUiBFeGVyY2lzZToKCjEuIE5vdGUgdGhlIGNvbnRlbnRzIG9mIG91ciBhbmFseXNpcyBkaXJlY3RvcnksIGluY2x1ZGluZyB0aGUgU1RBUiBjb250ZW50cwoyLiBDb25zdHJ1Y3QgYSBNdWx0aVFDIGNvbW1hbmQgYW5kIGV4ZWN1dGUgaXQgb24gdGhpcyBkaXJlY3RvcnkKMy4gVmlldyB0aGUgTXVsdGlRQyByZXBvcnQKCmBgYAojIEVuc3VyZSB0aGF0IHdlJ3JlIGluIG91ciBhbmFseXNpcyBkaXJlY3RvcnksIG5vdGUgdGhlIGNvbnRlbnRzCmNkIH4vUlNEX1NoZWxsL2FuYWx5c2lzCmxzIC1sCiMgVmlldyBNdWx0aVFDIGhlbHAgcGFnZQptdWx0aXFjIC0taGVscAojIENvbnN0cnVjdCBhIE11bHRpUUMgY29tbWFuZCBhbmQgZXhlY3V0ZSBpdAptdWx0aXFjIC0tb3V0ZGlyIG91dF9tdWx0aXFjX3JzZW0gb3V0X3JzZW0vCiMgVmVyaWZ5IHRoYXQgdGhlIG91dHB1dCBmaWxlcyBhcmUgcHJlc2VudApgYGAKCldlIGp1c3QgbGVhcm5lZCBob3cgdG8gdmlldyBhbGwgb2Ygb3VyIGFsaWdubWVudCByZXN1bHRzIGluIG9uZSByZXBvcnQgd2l0aCB0aGUgaGVscCBvZiBNdWx0aVFDLiBNdWx0aVFDIGlzIGFsc28gdXNlZnVsIGJlY2F1c2UgaXQgY2FuIHV0aWxpemUgbXVsdGlwbGUgc2VwYXJhdGUgbW9kdWxlcyB0byBjcmVhdGUgc3VtbWFyeSBmaWd1cmVzIG9mIGRpZmZlcmVudCBzdGVwcyBpbiBvdXIgcGlwZWxpbmUuCgpXaGF0IHdvdWxkIGhhcHBlbiBpZiB3ZSBwb2ludGVkIG11bHRpUUMgYXQgb3VyIHRyaW1tZWQgRmFzdFFDIGRpcmVjdG9yeT8gV2hhdCBhYm91dCBpZiB3ZSBwb2ludCBpdCB0byB0aGUgZW50aXJlIGFuYWx5c2lzIGRpcmVjdG9yeT8gTGV0J3MgdHJ5IGl0IG91dCBhbmQgc2VlOgoKYGBgCiMgQSBNdWx0aVFDIGNvbW1hbmQgdG8gYW5hbHl6ZSB0aGUgdHJpbW1lZCByZWFkIHJlc3VsdHMKbXVsdGlxYyAtLW91dGRpciBvdXRfbXVsdGlxY19jdXRhZGFwdCBvdXRfZmFzdHFjX3RyaW1tZWQvCiMgQSBtdWx0aVFDIGNvbW1hbmQgZm9yIGEgcmVwb3J0IG9mIHRoZSBlbnRpcmUgcHJvamVjdAptdWx0aXFjIC0tb3V0ZGlyIG91dF9tdWx0aXFjX2FsbCAgLgpgYGAKCiMjIFRyYW5zZmVyIE11bHRpUUMgUmVwb3J0cyBXaXRoIGBzY3BgIEV4ZXJjaXNlCgpCZWZvcmUgc3RhcnRpbmcgb3VyIGZpbGUgdHJhbnNmZXIgZXhlcmNpc2UsIHdlIHNob3VsZCBtYWtlIHN1cmUgdGhhdCB3ZSBhcmUgb24gdGhlIHNhbWUgcGFnZS4gRm9sbG93IHRoZSBsaW5rIGJlbG93OgoKW0xpbmsgdG8gcmVwb3J0IHRyYW5zZmVyIGV4ZXJjaXNlXShNb2R1bGUwNF9icmVha291dDAzX3NvbC5odG1sKQoKCjxicj4KPGJyPgoKIyMgQ3JlYXRpbmcgdGhlIGNvdW50IG1hdHJpeAoKV2UgaGF2ZSB2aWV3ZWQgc29tZSBvZiB0aGUgZ2VuZSBleHByZXNzaW9uIHF1YW50aWZpY2F0aW9uIHJlc3VsdHMgaW5kaXZpZHVhbGx5LiBJdCBjYW4gYmUgdXNlZnVsIHRvIGNvbWJpbmUgdGhlc2UgZXhwcmVzc2lvbiB2YWx1ZXMgaW50byBhIGNvdW50IG1hdHJpeC4gVGhpcyBpcyBoZWxwZnVsIHdoZW4gZ2F0aGVyaW5nIGV4cHJlc3Npb24tbGV2ZWwgUUMgbWV0cmljcywgYXMgd2VsbCBhcyBmb3IgaW5wdXQgaW50byBhIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gcHJvZ3JhbSBzdWNoIGFzIERFU2VxMi4KClRoZXJlIGFyZSBtYW55IHdheXMgdG8gY29tYmluZSB0aGVzZSByZXN1bHRzIGludG8gYSBjb3VudCBtYXRyaXguIEZvciB0aGlzIHdvcmtzaG9wLCB3ZSdsbCB1c2UgYSBzbWFsbCBweXRob24gc2NyaXB0IHRvIGBjb21iaW5lLnB5YCB0aGF0IHdlJ3ZlIG1hZGUgZm9yIHRoaXMgcHVycG9zZS4gVG8gdW5kZXJzdGFuZCB0aGUgcHJvY2VzcyBhIGJpdCBtb3JlLCBsZXQncyByZXZpZXcgdGhlIGAuZ2VuZXMucmVzdWx0c2AgZmlsZXMgdGhhdCB3ZSB3YW50IHRvIGNvbWJpbmUgYW5kIGRpc2N1c3Mgc29tZSBkZXRhaWxzIG9mIHRoZSBzY3JpcHQuIEZpbmFsbHksIHdlJ2xsIGVuZCB3aXRoIGFuIGV4ZXJjaXNlIGNyZWF0aW5nIGEgY291bnQgbWF0cml4LgoKCklmIHdlIHJldmlldyB0aGUgKi5nZW5lcy5yZXN1bHRzIGZpbGVzLCB3ZSBjYW4gc2VlIHZhcmlvdXMgY29sdW1ucyBvZiBkYXRhIG91dHB1dCBmcm9tIFJTRU0gdGhhdCB3ZSBkaXNjdXNzZWQgaW4gdGhlIGxhc3QgbW9kdWxlLgoKICAgICMgUmV2aWV3IG9mICouZ2VuZXMucmVzdWx0cyBmaWxlIGNvbnRlbnRzCiAgICBnZW5lX2lkICAgICAgICAgICAgICAgICB0cmFuc2NyaXB0X2lkKHMpICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoICBlZmZlY3RpdmVfbGVuZ3RoICAgICAgICBleHBlY3RlZF9jb3VudCAgVFBNICAgICBGUEtNCiAgICBFTlNNVVNHMDAwMDAwMDAwMDEgICAgICBFTlNNVVNUMDAwMDAwMDAwMDEgICAgICAgICAgICAgICAgICAgICAgMzI2Mi4wMCAzMTE2LjI4ICAgICAgICAgICAgICAgICA2MDEuMDAgICAgICAgICAgNDUuNTAgICAzNi43MAogICAgRU5TTVVTRzAwMDAwMDAwMDAzICAgICAgRU5TTVVTVDAwMDAwMDAwMDAzLEVOU01VU1QwMDAwMDExNDA0MSAgIDc5OS41MCAgNjUzLjc4ICAgICAgICAgICAgICAgICAgMC4wMCAgICAgICAgICAgIDAuMDAgICAgMC4wMAoKV2UnbGwgdGFrZSB0aGUgYGV4cGVjdGVkX2NvdW50YCBjb2x1bW4gZnJvbSBlYWNoIHNhbXBsZSdzIGRhdGEsIGFuZCBjb21iaW5lIHRoZXNlIHNvIHRoYXQgd2UgaGF2ZSBhbiBhZ2dyZWdhdGVkIGRhdGEgbWF0cml4IHdpdGggYSByb3cgZm9yIGVhY2ggZ2VuZSBhbmQgYSBjb2x1bW4gZm9yIGVhY2ggc2FtcGxlLgoKVGhlIGlucHV0IGZvciB0aGlzIHN0ZXAgd2lsbCBiZSB0aGUgZGlyZWN0b3J5IG9mICouZ2VuZXMucmVzdWx0cyBmaWxlcyBmcm9tIFJTRU0sIGFuZCB0aGUgb3V0cHV0IHdpbGwgYmUgYSB0YWItc2VwYXJhdGVkIGNvdW50IG1hdHJpeCBmaWxlIHdoaWNoIHdlIGNhbiB1c2UgZm9yIGNvdW50LWxldmVsIFFDIGFuZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KCgo8ZGV0YWlscz4KPHN1bW1hcnk+Q29udGVudHMgb2YgY29tYmluZS5weSBzY3JpcHQ8L3N1bW1hcnk+CgpbSGVyZV0oaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdHdzYWFyaS8xMmM1YWEyNzczMjkyYzA5YzE4MDlkNWEzZGI2NjkwMykgYXJlIHRoZSBjb250ZW50cyBvZiB0aGUgcHl0aG9uIHNjcmlwdCB3ZSdsbCB1c2UsIGBjb21iaW5lLnB5YDoKPC9kZXRhaWxzPgoKIyMgQ291bnQgTWF0cml4IEV4ZXJjaXNlOgoKMS4gVmlldyB0aGUgaGVscCBmaWxlIG9mIGBjb21iaW5lLnB5YAoyLiBDb25zdHJ1Y3QgLyBleGVjdXRlIGEgY29tbWFuZCB0byBjb21iaW5lIG91ciByZXN1bHRzIGludG8gYSBjb3VudCBtYXRyaXgKMy4gVmlldyB0aGUgcmVzdWx0aW5nIGNvdW50IG1hdHJpeAoKYGBgCiMgVmlldyB0aGUgaGVscCBmaWxlIG9mIGNvbWJpbmUucHkKY29tYmluZS5weSAtLWhlbHAKIyBDb25zdHJ1Y3QgYW5kIGV4ZWN1dGUgdGhlIGNvbW1hbmQgdG8gY29tYmluZS5weQpjb21iaW5lLnB5IC0taW5wdXRfcGF0aCAib3V0X3JzZW0vKi5nZW5lcy5yZXN1bHRzIiAtLW91dHB1dF9maWxlIGNvbWJpbmVkX2NvdW50cy50eHQgLWMgZXhwZWN0ZWRfY291bnQgLS1pZF9jb2x1bW5zIGdlbmVfaWQKIyBWaWV3IHRoZSByZXN1bHRpbmcgY291bnQgbWF0cml4Cmxlc3MgLVMgY29tYmluZWRfY291bnRzLnR4dApgYGAKCjxicj4KPGJyPgoKLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGNyZWF0ZWQgYnkgdGhlIFtIYXJ2YXJkIENoYW4gQmlvaW5mb3JtYXRpY3MgQ29yZSAoSEJDKV0oaHR0cDovL2Jpb2luZm9ybWF0aWNzLnNwaC5oYXJ2YXJkLmVkdS8pLiBUaGVzZSBhcmUgb3BlbiBhY2Nlc3MgbWF0ZXJpYWxzIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgW0NyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24gbGljZW5zZSAoQ0MgQlkgNC4wKV0oaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyksIHdoaWNoIHBlcm1pdHMgdW5yZXN0cmljdGVkIHVzZSwgZGlzdHJpYnV0aW9uLCBhbmQgcmVwcm9kdWN0aW9uIGluIGFueSBtZWRpdW0sIHByb3ZpZGVkIHRoZSBvcmlnaW5hbCBhdXRob3IgYW5kIHNvdXJjZSBhcmUgY3JlZGl0ZWQuCg==