Alignment and Quantification

Two workflows are possible with RNA-seq data - with the difference being whether one performs an alignment to the reference genome or not.

Recent tools for RNA-seq analysis (e.g. salmon, kallisto) do not require the time-consuming step of whole-genome alignment to be performed, and can therefore produce gene-level counts in a much faster time frame. They not require the creation of large bam files, which is useful if constrained by file space on Galaxy.

(image from Harvard Bioinformatics Core)

We will demonstrate both methods, but for further analysis in R we will use counts that have been generated with salmon.

A much reduced dataset will be used for illustration although the commands used can applied to more-realistic sizes of data.

We first make sure that we are located in the directory with our fastq files

cd ~/rnaseq_data
ls

We will need some reference data for the alignment, and a good place to obtain these data is from Ensembl.

Links to downloads for a variety of different organisms can be found at https://www.ensembl.org/info/data/ftp/index.html. We can navigate to the particular organism we are interested in through the interface and download the transcript and genome sequences to our laptop. However, we need the reference files to be present on the remote server that we are performing the analysis on.

The command wget can be used to download a file from an FTP site to a local directory if you know the path to the file (URL). This path could be obtained by first locating the file in Ensembl and right-clicking to copy the link address.

The homepage of Ensembl FTP index links to reference data for common genomes

Files can be downloaded by clicking on the relevant link. However, we want to download the data using the command line so we have to right-click and select “Copy Link Location” (or similar)

The following commands should download reference data from Ensembl into a newly-created ref_data folder. It is good practice to keep your reference data stored in a sub-folder. However, check first (with I.T or local Bioinformaticians) that you don’t have a local copy of reference genomes on your file system

mkdir ref_data
wget ftp://ftp.ensembl.org/pub/release-91/fasta/mus_musculus/cdna/Mus_musculus.GRCm38.cdna.all.fa.gz -P ref_data/
wget ftp://ftp.ensembl.org/pub/release-91/gtf/mus_musculus/Mus_musculus.GRCm38.91.chr.gtf.gz -P ref_data
wget ftp://ftp.ensembl.org/pub/release-91/fasta/mus_musculus/dna/Mus_musculus.GRCm38.dna.chromosome.1.fa.gz -P ref_data/

Workflow 1: Quantify the transcripts with salmon

Salmon is a tool for quantifying the expression of transcripts using RNA-seq data. It is based on a new algorithm that couples the concept of quasi-mapping with a two-phase inference procedure, providing accurate expression estimates very quickly and using little memory. It quantifies the expression of the transcripts from a given annotation, so it is not able to identify non annotated genes and transcripts.

The documentation of the tool is available at https://salmon.readthedocs.io/en/latest/salmon.html

Salmon is able to quantify transcript expression by using a quasi-mapping algorithm. Quasi-mappings are mappings of reads to transcript positions that are computed without performing a base-to-base alignment of the read to the transcript. This approach is typically much faster to compute than traditional (or full) alignments, and can sometimes provide superior accuracy by being more robust to errors in the read or genomic variation from the reference sequence

salmon requires the user to create an index from the fasta file of the transcripts. We have to specify a prefix name for all the files that salmon is going to create.

salmon index -i index/GRCm38_salmon -t ref_data/Mus_musculus.GRCm38.cdna.all.fa.gz

The quasi-mapping approach of salmon can be run with the salmon quant command. Help on running this tool can be displayed with the following command.

salmon quant --help-reads

