Objectives

This notebook will demonstrate how to:

  • Navigate the terminal interface
  • Organize an analysis project
  • Apply FastQC for quality control analysis of Illumina sequencing data
  • Preprocess sequencing reads with fastp
  • Quantify RNA-seq expression with Salmon

We will first learn how to process RNA-seq data at the command line using samples that were assayed with paired-end sequencing.

These samples come from a project (PRJNA178120) that includes 8 samples from normal gastric tissue, gastric cancer cell lines and primary gastric tumor cell cultures.

Here we will perform quality control checks, trimming, and estimate the transcript abundances for a single sample, SRR585570.

Later, we will use the full dataset (n = 8) to explore how to summarize estimates to the gene level and do some exploratory data analyses with data the course directors have processed ahead of time.


We’ll first want to set our working directory to the top-level of the RNA-seq folder.

Copy and paste the text in the code blocks below into your Terminal window in RStudio. It should be in the lower left hand corner as a tab next to Console.

Set current directory to the top-level of the RNA-seq module:

cd ~/training-modules/RNA-seq

Here ~/ refers to your home directory on the RStudio Server, which is the base folder in which your files live, including most of the materials for training. This is also the default working directory when you open a new RStudio session. A home directory is specific to you as a user on the RStudio Server; each user has their own folder to store their files.

Because these steps are computationally time intensive, we’ve prepared a script to start running things. Once we start running the script, we will give a short lecture to introduce this module and then walk through and explain each of the individual steps that the script is executing.

Enter the following in the Terminal to start running the script:

bash scripts/run_SRR585570.sh

Note: Don’t worry if the Salmon step does not complete by the time we move on to the next notebook. This is a time and resource intensive step, so we have prepared the required output in case we need it.

Input files

The raw data FASTQ files (fastq.gz) for this sample, SRR585570, are in data/gastric-cancer/fastq/SRR585570. The first two directories, data/ and gastric-cancer/, tell us that these files are data and which experiment or dataset these data are from. We’ll be working with an additional dataset later in the module, so this latter distinction will become more important. The third directory, fastq/, tells us that this is where we will be storing fastq files.

The final directory, SRR585570, is specific to the sample we are working with. The use of the SRR585570 folder might seem unnecessary because we are only processing a single sample here, but keeping files for individual samples in their own folder helps keep things organized for multi-sample workflows. (You can peek ahead and look at the data/NB-cell/quant folder for such an example.)

There is no “one size fits all” approach for project organization. It’s most important that it’s consistent, easy for you and others to find the files you need quickly, and minimizes the likelihood for errors (e.g., writing over files accidentally).

Quality control with FastQC

The first thing our script does is use FastQC for quality control in command line mode. Here’s a link to the FastQC documentation: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/

Let’s take a look at some example reports from the authors of FastQC:

FastQC runs a series of quality checks on sequencing data and provides an HTML report. As the authors point out in the docs:

It is important to stress that although the analysis results appear to give a pass/fail result, these evaluations must be taken in the context of what you expect from your library.

The documentation for individual modules/analyses in FastQC is a great resource!

To save time, our script only runs one FASTQ file for SRR585570 with the following commands:

mkdir -p QC/gastric-cancer/fastqc/SRR585570

mkdir allows us to create a new folder in the QC/gastric-cancer/fastqc directory specifically to hold the report information that will be generated by FastQC for this sample. The -p allows us to create parent directories and will prevent an error if the directory we specify already exists.

# In the interest of time, we'll run one of the fastq files through FastQC
fastqc data/gastric-cancer/fastq/SRR585570/SRR585570_1.fastq.gz \
    -o QC/gastric-cancer/fastqc/SRR585570

-o

The -o flag allows us to specify where the output of FastQC is saved. Note that this is saved in a separate place than the raw data files and in a directory specifically for quality control information.

For comparison to the report for SRR585570_1.fastq.gz we generate with our script, we’ve prepared a FastQC report for one of the sets of reads for another sample in the experiment. It can be found at QC/gastric-cancer/fastqc/SRR585574/SRR585574_1_fastqc.html.

Let’s look at the reports for both samples.

Preprocessing with fastp

We use fastp to preprocess the FASTQ files (Chen et al. Bioinformatics. 2018.). Note that fastp has quality control functionality and many different options for preprocessing (see all options on GitHub), most of which we will not cover. Here, we focus on adapter trimming, quality filtering, and length filtering.

Below, we discuss the commands we used in the script.

Create output directories

# Create a directory to hold the trimmed fastq files
mkdir -p data/gastric-cancer/fastq-trimmed/SRR585570
# Create a directory to hold the QC output from fastp
mkdir -p QC/gastric_cancer/fastp/SRR585570

As we’ll cover below, fastp essentially has two kinds of output: trimmed and filtered FASTQ files (data) and reports (quality control).

fastp

# Run the adapter and quality trimming step -- also produces QC report
fastp -i data/gastric-cancer/fastq/SRR585570/SRR585570_1.fastq.gz \
    -I data/gastric-cancer/fastq/SRR585570/SRR585570_2.fastq.gz \
    -o data/gastric-cancer/fastq-trimmed/SRR585570/SRR585570_fastp_1.fastq.gz \
    -O data/gastric-cancer/fastq-trimmed/SRR585570/SRR585570_fastp_2.fastq.gz \
    --qualified_quality_phred 15 \
    --length_required 20 \
    --report_title "SRR585570" \
    --json QC/gastric-cancer/fastp/SRR585570/SRR585570_fastp.json \
    --html QC/gastric-cancer/fastp/SRR585570/SRR585570_fastp.html

Below, we’ll walk through the arguments/options we used to run fastp. By default, fastp performs adapter trimming, which you can read more about here. For paired-end data like the data we have for SRR585570, adapters can be detected automatically without specifying an adapter sequence.

Input: -i and -I

These arguments specify the read1 input and read2 (sometimes called left and right) input, respectively.

fastq output: -o and -O

These arguments specify the read1 output and read2 output, respectively. Note that the output is being placed in data/gastric-cancer/fastq-trimmed/SRR585570/, so the processed FASTQ files will be kept separate from from the original files. It is generally good practice to treat your “raw” data and its directories as fixed and separate from any processing and analysis that you do, to prevent accidentally modification of those original files. And in the event that you accidentally do modify the originals, you know exactly which files and directories to reset.

--qualified_quality_phred

Phred scores are the quality information included in a FASTQ file and the values indicate the chances that a base is called incorrectly. Let’s look at a screenshot of the Per Base Sequence Quality module from FastQC bad Illumina example we linked to above.

per-base-quality-screenshot
per-base-quality-screenshot

Anything below 20, where a Phred score of 20 represents a 1 in 100 chance that the call is incorrect, is considered poor quality by FastQC. Using --qualified_quality_phred 15 (which is the default), means scores >= 15 are considered “qualified.” Using the default parameters as we do here, reads will be filtered out if >40% of the bases are unqualified. You can read more about the quality filtering functionality of fastp here. The Salmon documentation notes that, given the way we run salmon quant, quantification may be more sensitive to calls that are likely to be erroneous (of low quality) and, therefore, quality trimming may be important.

Trimming, in contrast to filtering, refers to removing low quality base calls from the (typically 3’) end of reads. A recent paper from the Salmon authors (Srivastava et al. 2020) notes that trimming did not affect mapping rates from random publicly available human bulk (paired-end) RNA-seq samples (they used TrimGalore). fastp does have the functionality to perform trimming using a sliding window, which must be enabled. We are not using it here.

Note that there are two kinds of encoding for Phred scores: Phred 33 encoding and Phred 64 encoding. FastQC guessed that the file for SRR585570 uses Sanger/Illumina 1.9 encoding (Phred 33). If we had Phred 64 data, we’d use the --phred64 flag. You can read a little bit more about the encoding here.

--length_required

Trimming reads may result in short reads, which may affect gene expression estimates (Williams et al. BMC Bioinformatics. 2016.). Using --length_required 20 means that reads shorter than 20bp will be discarded (similar to what was used in Srivastava et al. above).

--report_title

