Next steps in the pipeline

Once we are happy with the QC or our data, the next steps in an NGS analysis is usually to align your samples to a reference genome. We have supplied a couple of required files in the /home/dcuser/rnaseq_data/ref_data folder.

ls /home/dcuser/rnaseq_data/ref_data

Obtaining reference data

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.

If you are running this workshop in-person using the cloud-based environment, make sure to use web-browse within the environment to access the Ensembl FTP site.

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)

Exercise

We will need the Human reference genome sequence for chromosome 22 from the DNA (FASTA) section. Download the repeat-masked (rm) version of this sequence to the ref_data folder, and extract the contents of the file using gzip.

More recommendations on downloading reference data

To avoid duplication. check first (with I.T or local Bioinformaticians) that you don’t have a local copy of reference genomes on your file system

We also recommend obtaining reference genomes from the AWS iGenomes project

This project provides a stable and versioned resource for obtaining many reference genomes. Moreover, it has a convenient command-based interface for downloading the genomes. As it is capable of downloading large volumes of data we will not be using it in the workshop.

aws-igenomes.sh

Alignment of NGS data

The general procedure of aligning of our sequencing reads is illustrated below:-

There are numerous tools available for aligning sequencing reads, and main consideration is the type of sequencing experiement being performed (e.g. DNA-seq vs RNA-seq etc). Each tool also requires you to build an index from the genome reference file.

Or recommended tool for RNA-seq is salmon and the particular command for one sample is:-

salmon quant -i index/GRCh38_salmon --libType A -r fastq/ERR732901_sub.fastq.gz -o quant/ERR732901

Where

  • fastq/ERR732901_sub.fastq.gz contains the data for the particular biological sample
  • index/GRCh38_salmon contains the processed reference genome
  • quant/ERR732901 is where we want to output to be created
  • libType A is a specific argument for salmon specifying what type of RNA-seq has been performed.

Why do we need a pipeline? How might we create one?

We have now learnt a few commands that, when joined together, can form the basis for a minimal analysis pipeline

  • use fastqc to create QC reports for each sample
  • use multiqc to combine the QC
  • align our data using salmon

The next natural step would be to record these commands in a script so that we can document our analysis and re-run as required. We don’t usually have a text editor in our Unix environment, so it is good practice to create and edit the script using the command-line. The nano editor is commonly-found on Unix systems and running the command nano with a file name will create that file if it does not exist.

nano analysis_script.sh

Enter the following in the nano window that appears

