Objectives
This notebook will demonstrate how to:
- Navigate the terminal interface
- Quantify single cell expression data with Alevin
- Perform basic quality control and interpret results
In this notebook, we will be running through the basics of processing
raw single-cell RNA-seq data.
We will be using a tag-based scRNA-seq sample from the Tabula
Muris project. This dataset is made of 20 mouse organs that
were sequenced using 10x Genomics Chromium single cell sequencing
methods. For 10x Genomics scRNA-seq data, cells are separated by
emulsion/droplets, and individual cells are given barcodes (often
abbreviated ‘CB’ in documentation). Each transcript will also contain a
Unique
Molecular Identifiers (UMIs) which allow us to examine PCR
amplification errors and biases.
Roadmap: Preprocessing & Initial QC
About the data
We obtained these data from Tabula Muris project’s Figshare.
The BAM files that were on Figshare were converted to fastq
files using bamtofastq
from 10x Genomics. We will process a fastq
file from mouse
bladder for this as an example.
To limit the amount of time this takes to run in the context of this
workshop, we are only running part of the sample’s reads.
Note: Depending on the format of the data you are working
with, i.e., if you have a set of .bcl
files, you may need
to use cellranger mkfastq
to create .fastq
files for each sample. However, most
public data is available in fastq
format, and most
sequencing cores will generate the .fastq
files, so that is
where we will start.
Checking directories and files
If you have opened the scRNA-seq.Rproj
file, your
Terminal should already be set to the scRNA-seq
directory,
but it is worth checking with the pwd
command in the
Terminal (or by looking at the path shown in the command prompt or at
the top of the Terminal pane).
If you are in a different directory, we will want to use
cd
to change to the training-modules/scRNA-seq
directory.
Copy and paste the text in the code blocks below into your
Terminal
window in RStudio. It should be in the lower left
hand corner as a tab next to Console
.
cd ~/training-modules/scRNA-seq
Once you are there, you should be able to run the following command
in the Terminal to look at the contents of the
data/tabula-muris
directory:
ls data/tabula-muris
Here you will see the fastq
directory, which is actually
a link to a shared folder with the raw fastq files, split by sample. We
will use these files, but we will not write to this directory.
We can look inside the contents of the fastq
directory,
and we should see 16 subfolders corresponding to 16 different samples.
Within each of these folders should be the fastq
files
associated with that sample. Again, we can use the ls
command to show the contents of each of the directories.
In this scenario, 10X_P4_3
refers to the sample name
that we will be processing, which contains data from mouse bladder
cells.
ls data/tabula-muris/fastq/10X_P4_3
You should see a list of multiple fastq
files all
starting with 10X_P4_3
, indicating the sample name.
If you notice, each fastq
file name contains either
R1
or R2
. These correspond to the two
sequencing reads of a paired-end library. For 10x data, the first read
(the R1
file) will contain the cell barcode and UMI
sequence, and the second read (the R2
file) will contain a
cDNA sequence corresponding to the captured transcript. We will need
both of these files to quantify our data.
10X_P4_3_L001_R1_001.fastq.gz 10X_P4_3_L002_R1_001.fastq.gz
10X_P4_3_L001_R1_002.fastq.gz 10X_P4_3_L002_R1_002.fastq.gz
10X_P4_3_L001_R1_003.fastq.gz 10X_P4_3_L002_R1_003.fastq.gz
10X_P4_3_L001_R2_001.fastq.gz 10X_P4_3_L002_R2_001.fastq.gz
10X_P4_3_L001_R2_002.fastq.gz 10X_P4_3_L002_R2_002.fastq.gz
10X_P4_3_L001_R2_003.fastq.gz 10X_P4_3_L002_R2_003.fastq.gz
Sequencing runs are often split into multiple fastq
files, both when a sample was run across multiple lanes and to keep the
individual file sizes down. This was the case for the Tabula
Muris data we are using here as well. The files that you see in the
data/tabula-muris/fastq/10X_P4_3
directory shown above
represent 2 lanes worth of data, with three R1 and three R2 files per
lane.
You will also see the file TM_droplet_metadata.csv
,
which contains metadata for the Tabula Muris experiments.
Set up output directory
Now that we are in scRNA-seq
, we’ll make a directory for
us to store our quantification files in. In Terminal
, run
the following command:
mkdir -p data/tabula-muris/alevin-quant/10X_P4_3_subset
Quantifying cell expression with Salmon Alevin
Alevin
is run from the command line (Terminal) to perform mapping and
quantification of tag-based single cell expression data.
Indexing the mouse transcriptome
Before you can quantify with Salmon and Alevin
we need to index the transcriptome for the species we will be mapping
to. This step would be the same for mapping bulk RNA-seq data, and you
can use the same transcriptome indexes as bulk RNA-seq, however, due to
the shorter read lengths in the 10x sequencing, we may want to use
shorter kmers than the default index size that salmon uses. In this
instance, we used a -k
of 23.
In the interest of time, we have already run the command below and
have the index built and ready for you in a shared directory.
But for your own reference, here is how you might do it yourself:
# salmon --threads=16 --no-version-check index \
# -t Mus_musculus.GRCm38.cdna.all.fa.gz \
# -i index/Mus_musculus/short_index \
# -k 23
Scripts to build the indexes like those we are using here (and
others) can be found in this
repository.
Running Salmon Alevin
Copy and paste this in your Terminal
to run the Alevin
quantification. This will take about 20 minutes to run, so we will start
now, then talk about the options.
Note that here we are only giving the full paths to one of the
R1
files and one of the R2
files. For the sake
of time, we are only going to be running this on a subset of reads, but
will also show you how to run it on the full sample.
salmon alevin \
-i index/Mus_musculus/short_index \
-l ISR \
-1 data/tabula-muris/fastq/10X_P4_3/10X_P4_3_L001_R1_001.fastq.gz \
-2 data/tabula-muris/fastq/10X_P4_3/10X_P4_3_L001_R2_001.fastq.gz \
-o data/tabula-muris/alevin-quant/10X_P4_3_subset \
-p 4 \
--chromium \
--tgMap index/Mus_musculus/Mus_musculus.GRCm38.95.versioned_tx2gene.tsv \
--dumpFeatures
Salmon Alevin command line options
For detailed information about all options available see the Alevin
documentation and Salmon
documentation.
Many of the options for the salmon alevin
command are
the same as those you would see when mapping and quantifying bulk
RNA-seq data with salmon quant
:
-i
gives the location of the transcriptome index
-1
and -2
are the paired read input
files
-o
designates the output folder
-p
allows us to specify how many processors to use; in
this case we will use 4
-l
The -l
option is for designating the library format. For
most single-cell quantification, you will want to use the
ISR
library type. See Salmon’s
documentation for more information on fragment library types (and
all of the other options available). Note that this option must come
before the read files.
--chromium
Because we are using 10x v2 chromium data, we have to use this flag
to tell alevin
where to expect the barcodes, UMIs and
sequence data. If we were using 10x v3 data, we would need the
--chromiumV3
flag instead. Drop-seq data is also supported,
for which we would use the --dropseq
flag instead of
this.
--tgMap
The transcriptome file that we are mapping to has separate sequences
for each transcript of a gene, but due to the sparse nature of
single-cell data, we are not likely to be able to meaningfully
distinguish among different transcripts. For this reason,
alevin
will quantify our results at the gene level, so we
need to provide a file that maps each transcript to its gene. For this
example, we’ve pre-made the file
Mus_musculus.GRCm38.95.versioned_tx2gene.tsv
from the
Ensembl transcriptome that we indexed above. The file is a TSV
(tab-separated values) file with 2 columns: one of transcripts and the
other the gene that each comes from.
--dumpFeatures
This option will print out information that we will use for quality
checks later on, including files with information on the UMIs and cell
barcodes.
See the Alevin
documentation for a complete list of the Alevin options. There are
also a number of example analyses at the Alevin
tutorial website.
Note: Running the FULL sample.
When we took a look at the
data/tabula-muris/fastq/10X_P4_3
directory earlier, we
noticed that there were multiple files representing 2 lanes worth of
data, with three R1 and three R2 files per lane:
We should really run all of these through Salmon, though that will
take about six times as long as the single pair of reads we used. To do
this, we could list each R1 and R2 file (space separated) after the
-1
and -2
arguments, respectively. But that is
a lot of typing, so a nice shortcut is to use a *
character
to represent a wildcard that will be filled in with whatever characters
are present in the files at the given path. In the pattern
10X_P4_3_L*_R1_*.fastq.gz
, this would allow any lane number
and any subset, so would match all of the following files (all the R1
files in this case):
10X_P4_3_L001_R1_001.fastq.gz 10X_P4_3_L002_R1_001.fastq.gz
10X_P4_3_L001_R1_002.fastq.gz 10X_P4_3_L002_R1_002.fastq.gz
10X_P4_3_L001_R1_003.fastq.gz 10X_P4_3_L002_R1_003.fastq.gz
For this directory, that would make our full
salmon alevin
command look like this (don’t run this
now!):
# salmon alevin \
# -i index/Mus_musculus/short_index \
# -l ISR \
# -1 data/tabula-muris/fastq/10X_P4_3/10X_P4_3_L*_R1_*.fastq.gz \
# -2 data/tabula-muris/fastq/10X_P4_3/10X_P4_3_L*_R2_*.fastq.gz \
# -o data/tabula-muris/alevin-quant/10X_P4_3 \
# -p 4 \
# --chromium \
# --tgMap index/Mus_musculus/Mus_musculus.GRCm38.95.versioned_tx2gene.tsv \
# --dumpFeatures
In general, you will want to run all lanes and all files for a given
sample together. But DO NOT combine multiple
samples into a single alevin
quantification! Keep
separate samples (and replicates) separate!
Initial quality control with alevinQC
Now that we have quantified our data with Alevin, we are ready to
perform initial quality control checks.
In order to perform these quality control checks, we’ll use the
alevinQC
R package. Note that alevinQC
depends
on files that we get using the--dumpFeatures
option in
Alevin.
About the alevinQCReport()
function: The first argument
needs to be where the sample’s output data was put when Alevin was run
(as a character string, aka using quotes). The rest of
alevinQCReport()
’s arguments tell R where to put the output
QC report.
# First, define path to alevin output:
alevin_path <- file.path("data", "tabula-muris", "alevin-quant", "10X_P4_3_subset")
# Produce a QC report of results found in the `alevin_path` directory
alevinQC::alevinQCReport(alevin_path,
sampleId = "10X_P4_3_subset",
outputFile = "10X_P4_3_subset-qc_report.html",
outputDir = "qc-reports")
Look for the 10X_P4_3_subset-qc_report.html
file created
in the qc-reports
directory to examine the quality of your
data and performance of Alevin.
We have also placed an example of a poor quality sample alevinQC
report in the qc-reports
directory, with the name
Bad_Example_10X_P4_2_qc_report.html
.
This report will show a few key metrics that inform you about the
quality of your sample. There is a lot of information included in the
report, so some key metrics to note are included in the
Summary tables
:
- Fraction of reads in whitelist barcodes
- Mean number of reads per cell
- Median number of detected genes per cell
The fraction of reads in whitelist barcodes is particularly important
as a low percentage here means the library contains many reads that do
not contain the expected cell barcodes. This is indicative of poor
single-cell capture during library construction.
The mean number of reads per cell and median number of detected genes
per cell can be helpful in understanding how deeply the library was
sequenced. The higher these numbers are, the more information you will
obtain per cell.
The knee plot shows the number of distinct UMIs for
each possible cell barcode on the y-axis, with the barcodes ranked from
the most UMIs to the fewest along the x-axis.
Cell barcodes with low UMI counts are likely to be empty droplets
that did not contain a cell. These droplets must be filtered out so we
only consider true cells for downstream analysis.
To do this, we can look for a “knee” on the curve where the number of
UMIs per barcode starts to drop off rapidly, with the intuition that
this is where we are reaching the end of the UMIs per cell distribution
for true cells. We can then choose a threshold below the knee and only
include barcodes above this threshold in the final cell barcode
list.
This “knee” method, which is implemented by alevin
, is
fairly effective and does not require any read mapping or quantification
before filtering. More recent versions of Cell Ranger use a somewhat
different method based on the “empty drops” method of Lun et al.
(2019), that is applied after initial gene quantification. This
allows filtering to retain cells with low counts that are nonetheless
likely to represent real cells.
Next steps: Loading Alevin output into R
After we have successfully quantified our tag-based scRNA-seq data
(and done some QC), we will want to read it into R to start to analyze
it. The easiest way to do this is to use the tximeta
package, which we will introduce in the next notebook.
Session Info
sessionInfo()
R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so; LAPACK version 3.10.0
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
time zone: Etc/UTC
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] optparse_1.7.5
loaded via a namespace (and not attached):
[1] digest_0.6.35 R6_2.5.1 fastmap_1.1.1 xfun_0.43
[5] magrittr_2.0.3 cachem_1.0.8 getopt_1.20.4 glue_1.7.0
[9] stringr_1.5.1 knitr_1.46 htmltools_0.5.8.1 rmarkdown_2.26
[13] lifecycle_1.0.4 cli_3.6.2 sass_0.4.9 vctrs_0.6.5
[17] jquerylib_0.1.4 compiler_4.4.1 tools_4.4.1 bslib_0.7.0
[21] evaluate_0.23 yaml_2.3.8 jsonlite_1.8.8 rlang_1.1.3
[25] stringi_1.8.3
LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyB0YWctYmFzZWQgc2luZ2xlLWNlbGwgUk5BLXNlcSBkYXRhIHdpdGggQWxldmluIgphdXRob3I6IENDREwgZm9yIEFMU0YKZGF0ZTogMjAyMQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIE5hdmlnYXRlIHRoZSB0ZXJtaW5hbCBpbnRlcmZhY2UKLSBRdWFudGlmeSBzaW5nbGUgY2VsbCBleHByZXNzaW9uIGRhdGEgd2l0aCBBbGV2aW4gCi0gUGVyZm9ybSBiYXNpYyBxdWFsaXR5IGNvbnRyb2wgYW5kIGludGVycHJldCByZXN1bHRzIAoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSB3aWxsIGJlIHJ1bm5pbmcgdGhyb3VnaCB0aGUgYmFzaWNzIG9mIHByb2Nlc3NpbmcgcmF3IHNpbmdsZS1jZWxsIFJOQS1zZXEgZGF0YS4KCldlIHdpbGwgYmUgdXNpbmcgYSB0YWctYmFzZWQgc2NSTkEtc2VxIHNhbXBsZSBmcm9tIHRoZSBbKlRhYnVsYSBNdXJpcyogcHJvamVjdF0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1ODYtMDE4LTA1OTAtNCkuClRoaXMgZGF0YXNldCBpcyBtYWRlIG9mIDIwIG1vdXNlIG9yZ2FucyB0aGF0IHdlcmUgc2VxdWVuY2VkIHVzaW5nIDEweCBHZW5vbWljcyBDaHJvbWl1bSBzaW5nbGUgY2VsbCBzZXF1ZW5jaW5nIG1ldGhvZHMuCkZvciAxMHggR2Vub21pY3Mgc2NSTkEtc2VxIGRhdGEsIGNlbGxzIGFyZSBzZXBhcmF0ZWQgYnkgZW11bHNpb24vZHJvcGxldHMsIGFuZCBpbmRpdmlkdWFsIGNlbGxzIGFyZSBnaXZlbiBiYXJjb2RlcyAob2Z0ZW4gYWJicmV2aWF0ZWQgJ0NCJyBpbiBkb2N1bWVudGF0aW9uKS4KRWFjaCB0cmFuc2NyaXB0IHdpbGwgYWxzbyBjb250YWluIGEgW1VuaXF1ZSBNb2xlY3VsYXIgSWRlbnRpZmllcnMgKFVNSXMpXShodHRwOi8vd3d3Lm5hdHVyZS5jb20vZG9pZmluZGVyLzEwLjEwMzgvbm1ldGguMjc3Mikgd2hpY2ggYWxsb3cgdXMgdG8gZXhhbWluZSBQQ1IgYW1wbGlmaWNhdGlvbiBlcnJvcnMgYW5kIGJpYXNlcy4KCiFbUm9hZG1hcDogUHJlcHJvY2Vzc2luZyAmIEluaXRpYWwgUUNdKGRpYWdyYW1zL3JvYWRtYXBfc2luZ2xlX3ByZXByb2Nlc3NfcWMucG5nKQoKIyMgQWJvdXQgdGhlIGRhdGEKCldlIG9idGFpbmVkIHRoZXNlIGRhdGEgZnJvbSBUYWJ1bGEgTXVyaXMgcHJvamVjdCdzIFtGaWdzaGFyZV0oaHR0cHM6Ly9maWdzaGFyZS5jb20vcHJvamVjdHMvVGFidWxhX011cmlzX1RyYW5zY3JpcHRvbWljX2NoYXJhY3Rlcml6YXRpb25fb2ZfMjBfb3JnYW5zX2FuZF90aXNzdWVzX2Zyb21fTXVzX211c2N1bHVzX2F0X3NpbmdsZV9jZWxsX3Jlc29sdXRpb24vMjc3MzMpLgpUaGUgQkFNIGZpbGVzIHRoYXQgd2VyZSBvbiBGaWdzaGFyZSB3ZXJlIGNvbnZlcnRlZCB0byBgZmFzdHFgIGZpbGVzIHVzaW5nCltgYmFtdG9mYXN0cWBdKGh0dHBzOi8vc3VwcG9ydC4xMHhnZW5vbWljcy5jb20vZG9jcy9iYW10b2Zhc3RxKSBmcm9tIDEweCBHZW5vbWljcy4KV2Ugd2lsbCBwcm9jZXNzIGEgYGZhc3RxYCBmaWxlIGZyb20gbW91c2UgYmxhZGRlciBmb3IgdGhpcyBhcyBhbiBleGFtcGxlLiAgClRvIGxpbWl0IHRoZSBhbW91bnQgb2YgdGltZSB0aGlzIHRha2VzIHRvIHJ1biBpbiB0aGUgY29udGV4dCBvZiB0aGlzIHdvcmtzaG9wLAp3ZSBhcmUgb25seSBydW5uaW5nIHBhcnQgb2YgdGhlIHNhbXBsZSdzIHJlYWRzLgoKKk5vdGUqOiBEZXBlbmRpbmcgb24gdGhlIGZvcm1hdCBvZiB0aGUgZGF0YSB5b3UgYXJlIHdvcmtpbmcgd2l0aCwgaS5lLiwgaWYgeW91IGhhdmUgYSBzZXQgb2YgYC5iY2xgIGZpbGVzLCB5b3UgbWF5IG5lZWQgdG8gdXNlIFtgY2VsbHJhbmdlciBta2Zhc3RxYF0oaHR0cHM6Ly9zdXBwb3J0LjEweGdlbm9taWNzLmNvbS9zaW5nbGUtY2VsbC1nZW5lLWV4cHJlc3Npb24vc29mdHdhcmUvcGlwZWxpbmVzL2xhdGVzdC91c2luZy9ta2Zhc3RxKSB0byBjcmVhdGUgYC5mYXN0cWAgZmlsZXMgZm9yIGVhY2ggc2FtcGxlLgpIb3dldmVyLCBtb3N0IHB1YmxpYyBkYXRhIGlzIGF2YWlsYWJsZSBpbiBgZmFzdHFgIGZvcm1hdCwgYW5kIG1vc3Qgc2VxdWVuY2luZyBjb3JlcyB3aWxsIGdlbmVyYXRlIHRoZSBgLmZhc3RxYCBmaWxlcywgc28gdGhhdCBpcyB3aGVyZSB3ZSB3aWxsIHN0YXJ0LgoKCiMjIENoZWNraW5nIGRpcmVjdG9yaWVzIGFuZCBmaWxlcwoKSWYgeW91IGhhdmUgb3BlbmVkIHRoZSBgc2NSTkEtc2VxLlJwcm9qYCBmaWxlLCB5b3VyIFRlcm1pbmFsIHNob3VsZCBhbHJlYWR5IGJlIHNldCB0byB0aGUgYHNjUk5BLXNlcWAgZGlyZWN0b3J5LCBidXQgaXQgaXMgd29ydGggY2hlY2tpbmcgd2l0aCB0aGUgYHB3ZGAgY29tbWFuZCBpbiB0aGUgVGVybWluYWwgCihvciBieSBsb29raW5nIGF0IHRoZSBwYXRoIHNob3duIGluIHRoZSBjb21tYW5kIHByb21wdCBvciBhdCB0aGUgdG9wIG9mIHRoZSBUZXJtaW5hbCBwYW5lKS4gCgpJZiB5b3UgYXJlIGluIGEgZGlmZmVyZW50IGRpcmVjdG9yeSwgd2Ugd2lsbCB3YW50IHRvIHVzZSBgY2RgIHRvIGNoYW5nZSB0byB0aGUgYHRyYWluaW5nLW1vZHVsZXMvc2NSTkEtc2VxYCBkaXJlY3RvcnkuIAoKQ29weSBhbmQgcGFzdGUgdGhlIHRleHQgaW4gdGhlIGNvZGUgYmxvY2tzIGJlbG93IGludG8geW91ciBgVGVybWluYWxgIHdpbmRvdyBpbiBSU3R1ZGlvLgpJdCBzaG91bGQgYmUgaW4gdGhlIGxvd2VyIGxlZnQgaGFuZCBjb3JuZXIgYXMgYSB0YWIgbmV4dCB0byBgQ29uc29sZWAuCgpgYGBiYXNoCmNkIH4vdHJhaW5pbmctbW9kdWxlcy9zY1JOQS1zZXEKYGBgCgpPbmNlIHlvdSBhcmUgdGhlcmUsIHlvdSBzaG91bGQgYmUgYWJsZSB0byBydW4gdGhlIGZvbGxvd2luZyBjb21tYW5kIGluIHRoZSBUZXJtaW5hbCB0byBsb29rIGF0IHRoZSBjb250ZW50cyBvZiB0aGUgYGRhdGEvdGFidWxhLW11cmlzYCBkaXJlY3Rvcnk6CgpgYGBiYXNoCmxzIGRhdGEvdGFidWxhLW11cmlzCmBgYAoKSGVyZSB5b3Ugd2lsbCBzZWUgdGhlIGBmYXN0cWAgZGlyZWN0b3J5LCB3aGljaCBpcyBhY3R1YWxseSBhIGxpbmsgdG8gYSBzaGFyZWQgZm9sZGVyIHdpdGggdGhlIHJhdyBmYXN0cSBmaWxlcywgc3BsaXQgYnkgc2FtcGxlLgpXZSB3aWxsIHVzZSB0aGVzZSBmaWxlcywgYnV0IHdlIHdpbGwgbm90IHdyaXRlIHRvIHRoaXMgZGlyZWN0b3J5LiAKCldlIGNhbiBsb29rIGluc2lkZSB0aGUgY29udGVudHMgb2YgdGhlIGBmYXN0cWAgZGlyZWN0b3J5LCBhbmQgd2Ugc2hvdWxkIHNlZSAxNiBzdWJmb2xkZXJzIGNvcnJlc3BvbmRpbmcgdG8gMTYgZGlmZmVyZW50IHNhbXBsZXMuIApXaXRoaW4gZWFjaCBvZiB0aGVzZSBmb2xkZXJzIHNob3VsZCBiZSB0aGUgYGZhc3RxYCBmaWxlcyBhc3NvY2lhdGVkIHdpdGggdGhhdCBzYW1wbGUuIApBZ2Fpbiwgd2UgY2FuIHVzZSB0aGUgYGxzYCBjb21tYW5kIHRvIHNob3cgdGhlIGNvbnRlbnRzIG9mIGVhY2ggb2YgdGhlIGRpcmVjdG9yaWVzLiAKCkluIHRoaXMgc2NlbmFyaW8sIGAxMFhfUDRfM2AgcmVmZXJzIHRvIHRoZSBzYW1wbGUgbmFtZSB0aGF0IHdlIHdpbGwgYmUgcHJvY2Vzc2luZywgd2hpY2ggY29udGFpbnMgZGF0YSBmcm9tIG1vdXNlIGJsYWRkZXIgY2VsbHMuCgpgYGBiYXNoCmxzIGRhdGEvdGFidWxhLW11cmlzL2Zhc3RxLzEwWF9QNF8zCmBgYAoKWW91IHNob3VsZCBzZWUgYSBsaXN0IG9mIG11bHRpcGxlIGBmYXN0cWAgZmlsZXMgYWxsIHN0YXJ0aW5nIHdpdGggYDEwWF9QNF8zYCwgaW5kaWNhdGluZyB0aGUgc2FtcGxlIG5hbWUuIAoKSWYgeW91IG5vdGljZSwgZWFjaCBgZmFzdHFgIGZpbGUgbmFtZSBjb250YWlucyBlaXRoZXIgYFIxYCBvciBgUjJgLgpUaGVzZSBjb3JyZXNwb25kIHRvIHRoZSB0d28gc2VxdWVuY2luZyByZWFkcyBvZiBhIHBhaXJlZC1lbmQgbGlicmFyeS4KRm9yIDEweCBkYXRhLCB0aGUgZmlyc3QgcmVhZCAodGhlIGBSMWAgZmlsZSkgd2lsbCBjb250YWluIHRoZSBjZWxsIGJhcmNvZGUgYW5kIFVNSSBzZXF1ZW5jZSwgYW5kIHRoZSBzZWNvbmQgcmVhZCAodGhlIGBSMmAgZmlsZSkgd2lsbCBjb250YWluIGEgY0ROQSBzZXF1ZW5jZSBjb3JyZXNwb25kaW5nIHRvIHRoZSBjYXB0dXJlZCB0cmFuc2NyaXB0LgpXZSB3aWxsIG5lZWQgYm90aCBvZiB0aGVzZSBmaWxlcyB0byBxdWFudGlmeSBvdXIgZGF0YS4gCgpgYGAKMTBYX1A0XzNfTDAwMV9SMV8wMDEuZmFzdHEuZ3ogIDEwWF9QNF8zX0wwMDJfUjFfMDAxLmZhc3RxLmd6CjEwWF9QNF8zX0wwMDFfUjFfMDAyLmZhc3RxLmd6ICAxMFhfUDRfM19MMDAyX1IxXzAwMi5mYXN0cS5negoxMFhfUDRfM19MMDAxX1IxXzAwMy5mYXN0cS5neiAgMTBYX1A0XzNfTDAwMl9SMV8wMDMuZmFzdHEuZ3oKMTBYX1A0XzNfTDAwMV9SMl8wMDEuZmFzdHEuZ3ogIDEwWF9QNF8zX0wwMDJfUjJfMDAxLmZhc3RxLmd6CjEwWF9QNF8zX0wwMDFfUjJfMDAyLmZhc3RxLmd6ICAxMFhfUDRfM19MMDAyX1IyXzAwMi5mYXN0cS5negoxMFhfUDRfM19MMDAxX1IyXzAwMy5mYXN0cS5neiAgMTBYX1A0XzNfTDAwMl9SMl8wMDMuZmFzdHEuZ3oKYGBgCgpTZXF1ZW5jaW5nIHJ1bnMgYXJlIG9mdGVuIHNwbGl0IGludG8gbXVsdGlwbGUgYGZhc3RxYCBmaWxlcywgYm90aCB3aGVuIGEgc2FtcGxlIHdhcyBydW4gYWNyb3NzIG11bHRpcGxlIGxhbmVzIGFuZCB0byBrZWVwIHRoZSBpbmRpdmlkdWFsIGZpbGUgc2l6ZXMgZG93bi4gClRoaXMgd2FzIHRoZSBjYXNlIGZvciB0aGUgKlRhYnVsYSBNdXJpcyogZGF0YSB3ZSBhcmUgdXNpbmcgaGVyZSBhcyB3ZWxsLiAKVGhlIGZpbGVzIHRoYXQgeW91IHNlZSBpbiB0aGUgYGRhdGEvdGFidWxhLW11cmlzL2Zhc3RxLzEwWF9QNF8zYCBkaXJlY3Rvcnkgc2hvd24gYWJvdmUgcmVwcmVzZW50IDIgbGFuZXMgd29ydGggb2YgZGF0YSwgd2l0aCB0aHJlZSBSMSBhbmQgdGhyZWUgUjIgZmlsZXMgcGVyIGxhbmUuCgpZb3Ugd2lsbCBhbHNvIHNlZSB0aGUgZmlsZSBgVE1fZHJvcGxldF9tZXRhZGF0YS5jc3ZgLCB3aGljaCBjb250YWlucyBtZXRhZGF0YSBmb3IgdGhlICpUYWJ1bGEgTXVyaXMqIGV4cGVyaW1lbnRzLgoKIyMjIFNldCB1cCBvdXRwdXQgZGlyZWN0b3J5CgpOb3cgdGhhdCB3ZSBhcmUgaW4gYHNjUk5BLXNlcWAsIHdlJ2xsIG1ha2UgYSBkaXJlY3RvcnkgZm9yIHVzIHRvIHN0b3JlIG91ciBxdWFudGlmaWNhdGlvbiBmaWxlcyBpbi4KSW4gYFRlcm1pbmFsYCwgcnVuIHRoZSBmb2xsb3dpbmcgY29tbWFuZDoKCmBgYGJhc2gKbWtkaXIgLXAgZGF0YS90YWJ1bGEtbXVyaXMvYWxldmluLXF1YW50LzEwWF9QNF8zX3N1YnNldApgYGAKCiMjIFF1YW50aWZ5aW5nIGNlbGwgZXhwcmVzc2lvbiB3aXRoIFNhbG1vbiBBbGV2aW4KCltBbGV2aW5dKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMzA1OS0wMTktMTY3MC15KSBpcyBydW4gZnJvbSB0aGUgY29tbWFuZCBsaW5lIChUZXJtaW5hbCkgdG8gcGVyZm9ybSBtYXBwaW5nIGFuZCBxdWFudGlmaWNhdGlvbiBvZiB0YWctYmFzZWQgc2luZ2xlIGNlbGwgZXhwcmVzc2lvbiBkYXRhLiAKCiMjIyBJbmRleGluZyB0aGUgbW91c2UgdHJhbnNjcmlwdG9tZSAKCkJlZm9yZSB5b3UgY2FuIHF1YW50aWZ5IHdpdGggU2FsbW9uIGFuZCBbQWxldmluXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE5LTE2NzAteSkgd2UgbmVlZCB0byBpbmRleCB0aGUgdHJhbnNjcmlwdG9tZSBmb3IgdGhlIHNwZWNpZXMgd2Ugd2lsbCBiZSBtYXBwaW5nIHRvLgpUaGlzIHN0ZXAgd291bGQgYmUgdGhlIHNhbWUgZm9yIG1hcHBpbmcgYnVsayBSTkEtc2VxIGRhdGEsIGFuZCB5b3UgY2FuIHVzZSB0aGUgc2FtZSB0cmFuc2NyaXB0b21lIGluZGV4ZXMgYXMgYnVsayBSTkEtc2VxLCBob3dldmVyLCBkdWUgdG8gdGhlIHNob3J0ZXIgcmVhZCBsZW5ndGhzIGluIHRoZSAxMHggc2VxdWVuY2luZywgd2UgbWF5IHdhbnQgdG8gdXNlIHNob3J0ZXIga21lcnMgdGhhbiB0aGUgZGVmYXVsdCBpbmRleCBzaXplIHRoYXQgc2FsbW9uIHVzZXMuCkluIHRoaXMgaW5zdGFuY2UsIHdlIHVzZWQgYSBgLWtgIG9mIDIzLgoKSW4gdGhlIGludGVyZXN0IG9mIHRpbWUsIHdlIGhhdmUgYWxyZWFkeSBydW4gdGhlIGNvbW1hbmQgYmVsb3cgYW5kIGhhdmUgdGhlIGluZGV4IGJ1aWx0IGFuZCByZWFkeSBmb3IgeW91IGluIGEgc2hhcmVkIGRpcmVjdG9yeS4KCkJ1dCBmb3IgeW91ciBvd24gcmVmZXJlbmNlLCBoZXJlIGlzIGhvdyB5b3UgbWlnaHQgZG8gaXQgeW91cnNlbGY6CmBgYAojIHNhbG1vbiAtLXRocmVhZHM9MTYgLS1uby12ZXJzaW9uLWNoZWNrIGluZGV4IFwKIyAgLXQgTXVzX211c2N1bHVzLkdSQ20zOC5jZG5hLmFsbC5mYS5neiBcCiMgIC1pIGluZGV4L011c19tdXNjdWx1cy9zaG9ydF9pbmRleCBcCiMgIC1rIDIzCmBgYAoKU2NyaXB0cyB0byBidWlsZCB0aGUgaW5kZXhlcyBsaWtlIHRob3NlIHdlIGFyZSB1c2luZyBoZXJlIChhbmQgb3RoZXJzKSBjYW4gYmUgZm91bmQgaW4gW3RoaXMgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvdHJhaW5pbmctdHhvbWUtcHJlcCkuIAoKCiMjIyBSdW5uaW5nIFNhbG1vbiBBbGV2aW4KCkNvcHkgYW5kIHBhc3RlIHRoaXMgaW4geW91ciBgVGVybWluYWxgIHRvIHJ1biB0aGUgQWxldmluIHF1YW50aWZpY2F0aW9uLgpUaGlzIHdpbGwgdGFrZSBhYm91dCAyMCBtaW51dGVzIHRvIHJ1biwgc28gd2Ugd2lsbCBzdGFydCBub3csIHRoZW4gdGFsayBhYm91dCB0aGUgb3B0aW9ucy4KCk5vdGUgdGhhdCBoZXJlIHdlIGFyZSBvbmx5IGdpdmluZyB0aGUgZnVsbCBwYXRocyB0byBvbmUgb2YgdGhlIGBSMWAgZmlsZXMgYW5kIG9uZSBvZiB0aGUgYFIyYCBmaWxlcy4gCkZvciB0aGUgc2FrZSBvZiB0aW1lLCB3ZSBhcmUgb25seSBnb2luZyB0byBiZSBydW5uaW5nIHRoaXMgb24gYSBzdWJzZXQgb2YgcmVhZHMsIGJ1dCB3aWxsIGFsc28gc2hvdyB5b3UgaG93IHRvIHJ1biBpdCBvbiB0aGUgZnVsbCBzYW1wbGUuICAKCmBgYGJhc2gKc2FsbW9uIGFsZXZpbiBcCiAgLWkgaW5kZXgvTXVzX211c2N1bHVzL3Nob3J0X2luZGV4IFwKICAtbCBJU1IgXAogIC0xIGRhdGEvdGFidWxhLW11cmlzL2Zhc3RxLzEwWF9QNF8zLzEwWF9QNF8zX0wwMDFfUjFfMDAxLmZhc3RxLmd6IFwKICAtMiBkYXRhL3RhYnVsYS1tdXJpcy9mYXN0cS8xMFhfUDRfMy8xMFhfUDRfM19MMDAxX1IyXzAwMS5mYXN0cS5neiBcCiAgLW8gZGF0YS90YWJ1bGEtbXVyaXMvYWxldmluLXF1YW50LzEwWF9QNF8zX3N1YnNldCBcCiAgLXAgNCBcCiAgLS1jaHJvbWl1bSAgXAogIC0tdGdNYXAgaW5kZXgvTXVzX211c2N1bHVzL011c19tdXNjdWx1cy5HUkNtMzguOTUudmVyc2lvbmVkX3R4MmdlbmUudHN2IFwKICAtLWR1bXBGZWF0dXJlcwpgYGAKCiMjIyBTYWxtb24gQWxldmluIGNvbW1hbmQgbGluZSBvcHRpb25zCgpGb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gYWJvdXQgYWxsIG9wdGlvbnMgYXZhaWxhYmxlIHNlZSB0aGUgW0FsZXZpbiBkb2N1bWVudGF0aW9uXShodHRwczovL3NhbG1vbi5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvYWxldmluLmh0bWwpIGFuZCBbU2FsbW9uIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vc2FsbW9uLnJlYWR0aGVkb2NzLmlvL2VuL2xhdGVzdC9zYWxtb24uaHRtbCkuCgpNYW55IG9mIHRoZSBvcHRpb25zIGZvciB0aGUgYHNhbG1vbiBhbGV2aW5gIGNvbW1hbmQgYXJlIHRoZSBzYW1lIGFzIHRob3NlIHlvdSB3b3VsZCBzZWUgd2hlbiBtYXBwaW5nIGFuZCBxdWFudGlmeWluZyBidWxrIFJOQS1zZXEgZGF0YSB3aXRoIGBzYWxtb24gcXVhbnRgOgoKLSBgLWlgIGdpdmVzIHRoZSBsb2NhdGlvbiBvZiB0aGUgdHJhbnNjcmlwdG9tZSBpbmRleAotIGAtMWAgYW5kIGAtMmAgYXJlIHRoZSBwYWlyZWQgcmVhZCBpbnB1dCBmaWxlcwotIGAtb2AgZGVzaWduYXRlcyB0aGUgb3V0cHV0IGZvbGRlcgotIGAtcGAgYWxsb3dzIHVzIHRvIHNwZWNpZnkgaG93IG1hbnkgcHJvY2Vzc29ycyB0byB1c2U7IGluIHRoaXMgY2FzZSB3ZSB3aWxsIHVzZSA0CgoKIyMjIyBgLWxgClRoZSBgLWxgIG9wdGlvbiBpcyBmb3IgZGVzaWduYXRpbmcgdGhlIGxpYnJhcnkgZm9ybWF0LiAKRm9yIG1vc3Qgc2luZ2xlLWNlbGwgcXVhbnRpZmljYXRpb24sIHlvdSB3aWxsIHdhbnQgdG8gdXNlIHRoZSBgSVNSYCBsaWJyYXJ5IHR5cGUuClNlZSBbU2FsbW9uJ3MgZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9zYWxtb24ucmVhZHRoZWRvY3MuaW8vZW4vbGF0ZXN0L2xpYnJhcnlfdHlwZS5odG1sI2ZyYWdsaWJ0eXBlKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBmcmFnbWVudCBsaWJyYXJ5IHR5cGVzIChhbmQgYWxsIG9mIHRoZSBvdGhlciBvcHRpb25zIGF2YWlsYWJsZSkuCk5vdGUgdGhhdCB0aGlzIG9wdGlvbiBtdXN0IGNvbWUgKmJlZm9yZSogdGhlIHJlYWQgZmlsZXMuCgojIyMjIGAtLWNocm9taXVtYApCZWNhdXNlIHdlIGFyZSB1c2luZyAxMHggdjIgY2hyb21pdW0gZGF0YSwgd2UgaGF2ZSB0byB1c2UgdGhpcyBmbGFnIHRvIHRlbGwgYGFsZXZpbmAgd2hlcmUgdG8gZXhwZWN0IHRoZSBiYXJjb2RlcywgVU1JcyBhbmQgc2VxdWVuY2UgZGF0YS4KSWYgd2Ugd2VyZSB1c2luZyAxMHggdjMgZGF0YSwgd2Ugd291bGQgbmVlZCB0aGUgYC0tY2hyb21pdW1WM2AgZmxhZyBpbnN0ZWFkLiAKRHJvcC1zZXEgZGF0YSBpcyBhbHNvIHN1cHBvcnRlZCwgZm9yIHdoaWNoIHdlIHdvdWxkIHVzZSB0aGUgYC0tZHJvcHNlcWAgZmxhZyBpbnN0ZWFkIG9mIHRoaXMuCgoKIyMjIyBgLS10Z01hcGAKVGhlIHRyYW5zY3JpcHRvbWUgZmlsZSB0aGF0IHdlIGFyZSBtYXBwaW5nIHRvIGhhcyBzZXBhcmF0ZSBzZXF1ZW5jZXMgZm9yIGVhY2ggdHJhbnNjcmlwdCBvZiBhIGdlbmUsIGJ1dCBkdWUgdG8gdGhlIHNwYXJzZSBuYXR1cmUgb2Ygc2luZ2xlLWNlbGwgZGF0YSwgd2UgYXJlIG5vdCBsaWtlbHkgdG8gYmUgYWJsZSB0byBtZWFuaW5nZnVsbHkgZGlzdGluZ3Vpc2ggYW1vbmcgZGlmZmVyZW50IHRyYW5zY3JpcHRzLgpGb3IgdGhpcyByZWFzb24sIGBhbGV2aW5gIHdpbGwgcXVhbnRpZnkgb3VyIHJlc3VsdHMgYXQgdGhlIGdlbmUgbGV2ZWwsIHNvIHdlIG5lZWQgdG8gcHJvdmlkZSBhIGZpbGUgdGhhdCBtYXBzIGVhY2ggdHJhbnNjcmlwdCB0byBpdHMgZ2VuZS4KRm9yIHRoaXMgZXhhbXBsZSwgd2UndmUgcHJlLW1hZGUgdGhlIGZpbGUgYE11c19tdXNjdWx1cy5HUkNtMzguOTUudmVyc2lvbmVkX3R4MmdlbmUudHN2YCBmcm9tIHRoZSBFbnNlbWJsIHRyYW5zY3JpcHRvbWUgdGhhdCB3ZSBpbmRleGVkIGFib3ZlLiAKVGhlIGZpbGUgaXMgYSBUU1YgKHRhYi1zZXBhcmF0ZWQgdmFsdWVzKSBmaWxlIHdpdGggMiBjb2x1bW5zOiBvbmUgb2YgdHJhbnNjcmlwdHMgYW5kIHRoZSBvdGhlciB0aGUgZ2VuZSB0aGF0IGVhY2ggY29tZXMgZnJvbS4KCgojIyMjIGAtLWR1bXBGZWF0dXJlc2AKVGhpcyBvcHRpb24gd2lsbCBwcmludCBvdXQgaW5mb3JtYXRpb24gdGhhdCB3ZSB3aWxsIHVzZSBmb3IgcXVhbGl0eSBjaGVja3MgbGF0ZXIgb24sIGluY2x1ZGluZyBmaWxlcyB3aXRoIGluZm9ybWF0aW9uIG9uIHRoZSBVTUlzIGFuZCBjZWxsIGJhcmNvZGVzLgoKU2VlIHRoZSBbQWxldmluIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vc2FsbW9uLnJlYWR0aGVkb2NzLmlvL2VuL2xhdGVzdC9hbGV2aW4uaHRtbCkgZm9yIGEgY29tcGxldGUgbGlzdCBvZiB0aGUgQWxldmluIG9wdGlvbnMuClRoZXJlIGFyZSBhbHNvIGEgbnVtYmVyIG9mIGV4YW1wbGUgYW5hbHlzZXMgYXQgdGhlIFtBbGV2aW4gdHV0b3JpYWxdKGh0dHBzOi8vY29tYmluZS1sYWIuZ2l0aHViLmlvL2FsZXZpbi10dXRvcmlhbC8pIHdlYnNpdGUuCgoKIyMjIE5vdGU6IFJ1bm5pbmcgdGhlIEZVTEwgc2FtcGxlLgoKV2hlbiB3ZSB0b29rIGEgbG9vayBhdCB0aGUgYGRhdGEvdGFidWxhLW11cmlzL2Zhc3RxLzEwWF9QNF8zYCBkaXJlY3RvcnkgZWFybGllciwgd2Ugbm90aWNlZCB0aGF0IHRoZXJlIHdlcmUgbXVsdGlwbGUgZmlsZXMgcmVwcmVzZW50aW5nIDIgbGFuZXMgd29ydGggb2YgZGF0YSwgd2l0aCB0aHJlZSBSMSBhbmQgdGhyZWUgUjIgZmlsZXMgcGVyIGxhbmU6CgpXZSBzaG91bGQgcmVhbGx5IHJ1biBhbGwgb2YgdGhlc2UgdGhyb3VnaCBTYWxtb24sIHRob3VnaCB0aGF0IHdpbGwgdGFrZSBhYm91dCBzaXggdGltZXMgYXMgbG9uZyBhcyB0aGUgc2luZ2xlIHBhaXIgb2YgcmVhZHMgd2UgdXNlZC4gClRvIGRvIHRoaXMsIHdlIGNvdWxkIGxpc3QgZWFjaCBSMSBhbmQgUjIgZmlsZSAoc3BhY2Ugc2VwYXJhdGVkKSBhZnRlciB0aGUgYC0xYCBhbmQgYC0yYCBhcmd1bWVudHMsIHJlc3BlY3RpdmVseS4KQnV0IHRoYXQgaXMgYSBsb3Qgb2YgdHlwaW5nLCBzbyBhIG5pY2Ugc2hvcnRjdXQgaXMgdG8gdXNlIGEgYCpgIGNoYXJhY3RlciB0byByZXByZXNlbnQgYSB3aWxkY2FyZCB0aGF0IHdpbGwgYmUgZmlsbGVkIGluIHdpdGggd2hhdGV2ZXIgY2hhcmFjdGVycyBhcmUgcHJlc2VudCBpbiB0aGUgZmlsZXMgYXQgdGhlIGdpdmVuIHBhdGguCkluIHRoZSBwYXR0ZXJuIGAxMFhfUDRfM19MKl9SMV8qLmZhc3RxLmd6YCwgdGhpcyB3b3VsZCBhbGxvdyBhbnkgbGFuZSBudW1iZXIgYW5kIGFueSBzdWJzZXQsIHNvIHdvdWxkIG1hdGNoIGFsbCBvZiB0aGUgZm9sbG93aW5nIGZpbGVzIChhbGwgdGhlIFIxIGZpbGVzIGluIHRoaXMgY2FzZSk6CgpgYGAKMTBYX1A0XzNfTDAwMV9SMV8wMDEuZmFzdHEuZ3ogIDEwWF9QNF8zX0wwMDJfUjFfMDAxLmZhc3RxLmd6CjEwWF9QNF8zX0wwMDFfUjFfMDAyLmZhc3RxLmd6ICAxMFhfUDRfM19MMDAyX1IxXzAwMi5mYXN0cS5negoxMFhfUDRfM19MMDAxX1IxXzAwMy5mYXN0cS5neiAgMTBYX1A0XzNfTDAwMl9SMV8wMDMuZmFzdHEuZ3oKYGBgCgpGb3IgdGhpcyBkaXJlY3RvcnksIHRoYXQgd291bGQgbWFrZSBvdXIgZnVsbCBgc2FsbW9uIGFsZXZpbmAgY29tbWFuZCBsb29rIGxpa2UgdGhpcyAoZG9uJ3QgcnVuIHRoaXMgbm93ISk6CgpgYGAKIyBzYWxtb24gYWxldmluIFwKIyAgIC1pIGluZGV4L011c19tdXNjdWx1cy9zaG9ydF9pbmRleCBcCiMgICAtbCBJU1IgXAojICAgLTEgZGF0YS90YWJ1bGEtbXVyaXMvZmFzdHEvMTBYX1A0XzMvMTBYX1A0XzNfTCpfUjFfKi5mYXN0cS5neiBcCiMgICAtMiBkYXRhL3RhYnVsYS1tdXJpcy9mYXN0cS8xMFhfUDRfMy8xMFhfUDRfM19MKl9SMl8qLmZhc3RxLmd6IFwKIyAgIC1vIGRhdGEvdGFidWxhLW11cmlzL2FsZXZpbi1xdWFudC8xMFhfUDRfMyBcCiMgICAtcCA0IFwKIyAgIC0tY2hyb21pdW0gIFwKIyAgIC0tdGdNYXAgaW5kZXgvTXVzX211c2N1bHVzL011c19tdXNjdWx1cy5HUkNtMzguOTUudmVyc2lvbmVkX3R4MmdlbmUudHN2IFwKIyAgIC0tZHVtcEZlYXR1cmVzCmBgYAoKSW4gZ2VuZXJhbCwgeW91IHdpbGwgd2FudCB0byBydW4gYWxsIGxhbmVzIGFuZCBhbGwgZmlsZXMgZm9yIGEgZ2l2ZW4gc2FtcGxlIHRvZ2V0aGVyLgpCdXQgKipETyBOT1QqKiBjb21iaW5lIG11bHRpcGxlICpzYW1wbGVzKiBpbnRvIGEgc2luZ2xlIGBhbGV2aW5gIHF1YW50aWZpY2F0aW9uIQpLZWVwIHNlcGFyYXRlIHNhbXBsZXMgKGFuZCByZXBsaWNhdGVzKSBzZXBhcmF0ZSEKCiMjIEluaXRpYWwgcXVhbGl0eSBjb250cm9sIHdpdGggYGFsZXZpblFDYAoKTm93IHRoYXQgd2UgaGF2ZSBxdWFudGlmaWVkIG91ciBkYXRhIHdpdGggQWxldmluLCB3ZSBhcmUgcmVhZHkgdG8gcGVyZm9ybSBpbml0aWFsIHF1YWxpdHkgY29udHJvbCBjaGVja3MuCgpJbiBvcmRlciB0byBwZXJmb3JtIHRoZXNlIHF1YWxpdHkgY29udHJvbCBjaGVja3MsIHdlJ2xsIHVzZSB0aGUgYGFsZXZpblFDYCBSIHBhY2thZ2UuCk5vdGUgdGhhdCBgYWxldmluUUNgIGRlcGVuZHMgb24gZmlsZXMgdGhhdCB3ZSBnZXQgdXNpbmcgdGhlYC0tZHVtcEZlYXR1cmVzYCBvcHRpb24gaW4gQWxldmluLgoKQWJvdXQgdGhlIGBhbGV2aW5RQ1JlcG9ydCgpYCBmdW5jdGlvbjoKVGhlIGZpcnN0IGFyZ3VtZW50IG5lZWRzIHRvIGJlIHdoZXJlIHRoZSBzYW1wbGUncyBvdXRwdXQgZGF0YSB3YXMgcHV0IHdoZW4gQWxldmluIHdhcyBydW4gKGFzIGEgY2hhcmFjdGVyIHN0cmluZywgYWthIHVzaW5nIHF1b3RlcykuClRoZSByZXN0IG9mIGBhbGV2aW5RQ1JlcG9ydCgpYCdzIGFyZ3VtZW50cyB0ZWxsIFIgd2hlcmUgdG8gcHV0IHRoZSBvdXRwdXQgUUMgcmVwb3J0LgoKYGBge3IgYWxldmluUUMsIGV2YWwgPSBGQUxTRX0KIyBGaXJzdCwgZGVmaW5lIHBhdGggdG8gYWxldmluIG91dHB1dDoKYWxldmluX3BhdGggPC0gZmlsZS5wYXRoKCJkYXRhIiwgInRhYnVsYS1tdXJpcyIsICJhbGV2aW4tcXVhbnQiLCAiMTBYX1A0XzNfc3Vic2V0IikKCiMgUHJvZHVjZSBhIFFDIHJlcG9ydCBvZiByZXN1bHRzIGZvdW5kIGluIHRoZSBgYWxldmluX3BhdGhgIGRpcmVjdG9yeQphbGV2aW5RQzo6YWxldmluUUNSZXBvcnQoYWxldmluX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVJZCA9ICIxMFhfUDRfM19zdWJzZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0RmlsZSA9ICIxMFhfUDRfM19zdWJzZXQtcWNfcmVwb3J0Lmh0bWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0RGlyID0gInFjLXJlcG9ydHMiKQpgYGAKCkxvb2sgZm9yIHRoZSBgMTBYX1A0XzNfc3Vic2V0LXFjX3JlcG9ydC5odG1sYCBmaWxlIGNyZWF0ZWQgaW4gdGhlIGBxYy1yZXBvcnRzYCBkaXJlY3RvcnkgdG8gZXhhbWluZSB0aGUgcXVhbGl0eSBvZiB5b3VyIGRhdGEgYW5kIHBlcmZvcm1hbmNlIG9mIEFsZXZpbi4KCldlIGhhdmUgYWxzbyBwbGFjZWQgYW4gZXhhbXBsZSBvZiBhIHBvb3IgcXVhbGl0eSBzYW1wbGUgYWxldmluUUMgcmVwb3J0IGluIHRoZSBgcWMtcmVwb3J0c2AgZGlyZWN0b3J5LCB3aXRoIHRoZSBuYW1lIGBCYWRfRXhhbXBsZV8xMFhfUDRfMl9xY19yZXBvcnQuaHRtbGAuCgpUaGlzIHJlcG9ydCB3aWxsIHNob3cgYSBmZXcga2V5IG1ldHJpY3MgdGhhdCBpbmZvcm0geW91IGFib3V0IHRoZSBxdWFsaXR5IG9mIHlvdXIgc2FtcGxlLiAKVGhlcmUgaXMgYSBsb3Qgb2YgaW5mb3JtYXRpb24gaW5jbHVkZWQgaW4gdGhlIHJlcG9ydCwgc28gc29tZSBrZXkgbWV0cmljcyB0byBub3RlIGFyZSBpbmNsdWRlZCBpbiB0aGUgYFN1bW1hcnkgdGFibGVzYDoKCi0gRnJhY3Rpb24gb2YgcmVhZHMgaW4gd2hpdGVsaXN0IGJhcmNvZGVzIAotIE1lYW4gbnVtYmVyIG9mIHJlYWRzIHBlciBjZWxsCi0gTWVkaWFuIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcyBwZXIgY2VsbCAKClRoZSBmcmFjdGlvbiBvZiByZWFkcyBpbiB3aGl0ZWxpc3QgYmFyY29kZXMgaXMgcGFydGljdWxhcmx5IGltcG9ydGFudCBhcyBhIGxvdyBwZXJjZW50YWdlIGhlcmUgbWVhbnMgdGhlIGxpYnJhcnkgY29udGFpbnMgbWFueSByZWFkcyB0aGF0IGRvIG5vdCBjb250YWluIHRoZSBleHBlY3RlZCBjZWxsIGJhcmNvZGVzLiAKVGhpcyBpcyBpbmRpY2F0aXZlIG9mIHBvb3Igc2luZ2xlLWNlbGwgY2FwdHVyZSBkdXJpbmcgbGlicmFyeSBjb25zdHJ1Y3Rpb24uIAoKVGhlIG1lYW4gbnVtYmVyIG9mIHJlYWRzIHBlciBjZWxsIGFuZCBtZWRpYW4gbnVtYmVyIG9mIGRldGVjdGVkIGdlbmVzIHBlciBjZWxsIGNhbiBiZSBoZWxwZnVsIGluIHVuZGVyc3RhbmRpbmcgaG93IGRlZXBseSB0aGUgbGlicmFyeSB3YXMgc2VxdWVuY2VkLiAKVGhlIGhpZ2hlciB0aGVzZSBudW1iZXJzIGFyZSwgdGhlIG1vcmUgaW5mb3JtYXRpb24geW91IHdpbGwgb2J0YWluIHBlciBjZWxsLiAKClRoZSAqKmtuZWUgcGxvdCoqIHNob3dzIHRoZSBudW1iZXIgb2YgZGlzdGluY3QgVU1JcyBmb3IgZWFjaCBwb3NzaWJsZSBjZWxsIGJhcmNvZGUgb24gdGhlIHktYXhpcywgd2l0aCB0aGUgYmFyY29kZXMgcmFua2VkIGZyb20gdGhlIG1vc3QgVU1JcyB0byB0aGUgZmV3ZXN0IGFsb25nIHRoZSB4LWF4aXMuCgpDZWxsIGJhcmNvZGVzIHdpdGggbG93IFVNSSBjb3VudHMgYXJlIGxpa2VseSB0byBiZSBlbXB0eSBkcm9wbGV0cyB0aGF0IGRpZCBub3QgY29udGFpbiBhIGNlbGwuIApUaGVzZSBkcm9wbGV0cyBtdXN0IGJlIGZpbHRlcmVkIG91dCBzbyB3ZSBvbmx5IGNvbnNpZGVyIHRydWUgY2VsbHMgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCgpUbyBkbyB0aGlzLCB3ZSBjYW4gbG9vayBmb3IgYSAia25lZSIgb24gdGhlIGN1cnZlIHdoZXJlIHRoZSBudW1iZXIgb2YgVU1JcyBwZXIgYmFyY29kZSBzdGFydHMgdG8gZHJvcCBvZmYgcmFwaWRseSwgd2l0aCB0aGUgaW50dWl0aW9uIHRoYXQgdGhpcyBpcyB3aGVyZSB3ZSBhcmUgcmVhY2hpbmcgdGhlIGVuZCBvZiB0aGUgVU1JcyBwZXIgY2VsbCBkaXN0cmlidXRpb24gZm9yIHRydWUgY2VsbHMuCldlIGNhbiB0aGVuIGNob29zZSBhIHRocmVzaG9sZCBiZWxvdyB0aGUga25lZSBhbmQgb25seSBpbmNsdWRlIGJhcmNvZGVzIGFib3ZlIHRoaXMgdGhyZXNob2xkIGluIHRoZSBmaW5hbCBjZWxsIGJhcmNvZGUgbGlzdC4KClRoaXMgImtuZWUiIG1ldGhvZCwgd2hpY2ggaXMgaW1wbGVtZW50ZWQgYnkgYGFsZXZpbmAsIGlzIGZhaXJseSBlZmZlY3RpdmUgYW5kIGRvZXMgbm90IHJlcXVpcmUgYW55IHJlYWQgbWFwcGluZyBvciBxdWFudGlmaWNhdGlvbiBiZWZvcmUgZmlsdGVyaW5nLgpNb3JlIHJlY2VudCB2ZXJzaW9ucyBvZiBDZWxsIFJhbmdlciB1c2UgYSBzb21ld2hhdCBkaWZmZXJlbnQgbWV0aG9kIGJhc2VkIG9uIHRoZSAiZW1wdHkgZHJvcHMiIG1ldGhvZCBvZiBbTHVuICpldCBhbC4qICgyMDE5KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjExODYvczEzMDU5LTAxOS0xNjYyLXkpLCB0aGF0IGlzIGFwcGxpZWQgYWZ0ZXIgaW5pdGlhbCBnZW5lIHF1YW50aWZpY2F0aW9uLgpUaGlzIGFsbG93cyBmaWx0ZXJpbmcgdG8gcmV0YWluIGNlbGxzIHdpdGggbG93IGNvdW50cyB0aGF0IGFyZSBub25ldGhlbGVzcyBsaWtlbHkgdG8gcmVwcmVzZW50IHJlYWwgY2VsbHMuCgojIyBOZXh0IHN0ZXBzOiBMb2FkaW5nIEFsZXZpbiBvdXRwdXQgaW50byBSCgpBZnRlciB3ZSBoYXZlIHN1Y2Nlc3NmdWxseSBxdWFudGlmaWVkIG91ciB0YWctYmFzZWQgc2NSTkEtc2VxIGRhdGEgKGFuZCBkb25lIHNvbWUgUUMpLCB3ZSB3aWxsIHdhbnQgdG8gcmVhZCBpdCBpbnRvIFIgdG8gc3RhcnQgdG8gYW5hbHl6ZSBpdC4gClRoZSBlYXNpZXN0IHdheSB0byBkbyB0aGlzIGlzIHRvIHVzZSB0aGUgYHR4aW1ldGFgIHBhY2thZ2UsIHdoaWNoIHdlIHdpbGwgaW50cm9kdWNlIGluIHRoZSBuZXh0IG5vdGVib29rLgoKCiMjIFNlc3Npb24gSW5mbwoKYGBge3Igc2Vzc2lvbmluZm99CnNlc3Npb25JbmZvKCkKYGBgCg==