When we look at the HTML report, it’s helpful to quickly identify what sample the report is for. Using --report title "SRR585570" means that the report will be titled “SRR585570” rather than the default (“fastp report”).

--json and --html

With these options, we’re specifying where the JSON and HTML reports will be saved (in the QC/gastric-cancer/fastp/ directory we created) and what the filenames will be. Including the sample name in the filenames again may help us with project organization.

If we look at QC/gastric-cancer/fastp/SRR585570_fastp.json or the top of the HTML report, we can see that fastp reports certain metrics before and after filtering, which can be very useful in making analysis decisions.

Quantification with Salmon

We’ll use Salmon for quantifying transcript expression (documentation). Salmon (Patro, et al. Nature Methods. 2017.) is fast and requires very little memory, which makes it a great choice for running on your laptop during training. We can use the output for downstream analyses like differential expression analysis and clustering. We use Salmon in mapping mode, with mapping validation enabled, using the following command:

# We perform quantification on the files that have been trimmed
# and use the index generated with -k 23, as this may "improve sensitivity"
# per the Salmon documentation
salmon quant -i index/Homo_sapiens/short_index \
    -l A \
    -1 data/gastric-cancer/fastq-trimmed/SRR585570/SRR585570_fastp_1.fastq.gz \
    -2 data/gastric-cancer/fastq-trimmed/SRR585570/SRR585570_fastp_2.fastq.gz \
    -o data/gastric-cancer/salmon_quant/SRR585570 \
    --validateMappings --rangeFactorizationBins 4 \
    --gcBias --seqBias \
    --threads 4

Below, we’ll walk through the arguments/options we used to run salmon quant.

Transcriptome index: -i

Salmon requires a set of transcripts (what we want to quantify) in the form of a transcriptome index built with salmon index. Building an index can take a while (but you only have to do it once!), so we’ve built the one we use today ahead of time. Before we use it, we’ll take a moment to give a bit of background.

You can see how we obtained this index and others on GitHub. Note that we used Homo sapiens GRCh38, Ensembl release 95. It is important to keep track of what build, resource, and files were used and putting our shell scripts on GitHub allows us to do that.

The salmon index command has a parameter -k which sets the k-mer length. The index we used was built with -k 23 and can be found here:

index/Homo_sapiens/short_index

Using a smaller value for k than the default (k = 31) is appropriate for shorter reads and may improve sensitivity when using --validateMappings according to the Salmon documentation.

-l

We use -l A to allow Salmon to automatically infer the library type based on a subset of reads, but you can also provide the library type to Salmon with this argument.

Input: -1 and -2

These data are paired-end, we use -1 and -2 to specify read1 and read2, respectively.

-o

Output directory, salmon quant should create this for us if it doesn’t exist yet.

--validateMappings and --rangeFactorizationBins

Using --validateMappings enables mapping validation, where Salmon checks its mappings using traditional alignment. This helps prevent “spurious mappings” where a read maps to a target but does not arise from it (see documentation for flag and the release notes for v0.10.0 where this was introduced).

When enabling mapping validation with --validateMappings, setting --rangeFactorizationBins 4 can improve quantification for certain classes of transcripts (docs).

--gcBias

With this option enabled, Salmon will attempt to correct for fragment GC-bias. Regions with high or low GC content tend to be underrepresented in sequencing data.

It should be noted that this is only appropriate for use with paired-end reads, as fragment length can not be inferred from single-end reads (see this GitHub issue).

--seqBias

With this option enabled, Salmon will attempt to correct for the bias that occurs when using random hexamer priming (preferential sequencing of reads when certain motifs appear at the beginning).

--threads

The --threads argument controls the number of threads that are available to Salmon during quantification. This in essence controls how much of the mapping can occur in parallel. If you had access to a computer with many cores, you could increase the number of threads to make quantification go faster.

Navigate to data/gastric-cancer/salmon_quant/SRR585571/aux_info and open meta_info.json. Look for a field called percent_mapped – what value does this sample have?

LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIGJ1bGsgUk5BLXNlcSBkYXRhIHByb2Nlc3NpbmciCmF1dGhvcjogQ0NETCBmb3IgQUxTRgpkYXRlOiAyMDIxCm91dHB1dDogICAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyMgT2JqZWN0aXZlcwoKVGhpcyBub3RlYm9vayB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0bzoKCi0gTmF2aWdhdGUgdGhlIHRlcm1pbmFsIGludGVyZmFjZSAgCi0gT3JnYW5pemUgYW4gYW5hbHlzaXMgcHJvamVjdCAKLSBBcHBseSBbRmFzdFFDXShodHRwczovL3d3dy5iaW9pbmZvcm1hdGljcy5iYWJyYWhhbS5hYy51ay9wcm9qZWN0cy9mYXN0cWMvKSBmb3IgcXVhbGl0eSBjb250cm9sIGFuYWx5c2lzIG9mIElsbHVtaW5hIHNlcXVlbmNpbmcgZGF0YQotIFByZXByb2Nlc3Mgc2VxdWVuY2luZyByZWFkcyB3aXRoIFtmYXN0cF0oaHR0cHM6Ly9naXRodWIuY29tL09wZW5HZW5lL2Zhc3RwKQotIFF1YW50aWZ5IFJOQS1zZXEgZXhwcmVzc2lvbiB3aXRoIFtTYWxtb25dKGh0dHBzOi8vY29tYmluZS1sYWIuZ2l0aHViLmlvL3NhbG1vbi8pCgotLS0KCldlIHdpbGwgZmlyc3QgbGVhcm4gaG93IHRvIHByb2Nlc3MgUk5BLXNlcSBkYXRhIGF0IHRoZSBjb21tYW5kIGxpbmUgdXNpbmcgc2FtcGxlcyB0aGF0IHdlcmUgYXNzYXllZCB3aXRoIHBhaXJlZC1lbmQgc2VxdWVuY2luZy4KClRoZXNlIHNhbXBsZXMgY29tZSBmcm9tIGEgcHJvamVjdCAoW2BQUkpOQTE3ODEyMGBdKGh0dHBzOi8vd3d3LmViaS5hYy51ay9lbmEvZGF0YS92aWV3L1BSSk5BMTc4MTIwKSkgdGhhdCBpbmNsdWRlcyA4IHNhbXBsZXMgZnJvbSBub3JtYWwgZ2FzdHJpYyB0aXNzdWUsIGdhc3RyaWMgY2FuY2VyIGNlbGwgbGluZXMgYW5kIHByaW1hcnkgZ2FzdHJpYyB0dW1vciBjZWxsIGN1bHR1cmVzLgoKSGVyZSB3ZSB3aWxsIHBlcmZvcm0gcXVhbGl0eSBjb250cm9sIGNoZWNrcywgdHJpbW1pbmcsIGFuZCBlc3RpbWF0ZSB0aGUgdHJhbnNjcmlwdCBhYnVuZGFuY2VzIGZvciBhIHNpbmdsZSBzYW1wbGUsIFNSUjU4NTU3MC4KCiFbXShkaWFncmFtcy9ybmEtc2VxXzEucG5nKQoKTGF0ZXIsIHdlIHdpbGwgdXNlIHRoZSBmdWxsIGRhdGFzZXQgKG4gPSA4KSB0byBleHBsb3JlIGhvdyB0byBzdW1tYXJpemUgZXN0aW1hdGVzIHRvIHRoZSBnZW5lIGxldmVsIGFuZCBkbyBzb21lIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzZXMgd2l0aCBkYXRhIHRoZSBjb3Vyc2UgZGlyZWN0b3JzIGhhdmUgcHJvY2Vzc2VkIGFoZWFkIG9mIHRpbWUuCgotLS0KCldlJ2xsIGZpcnN0IHdhbnQgdG8gc2V0IG91ciB3b3JraW5nIGRpcmVjdG9yeSB0byB0aGUgdG9wLWxldmVsIG9mIHRoZSBSTkEtc2VxIGZvbGRlci4KCkNvcHkgYW5kIHBhc3RlIHRoZSB0ZXh0IGluIHRoZSBjb2RlIGJsb2NrcyBiZWxvdyBpbnRvIHlvdXIgYFRlcm1pbmFsYCB3aW5kb3cgaW4gUlN0dWRpby4KSXQgc2hvdWxkIGJlIGluIHRoZSBsb3dlciBsZWZ0IGhhbmQgY29ybmVyIGFzIGEgdGFiIG5leHQgdG8gYENvbnNvbGVgLgoKKipTZXQgY3VycmVudCBkaXJlY3RvcnkgdG8gdGhlIHRvcC1sZXZlbCBvZiB0aGUgUk5BLXNlcSBtb2R1bGU6KioKCmBgYGJhc2gKY2Qgfi90cmFpbmluZy1tb2R1bGVzL1JOQS1zZXEKYGBgCgpIZXJlIGB+L2AgcmVmZXJzIHRvIHlvdXIgX2hvbWUgZGlyZWN0b3J5XyBvbiB0aGUgUlN0dWRpbyBTZXJ2ZXIsIHdoaWNoIGlzIHRoZSBiYXNlIGZvbGRlciBpbiB3aGljaCB5b3VyIGZpbGVzIGxpdmUsIGluY2x1ZGluZyBtb3N0IG9mIHRoZSBtYXRlcmlhbHMgZm9yIHRyYWluaW5nLgpUaGlzIGlzIGFsc28gdGhlIGRlZmF1bHQgd29ya2luZyBkaXJlY3Rvcnkgd2hlbiB5b3Ugb3BlbiBhIG5ldyBSU3R1ZGlvIHNlc3Npb24uCkEgaG9tZSBkaXJlY3RvcnkgaXMgc3BlY2lmaWMgdG8geW91IGFzIGEgdXNlciBvbiB0aGUgUlN0dWRpbyBTZXJ2ZXI7IGVhY2ggdXNlciBoYXMgdGhlaXIgb3duIGZvbGRlciB0byBzdG9yZSB0aGVpciBmaWxlcy4KCioqQmVjYXVzZSB0aGVzZSBzdGVwcyBhcmUgY29tcHV0YXRpb25hbGx5IHRpbWUgaW50ZW5zaXZlLCB3ZSd2ZSBwcmVwYXJlZCBhIHNjcmlwdCB0byBzdGFydCBydW5uaW5nIHRoaW5ncy4qKgpPbmNlIHdlIHN0YXJ0IHJ1bm5pbmcgdGhlIHNjcmlwdCwgd2Ugd2lsbCBnaXZlIGEgc2hvcnQgbGVjdHVyZSB0byBpbnRyb2R1Y2UgdGhpcyBtb2R1bGUgYW5kIHRoZW4gd2FsayB0aHJvdWdoIGFuZCBleHBsYWluIGVhY2ggb2YgdGhlIGluZGl2aWR1YWwgc3RlcHMgdGhhdCB0aGUgc2NyaXB0IGlzIGV4ZWN1dGluZy4KCioqRW50ZXIgdGhlIGZvbGxvd2luZyBpbiB0aGUgVGVybWluYWwgdG8gc3RhcnQgcnVubmluZyB0aGUgc2NyaXB0OioqCgpgYGBiYXNoCmJhc2ggc2NyaXB0cy9ydW5fU1JSNTg1NTcwLnNoCmBgYAoKX05vdGU6IERvbid0IHdvcnJ5IGlmIHRoZSBTYWxtb24gc3RlcCBkb2VzIG5vdCBjb21wbGV0ZSBieSB0aGUgdGltZSB3ZSBtb3ZlIG9uIHRvIHRoZSBuZXh0IG5vdGVib29rLgpUaGlzIGlzIGEgdGltZSBhbmQgcmVzb3VyY2UgaW50ZW5zaXZlIHN0ZXAsIHNvIHdlIGhhdmUgcHJlcGFyZWQgdGhlIHJlcXVpcmVkIG91dHB1dCBpbiBjYXNlIHdlIG5lZWQgaXQuXwoKIyMjIElucHV0IGZpbGVzCgpUaGUgcmF3IGRhdGEgRkFTVFEgZmlsZXMgKGBmYXN0cS5nemApIGZvciB0aGlzIHNhbXBsZSwgU1JSNTg1NTcwLCBhcmUgaW4gYGRhdGEvZ2FzdHJpYy1jYW5jZXIvZmFzdHEvU1JSNTg1NTcwYC4KVGhlIGZpcnN0IHR3byBkaXJlY3RvcmllcywgYGRhdGEvYCBhbmQgYGdhc3RyaWMtY2FuY2VyL2AsIHRlbGwgdXMgdGhhdCB0aGVzZSBmaWxlcyBhcmUgZGF0YSBhbmQgd2hpY2ggZXhwZXJpbWVudCBvciBkYXRhc2V0IHRoZXNlIGRhdGEgYXJlIGZyb20uCldlJ2xsIGJlIHdvcmtpbmcgd2l0aCBhbiBhZGRpdGlvbmFsIGRhdGFzZXQgbGF0ZXIgaW4gdGhlIG1vZHVsZSwgc28gdGhpcyBsYXR0ZXIgZGlzdGluY3Rpb24gd2lsbCBiZWNvbWUgbW9yZSBpbXBvcnRhbnQuClRoZSB0aGlyZCBkaXJlY3RvcnksIGBmYXN0cS9gLCB0ZWxscyB1cyB0aGF0IHRoaXMgaXMgd2hlcmUgd2Ugd2lsbCBiZSBzdG9yaW5nIGZhc3RxIGZpbGVzLgoKVGhlIGZpbmFsIGRpcmVjdG9yeSwgYFNSUjU4NTU3MGAsIGlzIHNwZWNpZmljIHRvIHRoZSBzYW1wbGUgd2UgYXJlIHdvcmtpbmcgd2l0aC4KVGhlIHVzZSBvZiB0aGUgYFNSUjU4NTU3MGAgZm9sZGVyIG1pZ2h0IHNlZW0gdW5uZWNlc3NhcnkgYmVjYXVzZSB3ZSBhcmUgb25seSBwcm9jZXNzaW5nIGEgc2luZ2xlIHNhbXBsZSBoZXJlLCBidXQga2VlcGluZyBmaWxlcyBmb3IgaW5kaXZpZHVhbCBzYW1wbGVzIGluIHRoZWlyIG93biBmb2xkZXIgaGVscHMga2VlcCB0aGluZ3Mgb3JnYW5pemVkIGZvciBtdWx0aS1zYW1wbGUgd29ya2Zsb3dzLgooWW91IGNhbiBwZWVrIGFoZWFkIGFuZCBsb29rIGF0IHRoZSBgZGF0YS9OQi1jZWxsL3F1YW50YCBmb2xkZXIgZm9yIHN1Y2ggYW4gZXhhbXBsZS4pCgpUaGVyZSBpcyBubyAib25lIHNpemUgZml0cyBhbGwiIGFwcHJvYWNoIGZvciBwcm9qZWN0IG9yZ2FuaXphdGlvbi4KSXQncyBtb3N0IGltcG9ydGFudCB0aGF0IGl0J3MgY29uc2lzdGVudCwgZWFzeSBmb3IgeW91IGFuZCBvdGhlcnMgdG8gZmluZCB0aGUgZmlsZXMgeW91IG5lZWQgcXVpY2tseSwgYW5kIG1pbmltaXplcyB0aGUgbGlrZWxpaG9vZCBmb3IgZXJyb3JzIChlLmcuLCB3cml0aW5nIG92ZXIgZmlsZXMgYWNjaWRlbnRhbGx5KS4KCiMjIFF1YWxpdHkgY29udHJvbCB3aXRoIEZhc3RRQwoKIVtdKGRpYWdyYW1zL3JuYS1zZXFfMi5wbmcpCgpUaGUgZmlyc3QgdGhpbmcgb3VyIHNjcmlwdCBkb2VzIGlzIHVzZSBbRmFzdFFDXShodHRwczovL3d3dy5iaW9pbmZvcm1hdGljcy5iYWJyYWhhbS5hYy51ay9wcm9qZWN0cy9mYXN0cWMvKSBmb3IgcXVhbGl0eSBjb250cm9sIGluIGNvbW1hbmQgbGluZSBtb2RlLgpIZXJlJ3MgYSBsaW5rIHRvIHRoZSBGYXN0UUMgZG9jdW1lbnRhdGlvbjogaHR0cHM6Ly93d3cuYmlvaW5mb3JtYXRpY3MuYmFicmFoYW0uYWMudWsvcHJvamVjdHMvZmFzdHFjL0hlbHAvCgoqKkxldCdzIHRha2UgYSBsb29rIGF0IHNvbWUgZXhhbXBsZSByZXBvcnRzIGZyb20gdGhlIGF1dGhvcnMgb2YgRmFzdFFDOioqCgoqIFtHb29kIElsbHVtaW5hIGRhdGEgZXhhbXBsZSByZXBvcnRdKGh0dHBzOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3Byb2plY3RzL2Zhc3RxYy9nb29kX3NlcXVlbmNlX3Nob3J0X2Zhc3RxYy5odG1sKSBmcm9tIEZhc3RRQwoqIFtCYWQgSWxsdW1pbmEgZGF0YSBleGFtcGxlIHJlcG9ydF0oaHR0cHM6Ly93d3cuYmlvaW5mb3JtYXRpY3MuYmFicmFoYW0uYWMudWsvcHJvamVjdHMvZmFzdHFjL2JhZF9zZXF1ZW5jZV9mYXN0cWMuaHRtbCkgZnJvbSBGYXN0UUMKCkZhc3RRQyBydW5zIGEgc2VyaWVzIG9mIHF1YWxpdHkgY2hlY2tzIG9uIHNlcXVlbmNpbmcgZGF0YSBhbmQgcHJvdmlkZXMgYW4gSFRNTCByZXBvcnQuIEFzIHRoZSBhdXRob3JzIHBvaW50IG91dCBpbiB0aGUgW2RvY3NdKGh0dHBzOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3Byb2plY3RzL2Zhc3RxYy9IZWxwLzIlMjBCYXNpYyUyME9wZXJhdGlvbnMvMi4yJTIwRXZhbHVhdGluZyUyMFJlc3VsdHMuaHRtbCk6Cgo+IEl0IGlzIGltcG9ydGFudCB0byBzdHJlc3MgdGhhdCBhbHRob3VnaCB0aGUgYW5hbHlzaXMgcmVzdWx0cyBhcHBlYXIgdG8gZ2l2ZSBhIHBhc3MvZmFpbCByZXN1bHQsIHRoZXNlIGV2YWx1YXRpb25zIG11c3QgYmUgdGFrZW4gaW4gdGhlIGNvbnRleHQgb2Ygd2hhdCB5b3UgZXhwZWN0IGZyb20geW91ciBsaWJyYXJ5LgoKVGhlIFtkb2N1bWVudGF0aW9uIGZvciBpbmRpdmlkdWFsIG1vZHVsZXMvYW5hbHlzZXNdKGh0dHBzOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3Byb2plY3RzL2Zhc3RxYy9IZWxwLzMlMjBBbmFseXNpcyUyME1vZHVsZXMvKSBpbiBGYXN0UUMgaXMgYSBncmVhdCByZXNvdXJjZSEKClRvIHNhdmUgdGltZSwgb3VyIHNjcmlwdCBvbmx5IHJ1bnMgb25lIEZBU1RRIGZpbGUgZm9yIFNSUjU4NTU3MCB3aXRoIHRoZSBmb2xsb3dpbmcgY29tbWFuZHM6CgpgYGBiYXNoCm1rZGlyIC1wIFFDL2dhc3RyaWMtY2FuY2VyL2Zhc3RxYy9TUlI1ODU1NzAKYGBgCgpgbWtkaXJgIGFsbG93cyB1cyB0byBjcmVhdGUgYSBuZXcgZm9sZGVyIGluIHRoZSBgUUMvZ2FzdHJpYy1jYW5jZXIvZmFzdHFjYCBkaXJlY3Rvcnkgc3BlY2lmaWNhbGx5IHRvIGhvbGQgdGhlIHJlcG9ydCBpbmZvcm1hdGlvbiB0aGF0IHdpbGwgYmUgZ2VuZXJhdGVkIGJ5IEZhc3RRQyBfZm9yIHRoaXMgc2FtcGxlXy4KVGhlIGAtcGAgYWxsb3dzIHVzIHRvIGNyZWF0ZSBfcGFyZW50XyBkaXJlY3RvcmllcyBhbmQgd2lsbCBwcmV2ZW50IGFuIGVycm9yIGlmIHRoZSBkaXJlY3Rvcnkgd2Ugc3BlY2lmeSBhbHJlYWR5IGV4aXN0cy4KCmBgYGJhc2gKIyBJbiB0aGUgaW50ZXJlc3Qgb2YgdGltZSwgd2UnbGwgcnVuIG9uZSBvZiB0aGUgZmFzdHEgZmlsZXMgdGhyb3VnaCBGYXN0UUMKZmFzdHFjIGRhdGEvZ2FzdHJpYy1jYW5jZXIvZmFzdHEvU1JSNTg1NTcwL1NSUjU4NTU3MF8xLmZhc3RxLmd6IFwKCS1vIFFDL2dhc3RyaWMtY2FuY2VyL2Zhc3RxYy9TUlI1ODU1NzAKYGBgCgojIyMjIGAtb2AKClRoZSBgLW9gIGZsYWcgYWxsb3dzIHVzIHRvIHNwZWNpZnkgd2hlcmUgdGhlIG91dHB1dCBvZiBGYXN0UUMgaXMgc2F2ZWQuCk5vdGUgdGhhdCB0aGlzIGlzIHNhdmVkIGluIGEgc2VwYXJhdGUgcGxhY2UgdGhhbiB0aGUgcmF3IGRhdGEgZmlsZXMgYW5kIGluIGEgZGlyZWN0b3J5IHNwZWNpZmljYWxseSBmb3IgcXVhbGl0eSBjb250cm9sIGluZm9ybWF0aW9uLgoKRm9yIGNvbXBhcmlzb24gdG8gdGhlIHJlcG9ydCBmb3IgYFNSUjU4NTU3MF8xLmZhc3RxLmd6YCB3ZSBnZW5lcmF0ZSB3aXRoIG91ciBzY3JpcHQsIHdlJ3ZlIHByZXBhcmVkIGEgRmFzdFFDIHJlcG9ydCBmb3Igb25lIG9mIHRoZSBzZXRzIG9mIHJlYWRzIGZvciBhbm90aGVyIHNhbXBsZSBpbiB0aGUgZXhwZXJpbWVudC4KSXQgY2FuIGJlIGZvdW5kIGF0IGBRQy9nYXN0cmljLWNhbmNlci9mYXN0cWMvU1JSNTg1NTc0L1NSUjU4NTU3NF8xX2Zhc3RxYy5odG1sYC4KCioqTGV0J3MgbG9vayBhdCB0aGUgcmVwb3J0cyBmb3IgYm90aCBzYW1wbGVzLioqCgojIyBQcmVwcm9jZXNzaW5nIHdpdGggZmFzdHAKCiFbXShkaWFncmFtcy9ybmEtc2VxXzMucG5nKQoKV2UgdXNlIFtmYXN0cF0oaHR0cHM6Ly9naXRodWIuY29tL09wZW5HZW5lL2Zhc3RwKSB0byBwcmVwcm9jZXNzIHRoZSBGQVNUUSBmaWxlcyAoW0NoZW4gZXQgYWwuIF9CaW9pbmZvcm1hdGljcy5fIDIwMTguXShodHRwczovL2RvaS5vcmcvMTAuMTA5My9iaW9pbmZvcm1hdGljcy9idHk1NjApKS4KTm90ZSB0aGF0IGZhc3RwIGhhcyBxdWFsaXR5IGNvbnRyb2wgZnVuY3Rpb25hbGl0eSBhbmQgbWFueSBkaWZmZXJlbnQgb3B0aW9ucyBmb3IgcHJlcHJvY2Vzc2luZyAoc2VlIFthbGwgb3B0aW9ucyBvbiBHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9PcGVuR2VuZS9mYXN0cC9ibG9iL21hc3Rlci9SRUFETUUubWQjYWxsLW9wdGlvbnMpKSwgbW9zdCBvZiB3aGljaCB3ZSB3aWxsIG5vdCBjb3Zlci4KSGVyZSwgd2UgZm9jdXMgb24gYWRhcHRlciB0cmltbWluZywgcXVhbGl0eSBmaWx0ZXJpbmcsIGFuZCBsZW5ndGggZmlsdGVyaW5nLgoKQmVsb3csIHdlIGRpc2N1c3MgdGhlIGNvbW1hbmRzIHdlIHVzZWQgaW4gdGhlIHNjcmlwdC4KCiMjIyBDcmVhdGUgb3V0cHV0IGRpcmVjdG9yaWVzCgpgYGBiYXNoCiMgQ3JlYXRlIGEgZGlyZWN0b3J5IHRvIGhvbGQgdGhlIHRyaW1tZWQgZmFzdHEgZmlsZXMKbWtkaXIgLXAgZGF0YS9nYXN0cmljLWNhbmNlci9mYXN0cS10cmltbWVkL1NSUjU4NTU3MAojIENyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIHRoZSBRQyBvdXRwdXQgZnJvbSBmYXN0cApta2RpciAtcCBRQy9nYXN0cmljX2NhbmNlci9mYXN0cC9TUlI1ODU1NzAKYGBgCgpBcyB3ZSdsbCBjb3ZlciBiZWxvdywgZmFzdHAgZXNzZW50aWFsbHkgaGFzIHR3byBraW5kcyBvZiBvdXRwdXQ6IHRyaW1tZWQgYW5kIGZpbHRlcmVkIEZBU1RRIGZpbGVzIChkYXRhKSBhbmQgcmVwb3J0cyAocXVhbGl0eSBjb250cm9sKS4KCiMjIyBmYXN0cAoKYGBgYmFzaAojIFJ1biB0aGUgYWRhcHRlciBhbmQgcXVhbGl0eSB0cmltbWluZyBzdGVwIC0tIGFsc28gcHJvZHVjZXMgUUMgcmVwb3J0CmZhc3RwIC1pIGRhdGEvZ2FzdHJpYy1jYW5jZXIvZmFzdHEvU1JSNTg1NTcwL1NSUjU4NTU3MF8xLmZhc3RxLmd6IFwKICAgIC1JIGRhdGEvZ2FzdHJpYy1jYW5jZXIvZmFzdHEvU1JSNTg1NTcwL1NSUjU4NTU3MF8yLmZhc3RxLmd6IFwKICAgIC1vIGRhdGEvZ2FzdHJpYy1jYW5jZXIvZmFzdHEtdHJpbW1lZC9TUlI1ODU1NzAvU1JSNTg1NTcwX2Zhc3RwXzEuZmFzdHEuZ3ogXAogICAgLU8gZGF0YS9nYXN0cmljLWNhbmNlci9mYXN0cS10cmltbWVkL1NSUjU4NTU3MC9TUlI1ODU1NzBfZmFzdHBfMi5mYXN0cS5neiBcCiAgICAtLXF1YWxpZmllZF9xdWFsaXR5X3BocmVkIDE1IFwKICAgIC0tbGVuZ3RoX3JlcXVpcmVkIDIwIFwKICAgIC0tcmVwb3J0X3RpdGxlICJTUlI1ODU1NzAiIFwKICAgIC0tanNvbiBRQy9nYXN0cmljLWNhbmNlci9mYXN0cC9TUlI1ODU1NzAvU1JSNTg1NTcwX2Zhc3RwLmpzb24gXAogICAgLS1odG1sIFFDL2dhc3RyaWMtY2FuY2VyL2Zhc3RwL1NSUjU4NTU3MC9TUlI1ODU1NzBfZmFzdHAuaHRtbApgYGAKCkJlbG93LCB3ZSdsbCB3YWxrIHRocm91Z2ggdGhlIGFyZ3VtZW50cy9vcHRpb25zIHdlIHVzZWQgdG8gcnVuIGBmYXN0cGAuCkJ5IGRlZmF1bHQsIGZhc3RwIHBlcmZvcm1zIGFkYXB0ZXIgdHJpbW1pbmcsIHdoaWNoIHlvdSBjYW4gcmVhZCBtb3JlIGFib3V0IFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vT3BlbkdlbmUvZmFzdHAjYWRhcHRlcnMpLgpGb3IgcGFpcmVkLWVuZCBkYXRhIGxpa2UgdGhlIGRhdGEgd2UgaGF2ZSBmb3IgU1JSNTg1NTcwLCBhZGFwdGVycyBjYW4gYmUgZGV0ZWN0ZWQgYXV0b21hdGljYWxseSB3aXRob3V0IHNwZWNpZnlpbmcgYW4gYWRhcHRlciBzZXF1ZW5jZS4KCiMjIyMgSW5wdXQ6IGAtaWAgYW5kIGAtSWAKClRoZXNlIGFyZ3VtZW50cyBzcGVjaWZ5IHRoZSByZWFkMSBpbnB1dCBhbmQgcmVhZDIgKHNvbWV0aW1lcyBjYWxsZWQgbGVmdCBhbmQgcmlnaHQpIGlucHV0LCByZXNwZWN0aXZlbHkuCgojIyMjIGZhc3RxIG91dHB1dDogYC1vYCBhbmQgYC1PYAoKVGhlc2UgYXJndW1lbnRzIHNwZWNpZnkgdGhlIHJlYWQxIG91dHB1dCBhbmQgcmVhZDIgb3V0cHV0LCByZXNwZWN0aXZlbHkuCk5vdGUgdGhhdCB0aGUgb3V0cHV0IGlzIGJlaW5nIHBsYWNlZCBpbiBgZGF0YS9nYXN0cmljLWNhbmNlci9mYXN0cS10cmltbWVkL1NSUjU4NTU3MC9gLCBzbyB0aGUgcHJvY2Vzc2VkIEZBU1RRIGZpbGVzIHdpbGwgYmUga2VwdCBzZXBhcmF0ZSBmcm9tIGZyb20gdGhlIG9yaWdpbmFsIGZpbGVzLgpJdCBpcyBnZW5lcmFsbHkgZ29vZCBwcmFjdGljZSB0byB0cmVhdCB5b3VyICJyYXciIGRhdGEgYW5kIGl0cyBkaXJlY3RvcmllcyBhcyBmaXhlZCBhbmQgc2VwYXJhdGUgZnJvbSBhbnkgcHJvY2Vzc2luZyBhbmQgYW5hbHlzaXMgdGhhdCB5b3UgZG8sIHRvIHByZXZlbnQgYWNjaWRlbnRhbGx5IG1vZGlmaWNhdGlvbiBvZiB0aG9zZSBvcmlnaW5hbCBmaWxlcy4gCkFuZCBpbiB0aGUgZXZlbnQgdGhhdCB5b3UgYWNjaWRlbnRhbGx5IGRvIG1vZGlmeSB0aGUgb3JpZ2luYWxzLCB5b3Uga25vdyBleGFjdGx5IHdoaWNoIGZpbGVzIGFuZCBkaXJlY3RvcmllcyB0byByZXNldC4KCiMjIyMgYC0tcXVhbGlmaWVkX3F1YWxpdHlfcGhyZWRgCgpbUGhyZWQgc2NvcmVzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QaHJlZF9xdWFsaXR5X3Njb3JlKSBhcmUgdGhlIHF1YWxpdHkgaW5mb3JtYXRpb24gaW5jbHVkZWQgaW4gYSBGQVNUUSBmaWxlIGFuZCB0aGUgdmFsdWVzIGluZGljYXRlIHRoZSBjaGFuY2VzIHRoYXQgYSBiYXNlIGlzIGNhbGxlZCBpbmNvcnJlY3RseS4gTGV0J3MgbG9vayBhdCBhIHNjcmVlbnNob3Qgb2YgdGhlIFBlciBCYXNlIFNlcXVlbmNlIFF1YWxpdHkgbW9kdWxlIGZyb20gW0Zhc3RRQyBiYWQgSWxsdW1pbmEgZXhhbXBsZV0oaHR0cHM6Ly93d3cuYmlvaW5mb3JtYXRpY3MuYmFicmFoYW0uYWMudWsvcHJvamVjdHMvZmFzdHFjL2JhZF9zZXF1ZW5jZV9mYXN0cWMuaHRtbCkgd2UgbGlua2VkIHRvIGFib3ZlLgoKIVtwZXItYmFzZS1xdWFsaXR5LXNjcmVlbnNob3RdKGh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzE5NTM0MjA1LzU5MjkyMzE2LWQ2MjkyNTAwLThjNGEtMTFlOS05NjdiLTEzMmJkOGM1NDU3Ny5wbmcpCgpBbnl0aGluZyBiZWxvdyAyMCwgd2hlcmUgYSBQaHJlZCBzY29yZSBvZiAyMCByZXByZXNlbnRzIGEgMSBpbiAxMDAgY2hhbmNlIHRoYXQgdGhlIGNhbGwgaXMgaW5jb3JyZWN0LCBpcyBjb25zaWRlcmVkIHBvb3IgcXVhbGl0eSBieSBGYXN0UUMuClVzaW5nIGAtLXF1YWxpZmllZF9xdWFsaXR5X3BocmVkIDE1YCAod2hpY2ggaXMgdGhlIGRlZmF1bHQpLCBtZWFucyBzY29yZXMgPj0gMTUgYXJlIGNvbnNpZGVyZWQgInF1YWxpZmllZC4iClVzaW5nIHRoZSBkZWZhdWx0IHBhcmFtZXRlcnMgYXMgd2UgZG8gaGVyZSwgcmVhZHMgd2lsbCBiZSBmaWx0ZXJlZCBvdXQgaWYgPjQwJSBvZiB0aGUgYmFzZXMgYXJlIHVucXVhbGlmaWVkLgpZb3UgY2FuIHJlYWQgbW9yZSBhYm91dCB0aGUgKipxdWFsaXR5IGZpbHRlcmluZyoqIGZ1bmN0aW9uYWxpdHkgb2YgZmFzdHAgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9PcGVuR2VuZS9mYXN0cC9ibG9iL21hc3Rlci9SRUFETUUubWQjcXVhbGl0eS1maWx0ZXIpLgpUaGUgU2FsbW9uIGRvY3VtZW50YXRpb24gbm90ZXMgdGhhdCwgZ2l2ZW4gdGhlIHdheSB3ZSBydW4gYHNhbG1vbiBxdWFudGAsIHF1YW50aWZpY2F0aW9uIG1heSBiZSBtb3JlIHNlbnNpdGl2ZSB0byBjYWxscyB0aGF0IGFyZSBsaWtlbHkgdG8gYmUgZXJyb25lb3VzIChvZiBsb3cgcXVhbGl0eSkgYW5kLCB0aGVyZWZvcmUsIHF1YWxpdHkgdHJpbW1pbmcgbWF5IGJlIGltcG9ydGFudC4KClRyaW1taW5nLCBpbiBjb250cmFzdCB0byBmaWx0ZXJpbmcsIHJlZmVycyB0byByZW1vdmluZyBsb3cgcXVhbGl0eSBiYXNlIGNhbGxzIGZyb20gdGhlICh0eXBpY2FsbHkgMycpIGVuZCBvZiByZWFkcy4KQSByZWNlbnQgcGFwZXIgZnJvbSB0aGUgU2FsbW9uIGF1dGhvcnMgKFtTcml2YXN0YXZhIF9ldCBhbC5fIDIwMjBdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMjAtMDIxNTEtOCkpIG5vdGVzIHRoYXQgdHJpbW1pbmcgZGlkIG5vdCBhZmZlY3QgbWFwcGluZyByYXRlcyBmcm9tIHJhbmRvbSBwdWJsaWNseSBhdmFpbGFibGUgaHVtYW4gYnVsayAocGFpcmVkLWVuZCkgUk5BLXNlcSBzYW1wbGVzICh0aGV5IHVzZWQgW1RyaW1HYWxvcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9GZWxpeEtydWVnZXIvVHJpbUdhbG9yZSkpLgpmYXN0cCBkb2VzIGhhdmUgW3RoZSBmdW5jdGlvbmFsaXR5XShodHRwczovL2dpdGh1Yi5jb20vT3BlbkdlbmUvZmFzdHAjcGVyLXJlYWQtY3V0dGluZy1ieS1xdWFsaXR5LXNjb3JlKSB0byBwZXJmb3JtIHRyaW1taW5nIHVzaW5nIGEgc2xpZGluZyB3aW5kb3csIHdoaWNoIG11c3QgYmUgZW5hYmxlZC4KV2UgYXJlIG5vdCB1c2luZyBpdCBoZXJlLgoKX05vdGUgdGhhdCB0aGVyZSBhcmUgdHdvIGtpbmRzIG9mIGVuY29kaW5nIGZvciBQaHJlZCBzY29yZXM6IFBocmVkIDMzIGVuY29kaW5nIGFuZCBQaHJlZCA2NCBlbmNvZGluZy4KRmFzdFFDIGd1ZXNzZWQgdGhhdCB0aGUgZmlsZSBmb3IgU1JSNTg1NTcwIHVzZXMgU2FuZ2VyL0lsbHVtaW5hIDEuOSBlbmNvZGluZyAoUGhyZWQgMzMpLgpJZiB3ZSBoYWQgUGhyZWQgNjQgZGF0YSwgd2UnZCB1c2UgdGhlIGAtLXBocmVkNjRgIGZsYWcuCllvdSBjYW4gcmVhZCBhIGxpdHRsZSBiaXQgbW9yZSBhYm91dCB0aGUgZW5jb2RpbmcgW2hlcmVdKGh0dHA6Ly9yZXNvdXJjZXMucWlhZ2VuYmlvaW5mb3JtYXRpY3MuY29tL21hbnVhbHMvY2xjZ2Vub21pY3N3b3JrYmVuY2gvNzAwL1F1YWxpdHlfc2NvcmVzX2luX0lsbHVtaW5hX3BsYXRmb3JtLmh0bWwpLl8KCiMjIyMgYC0tbGVuZ3RoX3JlcXVpcmVkYAoKVHJpbW1pbmcgcmVhZHMgbWF5IHJlc3VsdCBpbiBzaG9ydCByZWFkcywgd2hpY2ggbWF5IGFmZmVjdCBnZW5lIGV4cHJlc3Npb24gZXN0aW1hdGVzIChbV2lsbGlhbXMgZXQgYWwuIF9CTUMgQmlvaW5mb3JtYXRpY3MuXyAyMDE2Ll0oaHR0cHM6Ly9kb2kub3JnLzEwLjExODYvczEyODU5LTAxNi0wOTU2LTIpKS4KVXNpbmcgYC0tbGVuZ3RoX3JlcXVpcmVkIDIwYCBtZWFucyB0aGF0IHJlYWRzIHNob3J0ZXIgdGhhbiAyMGJwIHdpbGwgYmUgZGlzY2FyZGVkIChzaW1pbGFyIHRvIHdoYXQgd2FzIHVzZWQgaW4gU3JpdmFzdGF2YSBldCBhbC4gYWJvdmUpLgoKIyMjIyBgLS1yZXBvcnRfdGl0bGVgCgpXaGVuIHdlIGxvb2sgYXQgdGhlIEhUTUwgcmVwb3J0LCBpdCdzIGhlbHBmdWwgdG8gcXVpY2tseSBpZGVudGlmeSB3aGF0IHNhbXBsZSB0aGUgcmVwb3J0IGlzIGZvci4gVXNpbmcgYC0tcmVwb3J0IHRpdGxlICJTUlI1ODU1NzAiYCBtZWFucyB0aGF0IHRoZSByZXBvcnQgd2lsbCBiZSB0aXRsZWQgIlNSUjU4NTU3MCIgcmF0aGVyIHRoYW4gdGhlIGRlZmF1bHQgKCJmYXN0cCByZXBvcnQiKS4KCiMjIyMgYC0tanNvbmAgYW5kIGAtLWh0bWxgCgpXaXRoIHRoZXNlIG9wdGlvbnMsIHdlJ3JlIHNwZWNpZnlpbmcgd2hlcmUgdGhlIFtKU09OXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9KU09OKSBhbmQgSFRNTCByZXBvcnRzIHdpbGwgYmUgc2F2ZWQgKGluIHRoZSBgUUMvZ2FzdHJpYy1jYW5jZXIvZmFzdHAvYCBkaXJlY3Rvcnkgd2UgY3JlYXRlZCkgYW5kIHdoYXQgdGhlIGZpbGVuYW1lcyB3aWxsIGJlLgpJbmNsdWRpbmcgdGhlIHNhbXBsZSBuYW1lIGluIHRoZSBmaWxlbmFtZXMgYWdhaW4gbWF5IGhlbHAgdXMgd2l0aCBwcm9qZWN0IG9yZ2FuaXphdGlvbi4KCioqSWYgd2UgbG9vayBhdCoqIGBRQy9nYXN0cmljLWNhbmNlci9mYXN0cC9TUlI1ODU1NzBfZmFzdHAuanNvbmAgKipvciB0aGUgdG9wIG9mIHRoZSBIVE1MIHJlcG9ydCwgd2UgY2FuIHNlZSB0aGF0IGZhc3RwIHJlcG9ydHMgY2VydGFpbiBtZXRyaWNzIGJlZm9yZSBhbmQgYWZ0ZXIgZmlsdGVyaW5nLCB3aGljaCBjYW4gYmUgdmVyeSB1c2VmdWwgaW4gbWFraW5nIGFuYWx5c2lzIGRlY2lzaW9ucy4qKgoKIyMgUXVhbnRpZmljYXRpb24gd2l0aCBTYWxtb24KCiFbXShkaWFncmFtcy9ybmEtc2VxXzQucG5nKQoKV2UnbGwgdXNlIFtTYWxtb25dKGh0dHBzOi8vY29tYmluZS1sYWIuZ2l0aHViLmlvL3NhbG1vbi8pIGZvciBxdWFudGlmeWluZyB0cmFuc2NyaXB0IGV4cHJlc3Npb24gKFtkb2N1bWVudGF0aW9uXShodHRwOi8vc2FsbW9uLnJlYWR0aGVkb2NzLmlvL2VuL2xhdGVzdC8pKS4KU2FsbW9uIChbUGF0cm8sIGV0IGFsLiBfTmF0dXJlIE1ldGhvZHMuXyAyMDE3Ll0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvbm1ldGguNDE5NykpIGlzIGZhc3QgYW5kIHJlcXVpcmVzIHZlcnkgbGl0dGxlIG1lbW9yeSwgd2hpY2ggbWFrZXMgaXQgYSBncmVhdCBjaG9pY2UgZm9yIHJ1bm5pbmcgb24geW91ciBsYXB0b3AgZHVyaW5nIHRyYWluaW5nLgpXZSBjYW4gdXNlIHRoZSBvdXRwdXQgZm9yIGRvd25zdHJlYW0gYW5hbHlzZXMgbGlrZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBhbmQgY2x1c3RlcmluZy4KV2UgdXNlIFNhbG1vbiBpbiBtYXBwaW5nIG1vZGUsIHdpdGggbWFwcGluZyB2YWxpZGF0aW9uIGVuYWJsZWQsIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZDoKCmBgYGJhc2gKIyBXZSBwZXJmb3JtIHF1YW50aWZpY2F0aW9uIG9uIHRoZSBmaWxlcyB0aGF0IGhhdmUgYmVlbiB0cmltbWVkCiMgYW5kIHVzZSB0aGUgaW5kZXggZ2VuZXJhdGVkIHdpdGggLWsgMjMsIGFzIHRoaXMgbWF5ICJpbXByb3ZlIHNlbnNpdGl2aXR5IgojIHBlciB0aGUgU2FsbW9uIGRvY3VtZW50YXRpb24Kc2FsbW9uIHF1YW50IC1pIGluZGV4L0hvbW9fc2FwaWVucy9zaG9ydF9pbmRleCBcCiAgICAtbCBBIFwKICAgIC0xIGRhdGEvZ2FzdHJpYy1jYW5jZXIvZmFzdHEtdHJpbW1lZC9TUlI1ODU1NzAvU1JSNTg1NTcwX2Zhc3RwXzEuZmFzdHEuZ3ogXAogICAgLTIgZGF0YS9nYXN0cmljLWNhbmNlci9mYXN0cS10cmltbWVkL1NSUjU4NTU3MC9TUlI1ODU1NzBfZmFzdHBfMi5mYXN0cS5neiBcCiAgICAtbyBkYXRhL2dhc3RyaWMtY2FuY2VyL3NhbG1vbl9xdWFudC9TUlI1ODU1NzAgXAogICAgLS12YWxpZGF0ZU1hcHBpbmdzIC0tcmFuZ2VGYWN0b3JpemF0aW9uQmlucyA0IFwKICAgIC0tZ2NCaWFzIC0tc2VxQmlhcyBcCiAgICAtLXRocmVhZHMgNApgYGAKCkJlbG93LCB3ZSdsbCB3YWxrIHRocm91Z2ggdGhlIGFyZ3VtZW50cy9vcHRpb25zIHdlIHVzZWQgdG8gcnVuIGBzYWxtb24gcXVhbnRgLgoKIyMjIyBUcmFuc2NyaXB0b21lIGluZGV4OiBgLWlgCgpTYWxtb24gcmVxdWlyZXMgYSBzZXQgb2YgdHJhbnNjcmlwdHMgKHdoYXQgd2Ugd2FudCB0byBxdWFudGlmeSkgaW4gdGhlIGZvcm0gb2YgYSB0cmFuc2NyaXB0b21lIGluZGV4IGJ1aWx0IHdpdGggYHNhbG1vbiBpbmRleGAuCkJ1aWxkaW5nIGFuIGluZGV4IGNhbiB0YWtlIGEgd2hpbGUgKGJ1dCB5b3Ugb25seSBoYXZlIHRvIGRvIGl0IG9uY2UhKSwgc28gd2UndmUgYnVpbHQgdGhlIG9uZSB3ZSB1c2UgdG9kYXkgYWhlYWQgb2YgdGltZS4KQmVmb3JlIHdlIHVzZSBpdCwgd2UnbGwgdGFrZSBhIG1vbWVudCB0byBnaXZlIGEgYml0IG9mIGJhY2tncm91bmQuCgpZb3UgY2FuIHNlZSBob3cgd2Ugb2J0YWluZWQgdGhpcyBpbmRleCBhbmQgb3RoZXJzIFtvbiBHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL3RyYWluaW5nLXR4b21lLXByZXApLgpOb3RlIHRoYXQgd2UgdXNlZCBIb21vIHNhcGllbnMgR1JDaDM4LCBFbnNlbWJsIHJlbGVhc2UgOTUuCkl0IGlzIGltcG9ydGFudCB0byBrZWVwIHRyYWNrIG9mIHdoYXQgYnVpbGQsIHJlc291cmNlLCBhbmQgZmlsZXMgd2VyZSB1c2VkIGFuZCBwdXR0aW5nIG91ciBzaGVsbCBzY3JpcHRzIG9uIEdpdEh1YiBhbGxvd3MgdXMgdG8gZG8gdGhhdC4gIAoKVGhlIGBzYWxtb24gaW5kZXhgIGNvbW1hbmQgaGFzIGEgcGFyYW1ldGVyIGAta2Agd2hpY2ggc2V0cyB0aGUgay1tZXIgbGVuZ3RoLgpUaGUgaW5kZXggd2UgdXNlZCB3YXMgYnVpbHQgd2l0aCBgLWsgMjNgIGFuZCBjYW4gYmUgZm91bmQgaGVyZToKCmBgYAppbmRleC9Ib21vX3NhcGllbnMvc2hvcnRfaW5kZXgKYGBgCgpVc2luZyBhIHNtYWxsZXIgdmFsdWUgZm9yIF9rXyB0aGFuIHRoZSBkZWZhdWx0IChfa18gPSAzMSkgaXMgYXBwcm9wcmlhdGUgZm9yIHNob3J0ZXIgcmVhZHMgYW5kIG1heSBpbXByb3ZlIHNlbnNpdGl2aXR5IHdoZW4gdXNpbmcgYC0tdmFsaWRhdGVNYXBwaW5nc2AgYWNjb3JkaW5nIHRvIHRoZSBbU2FsbW9uIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vc2FsbW9uLnJlYWR0aGVkb2NzLmlvL2VuL2xhdGVzdC9zYWxtb24uaHRtbCNwcmVwYXJpbmctdHJhbnNjcmlwdG9tZS1pbmRpY2VzLW1hcHBpbmctYmFzZWQtbW9kZSkuCgojIyMjIGAtbGAKCldlIHVzZSBgLWwgQWAgdG8gYWxsb3cgU2FsbW9uIHRvIGF1dG9tYXRpY2FsbHkgaW5mZXIgdGhlIGxpYnJhcnkgdHlwZSBiYXNlZCBvbiBhIHN1YnNldCBvZiByZWFkcywgYnV0IHlvdSBjYW4gYWxzbyBwcm92aWRlIHRoZSBbbGlicmFyeSB0eXBlXShodHRwOi8vc2FsbW9uLnJlYWR0aGVkb2NzLmlvL2VuL2xhdGVzdC9zYWxtb24uaHRtbCN3aGF0LXMtdGhpcy1saWJ0eXBlKSB0byBTYWxtb24gd2l0aCB0aGlzIGFyZ3VtZW50LgoKIyMjIyBJbnB1dDogYC0xYCBhbmQgYC0yYAoKVGhlc2UgZGF0YSBhcmUgcGFpcmVkLWVuZCwgd2UgdXNlIGAtMWAgYW5kIGAtMmAgdG8gc3BlY2lmeSByZWFkMSBhbmQgcmVhZDIsIHJlc3BlY3RpdmVseS4KCiMjIyMgYC1vYAoKT3V0cHV0IGRpcmVjdG9yeSwgYHNhbG1vbiBxdWFudGAgc2hvdWxkIGNyZWF0ZSB0aGlzIGZvciB1cyBpZiBpdCBkb2Vzbid0IGV4aXN0IHlldC4KCiMjIyMgYC0tdmFsaWRhdGVNYXBwaW5nc2AgYW5kIGAtLXJhbmdlRmFjdG9yaXphdGlvbkJpbnNgCgpVc2luZyBgLS12YWxpZGF0ZU1hcHBpbmdzYCBlbmFibGVzIG1hcHBpbmcgdmFsaWRhdGlvbiwgd2hlcmUgU2FsbW9uIGNoZWNrcyBpdHMgbWFwcGluZ3MgdXNpbmcgdHJhZGl0aW9uYWwgYWxpZ25tZW50LgpUaGlzIGhlbHBzIHByZXZlbnQgInNwdXJpb3VzIG1hcHBpbmdzIiB3aGVyZSBhIHJlYWQgbWFwcyB0byBhIHRhcmdldCBidXQgZG9lcyBub3QgYXJpc2UgZnJvbSBpdCAoc2VlIFtkb2N1bWVudGF0aW9uIGZvciBmbGFnXShodHRwczovL3NhbG1vbi5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3Qvc2FsbW9uLmh0bWwjdmFsaWRhdGVtYXBwaW5ncykgYW5kIHRoZSBbcmVsZWFzZSBub3RlcyBmb3IgYHYwLjEwLjBgXShodHRwczovL2dpdGh1Yi5jb20vQ09NQklORS1sYWIvc2FsbW9uL3JlbGVhc2VzL3RhZy92MC4xMC4wKSB3aGVyZSB0aGlzIHdhcyBpbnRyb2R1Y2VkKS4KCldoZW4gZW5hYmxpbmcgbWFwcGluZyB2YWxpZGF0aW9uIHdpdGggYC0tdmFsaWRhdGVNYXBwaW5nc2AsIHNldHRpbmcgYC0tcmFuZ2VGYWN0b3JpemF0aW9uQmlucyA0YCBjYW4gaW1wcm92ZSBxdWFudGlmaWNhdGlvbiBmb3IgY2VydGFpbiBjbGFzc2VzIG9mIHRyYW5zY3JpcHRzIChbZG9jc10oaHR0cHM6Ly9zYWxtb24ucmVhZHRoZWRvY3MuaW8vZW4vbGF0ZXN0L3NhbG1vbi5odG1sI3JhbmdlZmFjdG9yaXphdGlvbmJpbnMpKS4KCiMjIyMgYC0tZ2NCaWFzYAoKV2l0aCB0aGlzIG9wdGlvbiBlbmFibGVkLCBTYWxtb24gd2lsbCBhdHRlbXB0IHRvIGNvcnJlY3QgZm9yIGZyYWdtZW50IEdDLWJpYXMuClJlZ2lvbnMgd2l0aCBoaWdoIG9yIGxvdyBHQyBjb250ZW50IHRlbmQgdG8gYmUgdW5kZXJyZXByZXNlbnRlZCBpbiBzZXF1ZW5jaW5nIGRhdGEuCgpJdCBzaG91bGQgYmUgbm90ZWQgdGhhdCB0aGlzIGlzIG9ubHkgYXBwcm9wcmlhdGUgZm9yIHVzZSB3aXRoIHBhaXJlZC1lbmQgcmVhZHMsIGFzIGZyYWdtZW50IGxlbmd0aCBjYW4gbm90IGJlIGluZmVycmVkIGZyb20gc2luZ2xlLWVuZCByZWFkcyAoc2VlIFt0aGlzIEdpdEh1YiBpc3N1ZV0oaHR0cHM6Ly9naXRodWIuY29tL0NPTUJJTkUtbGFiL3NhbG1vbi9pc3N1ZXMvODMpKS4KCiMjIyMgYC0tc2VxQmlhc2AKCldpdGggdGhpcyBvcHRpb24gZW5hYmxlZCwgU2FsbW9uIHdpbGwgYXR0ZW1wdCB0byBjb3JyZWN0IGZvciB0aGUgYmlhcyB0aGF0IG9jY3VycyB3aGVuIHVzaW5nIHJhbmRvbSBoZXhhbWVyIHByaW1pbmcgKHByZWZlcmVudGlhbCBzZXF1ZW5jaW5nIG9mIHJlYWRzIHdoZW4gY2VydGFpbiBtb3RpZnMgYXBwZWFyIGF0IHRoZSBiZWdpbm5pbmcpLgoKIyMjIyBgLS10aHJlYWRzYAoKVGhlIGAtLXRocmVhZHNgIGFyZ3VtZW50IGNvbnRyb2xzIHRoZSBudW1iZXIgb2YgdGhyZWFkcyB0aGF0IGFyZSBhdmFpbGFibGUgdG8gU2FsbW9uIGR1cmluZyBxdWFudGlmaWNhdGlvbi4KVGhpcyBpbiBlc3NlbmNlIGNvbnRyb2xzIGhvdyBtdWNoIG9mIHRoZSBtYXBwaW5nIGNhbiBvY2N1ciBpbiBwYXJhbGxlbC4KSWYgeW91IGhhZCBhY2Nlc3MgdG8gYSBjb21wdXRlciB3aXRoIG1hbnkgY29yZXMsIHlvdSBjb3VsZCBpbmNyZWFzZSB0aGUgbnVtYmVyIG9mIHRocmVhZHMgdG8gbWFrZSBxdWFudGlmaWNhdGlvbiBnbyBmYXN0ZXIuCgoqKk5hdmlnYXRlIHRvKiogYGRhdGEvZ2FzdHJpYy1jYW5jZXIvc2FsbW9uX3F1YW50L1NSUjU4NTU3MS9hdXhfaW5mb2AgKiphbmQgb3BlbioqIGBtZXRhX2luZm8uanNvbmAqKi4KTG9vayBmb3IgYSBmaWVsZCBjYWxsZWQqKiBgcGVyY2VudF9tYXBwZWRgICoqLS0gd2hhdCB2YWx1ZSBkb2VzIHRoaXMgc2FtcGxlIGhhdmU/KioK