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.
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
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.
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==