1. Basic Unix

UNIX is an operating system that was developed in the 1960s and it has been the base of widely used operating systems such as Linux and Mac OS. As the data coming out from biological outputs growing exponentially through time, Knowing the UNIX basics have become essential to do bioinformatics. Here there is a list of the most used commands:

Commad Use Examples
cd Tove between directories cd FASTQ (to enter a folder)
cd .. To change to the parent of the current directory
cd - To go to previous directory
pwd Where am I?
ls List files ls (on this directory); ls FASTQ (list files inside FASTQ)
mkdir make new directory mkdir my_folder
mv move a file or directory mv file my_folder/ (move file to a folder)
cp copy file cp my_folder/* new_folder/ (copy all files of al folder)
rm Remove a file or folder rm file; rm -rf folder
less Displays the contents of a file less file.txt (press q to quit)
head Displays the first ten lines of a file
tail Displays the last ten lines of a file
cat Print file content cat file1 file2 > file.total (merge files)
zcat Print file content of compresed files zcat SRR7889597.fastq.gz
gzcat mac OSx version of zcat gzcat SRR7889597.fastq.gz
wc word count wc -l file (count lines inside of a file)
grep Pattern matching grep patern file (get lines that match the patern)
cut extract columns from files cut -f 2 file (extract second column)
sort sort file by column sort -nr -k 2 file (sort file by second column)

Exercise 1.0.1

On the course directory, create a folder named test_folder and copy the smallest file inside FASTQ/ folder. Then change the name of the folder that you just create to to_delete and finally remove this folder.

1.2 Efficient use of the terminal

To accelerate our interaction with the terminal, we can use the TAB key:

For instance, if you are at course folder and you write ls Gene_ and then press TAB key once, it will complete the command to ls Gene_annotation. But if you just write ls G and press TAB two times, it will show you the two options (Gene_annotation and Genome).

Another powerful thing is to pipe (|) the output of one command into and another command. For example you can do:

ls FASTQ/*.fastq.gz | wc -l

To count all the FASTQ files that are stored inside of FASTQ/ folder.

Finally, if you want to know more, every command has its own options, you can see the entire description of a command using man. For instance, use man to know more about head typing on the command line:

man head

Exercise 1.2.1

Read the manual of head command. Then use it to extract the first 100 lines of Gene_annotation/dm6.Ensembl.genes.gtf and count all the lines that have CDS in the third column.

2. Input data format

In order to quantify mRNA by RNA-seq analysis, different files are required which as a different format. RNA-Seq reads are normally formatted as FASTQ, SRA or CRAM. To process this data, the most common initial step is to map the reads to the genome, which is often formatted as FASTA. Finally, to quantify gene expression, we need to know which part of the genome encodes for genes. This information can be found at gene annotation files, which can be formatted as GTF or BED files. Explore the different hyperlinks provided for detailed information about the different file formats.

2.1 FASTA format

FASTA files contain nucleotide or amino acid sequence information. The data is composed of identifiers, which starts with > and nucleotide o amino acid sequences. Hundred of thousands of genomes coming from different species are available in public repositories as FASTA files. It is important to notice that a given species can have different genomic assemblies. For instance, a popular assembly human genome assembly is hg19 that was published in February 2009, and as a huge amount of data is available for that assembly some researchers keep doing their analyses with that version. But other recent analyses have started to use hg38 instead, which was published in December 2013 and represent a more curated version of the human genome.

For today’s practice, we are going to work with a smaller genome. We already have downloaded for you the dm6 assembly of D. melanogaster’s genome, an invertebrate organism widely used as a model organism for genetic and behavioral studies. The genome file is at course material folder at Genome/dm6.fa.

Exercise 2.1.1

Count the number of sequences present at dm6.fa (hint: count lines that start with >, but as this is a special UNIX character, refer to it as \>. Which command can be used for pattern matching and count lines?).

If each sequence is related to a chromosome, why do we find so many sequences? How many main scaffolds can you see?

Exercise 2.1.2

Can you calculate the exact number of nucleotides that this D. melanogaster assembly is made of? (hint: explore additional options of grep and wc)

2.2 FASTQ format

RNA-seq reads often come as FASTQ, which is an extension of FASTA file format. Every read is represented by four lines:

  • First line: start with @ and correspond to a read identifier.
  • Second line: read nucleotide sequence.
  • Third line: Start with + and it can be followed by additional information, but often it is just +.
  • Fourth line: Phred quality score represent the reliability of a base call (as higher the better). It is encoded as ASCII characters. It can have different encodings, such as Sanger, Solexa, Illumina 1.3+ or Illumina 1.8+. Please check FASTQ format description for more details.

Exercise 2.2.1

Which of the fastq files found at FASTQ/ has the highest and the lowest amount of reads? How many reads do they have? What is the read length?

2.3 Gene annotation

Efforts have been made to identify the genomic regions that encode for genes. This information can be stored as BED or GTF files (among other formats). BED files store information about genomic coordinates. Each row of a BED file represents a genomic interval, in the case of gene annotation file, it represents the start and end of a transcript. Exons coordinates are encoded as blocks, where the blockStarts column is a list of all exon starts relative to the transcript starts and blockSizes column contain all the exon sizes. Please read BED description format for more information.

GTF files instead are gene-centric annotations, in which a gene can be described by multiple rows. Each row contains the coordinates and additional information of a particular gene feature. Please check the full GTF description

Exercise 2.3.1

Get D. melanogaster gene annotation files for dm6 assembly in from UCSC Table Browser. Choosing the following values:

  • clade: Insect.
  • genome: D. melanogaster.
  • assembly: Aug. 2014 (BDGP Release 6 + ISO1 MT/dm6).
  • group: Gene and Gene Predictions.
  • track: Ensembl Genes.
  • table: ensGene.

And get the annotation files as BED and GTF (choosing the right output formats). Save the BED file as dm6.Ensembl.genes.bed12 and GTF as dm6.Ensembl.genes.gtf at Gene_annotation folder.

Exercise 2.3.2

Process dm6.Ensembl.genes.bed12 to find the five transcripts with the highest number of exons. (hint use sort, look for -r and -k flags at its manual).

Exercise 2.3.3

Process dm6.Ensembl.genes.gtf to find the total number of genes

3. Quality control before alingment

3.1 FASTQC

To perform a QC of the Fastq files we can use FastQC. We can access to a basic manual of this tool by typing on fastqc --help on terminal.

Exercise 3.1.1

Create a directory named QC. Read fastqc help manual to run it for FASTQ/SRR7889585.fastq.gz and store the results at the newly created folder (QC). To speed it up, use 7 threads (which it will allow our machines to use 7 processors). You will get an html file as output; Open it with an internet browser (Chrome, Firefox, etc) and answer the following questions:

  1. What is the read length?
  2. Does the quality score very through the read length?
  3. How is the data’s quality?

3.2 Metadata analysis on R-studio

The fastq.gz files that we have at /FASTQ correspond to RNA-seq data extracted from D. melanogaster brain samples. The data corresponding to a full study published last year on Cell Reports and the full study was published in SRA archive.

Exercise 3.2.1

Check the study runs and compare with the file names from FASTQ/ folder. Which tissues the samples were extracted from? Select the Runs that correspond to the files that we have and download the RunInfo Table corresponding to these tables and save it as SraRunTable.txt on the course folder.

Exercise 3.2.2

Open R-studio and open the Day1.Rmd file that is located at the course folder. This is an R-notebook file, that has all the code used to generate this document. But, now when you open this file you will be able to run chunks of code interactively inside R-studio. Run the following chunk by clicking at the green play button.


library(data.table)

metadata <- fread("./SraRunTable.txt") #Please be sure to place the file under ./Course_Materials/00_Reproducible_RNA-Seq_Processing/

metadata

By running this code, we imported the metadata as a data.table object, which corresponds to a very convenient structure to manipulate data in R. For more information visit this link. We can now easily manipulate the data! For example, we can filter the metadata rows, so we only see the ones corresponding to “central neurons”

metadata[source_name=="central neurons" , ]

We can also get explore if we have a lowly sequenced sample filtering by the MBases values

metadata[MBases < 100 , ]

Or get the average Mega bases across this sample


metadata[  , mean(MBases) ]

To explore this visually, we can plot with ggplot2. This is one of the most popular packages to visualize data. A basic code to visualize the Mbases distribution can be run as:

library(ggplot2)

ggplot(data = metadata) +  # Frist layer - Data input
  geom_bar(aes(x=Run, y=MBases), stat = "identity")   # second layer - type of plot and axis

We can rotate the text to make it more visible


ggplot(data=metadata) +
  geom_bar(aes(x=Run, y=MBases, ), stat = "identity") + 
  theme(axis.text.x = element_text(angle = 45))  # Third layer -  visual configuration

inside aes( ) are the variables and we can also define a variable as the colour:

ggplot(data=metadata) +
  geom_bar(aes(x=Run, y=MBases, colour=source_name), stat = "identity") + 
  theme(axis.text.x = element_text(angle = 45))

But for this particular type of graph, filling the bar with one colour is more suitable

ggplot(data=metadata) +
  geom_bar(aes(x=Run, y=MBases, fill=source_name), stat = "identity") + 
  theme(axis.text.x = element_text(angle = 45))

From this plot, we can clearly see that one of the samples that were taken from central neurons is much smaller than the rest. This factor needs to be considered at the time we analyze the samples, as this poorly sequenced sample might have lead to less accurate mRNA quantification and it might need to be removed for quantitative analyses.

4. Mapping reads to the genome and getting raw counts

After we have a diagnosis of the data quality, we can start to analyse the data. Usually, the first step into the analysis requires mapping the RNA-seq reads to the genome. There are numerous tools to do perform short read alignment and the choice of it should be carefully made according to the analysis goals and requirements. Hisat2 is a very fastq tool that has been shown to have a good performance on published benchmarks.

4.1 Indexing the genome for Hisat2

To start mapping RNA-seq reads to the genome, we need to index the genome. The command to do this is hisat2-build.

hisat2-build --help

Exercise 4.1.1.

Go to Course_Materials/00_Reproducible_RNA-Seq_Processing directory using cd (This is going to be our base directory to solve all the following exercises). Do ls to list the directories and create a new folder called Index inside Genome folder. Then run the following command:

hisat2-build -p 7 Genome/dm6.fa Genome/Index/dm6

This command will use the genome (located at Genome/dm6.fa) and it will generate the index files on Genome/Index/. All the files will start with dm6 prefix. Take a look at hisat2-build help,

  1. Why do we use -p 7 and what is the maximun=m value we should use on these machines?
  2. How many files are created on this process?

4.2 Hisat2

To map the reads to the genome we need to run hisat2. Take a quick look to hisat2’s description

hisat2 --help

Exercise 4.2.1

Run hisat2 for the smallest file, located at FASTQ folder using 7 proccesors. Save the results inside a folder named hisat2 (create it before using mkdir) and save the results using the right extension (.sam). hint: use hisat --help and look for -p -U -x flags and tt the end of the command use > to save the resuls to a file.

4.3 Samtools

The output of hisat2 is a SAM file, which is a plain text file that has the alignment information. As they tend to be very large, is not a good idea to store them forever. BAM files are the binary form of SAM files, which means their information is much more compressed, which make them easier to store them, but also enable quicker processing of their data. We can transform from SAM to BAM following this general formula:

samtools view -b sample.sam > sample.bam

Where sample is just a generic name to reffer to our samples.

Exercise 4.3.1

Get a BAM file from the SAM file you just generated.

4.4 Visualise BAM files

BAMs can be used for different downstream analyses. But most of them require the BAM files to be sorted. When a BAM file is sorted, the alignments are ordered by the position they map to. BAM files can be sorted using samtools sort and following this formula:

samtools sort sample.bam -o sample.sorted.bam

To visualise a BAM’s alignment, we need to index it first. Which can be made with samtools index:

samtools index sample.sorted.bam

Exercise 4.4.1

  1. Sort and Index your BAM file.
  2. Open IGV, load D. melanogaster’s genome (dm6) and load the sorted bamfile.

IGV help

Load the reference genome. On the top menu bar find the genomes dropdown (top-left) and then find D. melanogaster assembly dm6. You might need to click on ‘More…’ to see a list of available genomes.

Load the BAM file. On the top menu bar go to ‘File –> Load from File…’ and select the sorted BAM file you have created on the previous step.

Zoom in into a particular gene to see the read alignments.

4.5 FeatureCounts

To count the number of reads we will use FeatureCounts, which uses a gene annotation file (GTF) to process the genomic intervals of every gene and count all the reads that map to the exonic regions.

Exercise 4.2.2

Run featureCounts providing the transcript annotation and the same file you produced with hisat2. To do this, read the featureCount help manual and find the rights flags run featureCounts (hint: read the Required arguments section).

Exercise 4.2.2

Find the gene with the highest number of counts. Can we say this gene is the one with higher expression levels on this sample?

5. Introduction to reporducible bioinformatics

Until this point, we have introduced every command mannually in the terminal. Please check how many files do we have at FASTQ/. Do you think you can get the raw counts from all the samples without having errors? what about processing hundred of samples, is it reasonable to do it manually?

The answer is clearly no. We need systematic ways to process data to avoid errors and enable reproducibility in our analyses. This is why we are going to use a workflow manager to execute the remaining steps to get the raw counts from our samples.

Snakemake is currently one most popular workflow managers to work with bioinformatic software. To install snakemake please the following command:

conda create -n snakemake_env snakemake

By doing this we created a virtual environment, called snakemake_env that has latest version of snakemake and our it dependencies installed. Now to activate this environment, write:

conda activate snakemake_env

As we already wrote a snakemake pipeline for you, we are going to demonstrate you some of its properties and how to use it. To see the code from the this pipeline, Use Atom (or any text editor) to open the Snakefile. Can you recognize some of the steps that we have done already?. Every rulerepresent a step of the analysis. For example:


rule hisat2_Genome_index:
    input:
        "Genome/dm6.fa"
    output:
        "Genome/Index/dm6.1.ht2"
    threads: 7
    conda:
        "envs/core.yaml"
    log:
        "logs/hisat2_Genome_index.log"
    shell:
        "hisat2-build -p {threads} {input} Genome/Index/dm6 2> {log}"

This code correspond to the indexing step, in which it takes Genome/dm6.fa as input and Genome/Index/dm6.1.ht2 as output. The rules have several key words by which different parts of the command are declared: * input: set of intput files, in this case just “Genome/dm6.fa” * output: set of output files. In this case, more output files are created, but they do not have to be pointed by the commands, we can just refer to one of the files that is created. Snakemake will check if this file is successfully created after the process is finished. * threads: number of processors * conda: the virtual environment in which the process will be run. * log : file that store anything that hisat2 outputs while is creating the index. * shell : This is the formula to create the shell command given all the parameters described above.

The indexing rule is directly connected to the following mapping rule:


rule hisat2_to_Genome:
    input:
        fastq = "FASTQ/{sample}.fastq.gz",
        genome = "Genome/Index/dm6.1.ht2"
    output:
        temp("hisat2/{sample}.sam")   # Temporary output
    threads: 3
    conda:
        "envs/core.yaml"
    log:
        "logs/hisat2_to_Genome.{sample}.log"
    shell:
        "hisat2 -p 3 -U {input.fastq} -x  Genome/Index/dm6  > {output} 2> {log}"

This rule takes fastq files as input and also the genome index files. All the sample names were obtained fromNCBI_accession_list.txt file, which contains the SRA accession codes corresponding to all the samples that we are analysing. Inside the rule {sample} takes the value of every accession code, and allow snakemake to generate all the mapping commands for every sample. As we here set threads as 3, every mapping process will use 3 processors, which means that 2 mapping processes can be run in parallel when 7 cores are provided.

The next rule bamstats take every SAM file and transform it to BAM, but also the BAM file is sorted and indexed at the same time:


rule samTobam:
    input:
        "hisat2/{sample}.sam"
    output:
        "hisat2/{sample}.sorted.bam"
    conda:
        "envs/core.yaml"
    shell:
        "samtools view -b  {input}  | samtools sort - -o {output} && samtools index {output} "

Because SAM files were produced as temporary files (temp("hisat2/{sample}.sam") ), as soon as samTobam finishes, SAM files are deleted. This optimizes the disk space, which is important when a large number of samples are processed.

Finally, all these steps converge at:

rule featureCounts:
    input:
        gtf = "Gene_annotation/dm6.Ensembl.genes.gtf",
        bam = expand("hisat2/{sample}.sorted.bam", sample=SAMPLES)
    output:
        "featureCounts/total_samples.gene_count.txt"
    threads: 1
    conda:
        "envs/core.yaml"
    log:
        "logs/featureCounts.total.log"
    shell:
        "featureCounts -a {input.gtf} -o {output} {input.bam} 2> {log}"

Where {input.gtf} list all the sorted bam that we generated. Notice that this Snakefile starts with include: "rules/00_download_data.skm", which is a statement that connects this Snakefile with rules/00_download_data.skm. This is a script that read all accession codes from NCBI_accession_list.txt and stores those at SAMPLES, which is then used by expand("hisat2/{sample}.sorted.bam", sample=SAMPLES) to generate the list of all sorted BAM files.

5.1 Quantifying all the samples at once

We have only qualified one sample so far, but now executing the Snakefie, we can process all the other samples in parallel. For this, we first are going to do a dry-run to check the list of commands that snakemake will run for us. On the command line (in our base folder, 00_Reproducible_RNA-Seq_Processing), please write:

snakemake -np featureCounts

This command shows us all the steps that snakemake will until executing a rule named featureCounts (see Snakefile’s code) which run featureCounts over all the samples. Where -n prevent snakemake from running the pipeline and -p prints the commands for each step.

To visualise these steps run:

snakemake featureCounts --dag | dot -Tpng > featureCounts.png

This will produce the following image:

Which help us to understand the planned job execution.

Finally, to run these steps we need to enable snakemake to use the environment files are needed for each rule by including --use-conda and also we should limit the number of processors to 7 with --cores 7.

Exercise 5.1.1

Execute snakemake to quantify all the samples using featureCounts. Look inside Snakefilecode to see where the final output of featureCount will be stored and compare it with the output we previously had using featureCounts.

Exercise 5.1.2

Snakemake can have individual files as a target. The following rule:

rule bamstats:
    input:
        "hisat2/{sample}.sorted.bam"
    output:
        stats_txt = "QC/{sample}/{sample}.stats",
        stats_html = "QC/{sample}/{sample}.plots.html"
    params:
        "QC/{sample}/{sample}.plots"
    conda:
        "envs/core.yaml"
    shell:
        "samtools stats {input} > {output.stats_txt} && plot-bamstats -p {params} {output.stats_txt}"

Was not included as part of our workflow, as it was not required to run featureCounts. To run this rule for a particular file, you have target one of the output that is generated by this rule for a particular file using the following formula:

snakemake --use-conda QC/SAMPLE/SAMPLE.plots.html

Where SAMPLE can be any of the accession codes from NCBI_accession_list.txt. Can you run this rule? What useful information can be found on the output html file>

LS0tCnRpdGxlOiAiIFJlcHJvZHVjaWJsZSBSTkEtc2VxIFByb2NlZXNzaW5nIC0gRGF5MSAiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgojIDEuIEJhc2ljIFVuaXgKCgpVTklYIGlzIGFuIG9wZXJhdGluZyBzeXN0ZW0gdGhhdCB3YXMgZGV2ZWxvcGVkIGluIHRoZSAxOTYwcyBhbmQgaXQgaGFzIGJlZW4gdGhlIGJhc2Ugb2Ygd2lkZWx5IHVzZWQgb3BlcmF0aW5nIHN5c3RlbXMgc3VjaCBhcyBMaW51eCBhbmQgTWFjIE9TLiBBcyB0aGUgZGF0YSBjb21pbmcgb3V0IGZyb20gYmlvbG9naWNhbCBvdXRwdXRzIGdyb3dpbmcgZXhwb25lbnRpYWxseSB0aHJvdWdoIHRpbWUsIEtub3dpbmcgdGhlIFVOSVggYmFzaWNzIGhhdmUgYmVjb21lIGVzc2VudGlhbCB0byBkbyBiaW9pbmZvcm1hdGljcy4gSGVyZSB0aGVyZSBpcyBhIGxpc3Qgb2YgdGhlIG1vc3QgdXNlZCBjb21tYW5kczoKCgp8Q29tbWFkfCBVc2V8IEV4YW1wbGVzfCAgCnwtLTp8LS06fC0tLS0tOnwKfGBjZGAgfCBUb3ZlIGJldHdlZW4gZGlyZWN0b3JpZXN8IGBjZCBGQVNUUWAgKHRvIGVudGVyIGEgZm9sZGVyKXwKfGBjZCAuLmAgfCBUbyBjaGFuZ2UgdG8gdGhlIHBhcmVudCBvZiB0aGUgY3VycmVudCBkaXJlY3Rvcnl8IHwKfGBjZCAtYCB8IFRvIGdvIHRvIHByZXZpb3VzIGRpcmVjdG9yeXwgfAp8YHB3ZGAgfCBXaGVyZSBhbSBJP3wgfAp8YGxzYCB8IExpc3QgZmlsZXMgfCBgbHNgIChvbiB0aGlzIGRpcmVjdG9yeSk7IGBscyBGQVNUUWAgKGxpc3QgZmlsZXMgaW5zaWRlIEZBU1RRKSB8CnxgbWtkaXJgIHwgbWFrZSBuZXcgZGlyZWN0b3J5fCBgbWtkaXIgbXlfZm9sZGVyYCB8CnxgbXZgIHwgbW92ZSBhIGZpbGUgb3IgZGlyZWN0b3J5fCBgbXYgZmlsZSBteV9mb2xkZXIvYCAobW92ZSBmaWxlIHRvIGEgZm9sZGVyKSB8CnxgY3BgIHwgY29weSBmaWxlfCBgY3AgbXlfZm9sZGVyLyogbmV3X2ZvbGRlci9gIChjb3B5IGFsbCBmaWxlcyBvZiBhbCBmb2xkZXIpIHwKfGBybWAgfFJlbW92ZSBhIGZpbGUgb3IgZm9sZGVyfCBgcm0gZmlsZWA7IGBybSAtcmYgZm9sZGVyYCB8CnxgbGVzc2AgfERpc3BsYXlzIHRoZSBjb250ZW50cyBvZiBhIGZpbGV8IGBsZXNzIGZpbGUudHh0YCAocHJlc3MgYHFgIHRvIHF1aXQpIHwKfGBoZWFkYHwgRGlzcGxheXMgdGhlIGZpcnN0IHRlbiBsaW5lcyBvZiBhIGZpbGUgfCB8CnxgdGFpbGB8IERpc3BsYXlzIHRoZSBsYXN0IHRlbiBsaW5lcyBvZiBhIGZpbGUgfCB8CnxgY2F0YCB8UHJpbnQgZmlsZSBjb250ZW50fGBjYXQgZmlsZTEgZmlsZTIgPiBmaWxlLnRvdGFsYCAobWVyZ2UgZmlsZXMpfAp8YHpjYXRgIHxQcmludCBmaWxlIGNvbnRlbnQgb2YgY29tcHJlc2VkIGZpbGVzfGB6Y2F0IFNSUjc4ODk1OTcuZmFzdHEuZ3pgIHwKfGBnemNhdGAgfG1hYyBPU3ggdmVyc2lvbiBvZiB6Y2F0ICAgfGBnemNhdCBTUlI3ODg5NTk3LmZhc3RxLmd6YCB8Cnxgd2NgIHwgd29yZCBjb3VudCB8IGB3YyAtbCBmaWxlIGAgKGNvdW50IGxpbmVzIGluc2lkZSBvZiBhIGZpbGUpfAp8YGdyZXBgIHwgUGF0dGVybiBtYXRjaGluZyB8IGBncmVwIHBhdGVybiBmaWxlYCAoZ2V0IGxpbmVzIHRoYXQgbWF0Y2ggdGhlIHBhdGVybil8CnwgYGN1dGAgfCBleHRyYWN0IGNvbHVtbnMgZnJvbSBmaWxlcyB8IGBjdXQgLWYgMiBmaWxlYCAoZXh0cmFjdCBzZWNvbmQgY29sdW1uKQp8YHNvcnRgIHwgc29ydCBmaWxlIGJ5IGNvbHVtbiB8YHNvcnQgLW5yIC1rIDIgZmlsZWAgKHNvcnQgZmlsZSBieSBzZWNvbmQgY29sdW1uKSB8IAoKCioqRXhlcmNpc2UgMS4wLjEqKgoKT24gdGhlIGNvdXJzZSBkaXJlY3RvcnksIGNyZWF0ZSBhIGZvbGRlciBuYW1lZCBgdGVzdF9mb2xkZXJgIGFuZCBjb3B5IHRoZSBzbWFsbGVzdCBmaWxlIGluc2lkZSBgRkFTVFEvYCBmb2xkZXIuIFRoZW4gY2hhbmdlIHRoZSBuYW1lIG9mIHRoZSBmb2xkZXIgdGhhdCB5b3UganVzdCBjcmVhdGUgdG8gYHRvX2RlbGV0ZWAgYW5kIGZpbmFsbHkgcmVtb3ZlIHRoaXMgZm9sZGVyLiAKCgojIyAxLjIgRWZmaWNpZW50IHVzZSBvZiB0aGUgdGVybWluYWwgCgoKVG8gYWNjZWxlcmF0ZSBvdXIgaW50ZXJhY3Rpb24gd2l0aCB0aGUgdGVybWluYWwsIHdlIGNhbiB1c2UgdGhlIFRBQiBrZXk6ICAKCiFbXShJbWFnZXMvVEFCLnBuZykgCgpGb3IgaW5zdGFuY2UsIGlmIHlvdSBhcmUgYXQgY291cnNlIGZvbGRlciBhbmQgeW91IHdyaXRlIGBscyBHZW5lX2AgYW5kIHRoZW4gcHJlc3MgVEFCIGtleSBvbmNlLCBpdCB3aWxsIGNvbXBsZXRlIHRoZSBjb21tYW5kIHRvIGBscyBHZW5lX2Fubm90YXRpb25gLiBCdXQgaWYgeW91IGp1c3Qgd3JpdGUgYGxzIEdgIGFuZCBwcmVzcyBUQUIgdHdvIHRpbWVzLCBpdCB3aWxsIHNob3cgeW91IHRoZSB0d28gb3B0aW9ucyAoR2VuZV9hbm5vdGF0aW9uIGFuZCBHZW5vbWUpLgoKQW5vdGhlciBwb3dlcmZ1bCB0aGluZyBpcyB0byBwaXBlIChgfGApIHRoZSBvdXRwdXQgb2Ygb25lIGNvbW1hbmQgaW50byBhbmQgYW5vdGhlciBjb21tYW5kLiBGb3IgZXhhbXBsZSB5b3UgY2FuIGRvOgoKYGxzIEZBU1RRLyouZmFzdHEuZ3ogfCB3YyAtbGAKClRvIGNvdW50IGFsbCB0aGUgRkFTVFEgZmlsZXMgdGhhdCBhcmUgc3RvcmVkIGluc2lkZSBvZiBgRkFTVFEvYCBmb2xkZXIuCgpGaW5hbGx5LCBpZiB5b3Ugd2FudCB0byBrbm93IG1vcmUsIGV2ZXJ5IGNvbW1hbmQgaGFzIGl0cyBvd24gb3B0aW9ucywgeW91IGNhbiBzZWUgdGhlIGVudGlyZSBkZXNjcmlwdGlvbiBvZiBhIGNvbW1hbmQgdXNpbmcgYG1hbmAuIEZvciBpbnN0YW5jZSwgdXNlIG1hbiB0byBrbm93IG1vcmUgYWJvdXQgYGhlYWRgIHR5cGluZyBvbiB0aGUgY29tbWFuZCBsaW5lOgoKYG1hbiBoZWFkYAoKKipFeGVyY2lzZSAxLjIuMSoqCgpSZWFkIHRoZSBtYW51YWwgb2YgYGhlYWRgIGNvbW1hbmQuIFRoZW4gdXNlIGl0IHRvIGV4dHJhY3QgdGhlIGZpcnN0IDEwMCBsaW5lcyBvZiBgR2VuZV9hbm5vdGF0aW9uL2RtNi5FbnNlbWJsLmdlbmVzLmd0ZmAgYW5kIGNvdW50IGFsbCB0aGUgbGluZXMgdGhhdCBoYXZlIGBDRFNgIGluIHRoZSB0aGlyZCBjb2x1bW4uIAoKIyAyLiBJbnB1dCBkYXRhIGZvcm1hdAoKCkluIG9yZGVyIHRvIHF1YW50aWZ5IG1STkEgYnkgUk5BLXNlcSBhbmFseXNpcywgZGlmZmVyZW50IGZpbGVzIGFyZSByZXF1aXJlZCB3aGljaCBhcyBhIGRpZmZlcmVudCBmb3JtYXQuIFJOQS1TZXEgcmVhZHMgYXJlIG5vcm1hbGx5IGZvcm1hdHRlZCBhcyBbRkFTVFFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ZBU1RRX2Zvcm1hdCksIFNSQSBvciBDUkFNLiBUbyBwcm9jZXNzIHRoaXMgZGF0YSwgdGhlIG1vc3QgY29tbW9uIGluaXRpYWwgc3RlcCBpcyB0byBtYXAgdGhlIHJlYWRzIHRvIHRoZSBnZW5vbWUsIHdoaWNoIGlzIG9mdGVuIGZvcm1hdHRlZCBhcyBbRkFTVEFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ZBU1RBX2Zvcm1hdCkuIEZpbmFsbHksIHRvIHF1YW50aWZ5IGdlbmUgZXhwcmVzc2lvbiwgd2UgbmVlZCB0byBrbm93IHdoaWNoIHBhcnQgb2YgdGhlIGdlbm9tZSBlbmNvZGVzIGZvciBnZW5lcy4gVGhpcyBpbmZvcm1hdGlvbiBjYW4gYmUgZm91bmQgYXQgZ2VuZSBhbm5vdGF0aW9uIGZpbGVzLCB3aGljaCBjYW4gYmUgZm9ybWF0dGVkIGFzIFtHVEZdKGh0dHA6Ly9tYmxhYi53dXN0bC5lZHUvR1RGMjIuaHRtbCNmaWVsZHMpIG9yIFtCRURdKGh0dHBzOi8vZ2Vub21lLnVjc2MuZWR1L0ZBUS9GQVFmb3JtYXQuaHRtbCNmb3JtYXQxKSBmaWxlcy4gRXhwbG9yZSB0aGUgZGlmZmVyZW50IGh5cGVybGlua3MgcHJvdmlkZWQgZm9yIGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IHRoZSBkaWZmZXJlbnQgZmlsZSBmb3JtYXRzLgoKCiMjIDIuMSBGQVNUQSBmb3JtYXQKCkZBU1RBIGZpbGVzIGNvbnRhaW4gbnVjbGVvdGlkZSBvciBhbWlubyBhY2lkIHNlcXVlbmNlIGluZm9ybWF0aW9uLiBUaGUgZGF0YSBpcyBjb21wb3NlZCBvZiBpZGVudGlmaWVycywgd2hpY2ggc3RhcnRzIHdpdGggYD5gIGFuZCBudWNsZW90aWRlIG8gYW1pbm8gYWNpZCBzZXF1ZW5jZXMuIEh1bmRyZWQgb2YgdGhvdXNhbmRzIG9mIGdlbm9tZXMgY29taW5nIGZyb20gZGlmZmVyZW50IHNwZWNpZXMgYXJlIGF2YWlsYWJsZSBpbiBwdWJsaWMgcmVwb3NpdG9yaWVzIGFzIEZBU1RBIGZpbGVzLiBJdCBpcyBpbXBvcnRhbnQgdG8gbm90aWNlIHRoYXQgYSBnaXZlbiBzcGVjaWVzIGNhbiBoYXZlIGRpZmZlcmVudCBnZW5vbWljIGFzc2VtYmxpZXMuIEZvciBpbnN0YW5jZSwgYSBwb3B1bGFyIGFzc2VtYmx5IGh1bWFuIGdlbm9tZSBhc3NlbWJseSBpcyBgaGcxOWAgdGhhdCB3YXMgcHVibGlzaGVkIGluIEZlYnJ1YXJ5IDIwMDksIGFuZCBhcyBhIGh1Z2UgYW1vdW50IG9mIGRhdGEgaXMgYXZhaWxhYmxlIGZvciB0aGF0IGFzc2VtYmx5IHNvbWUgcmVzZWFyY2hlcnMga2VlcCBkb2luZyB0aGVpciBhbmFseXNlcyB3aXRoIHRoYXQgdmVyc2lvbi4gQnV0IG90aGVyIHJlY2VudCBhbmFseXNlcyBoYXZlIHN0YXJ0ZWQgdG8gdXNlIGBoZzM4YCBpbnN0ZWFkLCB3aGljaCB3YXMgcHVibGlzaGVkIGluIERlY2VtYmVyIDIwMTMgYW5kIHJlcHJlc2VudCBhIG1vcmUgY3VyYXRlZCB2ZXJzaW9uIG9mIHRoZSBodW1hbiBnZW5vbWUuCgpGb3IgdG9kYXkncyBwcmFjdGljZSwgd2UgYXJlIGdvaW5nIHRvIHdvcmsgd2l0aCBhIHNtYWxsZXIgZ2Vub21lLiBXZSBhbHJlYWR5IGhhdmUgZG93bmxvYWRlZCBmb3IgeW91IHRoZSBgZG02YCBhc3NlbWJseSBvZiBELiBtZWxhbm9nYXN0ZXIncyBnZW5vbWUsIGFuIGludmVydGVicmF0ZSBvcmdhbmlzbSB3aWRlbHkgdXNlZCBhcyBhIG1vZGVsIG9yZ2FuaXNtIGZvciBnZW5ldGljIGFuZCBiZWhhdmlvcmFsIHN0dWRpZXMuIFRoZSBnZW5vbWUgZmlsZSBpcyBhdCBjb3Vyc2UgbWF0ZXJpYWwgZm9sZGVyIGF0IGBHZW5vbWUvZG02LmZhYC4gCgoqKkV4ZXJjaXNlIDIuMS4xKioKCkNvdW50IHRoZSBudW1iZXIgb2Ygc2VxdWVuY2VzIHByZXNlbnQgYXQgYGRtNi5mYWAgKGhpbnQ6IGNvdW50IGxpbmVzIHRoYXQgc3RhcnQgd2l0aCBgPmAsIGJ1dCBhcyB0aGlzIGlzIGEgc3BlY2lhbCBVTklYIGNoYXJhY3RlciwgcmVmZXIgdG8gaXQgYXMgYFw+YC4gV2hpY2ggY29tbWFuZCBjYW4gYmUgdXNlZCBmb3IgcGF0dGVybiBtYXRjaGluZyBhbmQgY291bnQgbGluZXM/KS4gCgpJZiBlYWNoIHNlcXVlbmNlIGlzIHJlbGF0ZWQgdG8gYSBjaHJvbW9zb21lLCB3aHkgZG8gd2UgZmluZCBzbyBtYW55IHNlcXVlbmNlcz8gSG93IG1hbnkgbWFpbiBzY2FmZm9sZHMgY2FuIHlvdSBzZWU/CgoqKkV4ZXJjaXNlIDIuMS4yKioKCkNhbiB5b3UgY2FsY3VsYXRlIHRoZSBleGFjdCBudW1iZXIgb2YgbnVjbGVvdGlkZXMgdGhhdCB0aGlzIEQuIG1lbGFub2dhc3RlciBhc3NlbWJseSBpcyBtYWRlIG9mPyAoaGludDogZXhwbG9yZSBhZGRpdGlvbmFsIG9wdGlvbnMgb2YgYGdyZXBgIGFuZCBgd2NgKQoKCiMjIDIuMiBGQVNUUSBmb3JtYXQKClJOQS1zZXEgcmVhZHMgb2Z0ZW4gY29tZSBhcyBGQVNUUSwgd2hpY2ggaXMgYW4gZXh0ZW5zaW9uIG9mIEZBU1RBIGZpbGUgZm9ybWF0LiBFdmVyeSByZWFkIGlzIHJlcHJlc2VudGVkIGJ5IGZvdXIgbGluZXM6CgoqIEZpcnN0IGxpbmU6IHN0YXJ0IHdpdGggYEBgIGFuZCBjb3JyZXNwb25kIHRvIGEgcmVhZCBpZGVudGlmaWVyLiAKKiBTZWNvbmQgbGluZTogcmVhZCBudWNsZW90aWRlIHNlcXVlbmNlLgoqIFRoaXJkIGxpbmU6IFN0YXJ0IHdpdGggYCtgIGFuZCBpdCBjYW4gYmUgZm9sbG93ZWQgYnkgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiwgYnV0IG9mdGVuIGl0IGlzIGp1c3QgYCtgLgoqIEZvdXJ0aCBsaW5lOiAgUGhyZWQgcXVhbGl0eSBzY29yZSByZXByZXNlbnQgdGhlIHJlbGlhYmlsaXR5IG9mIGEgYmFzZSBjYWxsIChhcyBoaWdoZXIgdGhlIGJldHRlcikuIEl0IGlzIGVuY29kZWQgYXMgQVNDSUkgY2hhcmFjdGVycy4gSXQgY2FuIGhhdmUgZGlmZmVyZW50IGVuY29kaW5ncywgc3VjaCBhcyBTYW5nZXIsIFNvbGV4YSwgSWxsdW1pbmEgMS4zKyBvciBJbGx1bWluYSAxLjgrLiBQbGVhc2UgY2hlY2sgW0ZBU1RRIGZvcm1hdCBkZXNjcmlwdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRkFTVFFfZm9ybWF0KSBmb3IgbW9yZSBkZXRhaWxzLgoKKipFeGVyY2lzZSAyLjIuMSoqCgpXaGljaCBvZiB0aGUgZmFzdHEgZmlsZXMgZm91bmQgYXQgYEZBU1RRL2AgaGFzIHRoZSBoaWdoZXN0IGFuZCB0aGUgbG93ZXN0IGFtb3VudCBvZiByZWFkcz8gSG93IG1hbnkgcmVhZHMgZG8gdGhleSBoYXZlPyBXaGF0IGlzIHRoZSByZWFkIGxlbmd0aD8KCgojIyAyLjMgR2VuZSBhbm5vdGF0aW9uCgpFZmZvcnRzIGhhdmUgYmVlbiBtYWRlIHRvIGlkZW50aWZ5IHRoZSBnZW5vbWljIHJlZ2lvbnMgdGhhdCBlbmNvZGUgZm9yIGdlbmVzLiBUaGlzIGluZm9ybWF0aW9uIGNhbiBiZSBzdG9yZWQgYXMgQkVEIG9yIEdURiBmaWxlcyAoYW1vbmcgb3RoZXIgZm9ybWF0cykuIEJFRCBmaWxlcyBzdG9yZSBpbmZvcm1hdGlvbiBhYm91dCBnZW5vbWljIGNvb3JkaW5hdGVzLiBFYWNoIHJvdyBvZiBhIEJFRCBmaWxlIHJlcHJlc2VudHMgYSBnZW5vbWljIGludGVydmFsLCBpbiB0aGUgY2FzZSBvZiBnZW5lIGFubm90YXRpb24gZmlsZSwgaXQgcmVwcmVzZW50cyB0aGUgc3RhcnQgYW5kIGVuZCBvZiBhIHRyYW5zY3JpcHQuIEV4b25zIGNvb3JkaW5hdGVzIGFyZSBlbmNvZGVkIGFzIGBibG9ja3NgLCB3aGVyZSB0aGUgYGJsb2NrU3RhcnRzYCBjb2x1bW4gaXMgYSBsaXN0IG9mIGFsbCBleG9uIHN0YXJ0cyByZWxhdGl2ZSB0byB0aGUgdHJhbnNjcmlwdCBzdGFydHMgYW5kIGBibG9ja1NpemVzYCBjb2x1bW4gY29udGFpbiBhbGwgdGhlIGV4b24gc2l6ZXMuIFBsZWFzZSByZWFkIFtCRUQgZGVzY3JpcHRpb24gZm9ybWF0XShodHRwczovL2dlbm9tZS51Y3NjLmVkdS9GQVEvRkFRZm9ybWF0Lmh0bWwjZm9ybWF0MSkgZm9yIG1vcmUgaW5mb3JtYXRpb24uIAoKR1RGIGZpbGVzIGluc3RlYWQgYXJlIGdlbmUtY2VudHJpYyBhbm5vdGF0aW9ucywgaW4gd2hpY2ggYSBnZW5lIGNhbiBiZSBkZXNjcmliZWQgYnkgbXVsdGlwbGUgcm93cy4gRWFjaCByb3cgY29udGFpbnMgdGhlIGNvb3JkaW5hdGVzIGFuZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIG9mIGEgcGFydGljdWxhciBnZW5lIGZlYXR1cmUuIFBsZWFzZSBjaGVjayB0aGUgZnVsbCBbR1RGIGRlc2NyaXB0aW9uXShodHRwOi8vbWJsYWIud3VzdGwuZWR1L0dURjIyLmh0bWwpCgoKKipFeGVyY2lzZSAyLjMuMSoqCgoKR2V0IEQuIG1lbGFub2dhc3RlciBnZW5lIGFubm90YXRpb24gZmlsZXMgZm9yIGBkbTZgIGFzc2VtYmx5IGluIGZyb20gW1VDU0MgVGFibGUgQnJvd3Nlcl0oaHR0cDovL2dlbm9tZS1ldXJvLnVjc2MuZWR1L2NnaS1iaW4vaGdUYWJsZXMpLiBDaG9vc2luZyB0aGUgZm9sbG93aW5nIHZhbHVlczoKCiogY2xhZGU6IEluc2VjdC4KKiBnZW5vbWU6IEQuIG1lbGFub2dhc3Rlci4KKiBhc3NlbWJseTogQXVnLiAyMDE0IChCREdQIFJlbGVhc2UgNiArIElTTzEgTVQvZG02KS4KKiBncm91cDogR2VuZSBhbmQgR2VuZSBQcmVkaWN0aW9ucy4KKiB0cmFjazogRW5zZW1ibCBHZW5lcy4KKiB0YWJsZTogZW5zR2VuZS4KCkFuZCBnZXQgdGhlIGFubm90YXRpb24gZmlsZXMgYXMgQkVEIGFuZCBHVEYgKGNob29zaW5nIHRoZSByaWdodCBvdXRwdXQgZm9ybWF0cykuIFNhdmUgdGhlIEJFRCBmaWxlIGFzIGBkbTYuRW5zZW1ibC5nZW5lcy5iZWQxMmAgYW5kIEdURiBhcyBgZG02LkVuc2VtYmwuZ2VuZXMuZ3RmYCBhdCBgR2VuZV9hbm5vdGF0aW9uYCBmb2xkZXIuCgoKKipFeGVyY2lzZSAyLjMuMioqCgpQcm9jZXNzIGBkbTYuRW5zZW1ibC5nZW5lcy5iZWQxMmAgdG8gZmluZCB0aGUgZml2ZSB0cmFuc2NyaXB0cyB3aXRoIHRoZSBoaWdoZXN0IG51bWJlciBvZiBleG9ucy4gKGhpbnQgdXNlIGBzb3J0YCwgbG9vayBmb3IgYC1yYCBhbmQgYC1rYCBmbGFncyBhdCBpdHMgbWFudWFsKS4gCgoqKkV4ZXJjaXNlIDIuMy4zKioKClByb2Nlc3MgYGRtNi5FbnNlbWJsLmdlbmVzLmd0ZmAgdG8gZmluZCB0aGUgdG90YWwgbnVtYmVyIG9mIGdlbmVzCgoKIyAzLiBRdWFsaXR5IGNvbnRyb2wgYmVmb3JlIGFsaW5nbWVudAoKCiMjIDMuMSBGQVNUUUMKCgpUbyBwZXJmb3JtIGEgUUMgb2YgdGhlIEZhc3RxIGZpbGVzIHdlIGNhbiB1c2UgKkZhc3RRQyouIFdlIGNhbiBhY2Nlc3MgdG8gYSBiYXNpYyBtYW51YWwgb2YgdGhpcyB0b29sIGJ5IHR5cGluZyBvbiBgZmFzdHFjIC0taGVscGAgb24gdGVybWluYWwuCgoKCioqRXhlcmNpc2UgMy4xLjEqKgoKQ3JlYXRlIGEgZGlyZWN0b3J5IG5hbWVkIGBRQ2AuIFJlYWQgYGZhc3RxY2AgaGVscCBtYW51YWwgdG8gcnVuIGl0IGZvciBgRkFTVFEvU1JSNzg4OTU4NS5mYXN0cS5nemAgYW5kIHN0b3JlIHRoZSByZXN1bHRzIGF0IHRoZSBuZXdseSBjcmVhdGVkIGZvbGRlciAoYFFDYCkuIFRvIHNwZWVkIGl0IHVwLCB1c2UgNyB0aHJlYWRzICh3aGljaCBpdCB3aWxsIGFsbG93IG91ciBtYWNoaW5lcyB0byB1c2UgNyBwcm9jZXNzb3JzKS4gWW91IHdpbGwgZ2V0IGFuIGh0bWwgZmlsZSBhcyBvdXRwdXQ7IE9wZW4gaXQgd2l0aCBhbiBpbnRlcm5ldCBicm93c2VyIChDaHJvbWUsIEZpcmVmb3gsIGV0YykgYW5kIGFuc3dlciB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoKCkEpIFdoYXQgaXMgdGhlIHJlYWQgbGVuZ3RoPwpCKSBEb2VzIHRoZSBxdWFsaXR5IHNjb3JlIHZlcnkgdGhyb3VnaCB0aGUgcmVhZCBsZW5ndGg/CkMpIEhvdyBpcyB0aGUgZGF0YSdzIHF1YWxpdHk/CgoKCiMjIDMuMiBNZXRhZGF0YSBhbmFseXNpcyBvbiBSLXN0dWRpbwoKClRoZSBmYXN0cS5neiBmaWxlcyB0aGF0IHdlIGhhdmUgYXQgYC9GQVNUUWAgY29ycmVzcG9uZCB0byBSTkEtc2VxIGRhdGEgZXh0cmFjdGVkIGZyb20gRC4gbWVsYW5vZ2FzdGVyIGJyYWluIHNhbXBsZXMuIFRoZSBkYXRhIGNvcnJlc3BvbmRpbmcgdG8gYSBmdWxsIHN0dWR5IHB1Ymxpc2hlZCBsYXN0IHllYXIgb24gW0NlbGwgUmVwb3J0c10oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi8vcHVibWVkLzMwMzU1NDg0KSBhbmQgdGhlIGZ1bGwgc3R1ZHkgd2FzIHB1Ymxpc2hlZCBpbiBbU1JBIGFyY2hpdmVdKGh0dHBzOi8vdHJhY2UubmNiaS5ubG0ubmloLmdvdi9UcmFjZXMvc3JhLz9zdHVkeT1TUlAxNjIzMzUpLiAKCioqRXhlcmNpc2UgMy4yLjEqKgoKQ2hlY2sgdGhlIFtzdHVkeSBydW5zXShodHRwczovL3RyYWNlLm5jYmkubmxtLm5paC5nb3YvVHJhY2VzL3N0dWR5Lz9hY2M9U1JQMTYyMzM1KSBhbmQgY29tcGFyZSB3aXRoIHRoZSBmaWxlIG5hbWVzIGZyb20gYEZBU1RRL2AgZm9sZGVyLiBXaGljaCB0aXNzdWVzIHRoZSBzYW1wbGVzIHdlcmUgZXh0cmFjdGVkIGZyb20/IFNlbGVjdCB0aGUgUnVucyB0aGF0IGNvcnJlc3BvbmQgdG8gdGhlIGZpbGVzIHRoYXQgd2UgaGF2ZSBhbmQgZG93bmxvYWQgdGhlIGBSdW5JbmZvIFRhYmxlYCBjb3JyZXNwb25kaW5nIHRvIHRoZXNlIHRhYmxlcyBhbmQgc2F2ZSBpdCBhcyBgU3JhUnVuVGFibGUudHh0YCBvbiB0aGUgY291cnNlIGZvbGRlci4gCgoqKkV4ZXJjaXNlIDMuMi4yKioKCk9wZW4gUi1zdHVkaW8gYW5kIG9wZW4gdGhlIGBEYXkxLlJtZGAgZmlsZSB0aGF0IGlzIGxvY2F0ZWQgYXQgdGhlIGNvdXJzZSBmb2xkZXIuIFRoaXMgaXMgYW4gUi1ub3RlYm9vayBmaWxlLCB0aGF0IGhhcyBhbGwgdGhlIGNvZGUgdXNlZCB0byBnZW5lcmF0ZSB0aGlzIGRvY3VtZW50LiBCdXQsIG5vdyB3aGVuIHlvdSBvcGVuIHRoaXMgZmlsZSB5b3Ugd2lsbCBiZSBhYmxlIHRvIHJ1biBgY2h1bmtzYCBvZiBjb2RlIGludGVyYWN0aXZlbHkgaW5zaWRlIFItc3R1ZGlvLiBSdW4gdGhlIGZvbGxvd2luZyBjaHVuayBieSBjbGlja2luZyBhdCB0aGUgZ3JlZW4gcGxheSBidXR0b24uCgpgYGB7cn0KCmxpYnJhcnkoZGF0YS50YWJsZSkKCm1ldGFkYXRhIDwtIGZyZWFkKCIuL1NyYVJ1blRhYmxlLnR4dCIpICNQbGVhc2UgYmUgc3VyZSB0byBwbGFjZSB0aGUgZmlsZSB1bmRlciAuL0NvdXJzZV9NYXRlcmlhbHMvMDBfUmVwcm9kdWNpYmxlX1JOQS1TZXFfUHJvY2Vzc2luZy8KCm1ldGFkYXRhCmBgYAoKCkJ5IHJ1bm5pbmcgdGhpcyBjb2RlLCB3ZSBpbXBvcnRlZCB0aGUgbWV0YWRhdGEgYXMgYSBkYXRhLnRhYmxlIG9iamVjdCwgd2hpY2ggY29ycmVzcG9uZHMgdG8gYSB2ZXJ5IGNvbnZlbmllbnQgc3RydWN0dXJlIHRvIG1hbmlwdWxhdGUgZGF0YSBpbiBSLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiB2aXNpdCB0aGlzIFtsaW5rXShodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY29tbXVuaXR5L3R1dG9yaWFscy9kYXRhLXRhYmxlLWNoZWF0LXNoZWV0KS4gV2UgY2FuIG5vdyBlYXNpbHkgbWFuaXB1bGF0ZSB0aGUgZGF0YSEgRm9yIGV4YW1wbGUsIHdlIGNhbiBmaWx0ZXIgdGhlIG1ldGFkYXRhIHJvd3MsIHNvIHdlIG9ubHkgc2VlIHRoZSBvbmVzIGNvcnJlc3BvbmRpbmcgdG8gImNlbnRyYWwgbmV1cm9ucyIKCgpgYGB7cn0KbWV0YWRhdGFbc291cmNlX25hbWU9PSJjZW50cmFsIG5ldXJvbnMiICwgXQpgYGAKCgpXZSBjYW4gYWxzbyBnZXQgZXhwbG9yZSBpZiB3ZSBoYXZlIGEgbG93bHkgc2VxdWVuY2VkIHNhbXBsZSBmaWx0ZXJpbmcgYnkgdGhlIGBNQmFzZXNgIHZhbHVlcwoKYGBge3J9Cm1ldGFkYXRhW01CYXNlcyA8IDEwMCAsIF0KYGBgCgoKT3IgZ2V0IHRoZSBhdmVyYWdlIE1lZ2EgYmFzZXMgYWNyb3NzIHRoaXMgc2FtcGxlCgpgYGB7cn0KCm1ldGFkYXRhWyAgLCBtZWFuKE1CYXNlcykgXQoKYGBgCgoKVG8gZXhwbG9yZSB0aGlzIHZpc3VhbGx5LCB3ZSBjYW4gcGxvdCB3aXRoIGdncGxvdDIuIFRoaXMgaXMgb25lIG9mIHRoZSBtb3N0IHBvcHVsYXIgcGFja2FnZXMgdG8gdmlzdWFsaXplIGRhdGEuIEEgYmFzaWMgY29kZSB0byB2aXN1YWxpemUgdGhlIE1iYXNlcyBkaXN0cmlidXRpb24gY2FuIGJlIHJ1biBhczoKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QoZGF0YSA9IG1ldGFkYXRhKSArICAjIEZyaXN0IGxheWVyIC0gRGF0YSBpbnB1dAogIGdlb21fYmFyKGFlcyh4PVJ1biwgeT1NQmFzZXMpLCBzdGF0ID0gImlkZW50aXR5IikgICAjIHNlY29uZCBsYXllciAtIHR5cGUgb2YgcGxvdCBhbmQgYXhpcwoKYGBgCgoKV2UgY2FuIHJvdGF0ZSB0aGUgdGV4dCB0byBtYWtlIGl0IG1vcmUgdmlzaWJsZQoKYGBge3J9CgpnZ3Bsb3QoZGF0YT1tZXRhZGF0YSkgKwogIGdlb21fYmFyKGFlcyh4PVJ1biwgeT1NQmFzZXMsICksIHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKSAgIyBUaGlyZCBsYXllciAtICB2aXN1YWwgY29uZmlndXJhdGlvbgoKYGBgCgoKaW5zaWRlIGBhZXMoIClgIGFyZSB0aGUgdmFyaWFibGVzIGFuZCB3ZSBjYW4gYWxzbyBkZWZpbmUgYSB2YXJpYWJsZSBhcyB0aGUgY29sb3VyOgoKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1tZXRhZGF0YSkgKwogIGdlb21fYmFyKGFlcyh4PVJ1biwgeT1NQmFzZXMsIGNvbG91cj1zb3VyY2VfbmFtZSksIHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKQpgYGAKCkJ1dCBmb3IgdGhpcyBwYXJ0aWN1bGFyIHR5cGUgb2YgZ3JhcGgsIGZpbGxpbmcgdGhlIGJhciB3aXRoIG9uZSBjb2xvdXIgaXMgbW9yZSBzdWl0YWJsZQoKYGBge3J9CmdncGxvdChkYXRhPW1ldGFkYXRhKSArCiAgZ2VvbV9iYXIoYWVzKHg9UnVuLCB5PU1CYXNlcywgZmlsbD1zb3VyY2VfbmFtZSksIHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKQpgYGAKCgpGcm9tIHRoaXMgcGxvdCwgd2UgY2FuIGNsZWFybHkgc2VlIHRoYXQgb25lIG9mIHRoZSBzYW1wbGVzIHRoYXQgd2VyZSB0YWtlbiBmcm9tIGNlbnRyYWwgbmV1cm9ucyBpcyBtdWNoIHNtYWxsZXIgdGhhbiB0aGUgcmVzdC4gVGhpcyBmYWN0b3IgbmVlZHMgdG8gYmUgY29uc2lkZXJlZCBhdCB0aGUgdGltZSB3ZSBhbmFseXplIHRoZSBzYW1wbGVzLCBhcyB0aGlzIHBvb3JseSBzZXF1ZW5jZWQgc2FtcGxlIG1pZ2h0IGhhdmUgbGVhZCB0byBsZXNzIGFjY3VyYXRlIG1STkEgcXVhbnRpZmljYXRpb24gYW5kIGl0IG1pZ2h0IG5lZWQgdG8gYmUgcmVtb3ZlZCBmb3IgcXVhbnRpdGF0aXZlIGFuYWx5c2VzLiAKCgoKIyA0LiBNYXBwaW5nIHJlYWRzIHRvIHRoZSBnZW5vbWUgYW5kIGdldHRpbmcgcmF3IGNvdW50cwoKQWZ0ZXIgd2UgaGF2ZSBhIGRpYWdub3NpcyBvZiB0aGUgZGF0YSBxdWFsaXR5LCB3ZSBjYW4gc3RhcnQgdG8gYW5hbHlzZSB0aGUgZGF0YS4gVXN1YWxseSwgdGhlIGZpcnN0IHN0ZXAgaW50byB0aGUgYW5hbHlzaXMgcmVxdWlyZXMgbWFwcGluZyB0aGUgUk5BLXNlcSByZWFkcyB0byB0aGUgZ2Vub21lLiBUaGVyZSBhcmUgbnVtZXJvdXMgdG9vbHMgdG8gZG8gcGVyZm9ybSBzaG9ydCByZWFkIGFsaWdubWVudCBhbmQgdGhlIGNob2ljZSBvZiBpdCBzaG91bGQgYmUgY2FyZWZ1bGx5IG1hZGUgYWNjb3JkaW5nIHRvIHRoZSBhbmFseXNpcyBnb2FscyBhbmQgcmVxdWlyZW1lbnRzLiBIaXNhdDIgaXMgYSB2ZXJ5IGZhc3RxIHRvb2wgdGhhdCBoYXMgYmVlbiBzaG93biB0byBoYXZlIGEgZ29vZCBwZXJmb3JtYW5jZSBvbiBwdWJsaXNoZWQgYmVuY2htYXJrcy4gCgoKIyMgNC4xIEluZGV4aW5nIHRoZSBnZW5vbWUgZm9yIEhpc2F0MgoKClRvIHN0YXJ0IG1hcHBpbmcgUk5BLXNlcSByZWFkcyB0byB0aGUgZ2Vub21lLCB3ZSBuZWVkIHRvIGluZGV4IHRoZSBnZW5vbWUuIFRoZSBjb21tYW5kIHRvIGRvIHRoaXMgaXMgYGhpc2F0Mi1idWlsZGAuIAoKYGhpc2F0Mi1idWlsZCAtLWhlbHBgCgoqKkV4ZXJjaXNlIDQuMS4xLioqCgoKR28gdG8gYENvdXJzZV9NYXRlcmlhbHMvMDBfUmVwcm9kdWNpYmxlX1JOQS1TZXFfUHJvY2Vzc2luZ2AgZGlyZWN0b3J5IHVzaW5nIGBjZGAgKFRoaXMgaXMgZ29pbmcgdG8gYmUgb3VyIGJhc2UgZGlyZWN0b3J5IHRvIHNvbHZlIGFsbCB0aGUgZm9sbG93aW5nIGV4ZXJjaXNlcykuIERvIGBsc2AgdG8gbGlzdCB0aGUgZGlyZWN0b3JpZXMgYW5kIGNyZWF0ZSBhIG5ldyBmb2xkZXIgY2FsbGVkIGBJbmRleGAgaW5zaWRlIGBHZW5vbWVgIGZvbGRlci4gVGhlbiBydW4gdGhlIGZvbGxvd2luZyBjb21tYW5kOiAKCmBoaXNhdDItYnVpbGQgLXAgNyBHZW5vbWUvZG02LmZhIEdlbm9tZS9JbmRleC9kbTZgCgpUaGlzIGNvbW1hbmQgd2lsbCB1c2UgdGhlIGdlbm9tZSAobG9jYXRlZCBhdCBgR2Vub21lL2RtNi5mYWApIGFuZCBpdCB3aWxsIGdlbmVyYXRlIHRoZSBpbmRleCBmaWxlcyBvbiBgR2Vub21lL0luZGV4L2AuIEFsbCB0aGUgZmlsZXMgd2lsbCBzdGFydCB3aXRoIGBkbTZgIHByZWZpeC4gVGFrZSBhIGxvb2sgYXQgYGhpc2F0Mi1idWlsZGAgaGVscCwgCgpBKSBXaHkgZG8gd2UgdXNlIGAtcCA3YCBhbmQgd2hhdCBpcyB0aGUgbWF4aW11bj1tIHZhbHVlIHdlIHNob3VsZCB1c2Ugb24gdGhlc2UgbWFjaGluZXM/CkIpIEhvdyBtYW55IGZpbGVzIGFyZSBjcmVhdGVkIG9uIHRoaXMgcHJvY2Vzcz8gCgoKIyMgNC4yIEhpc2F0MgoKClRvIG1hcCB0aGUgcmVhZHMgdG8gdGhlIGdlbm9tZSB3ZSBuZWVkIHRvIHJ1biBoaXNhdDIuIFRha2UgYSBxdWljayBsb29rIHRvIGhpc2F0MidzIGRlc2NyaXB0aW9uCgpgaGlzYXQyIC0taGVscGAKCioqRXhlcmNpc2UgNC4yLjEqKgoKUnVuIGhpc2F0MiBmb3IgdGhlIHNtYWxsZXN0IGZpbGUsIGxvY2F0ZWQgYXQgRkFTVFEgZm9sZGVyIHVzaW5nIDcgcHJvY2Nlc29ycy4gU2F2ZSB0aGUgcmVzdWx0cyBpbnNpZGUgYSBmb2xkZXIgbmFtZWQgYGhpc2F0MmAgKGNyZWF0ZSBpdCBiZWZvcmUgdXNpbmcgYG1rZGlyYCkgYW5kIHNhdmUgdGhlIHJlc3VsdHMgdXNpbmcgdGhlIHJpZ2h0IGV4dGVuc2lvbiAoYC5zYW1gKS4KaGludDogdXNlIGBoaXNhdCAtLWhlbHBgIGFuZCBsb29rIGZvciAtcCAtVSAteCBmbGFncyBhbmQgdHQgdGhlIGVuZCBvZiB0aGUgY29tbWFuZCB1c2UgYD5gIHRvIHNhdmUgdGhlIHJlc3VscyB0byBhIGZpbGUuIAoKCiMjIDQuMyBTYW10b29scwoKVGhlIG91dHB1dCBvZiBoaXNhdDIgaXMgYSBTQU0gZmlsZSwgd2hpY2ggaXMgYSBwbGFpbiB0ZXh0IGZpbGUgdGhhdCBoYXMgdGhlIGFsaWdubWVudCBpbmZvcm1hdGlvbi4gQXMgdGhleSB0ZW5kIHRvIGJlIHZlcnkgbGFyZ2UsIGlzIG5vdCBhIGdvb2QgaWRlYSB0byBzdG9yZSB0aGVtIGZvcmV2ZXIuIEJBTSBmaWxlcyBhcmUgdGhlIGJpbmFyeSBmb3JtIG9mIFNBTSBmaWxlcywgd2hpY2ggbWVhbnMgdGhlaXIgaW5mb3JtYXRpb24gaXMgbXVjaCBtb3JlIGNvbXByZXNzZWQsIHdoaWNoIG1ha2UgdGhlbSBlYXNpZXIgdG8gc3RvcmUgdGhlbSwgYnV0IGFsc28gZW5hYmxlIHF1aWNrZXIgcHJvY2Vzc2luZyBvZiB0aGVpciBkYXRhLiBXZSBjYW4gdHJhbnNmb3JtIGZyb20gU0FNIHRvIEJBTSBmb2xsb3dpbmcgdGhpcyBnZW5lcmFsIGZvcm11bGE6Cgpgc2FtdG9vbHMgdmlldyAtYiBzYW1wbGUuc2FtID4gc2FtcGxlLmJhbWAKCldoZXJlIGBzYW1wbGVgIGlzIGp1c3QgYSBnZW5lcmljIG5hbWUgdG8gcmVmZmVyIHRvIG91ciBzYW1wbGVzLiAKCioqRXhlcmNpc2UgNC4zLjEqKgoKR2V0IGEgQkFNIGZpbGUgZnJvbSB0aGUgU0FNIGZpbGUgeW91IGp1c3QgZ2VuZXJhdGVkLiAKCgojIyA0LjQgVmlzdWFsaXNlIEJBTSBmaWxlcwoKQkFNcyBjYW4gYmUgdXNlZCBmb3IgZGlmZmVyZW50IGRvd25zdHJlYW0gYW5hbHlzZXMuIEJ1dCBtb3N0IG9mIHRoZW0gcmVxdWlyZSB0aGUgQkFNIGZpbGVzIHRvIGJlIGBzb3J0ZWRgLiBXaGVuIGEgQkFNIGZpbGUgaXMgc29ydGVkLCB0aGUgYWxpZ25tZW50cyBhcmUgb3JkZXJlZCBieSB0aGUgcG9zaXRpb24gdGhleSBtYXAgdG8uIEJBTSBmaWxlcyBjYW4gYmUgc29ydGVkIHVzaW5nIGBzYW10b29scyBzb3J0YCBhbmQgZm9sbG93aW5nIHRoaXMgZm9ybXVsYToKCmBzYW10b29scyBzb3J0ICBzYW1wbGUuYmFtIC1vIHNhbXBsZS5zb3J0ZWQuYmFtYAoKVG8gdmlzdWFsaXNlIGEgQkFNJ3MgYWxpZ25tZW50LCB3ZSBuZWVkIHRvIGluZGV4IGl0IGZpcnN0LiBXaGljaCBjYW4gYmUgbWFkZSB3aXRoIGBzYW10b29scyBpbmRleGA6Cgpgc2FtdG9vbHMgaW5kZXggc2FtcGxlLnNvcnRlZC5iYW1gCgoqKkV4ZXJjaXNlIDQuNC4xKioKCkEpIFNvcnQgYW5kIEluZGV4IHlvdXIgQkFNIGZpbGUuCkIpIE9wZW4gSUdWLCBsb2FkIEQuIG1lbGFub2dhc3RlcidzIGdlbm9tZSAoZG02KSBhbmQgbG9hZCB0aGUgc29ydGVkIGJhbWZpbGUuCgoqKklHViBoZWxwKioKCipMb2FkIHRoZSByZWZlcmVuY2UgZ2Vub21lKi4gT24gdGhlIHRvcCBtZW51IGJhciBmaW5kIHRoZSBnZW5vbWVzIGRyb3Bkb3duICh0b3AtbGVmdCkgYW5kICB0aGVuIGZpbmQgIGBELiBtZWxhbm9nYXN0ZXJgIGFzc2VtYmx5IGRtNi4gWW91IG1pZ2h0IG5lZWQgdG8gY2xpY2sgb24g4oCYTW9yZS4uLuKAmSB0byBzZWUgYSBsaXN0IG9mIGF2YWlsYWJsZSBnZW5vbWVzLgoKKkxvYWQgdGhlIEJBTSBmaWxlKi4gT24gdGhlIHRvcCBtZW51IGJhciBnbyB0byDigJhGaWxlIOKAkz4gTG9hZCBmcm9tIEZpbGUuLi7igJkgYW5kIHNlbGVjdCB0aGUgc29ydGVkIEJBTSBmaWxlIHlvdSBoYXZlIGNyZWF0ZWQgb24gdGhlIHByZXZpb3VzIHN0ZXAuCgpab29tIGluIGludG8gYSBwYXJ0aWN1bGFyIGdlbmUgdG8gc2VlIHRoZSByZWFkIGFsaWdubWVudHMuIAoKCiMjIDQuNSBGZWF0dXJlQ291bnRzCgpUbyBjb3VudCB0aGUgbnVtYmVyIG9mIHJlYWRzIHdlIHdpbGwgdXNlIEZlYXR1cmVDb3VudHMsIHdoaWNoIHVzZXMgYSBnZW5lIGFubm90YXRpb24gZmlsZSAoR1RGKSB0byBwcm9jZXNzIHRoZSBnZW5vbWljIGludGVydmFscyBvZiBldmVyeSBnZW5lIGFuZCBjb3VudCBhbGwgdGhlIHJlYWRzIHRoYXQgbWFwIHRvIHRoZSBleG9uaWMgcmVnaW9ucy4gCgoqKkV4ZXJjaXNlIDQuMi4yKioKClJ1biBmZWF0dXJlQ291bnRzIHByb3ZpZGluZyB0aGUgdHJhbnNjcmlwdCBhbm5vdGF0aW9uIGFuZCB0aGUgc2FtZSBmaWxlIHlvdSBwcm9kdWNlZCB3aXRoIGhpc2F0Mi4gVG8gZG8gdGhpcywgcmVhZCB0aGUgZmVhdHVyZUNvdW50IGhlbHAgbWFudWFsIGFuZCBmaW5kIHRoZSByaWdodHMgZmxhZ3MgcnVuIGZlYXR1cmVDb3VudHMgKGhpbnQ6IHJlYWQgdGhlIGBSZXF1aXJlZCBhcmd1bWVudHNgIHNlY3Rpb24pLgoKKipFeGVyY2lzZSA0LjIuMioqCgpGaW5kIHRoZSBnZW5lIHdpdGggdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIGNvdW50cy4gQ2FuIHdlIHNheSB0aGlzIGdlbmUgaXMgdGhlIG9uZSB3aXRoIGhpZ2hlciBleHByZXNzaW9uIGxldmVscyBvbiB0aGlzIHNhbXBsZT8KCgojIDUuIEludHJvZHVjdGlvbiB0byByZXBvcmR1Y2libGUgYmlvaW5mb3JtYXRpY3MKClVudGlsIHRoaXMgcG9pbnQsIHdlIGhhdmUgaW50cm9kdWNlZCBldmVyeSBjb21tYW5kIG1hbm51YWxseSBpbiB0aGUgdGVybWluYWwuIFBsZWFzZSBjaGVjayBob3cgbWFueSBmaWxlcyBkbyB3ZSBoYXZlIGF0IGBGQVNUUS9gLiBEbyB5b3UgdGhpbmsgeW91IGNhbiBnZXQgdGhlIHJhdyBjb3VudHMgZnJvbSBhbGwgdGhlIHNhbXBsZXMgd2l0aG91dCBoYXZpbmcgZXJyb3JzPyB3aGF0IGFib3V0IHByb2Nlc3NpbmcgaHVuZHJlZCBvZiBzYW1wbGVzLCBpcyBpdCByZWFzb25hYmxlIHRvIGRvIGl0IG1hbnVhbGx5PyAgCgpUaGUgYW5zd2VyIGlzIGNsZWFybHkgKm5vKi4gV2UgbmVlZCBzeXN0ZW1hdGljIHdheXMgdG8gcHJvY2VzcyBkYXRhIHRvIGF2b2lkIGVycm9ycyBhbmQgZW5hYmxlIHJlcHJvZHVjaWJpbGl0eSBpbiBvdXIgYW5hbHlzZXMuIFRoaXMgaXMgd2h5IHdlIGFyZSBnb2luZyB0byB1c2UgYSB3b3JrZmxvdyBtYW5hZ2VyIHRvIGV4ZWN1dGUgdGhlIHJlbWFpbmluZyBzdGVwcyB0byBnZXQgdGhlIHJhdyBjb3VudHMgZnJvbSBvdXIgc2FtcGxlcy4gCgpTbmFrZW1ha2UgaXMgY3VycmVudGx5IG9uZSBtb3N0IHBvcHVsYXIgd29ya2Zsb3cgbWFuYWdlcnMgdG8gd29yayB3aXRoIGJpb2luZm9ybWF0aWMgc29mdHdhcmUuIFRvIGluc3RhbGwgc25ha2VtYWtlIHBsZWFzZSB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6CgpgY29uZGEgY3JlYXRlIC1uIHNuYWtlbWFrZV9lbnYgc25ha2VtYWtlIGAKCkJ5IGRvaW5nIHRoaXMgd2UgY3JlYXRlZCBhIHZpcnR1YWwgZW52aXJvbm1lbnQsIGNhbGxlZCBgc25ha2VtYWtlX2VudmAgdGhhdCBoYXMgbGF0ZXN0IHZlcnNpb24gb2Ygc25ha2VtYWtlIGFuZCBvdXIgaXQgZGVwZW5kZW5jaWVzIGluc3RhbGxlZC4gTm93IHRvIGFjdGl2YXRlIHRoaXMgZW52aXJvbm1lbnQsIHdyaXRlOgoKYGNvbmRhIGFjdGl2YXRlIHNuYWtlbWFrZV9lbnZgCgpBcyB3ZSBhbHJlYWR5IHdyb3RlIGEgYHNuYWtlbWFrZWAgcGlwZWxpbmUgZm9yIHlvdSwgd2UgYXJlIGdvaW5nIHRvIGRlbW9uc3RyYXRlIHlvdSBzb21lIG9mIGl0cyBwcm9wZXJ0aWVzIGFuZCBob3cgdG8gdXNlIGl0LiBUbyBzZWUgdGhlIGNvZGUgZnJvbSB0aGUgdGhpcyBwaXBlbGluZSwgVXNlIGBBdG9tYCAob3IgYW55IHRleHQgZWRpdG9yKSB0byBvcGVuIHRoZSBgU25ha2VmaWxlYC4gQ2FuIHlvdSByZWNvZ25pemUgc29tZSBvZiB0aGUgc3RlcHMgdGhhdCB3ZSBoYXZlIGRvbmUgYWxyZWFkeT8uIEV2ZXJ5IGBydWxlYHJlcHJlc2VudCBhIHN0ZXAgb2YgdGhlIGFuYWx5c2lzLiBGb3IgZXhhbXBsZToKCgpgYGB7cHl0aG9ufQoKcnVsZSBoaXNhdDJfR2Vub21lX2luZGV4OgogICAgaW5wdXQ6CiAgICAgICAgIkdlbm9tZS9kbTYuZmEiCiAgICBvdXRwdXQ6CiAgICAgICAgIkdlbm9tZS9JbmRleC9kbTYuMS5odDIiCiAgICB0aHJlYWRzOiA3CiAgICBjb25kYToKICAgICAgICAiZW52cy9jb3JlLnlhbWwiCiAgICBsb2c6CiAgICAgICAgImxvZ3MvaGlzYXQyX0dlbm9tZV9pbmRleC5sb2ciCiAgICBzaGVsbDoKICAgICAgICAiaGlzYXQyLWJ1aWxkIC1wIHt0aHJlYWRzfSB7aW5wdXR9IEdlbm9tZS9JbmRleC9kbTYgMj4ge2xvZ30iCmBgYAoKVGhpcyBjb2RlIGNvcnJlc3BvbmQgdG8gdGhlIGluZGV4aW5nIHN0ZXAsIGluIHdoaWNoIGl0IHRha2VzIGBHZW5vbWUvZG02LmZhYCBhcyBpbnB1dCBhbmQgYEdlbm9tZS9JbmRleC9kbTYuMS5odDJgIGFzIG91dHB1dC4gVGhlIHJ1bGVzIGhhdmUgc2V2ZXJhbCBga2V5YCB3b3JkcyBieSB3aGljaCBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIGNvbW1hbmQgYXJlIGRlY2xhcmVkOgoqIGlucHV0OiBzZXQgb2YgaW50cHV0IGZpbGVzLCBpbiB0aGlzIGNhc2UganVzdCAiR2Vub21lL2RtNi5mYSIKKiBvdXRwdXQ6IHNldCBvZiBvdXRwdXQgZmlsZXMuIEluIHRoaXMgY2FzZSwgbW9yZSBvdXRwdXQgZmlsZXMgYXJlIGNyZWF0ZWQsIGJ1dCB0aGV5IGRvIG5vdCBoYXZlIHRvIGJlIHBvaW50ZWQgYnkgdGhlIGNvbW1hbmRzLCB3ZSBjYW4ganVzdCByZWZlciB0byBvbmUgb2YgdGhlIGZpbGVzIHRoYXQgaXMgY3JlYXRlZC4gU25ha2VtYWtlIHdpbGwgY2hlY2sgaWYgdGhpcyBmaWxlIGlzIHN1Y2Nlc3NmdWxseSBjcmVhdGVkIGFmdGVyIHRoZSBwcm9jZXNzIGlzIGZpbmlzaGVkLgoqIHRocmVhZHM6IG51bWJlciBvZiBwcm9jZXNzb3JzCiogY29uZGE6IHRoZSB2aXJ0dWFsIGVudmlyb25tZW50IGluIHdoaWNoIHRoZSBwcm9jZXNzIHdpbGwgYmUgcnVuLgoqIGxvZyA6IGZpbGUgdGhhdCBzdG9yZSBhbnl0aGluZyB0aGF0IGhpc2F0MiBvdXRwdXRzIHdoaWxlIGlzIGNyZWF0aW5nIHRoZSBpbmRleC4KKiBzaGVsbCA6IFRoaXMgaXMgdGhlIGZvcm11bGEgdG8gY3JlYXRlIHRoZSBzaGVsbCBjb21tYW5kIGdpdmVuIGFsbCB0aGUgcGFyYW1ldGVycyBkZXNjcmliZWQgYWJvdmUuCgpUaGUgaW5kZXhpbmcgcnVsZSBpcyBkaXJlY3RseSBjb25uZWN0ZWQgdG8gdGhlIGZvbGxvd2luZyBtYXBwaW5nIHJ1bGU6CgpgYGB7cHl0aG9ufQoKcnVsZSBoaXNhdDJfdG9fR2Vub21lOgogICAgaW5wdXQ6CiAgICAgICAgZmFzdHEgPSAiRkFTVFEve3NhbXBsZX0uZmFzdHEuZ3oiLAogICAgICAgIGdlbm9tZSA9ICJHZW5vbWUvSW5kZXgvZG02LjEuaHQyIgogICAgb3V0cHV0OgogICAgICAgIHRlbXAoImhpc2F0Mi97c2FtcGxlfS5zYW0iKSAgICMgVGVtcG9yYXJ5IG91dHB1dAogICAgdGhyZWFkczogMwogICAgY29uZGE6CiAgICAgICAgImVudnMvY29yZS55YW1sIgogICAgbG9nOgogICAgICAgICJsb2dzL2hpc2F0Ml90b19HZW5vbWUue3NhbXBsZX0ubG9nIgogICAgc2hlbGw6CiAgICAgICAgImhpc2F0MiAtcCAzIC1VIHtpbnB1dC5mYXN0cX0gLXggIEdlbm9tZS9JbmRleC9kbTYgID4ge291dHB1dH0gMj4ge2xvZ30iCgpgYGAKClRoaXMgcnVsZSB0YWtlcyBmYXN0cSBmaWxlcyBhcyBpbnB1dCBhbmQgYWxzbyB0aGUgZ2Vub21lIGluZGV4IGZpbGVzLiBBbGwgdGhlIHNhbXBsZSBuYW1lcyB3ZXJlIG9idGFpbmVkIGZyb21gTkNCSV9hY2Nlc3Npb25fbGlzdC50eHRgIGZpbGUsIHdoaWNoIGNvbnRhaW5zIHRoZSBTUkEgYWNjZXNzaW9uIGNvZGVzIGNvcnJlc3BvbmRpbmcgdG8gYWxsIHRoZSBzYW1wbGVzIHRoYXQgd2UgYXJlIGFuYWx5c2luZy4gSW5zaWRlIHRoZSBydWxlIGB7c2FtcGxlfWAgdGFrZXMgdGhlIHZhbHVlIG9mIGV2ZXJ5IGFjY2Vzc2lvbiBjb2RlLCBhbmQgYWxsb3cgc25ha2VtYWtlIHRvIGdlbmVyYXRlIGFsbCB0aGUgbWFwcGluZyBjb21tYW5kcyBmb3IgZXZlcnkgc2FtcGxlLiBBcyB3ZSBoZXJlIHNldCBgdGhyZWFkc2AgYXMgMywgZXZlcnkgbWFwcGluZyBwcm9jZXNzIHdpbGwgdXNlIDMgcHJvY2Vzc29ycywgd2hpY2ggbWVhbnMgdGhhdCAyIG1hcHBpbmcgcHJvY2Vzc2VzIGNhbiBiZSBydW4gaW4gcGFyYWxsZWwgd2hlbiA3IGNvcmVzIGFyZSBwcm92aWRlZC4gCgpUaGUgbmV4dCBydWxlIGBiYW1zdGF0c2AgdGFrZSBldmVyeSBTQU0gZmlsZSBhbmQgdHJhbnNmb3JtIGl0IHRvIEJBTSwgYnV0IGFsc28gdGhlIEJBTSBmaWxlIGlzIHNvcnRlZCBhbmQgaW5kZXhlZCBhdCB0aGUgc2FtZSB0aW1lOgoKYGBge3B5dGhvbn0KCnJ1bGUgc2FtVG9iYW06CiAgICBpbnB1dDoKICAgICAgICAiaGlzYXQyL3tzYW1wbGV9LnNhbSIKICAgIG91dHB1dDoKICAgICAgICAiaGlzYXQyL3tzYW1wbGV9LnNvcnRlZC5iYW0iCiAgICBjb25kYToKICAgICAgICAiZW52cy9jb3JlLnlhbWwiCiAgICBzaGVsbDoKICAgICAgICAic2FtdG9vbHMgdmlldyAtYiAge2lucHV0fSAgfCBzYW10b29scyBzb3J0IC0gLW8ge291dHB1dH0gJiYgc2FtdG9vbHMgaW5kZXgge291dHB1dH0gIgoKYGBgCgpCZWNhdXNlIFNBTSBmaWxlcyB3ZXJlIHByb2R1Y2VkIGFzIHRlbXBvcmFyeSBmaWxlcyAoYHRlbXAoImhpc2F0Mi97c2FtcGxlfS5zYW0iKWAgKSwgCmFzIHNvb24gYXMgYHNhbVRvYmFtYCBmaW5pc2hlcywgU0FNIGZpbGVzIGFyZSBkZWxldGVkLiBUaGlzIG9wdGltaXplcyB0aGUgZGlzayBzcGFjZSwgd2hpY2ggaXMgaW1wb3J0YW50IHdoZW4gYSBsYXJnZSBudW1iZXIgb2Ygc2FtcGxlcyBhcmUgcHJvY2Vzc2VkLiAKCkZpbmFsbHksIGFsbCB0aGVzZSBzdGVwcyBjb252ZXJnZSBhdDoKCmBgYHtweXRob259CnJ1bGUgZmVhdHVyZUNvdW50czoKICAgIGlucHV0OgogICAgICAgIGd0ZiA9ICJHZW5lX2Fubm90YXRpb24vZG02LkVuc2VtYmwuZ2VuZXMuZ3RmIiwKICAgICAgICBiYW0gPSBleHBhbmQoImhpc2F0Mi97c2FtcGxlfS5zb3J0ZWQuYmFtIiwgc2FtcGxlPVNBTVBMRVMpCiAgICBvdXRwdXQ6CiAgICAgICAgImZlYXR1cmVDb3VudHMvdG90YWxfc2FtcGxlcy5nZW5lX2NvdW50LnR4dCIKICAgIHRocmVhZHM6IDEKICAgIGNvbmRhOgogICAgICAgICJlbnZzL2NvcmUueWFtbCIKICAgIGxvZzoKICAgICAgICAibG9ncy9mZWF0dXJlQ291bnRzLnRvdGFsLmxvZyIKICAgIHNoZWxsOgogICAgICAgICJmZWF0dXJlQ291bnRzIC1hIHtpbnB1dC5ndGZ9IC1vIHtvdXRwdXR9IHtpbnB1dC5iYW19IDI+IHtsb2d9IgpgYGAKCldoZXJlIGB7aW5wdXQuZ3RmfWAgbGlzdCBhbGwgdGhlIHNvcnRlZCBiYW0gdGhhdCB3ZSBnZW5lcmF0ZWQuIE5vdGljZSB0aGF0IHRoaXMgU25ha2VmaWxlIHN0YXJ0cyB3aXRoIGBpbmNsdWRlOiAicnVsZXMvMDBfZG93bmxvYWRfZGF0YS5za20iIGAsIHdoaWNoIGlzIGEgc3RhdGVtZW50IHRoYXQgY29ubmVjdHMgdGhpcyBTbmFrZWZpbGUgd2l0aCBgcnVsZXMvMDBfZG93bmxvYWRfZGF0YS5za21gLiBUaGlzIGlzIGEgc2NyaXB0IHRoYXQgcmVhZCBhbGwgYWNjZXNzaW9uIGNvZGVzIGZyb20gYE5DQklfYWNjZXNzaW9uX2xpc3QudHh0YCBhbmQgc3RvcmVzIHRob3NlIGF0IGBTQU1QTEVTYCwgd2hpY2ggaXMgdGhlbiB1c2VkIGJ5IGBleHBhbmQoImhpc2F0Mi97c2FtcGxlfS5zb3J0ZWQuYmFtIiwgc2FtcGxlPVNBTVBMRVMpYCB0byBnZW5lcmF0ZSB0aGUgbGlzdCBvZiBhbGwgc29ydGVkIEJBTSBmaWxlcy4gCgoKIyMgNS4xIFF1YW50aWZ5aW5nIGFsbCB0aGUgc2FtcGxlcyBhdCBvbmNlCgpXZSBoYXZlIG9ubHkgcXVhbGlmaWVkIG9uZSBzYW1wbGUgc28gZmFyLCBidXQgbm93IGV4ZWN1dGluZyB0aGUgU25ha2VmaWUsIHdlIGNhbiBwcm9jZXNzIGFsbCB0aGUgb3RoZXIgc2FtcGxlcyBpbiBwYXJhbGxlbC4gRm9yIHRoaXMsIHdlIGZpcnN0IGFyZSBnb2luZyB0byBkbyBhIGBkcnktcnVuYCB0byBjaGVjayB0aGUgbGlzdCBvZiBjb21tYW5kcyB0aGF0IHNuYWtlbWFrZSB3aWxsIHJ1biBmb3IgdXMuIE9uIHRoZSBjb21tYW5kIGxpbmUgKGluIG91ciBiYXNlIGZvbGRlciwgYDAwX1JlcHJvZHVjaWJsZV9STkEtU2VxX1Byb2Nlc3NpbmdgKSwgcGxlYXNlIHdyaXRlOgoKYHNuYWtlbWFrZSAtbnAgZmVhdHVyZUNvdW50c2AKClRoaXMgY29tbWFuZCBzaG93cyB1cyBhbGwgdGhlIHN0ZXBzIHRoYXQgc25ha2VtYWtlIHdpbGwgdW50aWwgZXhlY3V0aW5nIGEgcnVsZSBuYW1lZCAqZmVhdHVyZUNvdW50cyogKHNlZSBTbmFrZWZpbGUncyBjb2RlKSB3aGljaCBydW4gYGZlYXR1cmVDb3VudHNgIG92ZXIgYWxsIHRoZSBzYW1wbGVzLiBXaGVyZSBgLW5gIHByZXZlbnQgc25ha2VtYWtlIGZyb20gcnVubmluZyB0aGUgcGlwZWxpbmUgYW5kIGAtcGAgcHJpbnRzIHRoZSBjb21tYW5kcyBmb3IgZWFjaCBzdGVwLiAgCgpUbyB2aXN1YWxpc2UgdGhlc2Ugc3RlcHMgcnVuOgoKYHNuYWtlbWFrZSBmZWF0dXJlQ291bnRzIC0tZGFnIHwgZG90IC1UcG5nID4gZmVhdHVyZUNvdW50cy5wbmdgCgpUaGlzIHdpbGwgcHJvZHVjZSB0aGUgZm9sbG93aW5nIGltYWdlOgoKIVtdKEltYWdlcy9mZWF0dXJlQ291bnRzLnBuZykKCldoaWNoIGhlbHAgdXMgdG8gdW5kZXJzdGFuZCB0aGUgcGxhbm5lZCBqb2IgZXhlY3V0aW9uLgoKRmluYWxseSwgdG8gcnVuIHRoZXNlIHN0ZXBzIHdlIG5lZWQgdG8gZW5hYmxlIHNuYWtlbWFrZSB0byB1c2UgdGhlIGVudmlyb25tZW50IGZpbGVzIGFyZSBuZWVkZWQgZm9yIGVhY2ggcnVsZSBieSBpbmNsdWRpbmcgYC0tdXNlLWNvbmRhYCBhbmQgYWxzbyB3ZSBzaG91bGQgbGltaXQgdGhlIG51bWJlciBvZiBwcm9jZXNzb3JzIHRvIDcgd2l0aCBgLS1jb3JlcyA3YC4KCioqRXhlcmNpc2UgNS4xLjEqKgoKRXhlY3V0ZSBgc25ha2VtYWtlYCB0byBxdWFudGlmeSBhbGwgdGhlIHNhbXBsZXMgdXNpbmcgZmVhdHVyZUNvdW50cy4gTG9vayBpbnNpZGUgYFNuYWtlZmlsZWBjb2RlIHRvIHNlZSB3aGVyZSB0aGUgZmluYWwgb3V0cHV0IG9mIGZlYXR1cmVDb3VudCB3aWxsIGJlIHN0b3JlZCBhbmQgY29tcGFyZSBpdCB3aXRoIHRoZSBvdXRwdXQgd2UgcHJldmlvdXNseSBoYWQgdXNpbmcgZmVhdHVyZUNvdW50cy4gIAoKCioqRXhlcmNpc2UgNS4xLjIqKgoKU25ha2VtYWtlIGNhbiBoYXZlIGluZGl2aWR1YWwgZmlsZXMgYXMgYSB0YXJnZXQuIFRoZSBmb2xsb3dpbmcgcnVsZToKCmBgYHtweXRob259CnJ1bGUgYmFtc3RhdHM6CiAgICBpbnB1dDoKICAgICAgICAiaGlzYXQyL3tzYW1wbGV9LnNvcnRlZC5iYW0iCiAgICBvdXRwdXQ6CiAgICAgICAgc3RhdHNfdHh0ID0gIlFDL3tzYW1wbGV9L3tzYW1wbGV9LnN0YXRzIiwKICAgICAgICBzdGF0c19odG1sID0gIlFDL3tzYW1wbGV9L3tzYW1wbGV9LnBsb3RzLmh0bWwiCiAgICBwYXJhbXM6CiAgICAgICAgIlFDL3tzYW1wbGV9L3tzYW1wbGV9LnBsb3RzIgogICAgY29uZGE6CiAgICAgICAgImVudnMvY29yZS55YW1sIgogICAgc2hlbGw6CiAgICAgICAgInNhbXRvb2xzIHN0YXRzIHtpbnB1dH0gPiB7b3V0cHV0LnN0YXRzX3R4dH0gJiYgcGxvdC1iYW1zdGF0cyAtcCB7cGFyYW1zfSB7b3V0cHV0LnN0YXRzX3R4dH0iCmBgYAoKV2FzIG5vdCBpbmNsdWRlZCBhcyBwYXJ0IG9mIG91ciB3b3JrZmxvdywgYXMgaXQgd2FzIG5vdCByZXF1aXJlZCB0byBydW4gZmVhdHVyZUNvdW50cy4gVG8gcnVuIHRoaXMgcnVsZSBmb3IgYSBwYXJ0aWN1bGFyIGZpbGUsIHlvdSBoYXZlIHRhcmdldCBvbmUgb2YgdGhlIG91dHB1dCB0aGF0IGlzIGdlbmVyYXRlZCBieSB0aGlzIHJ1bGUgZm9yIGEgcGFydGljdWxhciBmaWxlIHVzaW5nIHRoZSBmb2xsb3dpbmcgZm9ybXVsYToKCmBzbmFrZW1ha2UgLS11c2UtY29uZGEgUUMvU0FNUExFL1NBTVBMRS5wbG90cy5odG1sYAoKV2hlcmUgU0FNUExFIGNhbiBiZSBhbnkgb2YgdGhlIGFjY2Vzc2lvbiBjb2RlcyBmcm9tIGBOQ0JJX2FjY2Vzc2lvbl9saXN0LnR4dGAuIENhbiB5b3UgcnVuIHRoaXMgcnVsZT8gV2hhdCB1c2VmdWwgaW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIG9uIHRoZSBvdXRwdXQgaHRtbCBmaWxlPgo=