fastqc fastq/*.fastq.gz
mkdir -p qc/
mv fastq/*.html qc/
mv fastq/*.zip qc
multiqc -f -o qc/ qc

Press CTRL + X following by Y (when asked if you want to save the modified buffer) to return to the terminal.

We can execute the script using the bash command

## remove qc directory first, so we can see the script in action
rm -r qc/
ls -l
bash analysis_script.sh

This will run through the QC steps of the pipeline. The next stage would be to add the steps for salmon quantification.

salmon quant -i index/GRCh38_salmon --libType A -r fastq/ERR732901_sub.fastq.gz -o quant/ERR732901

We also have a number of options for how to proceed with quantifying the remaining samples. The simplest approach copy-and-paste the salmon quant line with different sample names

salmon quant -i index/GRCh38_salmon --libType A -r fastq/ERR732902_sub.fastq.gz -o quant/ERR732902
salmon quant -i index/GRCh38_salmon --libType A -r fastq/ERR732903_sub.fastq.gz -o quant/ERR732903
###etc....

This is not particularly satisfactory as it is prone to typo errors. Our pipeline is already quite short and is running on a small dataset, but already it is taking a little while to run. The pipeline has been written in a linear fashion, so that each step must be completed in order. If our salmon alignment code needed to be changed we would have to re-run all the QC. This is not a huge problem here, but could be quite inefficient for a more-realistic dataset.

The order in which our various tools are being run can be visualised as follows:-

An alternative might be to employ a for loop, which you might have come across previously. This will make our code more efficient, but still not help with running our analysis in an efficient manner.

There is no particular reason why we should wait for Sample 1 to be processed before Sample 2 and Sample 2 before Sample 3 etc. Ideally, we would want to process all our samples at the same time. This is achievable is we run our analysis where we have several processors available; such as a HPC (High-Performance Computer) or computing cluster.

Why do we need a workflow manager

As we have discussed, there are a number of options to extend our pipeline to multiple samples. These require more programming knowledge than we might be comfortable with. There are a few other issues with the script that we have created.

  • As pipeline steps have to be re-run in sequence; even if the initial pipeline steps ran sucessfully they will still be re-run every time
  • The pipeline will not neccesarily run on another environment as it will assume that the fastqc, multiqc and other tools can be found.

Neither of these issues are impossible to solve, but this isn’t intended to be a workshop on software development best-practice.

In reality, we would recommend people re-using existing analysis pipelines rather than writing their own. We will look at an example using the nextflow workflow manager, although similar tools such as snakemake are also popular in Bioinformatics

Running a nf.core pipeline

In our opinion, nextflow is particular appealing as many popular Bioinformatics pipelines have already been written using nextflow and have been distributed as part of the nf.core project

We will be showing the RNA-seq pipeline in particular

The minimum number of options required to run an nf.core pipeline such as RNA-seq are:-

nextflow run nf-core/rnaseq --input samplesheet.csv --outdir <OUTDIR> --genome GRCh37 -profile docker

where:-

  • nf-core/rnaseq is a reference to the pipeline that we want to run
  • --input is the location of a samplesheet defining the raw data to be processed
  • --outdir is a directory that will contain the final results of the pipeline
  • genome is the shorthand name for the genome to be used as a reference
  • profile defines how software included in the pipeline is to be downloaded/installed (See later)

We have customised some of the options of the pipeline so run a reduced number of steps are run for the workshop, and using a custom genome containing a single chromosome.

cat scripts/run_nextflow.sh

The particular steps that we have modified are as follows:-

##use a particular pipeline version
-r 3.8.1 \
## Skip aligning to the whole genome
--skip_alignment \
## Skip trimming read sequences
--skip_trimming \
## use the salmon quantification tool
--pseudo_aligner salmon \
## Use our own set of references rather than downloading
--fasta ref_data/Homo_sapiens.GRCh38.dna_rm.chromosome.22.fa \
--transcript_fasta ref_data/Homo_sapiens.GRCh38.cdna.chr22.fa \
--gtf ref_data/Homo_sapiens.GRCh38.108.chr22.gtf \
## restrict the amount of memory requested \
--max_memory 2GB

The files that we want to analyse are defined in a sample sheet. The format of the sheet is checked by the pipeline as one of the first steps. The column names have to match exactly what the pipeline expects.

cat nf_samplesheet.csv

Before we can run the pipeline we need to move (or copy) the script to the working folder

cp scripts/run_nextflow.sh /home/dcuser/rnaseq_data/
bash run_nextflow.sh

The output from the workflow will be written to a directory nf_results, which doesn’t need to exist before the pipeline has been run. You should also see that a work directory is created.

Exercise

Exercise Run the script run_nextflow.sh (around ~5 to 10 minutes) and afterwards look at the output in the nf_results folder and familiarise yourself with the outputs. What extra steps have been performed in addition to the script that we created earlier?

What software does nextflow use in it’s pipeline?

You should notice that the nf.core pipeline has produced quantification files for each sample, and also combined the salmon outputs into a single file. It also run some QC plots from the DESeq2 R package. However, R is not part of our software environment. We can see this by running the following commands which would usually report the path that R is located at, or run the command-line R.

which R
R

nextflow has it’s own way of installing and running software which does not depend on the operating system that is being used to run the pipeline. This is specified by this part of the run script.

-profile singularity 

In fact, we could have run the pipeline if we didn’t have salmon and fastqc installed. The implication of this being that you can re-run the pipeline on your own machine or HPC environment with minimal software installation. The only dependancies are nextflow itself (which in turn requires java) and some containerisation or package management software such as singularity, docker or conda. This is a significantly easier requirement to fulfill than having to install each piece of software individually.

If you are using a HPC environment you will probably want to keep the singularity option in the profile.

Re-running the pipeline

Exercise There are two fastq files in our fastq folder that have not yet been analysed; ERR732908 and ERR732909. Make a copy of the samplesheet nf_samnplesheet.csv and use the nano editor to modify the new samplesheet to include ERR732908. Now edit the run script to use this new samplesheet and re-run the pipeline. What do you notice about the time taken to run the pipeline?

The pipeline should be quicker this time around. This is because we had all the software downloaded and installed from the previous run. However, it still re-analysed the first seven samples from the samplesheet - which is not ideal. If we want a report on what analyses have been performed and how long they took we can look at a report from the command-line

## You will have to use auto-complete and choose the most-recent report
cat nf_results/pipeline/execution_trace

There is also a HTML version of the report that we can view through the file system.

Resuming a pipeline

Ideally, we would like the pipeline to detect what jobs have been run successfully and not repeat those jobs. The option -resume in nextflow will allow you to do this. (note the single - when adding this option). Modify the nextflow script with nano to contain the following lines at the top

nextflow run nf-core/rnaseq -profile singularity \
-resume \
-r 3.8.1 \
....

We can now edit the samplesheet again to process the sample ERR732909

sample,fastq_1,fastq_2,strandedness
ERR732901,fastq/ERR732901_sub.fastq.gz,,unstranded
ERR732902,fastq/ERR732902_sub.fastq.gz,,unstranded
ERR732903,fastq/ERR732903_sub.fastq.gz,,unstranded
ERR732904,fastq/ERR732904_sub.fastq.gz,,unstranded
ERR732905,fastq/ERR732905_sub.fastq.gz,,unstranded
ERR732906,fastq/ERR732906_sub.fastq.gz,,unstranded
ERR732907,fastq/ERR732907_sub.fastq.gz,,unstranded
ERR732908,fastq/ERR732908_sub.fastq.gz,,unstranded
ERR732909,fastq/ERR732909_sub.fastq.gz,,unstranded

and re-run the pipeline.

bash run_nextflow.sh

The pipeline now runs significantly quicker because it detects that all the analyses have been completed and cached. If we check the execution_trace text file for this latest run it should say that most tasks were CACHED - meaning that the results from the previous analysis was used.

LS0tDQp0aXRsZTogIkludHJvIHRvIFdvcmtmbG93cywgUGlwZWxpbmVzIGFuZCBXb3JrZmxvdyBNYW5hZ2VycyINCmF1dGhvcjogIk1hcmsgRHVubmluZyINCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjc3M6IHN0eWxlc2hlZXRzL3N0eWxlcy5jc3MNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSxldmFsPUZBTFNFKQ0KYGBgDQoNCiMgTmV4dCBzdGVwcyBpbiB0aGUgcGlwZWxpbmUNCg0KT25jZSB3ZSBhcmUgaGFwcHkgd2l0aCB0aGUgUUMgb3Igb3VyIGRhdGEsIHRoZSBuZXh0IHN0ZXBzIGluIGFuIE5HUyBhbmFseXNpcyBpcyB1c3VhbGx5IHRvIGFsaWduIHlvdXIgc2FtcGxlcyB0byBhIHJlZmVyZW5jZSBnZW5vbWUuIFdlIGhhdmUgc3VwcGxpZWQgYSBjb3VwbGUgb2YgcmVxdWlyZWQgZmlsZXMgaW4gdGhlIGAvaG9tZS9kY3VzZXIvcm5hc2VxX2RhdGEvcmVmX2RhdGFgIGZvbGRlci4NCg0KYGBge30NCmxzIC9ob21lL2RjdXNlci9ybmFzZXFfZGF0YS9yZWZfZGF0YQ0KYGBgDQoNCg0KIyMgT2J0YWluaW5nIHJlZmVyZW5jZSBkYXRhDQoNCkxpbmtzIHRvIGRvd25sb2FkcyBmb3IgYSB2YXJpZXR5IG9mIGRpZmZlcmVudCBvcmdhbmlzbXMgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vd3d3LmVuc2VtYmwub3JnL2luZm8vZGF0YS9mdHAvaW5kZXguaHRtbC4gV2UgY2FuIG5hdmlnYXRlIHRvIHRoZSBwYXJ0aWN1bGFyIG9yZ2FuaXNtIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRocm91Z2ggdGhlIGludGVyZmFjZSBhbmQgZG93bmxvYWQgdGhlIHRyYW5zY3JpcHQgYW5kIGdlbm9tZSBzZXF1ZW5jZXMgdG8gb3VyIGxhcHRvcC4gDQoNCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4NCklmIHlvdSBhcmUgcnVubmluZyB0aGlzIHdvcmtzaG9wIGluLXBlcnNvbiB1c2luZyB0aGUgY2xvdWQtYmFzZWQgZW52aXJvbm1lbnQsIG1ha2Ugc3VyZSB0byB1c2Ugd2ViLWJyb3dzZSB3aXRoaW4gdGhlIGVudmlyb25tZW50IHRvIGFjY2VzcyB0aGUgRW5zZW1ibCBGVFAgc2l0ZS4NCjwvZGl2Pg0KDQpUaGUgY29tbWFuZCBgd2dldGAgY2FuIGJlIHVzZWQgdG8gZG93bmxvYWQgYSBmaWxlIGZyb20gYW4gRlRQIHNpdGUgdG8gYSBsb2NhbCBkaXJlY3RvcnkgaWYgeW91IGtub3cgdGhlIHBhdGggdG8gdGhlIGZpbGUgKFVSTCkuIFRoaXMgcGF0aCBjb3VsZCBiZSBvYnRhaW5lZCBieSBmaXJzdCBsb2NhdGluZyB0aGUgZmlsZSBpbiBFbnNlbWJsIGFuZCByaWdodC1jbGlja2luZyB0byBjb3B5IHRoZSBsaW5rIGFkZHJlc3MuDQoNClRoZSBob21lcGFnZSBvZiBFbnNlbWJsIEZUUCBpbmRleCBsaW5rcyB0byByZWZlcmVuY2UgZGF0YSBmb3IgY29tbW9uIGdlbm9tZXMNCg0KPGltZyBzcmM9ImltYWdlcy9lbnNlbWJsX2Rvd25sb2FkLnBuZyIvPg0KDQpGaWxlcyBjYW4gYmUgZG93bmxvYWRlZCBieSBjbGlja2luZyBvbiB0aGUgcmVsZXZhbnQgbGluay4gSG93ZXZlciwgd2Ugd2FudCB0byBkb3dubG9hZCB0aGUgZGF0YSB1c2luZyB0aGUgY29tbWFuZCBsaW5lIHNvIHdlIGhhdmUgdG8gcmlnaHQtY2xpY2sgYW5kIHNlbGVjdCAiQ29weSBMaW5rIExvY2F0aW9uIiAob3Igc2ltaWxhcikNCg0KPGltZyBzcmM9ImltYWdlcy9jZG5hX2Rvd25sb2FkLnBuZyIvPg0KDQoNCg0KDQojIyBFeGVyY2lzZQ0KPGRpdiBjbGFzcz0iZXhlcmNpc2UiPg0KV2Ugd2lsbCBuZWVkIHRoZSBIdW1hbiByZWZlcmVuY2UgZ2Vub21lIHNlcXVlbmNlIGZvciBjaHJvbW9zb21lIDIyIGZyb20gdGhlIEROQSAoRkFTVEEpIHNlY3Rpb24uIERvd25sb2FkIHRoZSByZXBlYXQtbWFza2VkIChgcm1gKSB2ZXJzaW9uIG9mIHRoaXMgc2VxdWVuY2UgdG8gdGhlIGByZWZfZGF0YWAgZm9sZGVyLCBhbmQgZXh0cmFjdCB0aGUgY29udGVudHMgb2YgdGhlIGZpbGUgdXNpbmcgYGd6aXBgLg0KDQo8L2Rpdj4NCg0KDQojIyMgTW9yZSByZWNvbW1lbmRhdGlvbnMgb24gZG93bmxvYWRpbmcgcmVmZXJlbmNlIGRhdGENCg0KPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPg0KKlRvIGF2b2lkIGR1cGxpY2F0aW9uLiBjaGVjayBmaXJzdCAod2l0aCBJLlQgb3IgbG9jYWwgQmlvaW5mb3JtYXRpY2lhbnMpIHRoYXQgeW91IGRvbid0IGhhdmUgYSBsb2NhbCBjb3B5IG9mIHJlZmVyZW5jZSBnZW5vbWVzIG9uIHlvdXIgZmlsZSBzeXN0ZW0qDQo8L2Rpdj4NCg0KDQo8ZGl2IGNsYXNzPSJpbmZvcm1hdGlvbiI+DQpXZSBhbHNvIHJlY29tbWVuZCBvYnRhaW5pbmcgcmVmZXJlbmNlIGdlbm9tZXMgZnJvbSB0aGUgQVdTIGlHZW5vbWVzIHByb2plY3QNCg0KLSBbaHR0cHM6Ly9ld2Vscy5naXRodWIuaW8vQVdTLWlHZW5vbWVzL10oaHR0cHM6Ly9ld2Vscy5naXRodWIuaW8vQVdTLWlHZW5vbWVzLykNCg0KVGhpcyBwcm9qZWN0IHByb3ZpZGVzIGEgc3RhYmxlIGFuZCB2ZXJzaW9uZWQgcmVzb3VyY2UgZm9yIG9idGFpbmluZyBtYW55IHJlZmVyZW5jZSBnZW5vbWVzLiBNb3Jlb3ZlciwgaXQgaGFzIGEgY29udmVuaWVudCBjb21tYW5kLWJhc2VkIGludGVyZmFjZSBmb3IgZG93bmxvYWRpbmcgdGhlIGdlbm9tZXMuIEFzIGl0IGlzIGNhcGFibGUgb2YgZG93bmxvYWRpbmcgbGFyZ2Ugdm9sdW1lcyBvZiBkYXRhIHdlIHdpbGwgbm90IGJlIHVzaW5nIGl0IGluIHRoZSB3b3Jrc2hvcC4NCg0KYGBgDQphd3MtaWdlbm9tZXMuc2gNCmBgYA0KDQo8L2Rpdj4NCg0KIyMgQWxpZ25tZW50IG9mIE5HUyBkYXRhDQoNClRoZSBnZW5lcmFsIHByb2NlZHVyZSBvZiBhbGlnbmluZyBvZiBvdXIgc2VxdWVuY2luZyByZWFkcyBpcyBpbGx1c3RyYXRlZCBiZWxvdzotDQoNCjxpbWcgc3JjPSJodHRwczovL3RyYWluaW5nLmdhbGF4eXByb2plY3Qub3JnL3RyYWluaW5nLW1hdGVyaWFsL3RvcGljcy9zZXF1ZW5jZS1hbmFseXNpcy9pbWFnZXMvbWFwcGluZy9tYXBwaW5nLnBuZyIvPg0KDQpUaGVyZSBhcmUgbnVtZXJvdXMgdG9vbHMgYXZhaWxhYmxlIGZvciBhbGlnbmluZyBzZXF1ZW5jaW5nIHJlYWRzLCBhbmQgbWFpbiBjb25zaWRlcmF0aW9uIGlzIHRoZSB0eXBlIG9mIHNlcXVlbmNpbmcgZXhwZXJpZW1lbnQgYmVpbmcgcGVyZm9ybWVkIChlLmcuIEROQS1zZXEgdnMgUk5BLXNlcSBldGMpLiBFYWNoIHRvb2wgYWxzbyByZXF1aXJlcyB5b3UgdG8gYnVpbGQgYW4gKippbmRleCoqIGZyb20gdGhlIGdlbm9tZSByZWZlcmVuY2UgZmlsZS4NCg0KT3IgcmVjb21tZW5kZWQgdG9vbCBmb3IgUk5BLXNlcSBpcyBgc2FsbW9uYCBhbmQgdGhlIHBhcnRpY3VsYXIgY29tbWFuZCBmb3Igb25lIHNhbXBsZSBpczotDQoNCmBgYHtiYXNofQ0Kc2FsbW9uIHF1YW50IC1pIGluZGV4L0dSQ2gzOF9zYWxtb24gLS1saWJUeXBlIEEgLXIgZmFzdHEvRVJSNzMyOTAxX3N1Yi5mYXN0cS5neiAtbyBxdWFudC9FUlI3MzI5MDENCmBgYA0KDQpXaGVyZSANCg0KLSBgZmFzdHEvRVJSNzMyOTAxX3N1Yi5mYXN0cS5neiBgIGNvbnRhaW5zIHRoZSBkYXRhIGZvciB0aGUgcGFydGljdWxhciBiaW9sb2dpY2FsIHNhbXBsZQ0KLSBgaW5kZXgvR1JDaDM4X3NhbG1vbmAgY29udGFpbnMgdGhlIHByb2Nlc3NlZCByZWZlcmVuY2UgZ2Vub21lDQotIGBxdWFudC9FUlI3MzI5MDFgIGlzIHdoZXJlIHdlIHdhbnQgdG8gb3V0cHV0IHRvIGJlIGNyZWF0ZWQNCi0gYGxpYlR5cGUgQWAgaXMgYSBzcGVjaWZpYyBhcmd1bWVudCBmb3Igc2FsbW9uIHNwZWNpZnlpbmcgd2hhdCB0eXBlIG9mIFJOQS1zZXEgaGFzIGJlZW4gcGVyZm9ybWVkLg0KDQoNCiMgV2h5IGRvIHdlIG5lZWQgYSBwaXBlbGluZT8gSG93IG1pZ2h0IHdlIGNyZWF0ZSBvbmU/DQoNCldlIGhhdmUgbm93IGxlYXJudCBhIGZldyBjb21tYW5kcyB0aGF0LCB3aGVuIGpvaW5lZCB0b2dldGhlciwgY2FuIGZvcm0gdGhlIGJhc2lzIGZvciBhIG1pbmltYWwgYW5hbHlzaXMgcGlwZWxpbmUNCg0KLSB1c2UgYGZhc3RxY2AgdG8gY3JlYXRlIFFDIHJlcG9ydHMgZm9yIGVhY2ggc2FtcGxlDQotIHVzZSBgbXVsdGlxY2AgdG8gY29tYmluZSB0aGUgUUMNCi0gYWxpZ24gb3VyIGRhdGEgdXNpbmcgYHNhbG1vbmANCg0KVGhlIG5leHQgbmF0dXJhbCBzdGVwIHdvdWxkIGJlIHRvIHJlY29yZCB0aGVzZSBjb21tYW5kcyBpbiBhICpzY3JpcHQqIHNvIHRoYXQgd2UgY2FuIGRvY3VtZW50IG91ciBhbmFseXNpcyBhbmQgcmUtcnVuIGFzIHJlcXVpcmVkLiBXZSBkb24ndCB1c3VhbGx5IGhhdmUgYSB0ZXh0IGVkaXRvciBpbiBvdXIgVW5peCBlbnZpcm9ubWVudCwgc28gaXQgaXMgZ29vZCBwcmFjdGljZSB0byBjcmVhdGUgYW5kIGVkaXQgdGhlIHNjcmlwdCB1c2luZyB0aGUgY29tbWFuZC1saW5lLiBUaGUgYG5hbm9gIGVkaXRvciBpcyBjb21tb25seS1mb3VuZCBvbiBVbml4IHN5c3RlbXMgYW5kIHJ1bm5pbmcgdGhlIGNvbW1hbmQgYG5hbm9gIHdpdGggYSBmaWxlIG5hbWUgd2lsbCBjcmVhdGUgdGhhdCBmaWxlIGlmIGl0IGRvZXMgbm90IGV4aXN0Lg0KDQpgYGB7YmFzaH0NCm5hbm8gYW5hbHlzaXNfc2NyaXB0LnNoDQpgYGANCg0KKipFbnRlciB0aGUgZm9sbG93aW5nIGluIHRoZSBuYW5vIHdpbmRvdyB0aGF0IGFwcGVhcnMqKg0KDQpgYGB7YmFzaH0NCmZhc3RxYyBmYXN0cS8qLmZhc3RxLmd6DQpta2RpciAtcCBxYy8NCm12IGZhc3RxLyouaHRtbCBxYy8NCm12IGZhc3RxLyouemlwIHFjDQptdWx0aXFjIC1mIC1vIHFjLyBxYw0KYGBgDQoNClByZXNzICoqQ1RSTCArIFgqKiBmb2xsb3dpbmcgYnkgKipZKiogKHdoZW4gYXNrZWQgaWYgeW91IHdhbnQgdG8gc2F2ZSB0aGUgbW9kaWZpZWQgYnVmZmVyKSB0byByZXR1cm4gdG8gdGhlIHRlcm1pbmFsLg0KDQpXZSBjYW4gZXhlY3V0ZSB0aGUgc2NyaXB0IHVzaW5nIHRoZSBgYmFzaGAgY29tbWFuZA0KDQpgYGB7YmFzaH0NCiMjIHJlbW92ZSBxYyBkaXJlY3RvcnkgZmlyc3QsIHNvIHdlIGNhbiBzZWUgdGhlIHNjcmlwdCBpbiBhY3Rpb24NCnJtIC1yIHFjLw0KbHMgLWwNCmJhc2ggYW5hbHlzaXNfc2NyaXB0LnNoDQpgYGANCg0KVGhpcyB3aWxsIHJ1biB0aHJvdWdoIHRoZSBRQyBzdGVwcyBvZiB0aGUgcGlwZWxpbmUuIFRoZSBuZXh0IHN0YWdlIHdvdWxkIGJlIHRvIGFkZCB0aGUgc3RlcHMgZm9yIGBzYWxtb25gIHF1YW50aWZpY2F0aW9uLg0KDQpgYGB7YmFzaH0NCnNhbG1vbiBxdWFudCAtaSBpbmRleC9HUkNoMzhfc2FsbW9uIC0tbGliVHlwZSBBIC1yIGZhc3RxL0VSUjczMjkwMV9zdWIuZmFzdHEuZ3ogLW8gcXVhbnQvRVJSNzMyOTAxDQpgYGANCg0KDQpXZSBhbHNvIGhhdmUgYSBudW1iZXIgb2Ygb3B0aW9ucyBmb3IgaG93IHRvIHByb2NlZWQgd2l0aCBxdWFudGlmeWluZyB0aGUgcmVtYWluaW5nIHNhbXBsZXMuIFRoZSBzaW1wbGVzdCBhcHByb2FjaCBjb3B5LWFuZC1wYXN0ZSB0aGUgYHNhbG1vbiBxdWFudGAgbGluZSB3aXRoIGRpZmZlcmVudCBzYW1wbGUgbmFtZXMNCg0KYGBge2Jhc2h9DQpzYWxtb24gcXVhbnQgLWkgaW5kZXgvR1JDaDM4X3NhbG1vbiAtLWxpYlR5cGUgQSAtciBmYXN0cS9FUlI3MzI5MDJfc3ViLmZhc3RxLmd6IC1vIHF1YW50L0VSUjczMjkwMg0Kc2FsbW9uIHF1YW50IC1pIGluZGV4L0dSQ2gzOF9zYWxtb24gLS1saWJUeXBlIEEgLXIgZmFzdHEvRVJSNzMyOTAzX3N1Yi5mYXN0cS5neiAtbyBxdWFudC9FUlI3MzI5MDMNCiMjI2V0Yy4uLi4NCg0KYGBgDQoNClRoaXMgaXMgbm90IHBhcnRpY3VsYXJseSBzYXRpc2ZhY3RvcnkgYXMgaXQgaXMgcHJvbmUgdG8gdHlwbyBlcnJvcnMuIE91ciBwaXBlbGluZSBpcyBhbHJlYWR5IHF1aXRlIHNob3J0IGFuZCBpcyBydW5uaW5nIG9uIGEgc21hbGwgZGF0YXNldCwgYnV0IGFscmVhZHkgaXQgaXMgdGFraW5nIGEgbGl0dGxlIHdoaWxlIHRvIHJ1bi4gVGhlIHBpcGVsaW5lIGhhcyBiZWVuIHdyaXR0ZW4gaW4gYSBsaW5lYXIgZmFzaGlvbiwgc28gdGhhdCBlYWNoIHN0ZXAgbXVzdCBiZSBjb21wbGV0ZWQgaW4gb3JkZXIuIElmIG91ciBzYWxtb24gYWxpZ25tZW50IGNvZGUgbmVlZGVkIHRvIGJlIGNoYW5nZWQgd2Ugd291bGQgaGF2ZSB0byByZS1ydW4gYWxsIHRoZSBRQy4gVGhpcyBpcyBub3QgYSBodWdlIHByb2JsZW0gaGVyZSwgYnV0IGNvdWxkIGJlIHF1aXRlIGluZWZmaWNpZW50IGZvciBhIG1vcmUtcmVhbGlzdGljIGRhdGFzZXQuDQoNCg0KVGhlIG9yZGVyIGluIHdoaWNoIG91ciB2YXJpb3VzIHRvb2xzIGFyZSBiZWluZyBydW4gY2FuIGJlIHZpc3VhbGlzZWQgYXMgZm9sbG93czotIA0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpICAjIGZvciAlPiUgcGlwZXMNCmxpYnJhcnkoRGlhZ3JhbW1lUikNCmxpYnJhcnkoRGlhZ3JhbW1lUnN2ZykgICMgZm9yIGNvbnZlcnNpb24gdG8gc3ZnDQpsaWJyYXJ5KHJzdmcpICAjIGZvciBzYXZpbmcgc3ZnDQoNCg0KZyA8LSBnclZpeihkaWFncmFtID0gImRpZ3JhcGggZmxvd2NoYXJ0IHsNCiAgICAgICMgZGVmaW5lIG5vZGUgYWVzdGhldGljcw0KICAgICAgbm9kZSBbZm9udG5hbWUgPSBBcmlhbCwgc2hhcGUgPSBvdmFsLCBjb2xvciA9ICcjOUFEQkU4Jywgc3R5bGUgPSBmaWxsZWQsIGZvbnRzaXplID0yMF0gIA0KICAgICAgZWRnZSBbY29sb3IgPSAnIzQ0MDA5OScsIHBlbndpZHRoPTNdDQogICAgICANCiAgICAgIG5vZGUgW2ZpbGxjb2xvciA9ICcjOUFEQkU4J10NCiAgICAgIA0KICAgICAgdGFiMiBbbGFiZWwgPSAnZmFzdFFDJ10NCiAgICAgIHRhYjMgW2xhYmVsID0gJ211bHRpUUMnXQ0KICAgICAgdGFiNCBbbGFiZWwgPSAnc2FsbW9uIGFsaWduIFNhbXBsZSAxJ10NCiAgICAgIHRhYjUgW2xhYmVsID0gJ3NhbG1vbiBhbGlnbiBTYW1wbGUgMiddDQogICAgICB0YWI2IFtsYWJlbCA9ICdzYWxtb24gYWxpZ24gU2FtcGxlIC4uLi4nXQ0KICAgICAgDQogICAgICB0YWIyIC0+IHRhYjM7DQogICAgICB0YWIzIC0+IHRhYjQ7DQogICAgICB0YWI0IC0+IHRhYjU7DQogICAgICB0YWI1IC0+IHRhYjY7DQoNCn0iKQ0KDQpnDQpnICU+JSANCiAgZXhwb3J0X3N2ZygpICU+JSANCiAgY2hhclRvUmF3ICU+JSANCiAgcnN2Z19wbmcoImltYWdlcy9mbG93Y2hhcnQxLnBuZyIpDQoNCmBgYA0KIVtdKGltYWdlcy9mbG93Y2hhcnQxLnBuZykNCg0KQW4gYWx0ZXJuYXRpdmUgbWlnaHQgYmUgdG8gZW1wbG95IGEgKmZvciBsb29wKiwgd2hpY2ggeW91IG1pZ2h0IGhhdmUgW2NvbWUgYWNyb3NzIHByZXZpb3VzbHldKGh0dHBzOi8vZGF0YWNhcnBlbnRyeS5vcmcvc2hlbGwtZ2Vub21pY3MvMDQtcmVkaXJlY3Rpb24vaW5kZXguaHRtbCkuIFRoaXMgd2lsbCBtYWtlIG91ciBjb2RlIG1vcmUgZWZmaWNpZW50LCBidXQgc3RpbGwgbm90IGhlbHAgd2l0aCBydW5uaW5nIG91ciBhbmFseXNpcyBpbiBhbiBlZmZpY2llbnQgbWFubmVyLiANCg0KVGhlcmUgaXMgbm8gcGFydGljdWxhciByZWFzb24gd2h5IHdlIHNob3VsZCB3YWl0IGZvciBTYW1wbGUgMSB0byBiZSBwcm9jZXNzZWQgYmVmb3JlIFNhbXBsZSAyIGFuZCBTYW1wbGUgMiBiZWZvcmUgU2FtcGxlIDMgZXRjLiBJZGVhbGx5LCB3ZSB3b3VsZCB3YW50IHRvIHByb2Nlc3MgYWxsIG91ciBzYW1wbGVzIGF0IHRoZSBzYW1lIHRpbWUuIFRoaXMgaXMgYWNoaWV2YWJsZSBpcyB3ZSBydW4gb3VyIGFuYWx5c2lzIHdoZXJlIHdlIGhhdmUgc2V2ZXJhbCBwcm9jZXNzb3JzIGF2YWlsYWJsZTsgc3VjaCBhcyBhICpIUEMqIChIaWdoLVBlcmZvcm1hbmNlIENvbXB1dGVyKSBvciBjb21wdXRpbmcgY2x1c3Rlci4NCg0KYGBge3IgZWNobz1GQUxTRX0NCmcgPC0gZ3JWaXooZGlhZ3JhbSA9ICJkaWdyYXBoIGZsb3djaGFydCB7DQogICAgICAjIGRlZmluZSBub2RlIGFlc3RoZXRpY3MNCiAgICAgIG5vZGUgW2ZvbnRuYW1lID0gQXJpYWwsIHNoYXBlID0gb3ZhbCwgY29sb3IgPSAnIzlBREJFOCcsIHN0eWxlID0gZmlsbGVkLCBmb250c2l6ZSA9MjBdICANCiAgICAgIGVkZ2UgW2NvbG9yID0gJyM0NDAwOTknLCBwZW53aWR0aD0zXQ0KICAgICAgDQogICAgICBub2RlIFtmaWxsY29sb3IgPSAnIzlBREJFOCddDQogICAgICANCiAgICAgIHRhYjIgW2xhYmVsID0gJ2Zhc3RRQyddDQogICAgICB0YWIzIFtsYWJlbCA9ICdtdWx0aVFDJ10NCiAgICAgIHRhYjQgW2xhYmVsID0gJ3NhbG1vbiBhbGlnbiBTYW1wbGUgMSddDQogICAgICB0YWI1IFtsYWJlbCA9ICdzYWxtb24gYWxpZ24gU2FtcGxlIDInXQ0KICAgICAgdGFiNiBbbGFiZWwgPSAnc2FsbW9uIGFsaWduIFNhbXBsZSAuLi4uJ10NCiAgICAgIA0KICAgICAgdGFiMiAtPiB0YWIzOw0KICAgICAgdGFiMyAtPiB0YWI0Ow0KICAgICAgdGFiMyAtPiB0YWI1Ow0KICAgICAgdGFiMyAtPiB0YWI2Ow0KDQp9IikNCg0KZw0KZyAlPiUgDQogIGV4cG9ydF9zdmcoKSAlPiUgDQogIGNoYXJUb1JhdyAlPiUgDQogIHJzdmdfcG5nKCJpbWFnZXMvZmxvd2NoYXJ0Mi5wbmciKQ0KDQpgYGANCg0KIVtdKGltYWdlcy9mbG93Y2hhcnQyLnBuZykNCg0KIyBXaHkgZG8gd2UgbmVlZCBhIHdvcmtmbG93IG1hbmFnZXINCg0KQXMgd2UgaGF2ZSBkaXNjdXNzZWQsIHRoZXJlIGFyZSBhIG51bWJlciBvZiBvcHRpb25zIHRvIGV4dGVuZCBvdXIgcGlwZWxpbmUgdG8gbXVsdGlwbGUgc2FtcGxlcy4gVGhlc2UgcmVxdWlyZSBtb3JlIHByb2dyYW1taW5nIGtub3dsZWRnZSB0aGFuIHdlIG1pZ2h0IGJlIGNvbWZvcnRhYmxlIHdpdGguIFRoZXJlIGFyZSBhIGZldyBvdGhlciBpc3N1ZXMgd2l0aCB0aGUgc2NyaXB0IHRoYXQgd2UgaGF2ZSBjcmVhdGVkLg0KDQotIEFzIHBpcGVsaW5lIHN0ZXBzIGhhdmUgdG8gYmUgcmUtcnVuIGluIHNlcXVlbmNlOyBldmVuIGlmIHRoZSBpbml0aWFsIHBpcGVsaW5lIHN0ZXBzIHJhbiBzdWNlc3NmdWxseSB0aGV5IHdpbGwgc3RpbGwgYmUgcmUtcnVuIGV2ZXJ5IHRpbWUNCi0gVGhlIHBpcGVsaW5lIHdpbGwgbm90IG5lY2Nlc2FyaWx5IHJ1biBvbiBhbm90aGVyIGVudmlyb25tZW50IGFzIGl0IHdpbGwgYXNzdW1lIHRoYXQgdGhlIGBmYXN0cWNgLCBgbXVsdGlxY2AgYW5kIG90aGVyIHRvb2xzIGNhbiBiZSBmb3VuZC4NCg0KTmVpdGhlciBvZiB0aGVzZSBpc3N1ZXMgYXJlIGltcG9zc2libGUgdG8gc29sdmUsIGJ1dCB0aGlzIGlzbid0IGludGVuZGVkIHRvIGJlIGEgd29ya3Nob3Agb24gc29mdHdhcmUgZGV2ZWxvcG1lbnQgYmVzdC1wcmFjdGljZS4gDQoNCioqSW4gcmVhbGl0eSwgd2Ugd291bGQgcmVjb21tZW5kIHBlb3BsZSByZS11c2luZyBleGlzdGluZyBhbmFseXNpcyBwaXBlbGluZXMgcmF0aGVyIHRoYW4gd3JpdGluZyB0aGVpciBvd24qKi4gV2Ugd2lsbCBsb29rIGF0IGFuIGV4YW1wbGUgdXNpbmcgdGhlIG5leHRmbG93IHdvcmtmbG93IG1hbmFnZXIsIGFsdGhvdWdoIHNpbWlsYXIgdG9vbHMgc3VjaCBhcyBzbmFrZW1ha2UgYXJlIGFsc28gcG9wdWxhciBpbiBCaW9pbmZvcm1hdGljcw0KDQotIFtuZXh0Zmxvd10oaHR0cHM6Ly93d3cubmV4dGZsb3cuaW8vKQ0KLSBbc25ha2VtYWtlXShodHRwczovL3NuYWtlbWFrZS5yZWFkdGhlZG9jcy5pby9lbi9zdGFibGUvKQ0KDQojIyBSdW5uaW5nIGEgbmYuY29yZSBwaXBlbGluZQ0KDQpJbiBvdXIgb3BpbmlvbiwgbmV4dGZsb3cgaXMgcGFydGljdWxhciBhcHBlYWxpbmcgYXMgbWFueSBwb3B1bGFyIEJpb2luZm9ybWF0aWNzIHBpcGVsaW5lcyBoYXZlIGFscmVhZHkgYmVlbiB3cml0dGVuIHVzaW5nIG5leHRmbG93IGFuZCBoYXZlIGJlZW4gZGlzdHJpYnV0ZWQgYXMgcGFydCBvZiB0aGUgbmYuY29yZSBwcm9qZWN0DQoNCi0gW25mLmNvcmUgaG9tZXBhZ2VdKGh0dHBzOi8vbmYtY28ucmUvKQ0KDQpXZSB3aWxsIGJlIHNob3dpbmcgdGhlIFJOQS1zZXEgcGlwZWxpbmUgaW4gcGFydGljdWxhcg0KDQohW10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL25mLWNvcmUvcm5hc2VxLzMuOS9kb2NzL2ltYWdlcy9uZi1jb3JlLXJuYXNlcV9tZXRyb19tYXBfZ3JleS5wbmcpDQoNCi0gW25mLmNvcmUgUk5BLXNlcSBwaXBlbGluZV0oaHR0cHM6Ly9uZi1jby5yZS9ybmFzZXEpDQoNCg0KVGhlIG1pbmltdW0gbnVtYmVyIG9mIG9wdGlvbnMgcmVxdWlyZWQgdG8gcnVuIGFuIG5mLmNvcmUgcGlwZWxpbmUgc3VjaCBhcyBSTkEtc2VxIGFyZTotDQoNCmBgYHtiYXNofQ0KbmV4dGZsb3cgcnVuIG5mLWNvcmUvcm5hc2VxIC0taW5wdXQgc2FtcGxlc2hlZXQuY3N2IC0tb3V0ZGlyIDxPVVRESVI+IC0tZ2Vub21lIEdSQ2gzNyAtcHJvZmlsZSBkb2NrZXINCmBgYA0KDQp3aGVyZTotDQoNCi0gYG5mLWNvcmUvcm5hc2VxYCBpcyBhIHJlZmVyZW5jZSB0byB0aGUgcGlwZWxpbmUgdGhhdCB3ZSB3YW50IHRvIHJ1bg0KLSBgLS1pbnB1dGAgaXMgdGhlIGxvY2F0aW9uIG9mIGEgc2FtcGxlc2hlZXQgZGVmaW5pbmcgdGhlIHJhdyBkYXRhIHRvIGJlIHByb2Nlc3NlZA0KLSBgLS1vdXRkaXJgIGlzIGEgZGlyZWN0b3J5IHRoYXQgd2lsbCBjb250YWluIHRoZSBmaW5hbCByZXN1bHRzIG9mIHRoZSBwaXBlbGluZQ0KLSBgZ2Vub21lYCBpcyB0aGUgc2hvcnRoYW5kIG5hbWUgZm9yIHRoZSBnZW5vbWUgdG8gYmUgdXNlZCBhcyBhIHJlZmVyZW5jZQ0KLSBgcHJvZmlsZWAgZGVmaW5lcyBob3cgc29mdHdhcmUgaW5jbHVkZWQgaW4gdGhlIHBpcGVsaW5lIGlzIHRvIGJlIGRvd25sb2FkZWQvaW5zdGFsbGVkICgqKlNlZSBsYXRlcioqKQ0KDQpXZSBoYXZlIGN1c3RvbWlzZWQgc29tZSBvZiB0aGUgb3B0aW9ucyBvZiB0aGUgcGlwZWxpbmUgc28gcnVuIGEgcmVkdWNlZCBudW1iZXIgb2Ygc3RlcHMgYXJlIHJ1biBmb3IgdGhlIHdvcmtzaG9wLCBhbmQgdXNpbmcgYSBjdXN0b20gZ2Vub21lIGNvbnRhaW5pbmcgYSBzaW5nbGUgY2hyb21vc29tZS4NCg0KYGBge2Jhc2h9DQpjYXQgc2NyaXB0cy9ydW5fbmV4dGZsb3cuc2gNCmBgYA0KDQpUaGUgcGFydGljdWxhciBzdGVwcyB0aGF0IHdlIGhhdmUgbW9kaWZpZWQgYXJlIGFzIGZvbGxvd3M6LQ0KDQpgYGANCiMjdXNlIGEgcGFydGljdWxhciBwaXBlbGluZSB2ZXJzaW9uDQotciAzLjguMSBcDQojIyBTa2lwIGFsaWduaW5nIHRvIHRoZSB3aG9sZSBnZW5vbWUNCi0tc2tpcF9hbGlnbm1lbnQgXA0KIyMgU2tpcCB0cmltbWluZyByZWFkIHNlcXVlbmNlcw0KLS1za2lwX3RyaW1taW5nIFwNCiMjIHVzZSB0aGUgc2FsbW9uIHF1YW50aWZpY2F0aW9uIHRvb2wNCi0tcHNldWRvX2FsaWduZXIgc2FsbW9uIFwNCiMjIFVzZSBvdXIgb3duIHNldCBvZiByZWZlcmVuY2VzIHJhdGhlciB0aGFuIGRvd25sb2FkaW5nDQotLWZhc3RhIHJlZl9kYXRhL0hvbW9fc2FwaWVucy5HUkNoMzguZG5hX3JtLmNocm9tb3NvbWUuMjIuZmEgXA0KLS10cmFuc2NyaXB0X2Zhc3RhIHJlZl9kYXRhL0hvbW9fc2FwaWVucy5HUkNoMzguY2RuYS5jaHIyMi5mYSBcDQotLWd0ZiByZWZfZGF0YS9Ib21vX3NhcGllbnMuR1JDaDM4LjEwOC5jaHIyMi5ndGYgXA0KIyMgcmVzdHJpY3QgdGhlIGFtb3VudCBvZiBtZW1vcnkgcmVxdWVzdGVkIFwNCi0tbWF4X21lbW9yeSAyR0INCmBgYA0KDQpUaGUgZmlsZXMgdGhhdCB3ZSB3YW50IHRvIGFuYWx5c2UgYXJlIGRlZmluZWQgaW4gYSBzYW1wbGUgc2hlZXQuIFRoZSBmb3JtYXQgb2YgdGhlIHNoZWV0IGlzIGNoZWNrZWQgYnkgdGhlIHBpcGVsaW5lIGFzIG9uZSBvZiB0aGUgZmlyc3Qgc3RlcHMuIFRoZSBjb2x1bW4gbmFtZXMgaGF2ZSB0byBtYXRjaCAqZXhhY3RseSogd2hhdCB0aGUgcGlwZWxpbmUgZXhwZWN0cy4NCg0KYGBge2Jhc2h9DQpjYXQgbmZfc2FtcGxlc2hlZXQuY3N2DQpgYGANCg0KDQpCZWZvcmUgd2UgY2FuIHJ1biB0aGUgcGlwZWxpbmUgd2UgbmVlZCB0byBtb3ZlIChvciBjb3B5KSB0aGUgc2NyaXB0IHRvIHRoZSB3b3JraW5nIGZvbGRlcg0KDQpgYGB7YmFzaH0NCmNwIHNjcmlwdHMvcnVuX25leHRmbG93LnNoIC9ob21lL2RjdXNlci9ybmFzZXFfZGF0YS8NCmJhc2ggcnVuX25leHRmbG93LnNoDQpgYGANCg0KDQpUaGUgb3V0cHV0IGZyb20gdGhlIHdvcmtmbG93IHdpbGwgYmUgd3JpdHRlbiB0byBhIGRpcmVjdG9yeSBgbmZfcmVzdWx0c2AsIHdoaWNoIGRvZXNuJ3QgbmVlZCB0byBleGlzdCBiZWZvcmUgdGhlIHBpcGVsaW5lIGhhcyBiZWVuIHJ1bi4gWW91IHNob3VsZCBhbHNvIHNlZSB0aGF0IGEgYHdvcmtgIGRpcmVjdG9yeSBpcyBjcmVhdGVkLg0KDQoNCiMjIEV4ZXJjaXNlDQo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+DQoqKkV4ZXJjaXNlKioNClJ1biB0aGUgc2NyaXB0IGBydW5fbmV4dGZsb3cuc2hgIChhcm91bmQgfjUgdG8gMTAgbWludXRlcykgYW5kIGFmdGVyd2FyZHMgbG9vayBhdCB0aGUgb3V0cHV0IGluIHRoZSBgbmZfcmVzdWx0c2AgZm9sZGVyIGFuZCBmYW1pbGlhcmlzZSB5b3Vyc2VsZiB3aXRoIHRoZSBvdXRwdXRzLiBXaGF0IGV4dHJhIHN0ZXBzIGhhdmUgYmVlbiBwZXJmb3JtZWQgaW4gYWRkaXRpb24gdG8gdGhlIHNjcmlwdCB0aGF0IHdlIGNyZWF0ZWQgZWFybGllcj8NCjwvZGl2Pg0KDQojIyBXaGF0IHNvZnR3YXJlIGRvZXMgbmV4dGZsb3cgdXNlIGluIGl0J3MgcGlwZWxpbmU/DQoNCllvdSBzaG91bGQgbm90aWNlIHRoYXQgdGhlIG5mLmNvcmUgcGlwZWxpbmUgaGFzIHByb2R1Y2VkIHF1YW50aWZpY2F0aW9uIGZpbGVzIGZvciBlYWNoIHNhbXBsZSwgYW5kIGFsc28gY29tYmluZWQgdGhlIGBzYWxtb25gIG91dHB1dHMgaW50byBhIHNpbmdsZSBmaWxlLiBJdCBhbHNvIHJ1biBzb21lIFFDIHBsb3RzIGZyb20gdGhlIERFU2VxMiBSIHBhY2thZ2UuIEhvd2V2ZXIsIFIgaXMgbm90IHBhcnQgb2Ygb3VyIHNvZnR3YXJlIGVudmlyb25tZW50LiBXZSBjYW4gc2VlIHRoaXMgYnkgcnVubmluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIHdoaWNoIHdvdWxkIHVzdWFsbHkgcmVwb3J0IHRoZSBwYXRoIHRoYXQgUiBpcyBsb2NhdGVkIGF0LCBvciBydW4gdGhlIGNvbW1hbmQtbGluZSBSLg0KDQoNCmBgYA0Kd2hpY2ggUg0KUg0KYGBgDQoNCm5leHRmbG93IGhhcyBpdCdzIG93biB3YXkgb2YgaW5zdGFsbGluZyBhbmQgcnVubmluZyBzb2Z0d2FyZSB3aGljaCBkb2VzIG5vdCBkZXBlbmQgb24gdGhlIG9wZXJhdGluZyBzeXN0ZW0gdGhhdCBpcyBiZWluZyB1c2VkIHRvIHJ1biB0aGUgcGlwZWxpbmUuIFRoaXMgaXMgc3BlY2lmaWVkIGJ5IHRoaXMgcGFydCBvZiB0aGUgcnVuIHNjcmlwdC4NCg0KLSBbbmYuY29yZSBwcm9maWxlIG9wdGlvbnNdKGh0dHBzOi8vbmYtY28ucmUvcm5hc2VxLzMuOS91c2FnZSNwcm9maWxlKQ0KDQpgYGANCi1wcm9maWxlIHNpbmd1bGFyaXR5IA0KYGBgDQoNCkluIGZhY3QsIHdlIGNvdWxkIGhhdmUgcnVuIHRoZSBwaXBlbGluZSBpZiB3ZSBkaWRuJ3QgaGF2ZSBgc2FsbW9uYCBhbmQgYGZhc3RxY2AgaW5zdGFsbGVkLiBUaGUgaW1wbGljYXRpb24gb2YgdGhpcyBiZWluZyB0aGF0IHlvdSBjYW4gcmUtcnVuIHRoZSBwaXBlbGluZSBvbiB5b3VyIG93biBtYWNoaW5lIG9yIEhQQyBlbnZpcm9ubWVudCB3aXRoIG1pbmltYWwgc29mdHdhcmUgaW5zdGFsbGF0aW9uLiBUaGUgb25seSBkZXBlbmRhbmNpZXMgYXJlIGBuZXh0Zmxvd2AgaXRzZWxmICh3aGljaCBpbiB0dXJuIHJlcXVpcmVzIGBqYXZhYCkgYW5kIHNvbWUgKmNvbnRhaW5lcmlzYXRpb24qIG9yIHBhY2thZ2UgbWFuYWdlbWVudCBzb2Z0d2FyZSBzdWNoIGFzIGBzaW5ndWxhcml0eWAsIGBkb2NrZXJgIG9yIGBjb25kYWAuIFRoaXMgaXMgYSBzaWduaWZpY2FudGx5IGVhc2llciByZXF1aXJlbWVudCB0byBmdWxmaWxsIHRoYW4gaGF2aW5nIHRvIGluc3RhbGwgZWFjaCBwaWVjZSBvZiBzb2Z0d2FyZSBpbmRpdmlkdWFsbHkuDQoNCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4NCklmIHlvdSBhcmUgdXNpbmcgYSBIUEMgZW52aXJvbm1lbnQgeW91IHdpbGwgcHJvYmFibHkgd2FudCB0byBrZWVwIHRoZSBzaW5ndWxhcml0eSBvcHRpb24gaW4gdGhlIHByb2ZpbGUuIA0KPC9kaXY+DQoNCiMjIFJlLXJ1bm5pbmcgdGhlIHBpcGVsaW5lDQoNCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4NCioqRXhlcmNpc2UqKg0KVGhlcmUgYXJlIHR3byBmYXN0cSBmaWxlcyBpbiBvdXIgYGZhc3RxYCBmb2xkZXIgdGhhdCBoYXZlIG5vdCB5ZXQgYmVlbiBhbmFseXNlZDsgYEVSUjczMjkwOGAgYW5kIGBFUlI3MzI5MDlgLiBNYWtlIGEgY29weSBvZiB0aGUgc2FtcGxlc2hlZXQgYG5mX3NhbW5wbGVzaGVldC5jc3ZgIGFuZCB1c2UgdGhlIGBuYW5vYCBlZGl0b3IgdG8gbW9kaWZ5IHRoZSBuZXcgc2FtcGxlc2hlZXQgdG8gaW5jbHVkZSBgRVJSNzMyOTA4YC4gTm93IGVkaXQgdGhlIHJ1biBzY3JpcHQgdG8gdXNlIHRoaXMgbmV3IHNhbXBsZXNoZWV0IGFuZCByZS1ydW4gdGhlIHBpcGVsaW5lLiBXaGF0IGRvIHlvdSBub3RpY2UgYWJvdXQgdGhlIHRpbWUgdGFrZW4gdG8gcnVuIHRoZSBwaXBlbGluZT8NCjwvZGl2Pg0KDQoNClRoZSBwaXBlbGluZSBzaG91bGQgYmUgcXVpY2tlciB0aGlzIHRpbWUgYXJvdW5kLiBUaGlzIGlzIGJlY2F1c2Ugd2UgaGFkIGFsbCB0aGUgc29mdHdhcmUgZG93bmxvYWRlZCBhbmQgaW5zdGFsbGVkIGZyb20gdGhlIHByZXZpb3VzIHJ1bi4gSG93ZXZlciwgaXQgc3RpbGwgcmUtYW5hbHlzZWQgdGhlIGZpcnN0IHNldmVuIHNhbXBsZXMgZnJvbSB0aGUgc2FtcGxlc2hlZXQgLSB3aGljaCBpcyBub3QgaWRlYWwuIElmIHdlIHdhbnQgYSByZXBvcnQgb24gd2hhdCBhbmFseXNlcyBoYXZlIGJlZW4gcGVyZm9ybWVkIGFuZCBob3cgbG9uZyB0aGV5IHRvb2sgd2UgY2FuIGxvb2sgYXQgYSByZXBvcnQgZnJvbSB0aGUgY29tbWFuZC1saW5lDQoNCmBgYA0KIyMgWW91IHdpbGwgaGF2ZSB0byB1c2UgYXV0by1jb21wbGV0ZSBhbmQgY2hvb3NlIHRoZSBtb3N0LXJlY2VudCByZXBvcnQNCmNhdCBuZl9yZXN1bHRzL3BpcGVsaW5lL2V4ZWN1dGlvbl90cmFjZQ0KYGBgDQpUaGVyZSBpcyBhbHNvIGEgSFRNTCB2ZXJzaW9uIG9mIHRoZSByZXBvcnQgdGhhdCB3ZSBjYW4gdmlldyB0aHJvdWdoIHRoZSBmaWxlIHN5c3RlbS4NCg0KIyMgUmVzdW1pbmcgYSBwaXBlbGluZQ0KDQpJZGVhbGx5LCB3ZSB3b3VsZCBsaWtlIHRoZSBwaXBlbGluZSB0byBkZXRlY3Qgd2hhdCBqb2JzIGhhdmUgYmVlbiBydW4gc3VjY2Vzc2Z1bGx5IGFuZCBub3QgcmVwZWF0IHRob3NlIGpvYnMuIFRoZSBvcHRpb24gYC1yZXN1bWVgIGluIG5leHRmbG93IHdpbGwgYWxsb3cgeW91IHRvIGRvIHRoaXMuIChub3RlIHRoZSBzaW5nbGUgYC1gIHdoZW4gYWRkaW5nIHRoaXMgb3B0aW9uKS4gTW9kaWZ5IHRoZSBuZXh0ZmxvdyBzY3JpcHQgd2l0aCBgbmFub2AgdG8gY29udGFpbiB0aGUgZm9sbG93aW5nIGxpbmVzIGF0IHRoZSB0b3ANCg0KYGBgDQpuZXh0ZmxvdyBydW4gbmYtY29yZS9ybmFzZXEgLXByb2ZpbGUgc2luZ3VsYXJpdHkgXA0KLXJlc3VtZSBcDQotciAzLjguMSBcDQouLi4uDQoNCmBgYA0KDQpXZSBjYW4gbm93IGVkaXQgdGhlIHNhbXBsZXNoZWV0IGFnYWluIHRvIHByb2Nlc3MgdGhlIHNhbXBsZSBgRVJSNzMyOTA5YCANCg0KYGBgDQpzYW1wbGUsZmFzdHFfMSxmYXN0cV8yLHN0cmFuZGVkbmVzcw0KRVJSNzMyOTAxLGZhc3RxL0VSUjczMjkwMV9zdWIuZmFzdHEuZ3osLHVuc3RyYW5kZWQNCkVSUjczMjkwMixmYXN0cS9FUlI3MzI5MDJfc3ViLmZhc3RxLmd6LCx1bnN0cmFuZGVkDQpFUlI3MzI5MDMsZmFzdHEvRVJSNzMyOTAzX3N1Yi5mYXN0cS5neiwsdW5zdHJhbmRlZA0KRVJSNzMyOTA0LGZhc3RxL0VSUjczMjkwNF9zdWIuZmFzdHEuZ3osLHVuc3RyYW5kZWQNCkVSUjczMjkwNSxmYXN0cS9FUlI3MzI5MDVfc3ViLmZhc3RxLmd6LCx1bnN0cmFuZGVkDQpFUlI3MzI5MDYsZmFzdHEvRVJSNzMyOTA2X3N1Yi5mYXN0cS5neiwsdW5zdHJhbmRlZA0KRVJSNzMyOTA3LGZhc3RxL0VSUjczMjkwN19zdWIuZmFzdHEuZ3osLHVuc3RyYW5kZWQNCkVSUjczMjkwOCxmYXN0cS9FUlI3MzI5MDhfc3ViLmZhc3RxLmd6LCx1bnN0cmFuZGVkDQpFUlI3MzI5MDksZmFzdHEvRVJSNzMyOTA5X3N1Yi5mYXN0cS5neiwsdW5zdHJhbmRlZA0KYGBgDQoNCg0KYW5kIHJlLXJ1biB0aGUgcGlwZWxpbmUuIA0KDQpgYGANCmJhc2ggcnVuX25leHRmbG93LnNoDQpgYGANCg0KVGhlIHBpcGVsaW5lIG5vdyBydW5zIHNpZ25pZmljYW50bHkgcXVpY2tlciBiZWNhdXNlIGl0IGRldGVjdHMgdGhhdCBhbGwgdGhlIGFuYWx5c2VzIGhhdmUgYmVlbiBjb21wbGV0ZWQgYW5kICpjYWNoZWQqLiBJZiB3ZSBjaGVjayB0aGUgYGV4ZWN1dGlvbl90cmFjZWAgdGV4dCBmaWxlIGZvciB0aGlzIGxhdGVzdCBydW4gaXQgc2hvdWxkIHNheSB0aGF0IG1vc3QgdGFza3Mgd2VyZSBgQ0FDSEVEYCAtIG1lYW5pbmcgdGhhdCB0aGUgcmVzdWx0cyBmcm9tIHRoZSBwcmV2aW91cyBhbmFseXNpcyB3YXMgdXNlZC4NCg==