We need to specify the path of the index we have just created in the previous step, locations of our fastq files, and the library type (if unsure the option A can be used. See the help page for more options https://salmon.readthedocs.io/en/latest/salmon.html#what-s-this-libtype). We can specify where salmon write it’s output files to, but this directory does not need to exist prior to running the command.

salmon quant -i index/GRCm38_salmon --libType A -r SRR1552444.fastq.gz -o quant/SRR1552444

Challenge 1

Navigate to the quant folder and explore the files that salmon has created. What file contains the quantifications? Use the salmon documentation to understand the various output files https://salmon.readthedocs.io/en/latest/file_formats.html#fileformats

Running for all samples

If we want to repeat the quantification step for all our samples, we could employ a for loop as we have seen previously.

for filename in *.fastq.gz
do
name=$(basename $filename .fastq.gz)
salmon quant -i index/GRCm38_salmon --libType A -r $filename.fastq.gz -o quant/$name
gzip quant/$name/quant.sf
done

Workflow 2: Align and then count

There are numerous tools performing short read alignment and the choice of aligner should be carefully made according to the analysis goals/requirements. Here we will use HISAT2, a fast aligner with low memory requirements that performs spliced alignments. It is the program suggested for the alignment in the new Tuxedo protocol (https://doi.org/10.1038/nprot.2016.095) and it requires an indexed genome to keep its memory footprint small and the running time short.

The program creates a genome index by using the FASTA file of the sequence we want to use as reference. A sequence in FASTA format begins with a single-line description, followed by lines of sequence data. The description line is distinguished from the sequence data by a greater-than (>) symbol at the beginning.

Creating a genome index

We have already downloaded the reference file that we want to use. However, it is in a compressed format and not immediately useful for building the genome index. We need to un-compress the file first using the gunzip command (similar to unzip that we used earlier in the course). This should now replace the file ref_data/Mus_musculus.GRCm38.dna.chromosome.1.fa.gz with ref_data/Mus_musculus.GRCm38.dna.chromosome.1.fa

The command hisat2-build is used to take the reference sequence and create an efficient data structure for searching. We need to specify a prefix that will be used to name the files that hisat2 generates.

The index step only needs to be run once for a particular genome versions. You can use the same index to align multiple samples.

Note that the following index procedure may take a few minutes to run

gunzip ref_data/Mus_musculus.GRCm38.dna.chromosome.1.fa.gz
hisat2-build ref_data/Mus_musculus.GRCm38.dna.chromosome.1.fa index/GRCm38_chr1_hisat

The index files of Mouse genome GRCm38 chromosome 1 should now be in the directory index/. All of them have a file name starting with the GRCm38_chr1_hisat prefix.

Alignment with hisat2

There are several parameters we might want to specify in order to align our reads with HISAT2. To view them all type.

hisat2 --help

To align one of our example files against the index that we have just created we need to

  • change the -x argument to be the file prefix of all the index files created in the previous step.
  • specify that our reads are un-paired and to be found in the file SRR1552444.fastq.gz
  • specify an output file for the alignments. We will create a new directory to save these alignments to aligned_reads
mkdir aligned_reads
hisat2 -x index/GRCm38_chr1_hisat -U SRR1552444.fastq.gz -S aligned_reads/SRR1552444.sam

The output file created is an example of a Sequence Alignment/Map (SAM) file. This is a human-readable file that tells us how well and where each of our sequencing reads aligned.

head aligned_reads/SRR1552444.sam

After a short section that describes the references sequences used for alignment (here we only have a single chromosome; chromosome 1). There is a tab-deliminted section describing the alignment of each read.

Column Official Name Brief
1 QNAME Sequence ID
2 FLAG Sequence quality expressed as a bitwise flag
3 RNAME Chromosome
4 POS Start Position
5 MAPQ Mapping Quality
6 CIGAR Describes positions of matches, insertions, deletions w.r.t reference
7 RNEXT Ref. name of mate / next read
8 PNEXT Postion of mate / next read
9 TLEN Observed Template length
10 SEQ Sequence
11 QUAL Base Qualities

There can also be all manner of optional tags as extra columns introduce by an aligner or downstream analysis tool. A common use is the RG tag which refers back to the read groups in the header.

The “flags” in the sam file can represent useful QC information

  • Read is unmapped
  • Read is paired / unpaired
  • Read failed QC
  • Read is a PCR duplicate (see later)

The combination of any of these properties is used to derive a numeric value

For instance, a particular read has a flag of 163

Derivation

There is a set of properties that a read can possess. If a particular property is observed, a corresponding power of 2 is added multiplied by 1. The final value is derived by summing all the powers of 2.

    ReadHasProperty     Binary  MultiplyBy
isPaired    TRUE    1   1
isProperPair    TRUE    1   2
isUnmappedQuery     FALSE   0   4
hasUnmappedMate     FALSE   0   8
isMinusStrand   FALSE   0   16
isMateMinusStrand   TRUE    1   32
isFirstMateRead     FALSE   0   64
isSecondMateRead    TRUE    1   128
isSecondaryAlignment    FALSE   0   256
isNotPassingQualityControls     FALSE   0   512
isDuplicate     FALSE   0   1024

Value of flag is given by

1x1 + 1x2 + 0x4 + 0x8 + 0x16 + 1x32 + 0x64 + 1x128 + 0x256 + 0x512 + 0x1024 = 163

See also

Challenge 2

Use HISAT2 to align one of the other fastq files to chromosome 1 of Mouse GRCm38 (say SRR1552445.fastq) What percentage of reads align?

Converting to a bam file

sam files are easy to read, but rarely used in analysis as they can require large amounts of disk space. The alignments from a sequencing run are more-commonly stored in compressed, binary file know as a bam file. Exactly the same information is contained, except they are more portable.

samtools is used for the conversion and manipulation of sam and bam files.

The steps in producing a bam file for analysis are given below. The final step is important as it creates an index file. This index needs to be present in order for analysis tools to access the reads in the file in an efficient manner.

samtools view -bS aligned_reads/SRR1552444.sam > aligned_reads/SRR1552444.bam
samtools sort aligned_reads/SRR1552444.bam -o aligned_reads/SRR1552444.sorted.bam
samtools index aligned_reads/SRR1552444.sorted.bam

The samtools suite also includes a couple of tools that are useful for QC purposes.

samtools flagstat aligned_reads/SRR1552444.sorted.bam
samtools idxstats aligned_reads/SRR1552444.sorted.bam

Counting features

We will use the featureCounts tool to obtain transcript-level counts for the aligned reads that we have just created. This requires that gtf files that we downloaded from Ensembl earlier and our newly-created bam file.

mkdir featureCounts
featureCounts -a ref_data/Mus_musculus.GRCm38.91.chr.gtf.gz -o featureCounts/SRR1552444.counts aligned_reads/SRR1552444.sorted.bam

The command is able to accept multiple bam files

Running alignment for all fastq files

If we wanted to process all the fastq files in our dataset, the following workflow could be employed. This creates a variable i that takes all values from 44 to 55 and processes the appropriate fastq file in turn. The echo command is used here to print the particular sample that is being processed.

for filename in *.fastq.gz
do
name=$(basename $filename .fastq.gz)
echo Processing sample $name
hisat2 -x index/GRCm38_chr1_hisat -U $filename -S aligned_reads/$name.sam 
samtools view -bS aligned_reads/$name.sam > aligned_reads/$name.bam
samtools sort aligned_reads/$name.bam -o aligned_reads/$name.sorted.bam
samtools index aligned_reads/$name.sorted.bam
done
LS0tCnRpdGxlOiAiQWxpZ25tZW50IGFuZCBxdWFudGlmaWNhdGlvbiIKYXV0aG9yOiAiTWFyayBEdW5uaW5nIgpkYXRlOiAnYHIgZm9ybWF0KFN5cy50aW1lKCksICJMYXN0IG1vZGlmaWVkOiAlZCAlYiAlWSIpYCcKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNzczogc3R5bGVzaGVldHMvc3R5bGVzLmNzcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyBBbGlnbm1lbnQgYW5kIFF1YW50aWZpY2F0aW9uCgpUd28gd29ya2Zsb3dzIGFyZSBwb3NzaWJsZSB3aXRoIFJOQS1zZXEgZGF0YSAtIHdpdGggdGhlIGRpZmZlcmVuY2UgYmVpbmcgd2hldGhlciBvbmUgcGVyZm9ybXMgYW4gYWxpZ25tZW50IHRvIHRoZSByZWZlcmVuY2UgZ2Vub21lIG9yIG5vdC4KClJlY2VudCB0b29scyBmb3IgUk5BLXNlcSBhbmFseXNpcyAoZS5nLiBgc2FsbW9uYCwgYGthbGxpc3RvYCkgZG8gbm90IHJlcXVpcmUgdGhlIHRpbWUtY29uc3VtaW5nIHN0ZXAgb2Ygd2hvbGUtZ2Vub21lIGFsaWdubWVudCB0byBiZSBwZXJmb3JtZWQsIGFuZCBjYW4gdGhlcmVmb3JlIHByb2R1Y2UgZ2VuZS1sZXZlbCBjb3VudHMgaW4gYSBtdWNoIGZhc3RlciB0aW1lIGZyYW1lLiBUaGV5IG5vdCByZXF1aXJlIHRoZSBjcmVhdGlvbiBvZiBsYXJnZSBiYW0gZmlsZXMsIHdoaWNoIGlzIHVzZWZ1bCBpZiBjb25zdHJhaW5lZCBieSBmaWxlIHNwYWNlIG9uIEdhbGF4eS4KCiFbXShodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9JbnRyby10by1ybmFzZXEtaHBjLWd0L2ltZy9hbGlnbm1lbnRmcmVlX3dvcmtmbG93X2F1ZzIwMTcucG5nKQoKKGltYWdlIGZyb20gSGFydmFyZCBCaW9pbmZvcm1hdGljcyBDb3JlKQoKCldlIHdpbGwgZGVtb25zdHJhdGUgYm90aCBtZXRob2RzLCBidXQgZm9yIGZ1cnRoZXIgYW5hbHlzaXMgaW4gUiB3ZSB3aWxsIHVzZSBjb3VudHMgdGhhdCBoYXZlIGJlZW4gZ2VuZXJhdGVkIHdpdGggYHNhbG1vbmAuIAoKQSBtdWNoIHJlZHVjZWQgZGF0YXNldCB3aWxsIGJlIHVzZWQgZm9yIGlsbHVzdHJhdGlvbiBhbHRob3VnaCB0aGUgY29tbWFuZHMgdXNlZCBjYW4gYXBwbGllZCB0byBtb3JlLXJlYWxpc3RpYyBzaXplcyBvZiBkYXRhLgoKV2UgZmlyc3QgbWFrZSBzdXJlIHRoYXQgd2UgYXJlIGxvY2F0ZWQgaW4gdGhlIGRpcmVjdG9yeSB3aXRoIG91ciBmYXN0cSBmaWxlcwoKYGBge2Jhc2ggZXZhbD1GQUxTRX0KY2Qgfi9ybmFzZXFfZGF0YQpscwpgYGAKCldlIHdpbGwgbmVlZCBzb21lIHJlZmVyZW5jZSBkYXRhIGZvciB0aGUgYWxpZ25tZW50LCBhbmQgYSBnb29kIHBsYWNlIHRvIG9idGFpbiB0aGVzZSBkYXRhIGlzIGZyb20gRW5zZW1ibC4gCgpMaW5rcyB0byBkb3dubG9hZHMgZm9yIGEgdmFyaWV0eSBvZiBkaWZmZXJlbnQgb3JnYW5pc21zIGNhbiBiZSBmb3VuZCBhdCBodHRwczovL3d3dy5lbnNlbWJsLm9yZy9pbmZvL2RhdGEvZnRwL2luZGV4Lmh0bWwuIFdlIGNhbiBuYXZpZ2F0ZSB0byB0aGUgcGFydGljdWxhciBvcmdhbmlzbSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiB0aHJvdWdoIHRoZSBpbnRlcmZhY2UgYW5kIGRvd25sb2FkIHRoZSB0cmFuc2NyaXB0IGFuZCBnZW5vbWUgc2VxdWVuY2VzIHRvIG91ciBsYXB0b3AuIEhvd2V2ZXIsIHdlIG5lZWQgdGhlIHJlZmVyZW5jZSBmaWxlcyB0byBiZSBwcmVzZW50IG9uIHRoZSByZW1vdGUgc2VydmVyIHRoYXQgd2UgYXJlIHBlcmZvcm1pbmcgdGhlIGFuYWx5c2lzIG9uLgoKVGhlIGNvbW1hbmQgYHdnZXRgIGNhbiBiZSB1c2VkIHRvIGRvd25sb2FkIGEgZmlsZSBmcm9tIGFuIEZUUCBzaXRlIHRvIGEgbG9jYWwgZGlyZWN0b3J5IGlmIHlvdSBrbm93IHRoZSBwYXRoIHRvIHRoZSBmaWxlIChVUkwpLiBUaGlzIHBhdGggY291bGQgYmUgb2J0YWluZWQgYnkgZmlyc3QgbG9jYXRpbmcgdGhlIGZpbGUgaW4gRW5zZW1ibCBhbmQgcmlnaHQtY2xpY2tpbmcgdG8gY29weSB0aGUgbGluayBhZGRyZXNzLgoKVGhlIGhvbWVwYWdlIG9mIEVuc2VtYmwgRlRQIGluZGV4IGxpbmtzIHRvIHJlZmVyZW5jZSBkYXRhIGZvciBjb21tb24gZ2Vub21lcwoKPGltZyBzcmM9ImltYWdlcy9lbnNlbWJsX2Rvd25sb2FkLnBuZyIvPgoKRmlsZXMgY2FuIGJlIGRvd25sb2FkZWQgYnkgY2xpY2tpbmcgb24gdGhlIHJlbGV2YW50IGxpbmsuIEhvd2V2ZXIsIHdlIHdhbnQgdG8gZG93bmxvYWQgdGhlIGRhdGEgdXNpbmcgdGhlIGNvbW1hbmQgbGluZSBzbyB3ZSBoYXZlIHRvIHJpZ2h0LWNsaWNrIGFuZCBzZWxlY3QgIkNvcHkgTGluayBMb2NhdGlvbiIgKG9yIHNpbWlsYXIpCgo8aW1nIHNyYz0iaW1hZ2VzL2NkbmFfZG93bmxvYWQucG5nIi8+CgpUaGUgZm9sbG93aW5nIGNvbW1hbmRzIHNob3VsZCBkb3dubG9hZCByZWZlcmVuY2UgZGF0YSBmcm9tIEVuc2VtYmwgaW50byBhIG5ld2x5LWNyZWF0ZWQgYHJlZl9kYXRhYCBmb2xkZXIuIEl0IGlzIGdvb2QgcHJhY3RpY2UgdG8ga2VlcCB5b3VyIHJlZmVyZW5jZSBkYXRhIHN0b3JlZCBpbiBhIHN1Yi1mb2xkZXIuICpIb3dldmVyLCBjaGVjayBmaXJzdCAod2l0aCBJLlQgb3IgbG9jYWwgQmlvaW5mb3JtYXRpY2lhbnMpIHRoYXQgeW91IGRvbid0IGhhdmUgYSBsb2NhbCBjb3B5IG9mIHJlZmVyZW5jZSBnZW5vbWVzIG9uIHlvdXIgZmlsZSBzeXN0ZW0qCgpgYGB7YmFzaCBldmFsPUZBTFNFfQpta2RpciByZWZfZGF0YQp3Z2V0IGZ0cDovL2Z0cC5lbnNlbWJsLm9yZy9wdWIvcmVsZWFzZS05MS9mYXN0YS9tdXNfbXVzY3VsdXMvY2RuYS9NdXNfbXVzY3VsdXMuR1JDbTM4LmNkbmEuYWxsLmZhLmd6IC1QIHJlZl9kYXRhLwp3Z2V0IGZ0cDovL2Z0cC5lbnNlbWJsLm9yZy9wdWIvcmVsZWFzZS05MS9ndGYvbXVzX211c2N1bHVzL011c19tdXNjdWx1cy5HUkNtMzguOTEuY2hyLmd0Zi5neiAtUCByZWZfZGF0YQp3Z2V0IGZ0cDovL2Z0cC5lbnNlbWJsLm9yZy9wdWIvcmVsZWFzZS05MS9mYXN0YS9tdXNfbXVzY3VsdXMvZG5hL011c19tdXNjdWx1cy5HUkNtMzguZG5hLmNocm9tb3NvbWUuMS5mYS5neiAtUCByZWZfZGF0YS8KYGBgCgoKCiMjIFdvcmtmbG93IDE6IFF1YW50aWZ5IHRoZSB0cmFuc2NyaXB0cyB3aXRoIHNhbG1vbgoKU2FsbW9uIGlzIGEgdG9vbCBmb3IgcXVhbnRpZnlpbmcgdGhlIGV4cHJlc3Npb24gb2YgdHJhbnNjcmlwdHMgdXNpbmcgUk5BLXNlcSBkYXRhLiBJdCBpcyBiYXNlZCBvbiBhIG5ldwphbGdvcml0aG0gdGhhdCBjb3VwbGVzIHRoZSBjb25jZXB0IG9mIHF1YXNpLW1hcHBpbmcgd2l0aCBhIHR3by1waGFzZSBpbmZlcmVuY2UgcHJvY2VkdXJlLCBwcm92aWRpbmcgYWNjdXJhdGUKZXhwcmVzc2lvbiBlc3RpbWF0ZXMgdmVyeSBxdWlja2x5IGFuZCB1c2luZyBsaXR0bGUgbWVtb3J5LiBJdCBxdWFudGlmaWVzIHRoZSBleHByZXNzaW9uIG9mIHRoZSB0cmFuc2NyaXB0cyBmcm9tCmEgZ2l2ZW4gYW5ub3RhdGlvbiwgc28gaXQgaXMgbm90IGFibGUgdG8gaWRlbnRpZnkgbm9uIGFubm90YXRlZCBnZW5lcyBhbmQgdHJhbnNjcmlwdHMuCgpUaGUgZG9jdW1lbnRhdGlvbiBvZiB0aGUgdG9vbCBpcyBhdmFpbGFibGUgYXQKaHR0cHM6Ly9zYWxtb24ucmVhZHRoZWRvY3MuaW8vZW4vbGF0ZXN0L3NhbG1vbi5odG1sCgpTYWxtb24gaXMgYWJsZSB0byBxdWFudGlmeSB0cmFuc2NyaXB0IGV4cHJlc3Npb24gYnkgdXNpbmcgYSBxdWFzaS1tYXBwaW5nIGFsZ29yaXRobS4gUXVhc2ktbWFwcGluZ3MgYXJlCm1hcHBpbmdzIG9mIHJlYWRzIHRvIHRyYW5zY3JpcHQgcG9zaXRpb25zIHRoYXQgYXJlIGNvbXB1dGVkIHdpdGhvdXQgcGVyZm9ybWluZyBhIGJhc2UtdG8tYmFzZSBhbGlnbm1lbnQKb2YgdGhlIHJlYWQgdG8gdGhlIHRyYW5zY3JpcHQuIFRoaXMgYXBwcm9hY2ggaXMgdHlwaWNhbGx5IG11Y2ggZmFzdGVyIHRvIGNvbXB1dGUgdGhhbiB0cmFkaXRpb25hbCAob3IgZnVsbCkKYWxpZ25tZW50cywgYW5kIGNhbiBzb21ldGltZXMgcHJvdmlkZSBzdXBlcmlvciBhY2N1cmFjeSBieSBiZWluZyBtb3JlIHJvYnVzdCB0byBlcnJvcnMgaW4gdGhlIHJlYWQgb3IKZ2Vub21pYyB2YXJpYXRpb24gZnJvbSB0aGUgcmVmZXJlbmNlIHNlcXVlbmNlCgpgc2FsbW9uYCByZXF1aXJlcyB0aGUgdXNlciB0byBjcmVhdGUgYW4gaW5kZXggZnJvbSB0aGUgYGZhc3RhYCBmaWxlIG9mIHRoZSB0cmFuc2NyaXB0cy4gV2UgaGF2ZSB0byBzcGVjaWZ5IGEgKnByZWZpeCogbmFtZSBmb3IgYWxsIHRoZSBmaWxlcyB0aGF0IGBzYWxtb25gIGlzIGdvaW5nIHRvIGNyZWF0ZS4gCgpgYGB7YmFzaCBldmFsPUZBTFNFfQpzYWxtb24gaW5kZXggLWkgaW5kZXgvR1JDbTM4X3NhbG1vbiAtdCByZWZfZGF0YS9NdXNfbXVzY3VsdXMuR1JDbTM4LmNkbmEuYWxsLmZhLmd6CmBgYAoKVGhlIHF1YXNpLW1hcHBpbmcgYXBwcm9hY2ggb2YgYHNhbG1vbmAgY2FuIGJlIHJ1biB3aXRoIHRoZSBgc2FsbW9uIHF1YW50YCBjb21tYW5kLiBIZWxwIG9uIHJ1bm5pbmcgdGhpcyB0b29sIGNhbiBiZSBkaXNwbGF5ZWQgd2l0aCB0aGUgZm9sbG93aW5nIGNvbW1hbmQuCgpgYGB7YmFzaCBldmFsPUZBTFNFfQpzYWxtb24gcXVhbnQgLS1oZWxwLXJlYWRzCmBgYAoKV2UgbmVlZCB0byBzcGVjaWZ5IHRoZSBwYXRoIG9mIHRoZSBpbmRleCB3ZSBoYXZlIGp1c3QgY3JlYXRlZCBpbiB0aGUgcHJldmlvdXMgc3RlcCwgbG9jYXRpb25zIG9mIG91ciBgZmFzdHFgIGZpbGVzLCBhbmQgdGhlIGxpYnJhcnkgdHlwZSAoaWYgdW5zdXJlIHRoZSBvcHRpb24gQSBjYW4gYmUgdXNlZC4gU2VlIHRoZSBoZWxwIHBhZ2UgZm9yIG1vcmUgb3B0aW9ucyBodHRwczovL3NhbG1vbi5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3Qvc2FsbW9uLmh0bWwjd2hhdC1zLXRoaXMtbGlidHlwZSkuIFdlIGNhbiBzcGVjaWZ5IHdoZXJlIGBzYWxtb25gIHdyaXRlIGl0J3Mgb3V0cHV0IGZpbGVzIHRvLCBidXQgdGhpcyBkaXJlY3RvcnkgZG9lcyBub3QgbmVlZCB0byBleGlzdCBwcmlvciB0byBydW5uaW5nIHRoZSBjb21tYW5kLgoKYGBge2Jhc2ggZXZhbD1GQUxTRX0Kc2FsbW9uIHF1YW50IC1pIGluZGV4L0dSQ20zOF9zYWxtb24gLS1saWJUeXBlIEEgLXIgU1JSMTU1MjQ0NC5mYXN0cS5neiAtbyBxdWFudC9TUlIxNTUyNDQ0CgpgYGAKCj4gIyMgQ2hhbGxlbmdlIDEgey5jaGFsbGVuZ2V9Cj4gCj4gTmF2aWdhdGUgdG8gdGhlIHF1YW50IGZvbGRlciBhbmQgZXhwbG9yZSB0aGUgZmlsZXMgdGhhdCBzYWxtb24gaGFzIGNyZWF0ZWQuCj4gV2hhdCBmaWxlIGNvbnRhaW5zIHRoZSBxdWFudGlmaWNhdGlvbnM/IAo+IFVzZSB0aGUgc2FsbW9uIGRvY3VtZW50YXRpb24gdG8gdW5kZXJzdGFuZCB0aGUgdmFyaW91cyBvdXRwdXQgZmlsZXMgaHR0cHM6Ly9zYWxtb24ucmVhZHRoZWRvY3MuaW8vZW4vbGF0ZXN0L2ZpbGVfZm9ybWF0cy5odG1sI2ZpbGVmb3JtYXRzCgojIyMgUnVubmluZyBmb3IgYWxsIHNhbXBsZXMKCklmIHdlIHdhbnQgdG8gcmVwZWF0IHRoZSBxdWFudGlmaWNhdGlvbiBzdGVwIGZvciBhbGwgb3VyIHNhbXBsZXMsIHdlIGNvdWxkIGVtcGxveSBhIGBmb3JgIGxvb3AgYXMgd2UgaGF2ZSBzZWVuIHByZXZpb3VzbHkuIAoKYGBge2Jhc2ggZXZhbD1GQUxTRX0KCmZvciBmaWxlbmFtZSBpbiAqLmZhc3RxLmd6CmRvCm5hbWU9JChiYXNlbmFtZSAkZmlsZW5hbWUgLmZhc3RxLmd6KQpzYWxtb24gcXVhbnQgLWkgaW5kZXgvR1JDbTM4X3NhbG1vbiAtLWxpYlR5cGUgQSAtciAkZmlsZW5hbWUuZmFzdHEuZ3ogLW8gcXVhbnQvJG5hbWUKZ3ppcCBxdWFudC8kbmFtZS9xdWFudC5zZgpkb25lCgpgYGAKCgojIyBXb3JrZmxvdyAyOiBBbGlnbiBhbmQgdGhlbiBjb3VudAoKVGhlcmUgYXJlIG51bWVyb3VzIHRvb2xzIHBlcmZvcm1pbmcgc2hvcnQgcmVhZCBhbGlnbm1lbnQgYW5kIHRoZSBjaG9pY2Ugb2YgYWxpZ25lciBzaG91bGQgYmUKY2FyZWZ1bGx5IG1hZGUgYWNjb3JkaW5nIHRvIHRoZSBhbmFseXNpcyBnb2Fscy9yZXF1aXJlbWVudHMuIEhlcmUgd2Ugd2lsbCB1c2UKSElTQVQyLCBhIGZhc3QgYWxpZ25lciB3aXRoIGxvdyBtZW1vcnkgcmVxdWlyZW1lbnRzIHRoYXQgcGVyZm9ybXMgc3BsaWNlZCBhbGlnbm1lbnRzLiBJdCBpcyB0aGUgcHJvZ3JhbQpzdWdnZXN0ZWQgZm9yIHRoZSBhbGlnbm1lbnQgaW4gdGhlIG5ldyBUdXhlZG8KcHJvdG9jb2wgKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L25wcm90LjIwMTYuMDk1KSBhbmQgaXQgcmVxdWlyZXMgYW4gaW5kZXhlZCBnZW5vbWUgdG8ga2VlcCBpdHMgbWVtb3J5IGZvb3RwcmludCBzbWFsbCBhbmQgdGhlIHJ1bm5pbmcKdGltZSBzaG9ydC4gCgpUaGUgcHJvZ3JhbSBjcmVhdGVzIGEgZ2Vub21lIGluZGV4IGJ5IHVzaW5nIHRoZSAqRkFTVEEqIGZpbGUgb2YgdGhlCnNlcXVlbmNlIHdlIHdhbnQgdG8gdXNlIGFzIHJlZmVyZW5jZS4gQSBzZXF1ZW5jZSBpbiAqRkFTVEEqIGZvcm1hdCBiZWdpbnMgd2l0aCBhIHNpbmdsZS1saW5lCmRlc2NyaXB0aW9uLCBmb2xsb3dlZCBieSBsaW5lcyBvZiBzZXF1ZW5jZSBkYXRhLiBUaGUgZGVzY3JpcHRpb24gbGluZSBpcyBkaXN0aW5ndWlzaGVkIGZyb20gdGhlCnNlcXVlbmNlIGRhdGEgYnkgYSBncmVhdGVyLXRoYW4gKGA+YCkgc3ltYm9sIGF0IHRoZSBiZWdpbm5pbmcuCgojIyMgQ3JlYXRpbmcgYSBnZW5vbWUgaW5kZXgKCldlIGhhdmUgYWxyZWFkeSBkb3dubG9hZGVkIHRoZSByZWZlcmVuY2UgZmlsZSB0aGF0IHdlIHdhbnQgdG8gdXNlLiBIb3dldmVyLCBpdCBpcyBpbiBhIGNvbXByZXNzZWQgZm9ybWF0IGFuZCBub3QgaW1tZWRpYXRlbHkgdXNlZnVsIGZvciBidWlsZGluZyB0aGUgZ2Vub21lIGluZGV4LiBXZSBuZWVkIHRvIHVuLWNvbXByZXNzIHRoZSBmaWxlIGZpcnN0IHVzaW5nIHRoZSBgZ3VuemlwYCBjb21tYW5kIChzaW1pbGFyIHRvIGB1bnppcGAgdGhhdCB3ZSB1c2VkIGVhcmxpZXIgaW4gdGhlIGNvdXJzZSkuIFRoaXMgc2hvdWxkIG5vdyByZXBsYWNlIHRoZSBmaWxlIGByZWZfZGF0YS9NdXNfbXVzY3VsdXMuR1JDbTM4LmRuYS5jaHJvbW9zb21lLjEuZmEuZ3pgIHdpdGggYHJlZl9kYXRhL011c19tdXNjdWx1cy5HUkNtMzguZG5hLmNocm9tb3NvbWUuMS5mYWAKCgoKVGhlIGNvbW1hbmQgYGhpc2F0Mi1idWlsZGAgaXMgdXNlZCB0byB0YWtlIHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UgYW5kIGNyZWF0ZSBhbiBlZmZpY2llbnQgZGF0YSBzdHJ1Y3R1cmUgZm9yIHNlYXJjaGluZy4gV2UgbmVlZCB0byBzcGVjaWZ5IGEgKnByZWZpeCogdGhhdCB3aWxsIGJlIHVzZWQgdG8gbmFtZSB0aGUgZmlsZXMgdGhhdCBgaGlzYXQyYCBnZW5lcmF0ZXMuIAoKVGhlIGluZGV4IHN0ZXAgb25seSBuZWVkcyB0byBiZSBydW4gb25jZSBmb3IgYSBwYXJ0aWN1bGFyIGdlbm9tZSB2ZXJzaW9ucy4gWW91IGNhbiB1c2UgdGhlIHNhbWUgaW5kZXggdG8gYWxpZ24gbXVsdGlwbGUgc2FtcGxlcy4KCioqTm90ZSB0aGF0IHRoZSBmb2xsb3dpbmcgaW5kZXggcHJvY2VkdXJlIG1heSB0YWtlIGEgZmV3IG1pbnV0ZXMgdG8gcnVuKioKCmBgYHtiYXNoIGV2YWw9RkFMU0V9Cmd1bnppcCByZWZfZGF0YS9NdXNfbXVzY3VsdXMuR1JDbTM4LmRuYS5jaHJvbW9zb21lLjEuZmEuZ3oKaGlzYXQyLWJ1aWxkIHJlZl9kYXRhL011c19tdXNjdWx1cy5HUkNtMzguZG5hLmNocm9tb3NvbWUuMS5mYSBpbmRleC9HUkNtMzhfY2hyMV9oaXNhdApgYGAKCgpUaGUgaW5kZXggZmlsZXMgb2YgTW91c2UgZ2Vub21lIEdSQ20zOCBjaHJvbW9zb21lIDEgIHNob3VsZCBub3cgYmUgaW4gdGhlIGRpcmVjdG9yeSBgaW5kZXgvYC4gQWxsIG9mIHRoZW0gaGF2ZSBhIGZpbGUgbmFtZSBzdGFydGluZyB3aXRoIHRoZSBgR1JDbTM4X2NocjFfaGlzYXRgIHByZWZpeC4KCiMjIyBBbGlnbm1lbnQgd2l0aCBoaXNhdDIKClRoZXJlIGFyZSBzZXZlcmFsIHBhcmFtZXRlcnMgd2UgbWlnaHQgd2FudCB0byBzcGVjaWZ5IGluIG9yZGVyIHRvIGFsaWduIG91ciByZWFkcyB3aXRoIEhJU0FUMi4gVG8gdmlldyB0aGVtIGFsbCB0eXBlLiAKCgpgYGB7YmFzaCBldmFsPUZBTFNFfQpoaXNhdDIgLS1oZWxwCmBgYAoKVG8gYWxpZ24gb25lIG9mIG91ciBleGFtcGxlIGZpbGVzIGFnYWluc3QgdGhlICppbmRleCogdGhhdCB3ZSBoYXZlIGp1c3QgY3JlYXRlZCB3ZSBuZWVkIHRvIAoKLSBjaGFuZ2UgdGhlIGAteGAgYXJndW1lbnQgdG8gYmUgdGhlIGZpbGUgcHJlZml4IG9mIGFsbCB0aGUgaW5kZXggZmlsZXMgY3JlYXRlZCBpbiB0aGUgcHJldmlvdXMgc3RlcC4gCi0gc3BlY2lmeSB0aGF0IG91ciByZWFkcyBhcmUgKnVuLXBhaXJlZCogYW5kIHRvIGJlIGZvdW5kIGluIHRoZSBmaWxlIGBTUlIxNTUyNDQ0LmZhc3RxLmd6YAotIHNwZWNpZnkgYW4gb3V0cHV0IGZpbGUgZm9yIHRoZSBhbGlnbm1lbnRzLiBXZSB3aWxsIGNyZWF0ZSBhIG5ldyBkaXJlY3RvcnkgdG8gc2F2ZSB0aGVzZSBhbGlnbm1lbnRzIHRvIGBhbGlnbmVkX3JlYWRzYAoKYGBge2Jhc2ggZXZhbD1GQUxTRX0KbWtkaXIgYWxpZ25lZF9yZWFkcwpoaXNhdDIgLXggaW5kZXgvR1JDbTM4X2NocjFfaGlzYXQgLVUgU1JSMTU1MjQ0NC5mYXN0cS5neiAtUyBhbGlnbmVkX3JlYWRzL1NSUjE1NTI0NDQuc2FtCmBgYAoKVGhlIG91dHB1dCBmaWxlIGNyZWF0ZWQgaXMgYW4gZXhhbXBsZSBvZiBhIFNlcXVlbmNlIEFsaWdubWVudC9NYXAgKFNBTSkgZmlsZS4gVGhpcyBpcyBhIGh1bWFuLXJlYWRhYmxlIGZpbGUgdGhhdCB0ZWxscyB1cyBob3cgd2VsbCBhbmQgd2hlcmUgZWFjaCBvZiBvdXIgc2VxdWVuY2luZyByZWFkcyBhbGlnbmVkLgoKYGBge2Jhc2ggZXZhbD1GQUxTRX0gCmhlYWQgYWxpZ25lZF9yZWFkcy9TUlIxNTUyNDQ0LnNhbQpgYGAKCkFmdGVyIGEgc2hvcnQgc2VjdGlvbiB0aGF0IGRlc2NyaWJlcyB0aGUgcmVmZXJlbmNlcyBzZXF1ZW5jZXMgdXNlZCBmb3IgYWxpZ25tZW50IChoZXJlIHdlIG9ubHkgaGF2ZSBhIHNpbmdsZSBjaHJvbW9zb21lOyBjaHJvbW9zb21lIDEpLiBUaGVyZSBpcyBhIHRhYi1kZWxpbWludGVkIHNlY3Rpb24gZGVzY3JpYmluZyB0aGUgYWxpZ25tZW50IG9mIGVhY2ggcmVhZC4KCgpDb2x1bW4gfCBPZmZpY2lhbCBOYW1lIHwgQnJpZWYKLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLQoxICAgICAgfCBRTkFNRSAgICAgICAgICB8IFNlcXVlbmNlIElECjIgICAgICB8IEZMQUcgICAgICAgICAgIHwgU2VxdWVuY2UgcXVhbGl0eSBleHByZXNzZWQgYXMgYSBiaXR3aXNlIGZsYWcKMyAgICAgIHwgUk5BTUUgICAgICAgICAgfCBDaHJvbW9zb21lCjQgICAgICB8IFBPUyAgICAgICAgICAgIHwgU3RhcnQgUG9zaXRpb24KNSAgICAgIHwgTUFQUSAgICAgICAgICAgfCBNYXBwaW5nIFF1YWxpdHkKNiAgICAgIHwgQ0lHQVIgICAgICAgICAgfCBEZXNjcmliZXMgcG9zaXRpb25zIG9mIG1hdGNoZXMsIGluc2VydGlvbnMsIGRlbGV0aW9ucyB3LnIudCByZWZlcmVuY2UKNyAgICAgIHwgUk5FWFQgICAgICAgICAgfCBSZWYuIG5hbWUgb2YgbWF0ZSAvIG5leHQgcmVhZAo4ICAgICAgfCBQTkVYVCAgICAgICAgICB8IFBvc3Rpb24gb2YgbWF0ZSAvIG5leHQgcmVhZAo5ICAgICAgfCBUTEVOICAgICAgICAgICB8IE9ic2VydmVkIFRlbXBsYXRlIGxlbmd0aAoxMCAgICAgfCBTRVEgICAgICAgICAgICB8IFNlcXVlbmNlCjExICAgICB8IFFVQUwgICAgICAgICAgIHwgQmFzZSBRdWFsaXRpZXMKClRoZXJlIGNhbiBhbHNvIGJlIGFsbCBtYW5uZXIgb2Ygb3B0aW9uYWwgdGFncyBhcyBleHRyYSBjb2x1bW5zIGludHJvZHVjZSBieSBhbiBhbGlnbmVyIG9yIGRvd25zdHJlYW0gYW5hbHlzaXMgdG9vbC4gQSBjb21tb24gdXNlIGlzIHRoZSBgUkdgIHRhZyB3aGljaCByZWZlcnMgYmFjayB0byB0aGUgcmVhZCBncm91cHMgaW4gdGhlIGhlYWRlci4KCgpUaGUgKiJmbGFncyIqIGluIHRoZSBzYW0gZmlsZSBjYW4gcmVwcmVzZW50IHVzZWZ1bCBRQyBpbmZvcm1hdGlvbgoKICArIFJlYWQgaXMgdW5tYXBwZWQKICArIFJlYWQgaXMgcGFpcmVkIC8gdW5wYWlyZWQKICArIFJlYWQgZmFpbGVkIFFDCiAgKyBSZWFkIGlzIGEgUENSIGR1cGxpY2F0ZSAoc2VlIGxhdGVyKQoKVGhlIGNvbWJpbmF0aW9uIG9mIGFueSBvZiB0aGVzZSBwcm9wZXJ0aWVzIGlzIHVzZWQgdG8gZGVyaXZlIGEgbnVtZXJpYyB2YWx1ZQoKCkZvciBpbnN0YW5jZSwgYSBwYXJ0aWN1bGFyIHJlYWQgaGFzIGEgZmxhZyBvZiAxNjMKCiFbXShpbWFnZXMvZmxhZy1oaWdobGlnaHQucG5nKQoKCiMjIyBEZXJpdmF0aW9uCgpUaGVyZSBpcyBhIHNldCBvZiBwcm9wZXJ0aWVzIHRoYXQgYSByZWFkIGNhbiBwb3NzZXNzLiBJZiBhIHBhcnRpY3VsYXIgcHJvcGVydHkgaXMgb2JzZXJ2ZWQsIGEgY29ycmVzcG9uZGluZyBwb3dlciBvZiAyIGlzIGFkZGVkIG11bHRpcGxpZWQgYnkgMS4gVGhlIGZpbmFsIHZhbHVlIGlzIGRlcml2ZWQgYnkgc3VtbWluZyBhbGwgdGhlIHBvd2VycyBvZiAyLgoKCmBgYAogCVJlYWRIYXNQcm9wZXJ0eSAJQmluYXJ5IAlNdWx0aXBseUJ5CmlzUGFpcmVkIAlUUlVFIAkxIAkxCmlzUHJvcGVyUGFpciAJVFJVRSAJMSAJMgppc1VubWFwcGVkUXVlcnkgCUZBTFNFIAkwIAk0Cmhhc1VubWFwcGVkTWF0ZSAJRkFMU0UgCTAgCTgKaXNNaW51c1N0cmFuZCAJRkFMU0UgCTAgCTE2CmlzTWF0ZU1pbnVzU3RyYW5kIAlUUlVFIAkxIAkzMgppc0ZpcnN0TWF0ZVJlYWQgCUZBTFNFIAkwIAk2NAppc1NlY29uZE1hdGVSZWFkIAlUUlVFIAkxIAkxMjgKaXNTZWNvbmRhcnlBbGlnbm1lbnQgCUZBTFNFIAkwIAkyNTYKaXNOb3RQYXNzaW5nUXVhbGl0eUNvbnRyb2xzIAlGQUxTRSAJMCAJNTEyCmlzRHVwbGljYXRlIAlGQUxTRSAJMCAJMTAyNAoKYGBgClZhbHVlIG9mIGZsYWcgaXMgZ2l2ZW4gYnkgCmBgYAoxeDEgKyAxeDIgKyAweDQgKyAweDggKyAweDE2ICsgMXgzMiArIDB4NjQgKyAxeDEyOCArIDB4MjU2ICsgMHg1MTIgKyAweDEwMjQgPSAxNjMKYGBgCgpTZWUgYWxzbwoKLSBodHRwczovL2Jyb2FkaW5zdGl0dXRlLmdpdGh1Yi5pby9waWNhcmQvZXhwbGFpbi1mbGFncy5odG1sCgoKPiAjIyBDaGFsbGVuZ2UgMiB7LmNoYWxsZW5nZX0KPiAKPiBVc2UgSElTQVQyIHRvIGFsaWduIG9uZSBvZiB0aGUgb3RoZXIgZmFzdHEgZmlsZXMgdG8gY2hyb21vc29tZSAxIG9mIE1vdXNlIEdSQ20zOCAoc2F5IFNSUjE1NTI0NDUuZmFzdHEpIAo+IFdoYXQgcGVyY2VudGFnZSBvZiByZWFkcyBhbGlnbj8KCiMjIyBDb252ZXJ0aW5nIHRvIGEgYmFtIGZpbGUKCmBzYW1gIGZpbGVzIGFyZSBlYXN5IHRvIHJlYWQsIGJ1dCByYXJlbHkgdXNlZCBpbiBhbmFseXNpcyBhcyB0aGV5IGNhbiByZXF1aXJlIGxhcmdlIGFtb3VudHMgb2YgZGlzayBzcGFjZS4gVGhlIGFsaWdubWVudHMgZnJvbSBhIHNlcXVlbmNpbmcgcnVuIGFyZSBtb3JlLWNvbW1vbmx5IHN0b3JlZCBpbiBjb21wcmVzc2VkLCBiaW5hcnkgZmlsZSBrbm93IGFzIGEgYGJhbWAgZmlsZS4gRXhhY3RseSB0aGUgc2FtZSBpbmZvcm1hdGlvbiBpcyBjb250YWluZWQsIGV4Y2VwdCB0aGV5IGFyZSBtb3JlIHBvcnRhYmxlLgoKYHNhbXRvb2xzYCBpcyB1c2VkIGZvciB0aGUgY29udmVyc2lvbiBhbmQgbWFuaXB1bGF0aW9uIG9mIHNhbSBhbmQgYmFtIGZpbGVzLiAKCi0gaHR0cDovL3d3dy5odHNsaWIub3JnLwoKVGhlIHN0ZXBzIGluIHByb2R1Y2luZyBhIGJhbSBmaWxlIGZvciBhbmFseXNpcyBhcmUgZ2l2ZW4gYmVsb3cuIFRoZSBmaW5hbCBzdGVwIGlzIGltcG9ydGFudCBhcyBpdCBjcmVhdGVzIGFuICppbmRleCBmaWxlKi4gVGhpcyBpbmRleCBuZWVkcyB0byBiZSBwcmVzZW50IGluIG9yZGVyIGZvciBhbmFseXNpcyB0b29scyB0byBhY2Nlc3MgdGhlIHJlYWRzIGluIHRoZSBmaWxlIGluIGFuIGVmZmljaWVudCBtYW5uZXIuCgoKYGBge2Jhc2ggZXZhbD1GQUxTRX0Kc2FtdG9vbHMgdmlldyAtYlMgYWxpZ25lZF9yZWFkcy9TUlIxNTUyNDQ0LnNhbSA+IGFsaWduZWRfcmVhZHMvU1JSMTU1MjQ0NC5iYW0Kc2FtdG9vbHMgc29ydCBhbGlnbmVkX3JlYWRzL1NSUjE1NTI0NDQuYmFtIC1vIGFsaWduZWRfcmVhZHMvU1JSMTU1MjQ0NC5zb3J0ZWQuYmFtCnNhbXRvb2xzIGluZGV4IGFsaWduZWRfcmVhZHMvU1JSMTU1MjQ0NC5zb3J0ZWQuYmFtCmBgYAoKVGhlIGBzYW10b29sc2Agc3VpdGUgYWxzbyBpbmNsdWRlcyBhIGNvdXBsZSBvZiB0b29scyB0aGF0IGFyZSB1c2VmdWwgZm9yIFFDIHB1cnBvc2VzLgoKYGBge2Jhc2ggZXZhbD1GQUxTRX0Kc2FtdG9vbHMgZmxhZ3N0YXQgYWxpZ25lZF9yZWFkcy9TUlIxNTUyNDQ0LnNvcnRlZC5iYW0Kc2FtdG9vbHMgaWR4c3RhdHMgYWxpZ25lZF9yZWFkcy9TUlIxNTUyNDQ0LnNvcnRlZC5iYW0KYGBgCgoKIyMjIENvdW50aW5nIGZlYXR1cmVzCgpXZSB3aWxsIHVzZSB0aGUgYGZlYXR1cmVDb3VudHNgIHRvb2wgdG8gb2J0YWluIHRyYW5zY3JpcHQtbGV2ZWwgY291bnRzIGZvciB0aGUgYWxpZ25lZCByZWFkcyB0aGF0IHdlIGhhdmUganVzdCBjcmVhdGVkLiBUaGlzIHJlcXVpcmVzIHRoYXQgYGd0ZmAgZmlsZXMgdGhhdCB3ZSBkb3dubG9hZGVkIGZyb20gRW5zZW1ibCBlYXJsaWVyIGFuZCBvdXIgbmV3bHktY3JlYXRlZCBiYW0gZmlsZS4KCgpgYGB7YmFzaCBldmFsPUZBTFNFfQpta2RpciBmZWF0dXJlQ291bnRzCmZlYXR1cmVDb3VudHMgLWEgcmVmX2RhdGEvTXVzX211c2N1bHVzLkdSQ20zOC45MS5jaHIuZ3RmLmd6IC1vIGZlYXR1cmVDb3VudHMvU1JSMTU1MjQ0NC5jb3VudHMgYWxpZ25lZF9yZWFkcy9TUlIxNTUyNDQ0LnNvcnRlZC5iYW0KCmBgYAoKVGhlIGNvbW1hbmQgaXMgYWJsZSB0byBhY2NlcHQgbXVsdGlwbGUgYmFtIGZpbGVzCgojIyMgUnVubmluZyBhbGlnbm1lbnQgZm9yIGFsbCBgZmFzdHFgIGZpbGVzCgpJZiB3ZSB3YW50ZWQgdG8gcHJvY2VzcyBhbGwgdGhlIGZhc3RxIGZpbGVzIGluIG91ciBkYXRhc2V0LCB0aGUgZm9sbG93aW5nIHdvcmtmbG93IGNvdWxkIGJlIGVtcGxveWVkLiBUaGlzIGNyZWF0ZXMgYSB2YXJpYWJsZSBgaWAgdGhhdCB0YWtlcyBhbGwgdmFsdWVzIGZyb20gNDQgdG8gNTUgYW5kIHByb2Nlc3NlcyB0aGUgYXBwcm9wcmlhdGUgZmFzdHEgZmlsZSBpbiB0dXJuLiBUaGUgYGVjaG9gIGNvbW1hbmQgaXMgdXNlZCBoZXJlIHRvIHByaW50IHRoZSBwYXJ0aWN1bGFyIHNhbXBsZSB0aGF0IGlzIGJlaW5nIHByb2Nlc3NlZC4KCmBgYHtiYXNoIGV2YWw9RkFMU0V9CmZvciBmaWxlbmFtZSBpbiAqLmZhc3RxLmd6CmRvCm5hbWU9JChiYXNlbmFtZSAkZmlsZW5hbWUgLmZhc3RxLmd6KQplY2hvIFByb2Nlc3Npbmcgc2FtcGxlICRuYW1lCmhpc2F0MiAteCBpbmRleC9HUkNtMzhfY2hyMV9oaXNhdCAtVSAkZmlsZW5hbWUgLVMgYWxpZ25lZF9yZWFkcy8kbmFtZS5zYW0gCnNhbXRvb2xzIHZpZXcgLWJTIGFsaWduZWRfcmVhZHMvJG5hbWUuc2FtID4gYWxpZ25lZF9yZWFkcy8kbmFtZS5iYW0Kc2FtdG9vbHMgc29ydCBhbGlnbmVkX3JlYWRzLyRuYW1lLmJhbSAtbyBhbGlnbmVkX3JlYWRzLyRuYW1lLnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggYWxpZ25lZF9yZWFkcy8kbmFtZS5zb3J0ZWQuYmFtCmRvbmUKYGBgCg==