Workflow Overview
Introduction
The field of single-cell expression measurements is still relatively
new
and therefore best practices for analysis and standards for publication
are continuing to evolve.
One of the goals of this workshop is to not only provide an example
of a start to end single-cell workflow but also explore some of the
relatively arbitrary decision points and the rationale for the choices
we included in our analysis.
Objectives
- Orient on RStudio.
- Create RStudio project for analysis.
- Create directory structure for analysis.
- Learn how to read 10X data into Seurat.
- Introduce the Seurat object, and how to access parts of it.
Orienting on RStudio
To orient ourselves on the interface of RStudio, look in the lower
right pane, that has a list of files and folders. Click on the
welcome.R
script.
There should now be four panes in your RStudio window, with the
welcome.R
script in the upper left pane, this is the
“Source” or “Scripts” pane. It displays the code that we will write to
perform our analysis.
In the Scripts pane, there is a line of icons, towards the right side
of the pane there is a “Run” button. First highlight all the code in the
Source pane, and then click “Run”.
Checkpoint
A number of things just happened in all the panes:
- There is a record of the code that was run along with the results in
the lower left pane. This is the “Console” panel, and here is where the
output of executing code is displayed. You can also type directly in the
prompt and execute code, but it is not saved in the script.
- There is a
phenotypes_by_species
object listed in the
upper right pane. This is the “Environment” panel, and it shows all the
existing variables in the session, as well as a brief description.
- There is a plot in the lower right pane where the file structure was
previously listed. This is the “Files / Plots / Help” pane, and it can
show the file browser that we saw at the start, display plots that are
executed in the Console, or display help pages as we’ll see later.
Project setup
To start, we will access our shared RStudio server by opening a web
browser to the following URL:
https://bfx-workshop02.med.umich.edu/
You should now be looking at a page that will allow you to login to
the RStudio server:
Image: Login page for RStudio server.
Enter your user credentials and click Sign In. Your
username and password were provided via email, but if you need help, a
helper can retrieve it for you if you ask in Slack. Once logged in, you
should now see the RStudio interface:
Image: Landing page after successful login to
RStudio server.
Creating a project
We will create an RStudio Project that will help us keep our files
organized. See the Projects
section of R for Data Science for
a more in-depth description of what a project is and how it’s
helpful.
To create a Project for this workshop, click File then
New Project…. In the New Project Wizard window that opens,
select Existing Directory, then Browse…. In the
Choose Directory window, select the ISC_R
folder by
clicking it once, and then click the Choose button. Finally,
click Create Project.
Once we do this, RStudio will restart and the Files pane (lower
right) should put us in the ~/ISC_R
folder where there is
an inputs/
folder and an ISC_R.Rproj
file.
Directory structure
We have included the data to be used in the workshop in the
inputs/
folder. However, the project will need to include
folders for our analysis and our analysis scripts. Let’s create that
directory structure with the dir.create()
function.
##### Day 1 - Getting Started with Seurat
# Create project directories ----------------------------------------------
dir.create('scripts', showWarnings = FALSE, recursive = TRUE)
dir.create('results/figures', showWarnings = FALSE, recursive = TRUE)
dir.create('results/tables', showWarnings = FALSE, recursive = TRUE)
dir.create('results/rdata', showWarnings = FALSE, recursive = TRUE)
In the Files pane we should see the new results/
and
scripts/
folders.
Analysis script
The two most important artifacts of our analysis are the data from
Cell Ranger, and the script to analyze the data. There will be outputs
in results/
, and these will be important, but if the
contents of results/
are ever lost, the script will be able
to re-generate them if we’ve captured all our steps as code, which we
aim to do.
To create the analysis script, click File, hover over
New File, and click on R Script. A new pane in the
upper left slides into view and is the Untitled script file. Save this
file, and name it, by clicking File then Save.
Double click the scripts/
folder, and in the File name:
text box type “analysis.R”. Then click Save.
As we proceed through the workshop, we should save this file (by
clicking the Floppy disk, clicking File then Save,
or by typing Control + S).
Good scripting practices
In any analysis script, we recommend using comments (lines preceded
by a “#”) to provide additional information about code that may not be
self-evident. This is to the benefit of others that may look at the
code, but also to your future-self.
Analysis initialization
We begin our analysis script by loading the libraries we expect to
use. It’s generally good practice to include all library()
calls at the top of a script for visibility.
# Load libraries ----------------------------------------------
library(Seurat)
library(BPCells)
library(tidyverse)
options(future.globals.maxSize = 1e9)
The libraries that we are loading are:
- The
Seurat
library, developed by the Satija lab, which will provide the
essential functions used in our single-cell analysis. The Seurat documentation is
extensive and indispensible.
- The
BPCells
library, developed by Benjamin
Parks, is a recent package with the primary goal of efficiently
storing single-cell data to reduce its memory footprint. The BPCells
documentation includes many useful tutorials.
- The
tidyverse
library, developed by Posit, is an
essential package for data manipulation and plotting. The tidyverse documentation is
essential for getting a handle on the array of functions in the many
packages contained therein.
Create a Seurat object
The most recent release of Seurat
, version 5, includes
changes which take advantage of the memory-efficient storage implemented
in BPCells
. To read the efficiently stored data with
BPCells
we will use the open_matrix_dir()
function.
# Puts the data "on disk" rather than "in memory" -------------
geo_mat = open_matrix_dir(dir = 'inputs/10x_mat_filtered')
This reads in the expression matrix which has genes as rows and cells
as columns. The expression matrix is the precursor to creating the
Seurat
object upon which all our analysis will be done. To
create the Seurat
object:
# Create seurat object ----------------------------------------------
geo_so = CreateSeuratObject(counts = geo_mat, min.cells = 1, min.features = 50)
geo_so
An object of class Seurat
26489 features across 35216 samples within 1 assay
Active assay: RNA (26489 features, 0 variable features)
1 layer present: counts
We have specified some parameters to remove genes and cells which do
not contain very much information. Specifically a gene is removed if it
is expressed in 1 or fewer cells, and a cell is removed if it contains
reads for 50 or fewer genes. In the context of this workshop, this helps
us minimize memory usage.
Reading in data from scratch
How to read in data and create BPCell matrix
Above, we’ve demonstrated how to read data that was already arranged
in the BPCells
manner. We have done this for the workshop
to reduce our memory footprint from the start, but when you get data
back from AGC, you will have to follow the steps below.
# To load data from raw files ----------------------------------------------
### DO NOT RUN ###
# Collect the directories containing the 3 files:
# barcodes.tsv.gz, features.tsv.gz, matrix.mtx.gz
# The [-1] drops the parent directory inputs/10x_analysis
sample_dirs = list.dirs('inputs/10x_analysis')[-1]
# Extract the sample names. NOTE, this will differ depending
# on the form the folder names take, this works for this data.
# We create a named vector
samples = str_split(basename(sample_dirs), pattern = '_', simplify = TRUE)[,2]
# Naming the sample_dirs vector makes Seurat name the
# samples in the corresponding manner, which is nice for us.
names(sample_dirs) = samples
# Create the expression matrix
geo_mat = Read10X(data.dir = sample_dirs)
This will generate the expression matrix that is the input to
CreateSeuratObject()
, as above. If you wanted to write this
expression matrix using BPCells
so that it could be used in
the more memory-efficient manner, you would do:
write_matrix_dir(mat = geo_mat, dir = 'inputs/10x_mat_filtered')
You’d then have the items needed to run the code we’ve already run.
Structure of a Seurat object
The Seurat
object is a complex data type, so let’s get a
birds eye view with an image from this
tutorial on single-cell analysis.
Image: Schematic of Seurat
object.
The three main “slots” in the object are:
- The
assays
slot stores the expression data as
Assay
objects.
- The
meta.data
slot which stores cell-level information,
including technical and phenotypic data.
- The
reductions
slot stores the results of dimension
reduction applied to the assays
.
There are other slots which store information that becomes relevant
as we progress through the analysis. We will highlight the other slots
as they come up.
After reading the data in and creating the Seurat object above, we
can imagine the following schematic representing our object:
Image: Schematic after creating the Seurat
object.
Note the RNA assay contains a count
layer consisting of
a raw count matrix where the rows are genes (features, more
generically), and the columns are all cells across all samples. Note
also the presence of a meta.data
table giving information
about each cell. We’ll pay close attention to this as we proceed. The
other slots include information about the active.assay
and
active.ident
which tell Seurat which expression data to use
and how the cells are to be identified.
Accessing parts of the object
The only slot of the Seurat
object that we’ll typically
access or modify by hand–that is, without a function from the
Seurat
package–is the meta.data
object. In R,
slots are accessed with the @
symbol, as in:
# Examine Seurat object ----------------------------------------------
head(geo_so@meta.data)
orig.ident nCount_RNA nFeature_RNA
HODay0replicate1_AAACCTGAGAGAACAG-1 HODay0replicate1 10234 3226
HODay0replicate1_AAACCTGGTCATGCAT-1 HODay0replicate1 3158 1499
HODay0replicate1_AAACCTGTCAGAGCTT-1 HODay0replicate1 13464 4102
HODay0replicate1_AAACGGGAGAGACTTA-1 HODay0replicate1 577 346
HODay0replicate1_AAACGGGAGGCCCGTT-1 HODay0replicate1 1189 629
HODay0replicate1_AAACGGGCAACTGGCC-1 HODay0replicate1 7726 2602
Here, each row is a cell, and each column is information about that
cell. The rows of the table are named according to the uniquely
identifiable name for the cell. In this case, the day and replicate, as
well as the barcode for that cell. As we continue the workshop, we will
check in on the meta.data
slot and observe changes we want
to make, and that other functions will make. We’ll also observe the
other assays and layers and note their changes.
Save our progress
Let’s Save our progress as an RDS file with saveRDS()
;
this allows us to have a copy of the object that we can read back into
our session with the readRDS()
commmand. Periodically we
will be saving our Seurat object so that we can have a version of it at
different steps of the analysis. These will also help us get untangled
if we get into an odd state.
# Save the Seurat object ----------------------------------------------
saveRDS(geo_so, file = 'results/rdata/geo_so_unfiltered.rds')
Summary
In this section we:
- Created an RStudio project for analysis.
- Created the directory structure for analysis.
- Learned how to read 10X data into Seurat.
- Introduced the Seurat object, and how to access parts of it.
These materials have been adapted and extended from materials listed
above. These are open access materials distributed under the terms of
the Creative
Commons Attribution license (CC BY 4.0), which permits unrestricted
use, distribution, and reproduction in any medium, provided the original
author and source are credited.
LS0tCnRpdGxlOiAiR2V0dGluZyBTdGFydGVkIHdpdGggU2V1cmF0IgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIHRvYzogdHJ1ZQogICAgICAgICAgICB0b2NfZGVwdGg6IDQKICAgICAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogICBmb250LXNpemU6IDE4cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTJweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTJweAp9Cgp0YWJsZXsKICAgd2lkdGg6MTAwJTsKfQo8L3N0eWxlPgoKYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9CmtsaXBweTo6a2xpcHB5KGxhbmcgPSBjKCJyIiwgIm1hcmtkb3duIiwgImJhc2giKSwgcG9zaXRpb24gPSBjKCJ0b3AiLCAicmlnaHQiKSkKYGBgCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpzb3VyY2UoIi4uL2Jpbi9jaHVuay1vcHRpb25zLlIiKQprbml0cl9maWdfcGF0aCgiMDEtR2V0dGluZ1N0YXJ0ZWQvMDEtIikKYGBgCgojIFdvcmtmbG93IE92ZXJ2aWV3IHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cgo8YnIvPgo8aW1nIHNyYz0iaW1hZ2VzL3dheWZpbmRlci93YXlmaW5kZXIucG5nIiBhbHQ9IndheWZpbmRlciIgc3R5bGU9ImhlaWdodDogNDAwcHg7Ii8+Cjxici8+Cjxici8+CgojIEludHJvZHVjdGlvbgoKVGhlIGZpZWxkIG9mIHNpbmdsZS1jZWxsIGV4cHJlc3Npb24gbWVhc3VyZW1lbnRzIGlzIHN0aWxsIHJlbGF0aXZlbHkgbmV3IDwhLS0gYWRkIGxpbmtzIHRvIGVhcmx5IHB1YmxpY2F0aW9ucyAmIGluZm9ybWF0aW9uIHJlZ2FyZGluZyAxMHggY29tbWVyY2lhbGl6YXRpb24gLS0+IGFuZCB0aGVyZWZvcmUgIGJlc3QgcHJhY3RpY2VzIGZvciBhbmFseXNpcyBhbmQgc3RhbmRhcmRzIGZvciBwdWJsaWNhdGlvbiBhcmUgY29udGludWluZyB0byBldm9sdmUuIAoKT25lIG9mIHRoZSBnb2FscyBvZiB0aGlzIHdvcmtzaG9wIGlzIHRvIG5vdCBvbmx5IHByb3ZpZGUgYW4gZXhhbXBsZSBvZiBhIHN0YXJ0IHRvIGVuZCBzaW5nbGUtY2VsbCB3b3JrZmxvdyBidXQgYWxzbyBleHBsb3JlIHNvbWUgb2YgdGhlIHJlbGF0aXZlbHkgYXJiaXRyYXJ5IGRlY2lzaW9uIHBvaW50cyBhbmQgdGhlIHJhdGlvbmFsZSBmb3IgdGhlIGNob2ljZXMgd2UgaW5jbHVkZWQgaW4gb3VyIGFuYWx5c2lzLgoKIyMgU2FtcGxlIGluZm9ybWF0aW9uCgpJbiB0aGlzIHdvcmtzaG9wLCB3ZSB3aWxsIGJlIHdvcmtpbmcgd2l0aCBzaW5nbGUtY2VsbCBSTkEtc2VxIGRhdGEgZnJvbSBtaWNlIHdoZXJlIHRoZSBnb2FsIGlzIHRvIHVuZGVyc3RhbmQgbWlzcmVndWxhdGlvbiBpbiB3b3VuZCBoZWFsaW5nIHRoYXQgY2FuIGxlYWQgdG8gYm9uZSBmb3JtYXRpb24gaW4gc29mdCB0aXNzdWUuIE9mIGludGVyZXN0IGFyZSB0aGUgY2VsbCB0eXBlcyBhbmQgZ2VuZXMgdGhhdCBhcmUgaW52b2x2ZWQgaW4gdGhhdCBtaXNyZWd1bGF0aW9uLiBFc3NlbnRpYWxseSwgIldoeSBtaWdodCBib25lIGZvcm0gYXQgdGhlIGluanVyeSBzaWdodCB3aGVuIGl0IHNob3VsZG4ndD8iCgpUbyBpbnZlc3RpZ2F0ZSB0aGlzLCBjZWxscyBmcm9tIGRheSAwIChwcmlvciB0byBpbmp1cnkpLCBhbmQgZGF5cyAzLCA3LCBhbmQgMjEgcG9zdC1pbmp1cnkgd2VyZSBzdWJqZWN0ZWQgdG8gc2NSTkEtc2VxIGluIHF1YWRydXBsaWNhdGUgZm9yIGVhY2ggZGF5LiBJbiB0aGUgaW50ZXJlc3Qgb2YgY29tcHV0YXRpb25hbCBjb25zdHJhaW50cyBpbiB0aGlzIHdvcmtzaG9wLCB3ZSBoYXZlIG9taXR0ZWQgZGF5IDMuCgohW10oLi9pbWFnZXMvY3VycmljdWx1bS9leHBlcmltZW50YWxfZGVzaWduLmpwZykKCiMjIE9iamVjdGl2ZXMKCi0gT3JpZW50IG9uIFJTdHVkaW8uCi0gQ3JlYXRlIFJTdHVkaW8gcHJvamVjdCBmb3IgYW5hbHlzaXMuCi0gQ3JlYXRlIGRpcmVjdG9yeSBzdHJ1Y3R1cmUgZm9yIGFuYWx5c2lzLgotIExlYXJuIGhvdyB0byByZWFkIDEwWCBkYXRhIGludG8gU2V1cmF0LgotIEludHJvZHVjZSB0aGUgU2V1cmF0IG9iamVjdCwgYW5kIGhvdyB0byBhY2Nlc3MgcGFydHMgb2YgaXQuCgotLS0KCiMgT3JpZW50aW5nIG9uIFJTdHVkaW8KClRvIG9yaWVudCBvdXJzZWx2ZXMgb24gdGhlIGludGVyZmFjZSBvZiBSU3R1ZGlvLCBsb29rIGluIHRoZSBsb3dlciByaWdodCBwYW5lLCB0aGF0IGhhcyBhIGxpc3Qgb2YgZmlsZXMgYW5kIGZvbGRlcnMuIENsaWNrIG9uIHRoZSBgd2VsY29tZS5SYCBzY3JpcHQuCgpUaGVyZSBzaG91bGQgbm93IGJlIGZvdXIgcGFuZXMgaW4geW91ciBSU3R1ZGlvIHdpbmRvdywgd2l0aCB0aGUgYHdlbGNvbWUuUmAgc2NyaXB0IGluIHRoZSB1cHBlciBsZWZ0IHBhbmUsIHRoaXMgaXMgdGhlICJTb3VyY2UiIG9yICJTY3JpcHRzIiBwYW5lLiBJdCBkaXNwbGF5cyB0aGUgY29kZSB0aGF0IHdlIHdpbGwgd3JpdGUgdG8gcGVyZm9ybSBvdXIgYW5hbHlzaXMuCgo8IS0tIEluc2VydCBzY3JlZW5zaG90IHdpdGggd2VsY29tZS5SIGluIHNjcmlwdCBwYW5lLiAtLT4KCkluIHRoZSBTY3JpcHRzIHBhbmUsIHRoZXJlIGlzIGEgbGluZSBvZiBpY29ucywgdG93YXJkcyB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgcGFuZSB0aGVyZSBpcyBhICJSdW4iIGJ1dHRvbi4gRmlyc3QgaGlnaGxpZ2h0IGFsbCB0aGUgY29kZSBpbiB0aGUgU291cmNlIHBhbmUsIGFuZCB0aGVuIGNsaWNrICJSdW4iLgoKPCEtLSBJbnNlcnQgc2NyZWVuc2hvdCBvZiB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgd2VsY29tZS5SIC0tPgoKKipDaGVja3BvaW50KioKCkEgbnVtYmVyIG9mIHRoaW5ncyBqdXN0IGhhcHBlbmVkIGluIGFsbCB0aGUgcGFuZXM6CgoxLiBUaGVyZSBpcyBhIHJlY29yZCBvZiB0aGUgY29kZSB0aGF0IHdhcyBydW4gYWxvbmcgd2l0aCB0aGUgcmVzdWx0cyBpbiB0aGUgbG93ZXIgbGVmdCBwYW5lLiBUaGlzIGlzIHRoZSAiQ29uc29sZSIgcGFuZWwsIGFuZCBoZXJlIGlzIHdoZXJlIHRoZSBvdXRwdXQgb2YgZXhlY3V0aW5nIGNvZGUgaXMgZGlzcGxheWVkLiBZb3UgY2FuIGFsc28gdHlwZSBkaXJlY3RseSBpbiB0aGUgcHJvbXB0IGFuZCBleGVjdXRlIGNvZGUsIGJ1dCBpdCBpcyBub3Qgc2F2ZWQgaW4gdGhlIHNjcmlwdC4KMi4gVGhlcmUgaXMgYSBgcGhlbm90eXBlc19ieV9zcGVjaWVzYCBvYmplY3QgbGlzdGVkIGluIHRoZSB1cHBlciByaWdodCBwYW5lLiBUaGlzIGlzIHRoZSAiRW52aXJvbm1lbnQiIHBhbmVsLCBhbmQgaXQgc2hvd3MgYWxsIHRoZSBleGlzdGluZyB2YXJpYWJsZXMgaW4gdGhlIHNlc3Npb24sIGFzIHdlbGwgYXMgYSBicmllZiBkZXNjcmlwdGlvbi4KMy4gVGhlcmUgaXMgYSBwbG90IGluIHRoZSBsb3dlciByaWdodCBwYW5lIHdoZXJlIHRoZSBmaWxlIHN0cnVjdHVyZSB3YXMgcHJldmlvdXNseSBsaXN0ZWQuIFRoaXMgaXMgdGhlICJGaWxlcyAvIFBsb3RzIC8gSGVscCIgcGFuZSwgYW5kIGl0IGNhbiBzaG93IHRoZSBmaWxlIGJyb3dzZXIgdGhhdCB3ZSBzYXcgYXQgdGhlIHN0YXJ0LCBkaXNwbGF5IHBsb3RzIHRoYXQgYXJlIGV4ZWN1dGVkIGluIHRoZSBDb25zb2xlLCBvciBkaXNwbGF5IGhlbHAgcGFnZXMgYXMgd2UnbGwgc2VlIGxhdGVyLgoKIyBQcm9qZWN0IHNldHVwCgpUbyBzdGFydCwgd2Ugd2lsbCBhY2Nlc3Mgb3VyIHNoYXJlZCBSU3R1ZGlvIHNlcnZlciBieSBvcGVuaW5nIGEgd2ViIGJyb3dzZXIgdG8gdGhlIGZvbGxvd2luZyBVUkw6CgpbaHR0cHM6Ly9iZngtd29ya3Nob3AwMi5tZWQudW1pY2guZWR1L10oaHR0cHM6Ly9iZngtd29ya3Nob3AwMi5tZWQudW1pY2guZWR1LykKCllvdSBzaG91bGQgbm93IGJlIGxvb2tpbmcgYXQgYSBwYWdlIHRoYXQgd2lsbCBhbGxvdyB5b3UgdG8gbG9naW4gdG8gdGhlIFJTdHVkaW8gc2VydmVyOgoKIVtJbWFnZTogTG9naW4gcGFnZSBmb3IgUlN0dWRpbyBzZXJ2ZXIuXShpbWFnZXMvY3VycmljdWx1bS9zZXJ2ZXJfbG9naW4ucG5nKQoKRW50ZXIgeW91ciB1c2VyIGNyZWRlbnRpYWxzIGFuZCBjbGljayA8a2JkPlNpZ24gSW48L2tiZD4uIFlvdXIgdXNlcm5hbWUgYW5kIHBhc3N3b3JkIHdlcmUgcHJvdmlkZWQgdmlhIGVtYWlsLCBidXQgaWYgeW91IG5lZWQgaGVscCwgYSBoZWxwZXIgY2FuIHJldHJpZXZlIGl0IGZvciB5b3UgaWYgeW91IGFzayBpbiBTbGFjay4gT25jZSBsb2dnZWQgaW4sIHlvdSBzaG91bGQgbm93IHNlZSB0aGUgUlN0dWRpbyBpbnRlcmZhY2U6CgohW0ltYWdlOiBMYW5kaW5nIHBhZ2UgYWZ0ZXIgc3VjY2Vzc2Z1bCBsb2dpbiB0byBSU3R1ZGlvIHNlcnZlci5dKGltYWdlcy9jdXJyaWN1bHVtL3JzdHVkaW9fbGFuZGluZy5wbmcpCgo8IS0tIGNvbnNpZGVyIGFkZGluZyB3ZWxjb21lIHNjcmlwdCBvciBleGFtcGxlIG9mIGZpbmFsIGdvYWw/LS0+CgojIyBDcmVhdGluZyBhIHByb2plY3QKCldlIHdpbGwgY3JlYXRlIGFuIFJTdHVkaW8gUHJvamVjdCB0aGF0IHdpbGwgaGVscCB1cyBrZWVwIG91ciBmaWxlcyBvcmdhbml6ZWQuIFNlZSB0aGUgW1Byb2plY3RzXShodHRwczovL3I0ZHMuaGFkbGV5Lm56L3dvcmtmbG93LXNjcmlwdHMuaHRtbCNwcm9qZWN0cykgc2VjdGlvbiBvZiBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkbGV5Lm56LykgZm9yIGEgbW9yZSBpbi1kZXB0aCBkZXNjcmlwdGlvbiBvZiB3aGF0IGEgcHJvamVjdCBpcyBhbmQgaG93IGl0J3MgaGVscGZ1bC4KClRvIGNyZWF0ZSBhIFByb2plY3QgZm9yIHRoaXMgd29ya3Nob3AsIGNsaWNrIDxrYmQ+RmlsZTwva2JkPiB0aGVuIDxrYmQ+TmV3IFByb2plY3QuLi48L2tiZD4uIEluIHRoZSBOZXcgUHJvamVjdCBXaXphcmQgd2luZG93IHRoYXQgb3BlbnMsIHNlbGVjdCA8a2JkPkV4aXN0aW5nIERpcmVjdG9yeTwva2JkPiwgdGhlbiA8a2JkPkJyb3dzZS4uLjwva2JkPi4gSW4gdGhlIENob29zZSBEaXJlY3Rvcnkgd2luZG93LCBzZWxlY3QgdGhlIGBJU0NfUmAgZm9sZGVyIGJ5IGNsaWNraW5nIGl0IG9uY2UsIGFuZCB0aGVuIGNsaWNrIHRoZSA8a2JkPkNob29zZTwva2JkPiBidXR0b24uIEZpbmFsbHksIGNsaWNrIDxrYmQ+Q3JlYXRlIFByb2plY3Q8L2tiZD4uCgpPbmNlIHdlIGRvIHRoaXMsIFJTdHVkaW8gd2lsbCByZXN0YXJ0IGFuZCB0aGUgRmlsZXMgcGFuZSAobG93ZXIgcmlnaHQpIHNob3VsZCBwdXQgdXMgaW4gdGhlIGB+L0lTQ19SYCBmb2xkZXIgd2hlcmUgdGhlcmUgaXMgYW4gYGlucHV0cy9gIGZvbGRlciBhbmQgYW4gYElTQ19SLlJwcm9qYCBmaWxlLgoKIyMgRGlyZWN0b3J5IHN0cnVjdHVyZQoKV2UgaGF2ZSBpbmNsdWRlZCB0aGUgZGF0YSB0byBiZSB1c2VkIGluIHRoZSB3b3Jrc2hvcCBpbiB0aGUgYGlucHV0cy9gIGZvbGRlci4gSG93ZXZlciwgdGhlIHByb2plY3Qgd2lsbCBuZWVkIHRvIGluY2x1ZGUgZm9sZGVycyBmb3Igb3VyIGFuYWx5c2lzIGFuZCBvdXIgYW5hbHlzaXMgc2NyaXB0cy4gTGV0J3MgY3JlYXRlIHRoYXQgZGlyZWN0b3J5IHN0cnVjdHVyZSB3aXRoIHRoZSBgZGlyLmNyZWF0ZSgpYCBmdW5jdGlvbi4KCmBgYHtyLCBkaXJfY3JlYXRlfQojIyMjIyBEYXkgMSAtIEdldHRpbmcgU3RhcnRlZCB3aXRoIFNldXJhdAoKIyBDcmVhdGUgcHJvamVjdCBkaXJlY3RvcmllcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmRpci5jcmVhdGUoJ3NjcmlwdHMnLCBzaG93V2FybmluZ3MgPSBGQUxTRSwgcmVjdXJzaXZlID0gVFJVRSkKZGlyLmNyZWF0ZSgncmVzdWx0cy9maWd1cmVzJywgc2hvd1dhcm5pbmdzID0gRkFMU0UsIHJlY3Vyc2l2ZSA9IFRSVUUpCmRpci5jcmVhdGUoJ3Jlc3VsdHMvdGFibGVzJywgc2hvd1dhcm5pbmdzID0gRkFMU0UsIHJlY3Vyc2l2ZSA9IFRSVUUpCmRpci5jcmVhdGUoJ3Jlc3VsdHMvcmRhdGEnLCBzaG93V2FybmluZ3MgPSBGQUxTRSwgcmVjdXJzaXZlID0gVFJVRSkKYGBgCgpJbiB0aGUgRmlsZXMgcGFuZSB3ZSBzaG91bGQgc2VlIHRoZSBuZXcgYHJlc3VsdHMvYCBhbmQgYHNjcmlwdHMvYCBmb2xkZXJzLiAKCiMjIEFuYWx5c2lzIHNjcmlwdAoKVGhlIHR3byBtb3N0IGltcG9ydGFudCBhcnRpZmFjdHMgb2Ygb3VyIGFuYWx5c2lzIGFyZSB0aGUgZGF0YSBmcm9tIENlbGwgUmFuZ2VyLCBhbmQgdGhlIHNjcmlwdCB0byBhbmFseXplIHRoZSBkYXRhLiBUaGVyZSB3aWxsIGJlIG91dHB1dHMgaW4gYHJlc3VsdHMvYCwgYW5kIHRoZXNlIHdpbGwgYmUgaW1wb3J0YW50LCBidXQgaWYgdGhlIGNvbnRlbnRzIG9mIGByZXN1bHRzL2AgYXJlIGV2ZXIgbG9zdCwgdGhlIHNjcmlwdCB3aWxsIGJlIGFibGUgdG8gcmUtZ2VuZXJhdGUgdGhlbSBpZiB3ZSd2ZSBjYXB0dXJlZCBhbGwgb3VyIHN0ZXBzIGFzIGNvZGUsIHdoaWNoIHdlIGFpbSB0byBkby4KClRvIGNyZWF0ZSB0aGUgYW5hbHlzaXMgc2NyaXB0LCBjbGljayA8a2JkPkZpbGU8L2tiZD4sIGhvdmVyIG92ZXIgPGtiZD5OZXcgRmlsZTwva2JkPiwgYW5kIGNsaWNrIG9uIDxrYmQ+UiBTY3JpcHQ8L2tiZD4uIEEgbmV3IHBhbmUgaW4gdGhlIHVwcGVyIGxlZnQgc2xpZGVzIGludG8gdmlldyBhbmQgaXMgdGhlIFVudGl0bGVkIHNjcmlwdCBmaWxlLiBTYXZlIHRoaXMgZmlsZSwgYW5kIG5hbWUgaXQsIGJ5IGNsaWNraW5nIDxrYmQ+RmlsZTwva2JkPiB0aGVuIDxrYmQ+U2F2ZTwva2JkPi4gRG91YmxlIGNsaWNrIHRoZSBgc2NyaXB0cy9gIGZvbGRlciwgYW5kIGluIHRoZSBGaWxlIG5hbWU6IHRleHQgYm94IHR5cGUgImFuYWx5c2lzLlIiLiBUaGVuIGNsaWNrIDxrYmQ+U2F2ZTwva2JkPi4KCkFzIHdlIHByb2NlZWQgdGhyb3VnaCB0aGUgd29ya3Nob3AsIHdlIHNob3VsZCBzYXZlIHRoaXMgZmlsZSAoYnkgY2xpY2tpbmcgdGhlIEZsb3BweSBkaXNrLCBjbGlja2luZyA8a2JkPkZpbGU8L2tiZD4gdGhlbiA8a2JkPlNhdmU8L2tiZD4sIG9yIGJ5IHR5cGluZyA8a2JkPkNvbnRyb2wgKyBTPC9rYmQ+KS4KCj4gKipHb29kIHNjcmlwdGluZyBwcmFjdGljZXMqKgo+IAo+IEluIGFueSBhbmFseXNpcyBzY3JpcHQsIHdlIHJlY29tbWVuZCB1c2luZyBjb21tZW50cyAobGluZXMgcHJlY2VkZWQgYnkgYSAiIyIpIHRvIHByb3ZpZGUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCBjb2RlIHRoYXQgbWF5IG5vdCBiZSBzZWxmLWV2aWRlbnQuIFRoaXMgaXMgdG8gdGhlIGJlbmVmaXQgb2Ygb3RoZXJzIHRoYXQgbWF5IGxvb2sgYXQgdGhlIGNvZGUsIGJ1dCBhbHNvIHRvIHlvdXIgZnV0dXJlLXNlbGYuCjxicj4KCiMgQW5hbHlzaXMgaW5pdGlhbGl6YXRpb24KCldlIGJlZ2luIG91ciBhbmFseXNpcyBzY3JpcHQgYnkgbG9hZGluZyB0aGUgbGlicmFyaWVzIHdlIGV4cGVjdCB0byB1c2UuIEl0J3MgZ2VuZXJhbGx5IGdvb2QgcHJhY3RpY2UgdG8gaW5jbHVkZSBhbGwgYGxpYnJhcnkoKWAgY2FsbHMgYXQgdGhlIHRvcCBvZiBhIHNjcmlwdCBmb3IgdmlzaWJpbGl0eS4KCmBgYHtyLCBsb2FkX2xpYnJhcmllcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgTG9hZCBsaWJyYXJpZXMgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoQlBDZWxscykKbGlicmFyeSh0aWR5dmVyc2UpCgpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSAxZTkpCmBgYAoKVGhlIGxpYnJhcmllcyB0aGF0IHdlIGFyZSBsb2FkaW5nIGFyZToKCi0gVGhlIGBTZXVyYXRgIGxpYnJhcnksIGRldmVsb3BlZCBieSB0aGUgW1NhdGlqYSBsYWJdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy8pLCB3aGljaCB3aWxsIHByb3ZpZGUgdGhlIGVzc2VudGlhbCBmdW5jdGlvbnMgdXNlZCBpbiBvdXIgc2luZ2xlLWNlbGwgYW5hbHlzaXMuIFRoZSBbU2V1cmF0IGRvY3VtZW50YXRpb25dKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKSBpcyBleHRlbnNpdmUgYW5kIGluZGlzcGVuc2libGUuCi0gVGhlIGBCUENlbGxzYCBsaWJyYXJ5LCBkZXZlbG9wZWQgYnkgW0JlbmphbWluIFBhcmtzXShodHRwczovL2JucHJrcy5naXRodWIuaW8vQlBDZWxscy9hdXRob3JzLmh0bWwjY2l0YXRpb24pLCBpcyBhIHJlY2VudCBwYWNrYWdlIHdpdGggdGhlIHByaW1hcnkgZ29hbCBvZiBlZmZpY2llbnRseSBzdG9yaW5nIHNpbmdsZS1jZWxsIGRhdGEgdG8gcmVkdWNlIGl0cyBtZW1vcnkgZm9vdHByaW50LiBUaGUgW0JQQ2VsbHMgZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9ibnBya3MuZ2l0aHViLmlvL0JQQ2VsbHMvaW5kZXguaHRtbCkgaW5jbHVkZXMgbWFueSB1c2VmdWwgdHV0b3JpYWxzLgotIFRoZSBgdGlkeXZlcnNlYCBsaWJyYXJ5LCBkZXZlbG9wZWQgYnkgUG9zaXQsIGlzIGFuIGVzc2VudGlhbCBwYWNrYWdlIGZvciBkYXRhIG1hbmlwdWxhdGlvbiBhbmQgcGxvdHRpbmcuIFRoZSBbdGlkeXZlcnNlIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKSBpcyBlc3NlbnRpYWwgZm9yIGdldHRpbmcgYSBoYW5kbGUgb24gdGhlIGFycmF5IG9mIGZ1bmN0aW9ucyBpbiB0aGUgbWFueSBwYWNrYWdlcyBjb250YWluZWQgdGhlcmVpbi4KCiMgSW5wdXQgZGF0YQoKVGhlIGBpbnB1dHMvYCBmb2xkZXIgaGFzIHRoZSBkYXRhIGZvciB0aGUgd29ya3Nob3Agc3RvcmVkIGluIHR3byBmb3Jtcy4gVGhlIGZpcnN0LCBgaW5wdXRzLzEweF9maWx0ZXJlZC9gIGlzIGNsb3NlciB0byB3aGF0IEFHQyB3b3VsZCBnZW5lcmF0ZSB3aXRoIENlbGwgUmFuZ2VyLCB3aGVyZSBlYWNoIHNhbXBsZSBoYXMgYSBmb2xkZXIsIGFuZCB3aXRoaW4gdGhhdCBmb2xkZXIgdGhlcmUgYXJlIHRocmVlIGZpbGVzOgoKLSBgYmFyY29kZXMudHN2Lmd6YAotIGBmZWF0dXJlcy50c3YuZ3pgCi0gYG1hdHJpeC5tdHguZ3pgCgpOb3RlLCB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBmaWx0ZXJlZCBtYXRyaWNlcy4KCjwhLS0gV2VyZSB0aGVzZSBmaWxlcyBleHBsYWluZWQgZWFybGllcj8gSWYgbm90LCBicmllZmx5IGV4cGxhaW4gdGhlbSBoZXJlIC0tPgoKVGhlIHNlY29uZCwgYGlucHV0cy8xMHhfbWF0X2ZpbHRlcmVkL2AgaXMgdGhlIHJlc3VsdCBvZiB0aGUgYEJQQ2VsbHNgIHBhY2thZ2UsIGFuZCBpcyBtb3JlIG1hY2hpbmUtcmVhZGFibGUgdGhhbiBodW1hbi1yZWFkYWJsZS4gSXQgaXMgYW4gZWZmaWNpZW50IHdheSBvZiBzdG9yaW5nIHRoZSBzYW1lIGRhdGEgY29udGFpbmVkIGluIHRoZSB0aHJlZSBmaWxlcyBhYm92ZS4gV2Ugd2lsbCB1c2UgdGhpcyBmb3JtIG9mIHRoZSBpbnB1dCBkYXRhIGJlY2F1c2UgaXQgaXMgbW9yZSBtZW1vcnkgZWZmaWNpZW50LgoKV2hpbGUgdGhlIGZ1bGwgZGF0YXNldCB3ZSBzZWxlY3RlZCBoYXMgdGltZS1zZXJpZXMgaW5mb3JtYXRpb24gZnJvbSBEYXkgMCwgRGF5IDMsIERheSA3LCBhbmQgRGF5IDIxLCB3ZSBoYXZlIHJlbW92ZWQgRGF5IDMgdG8gcmVkdWNlIHRoZSBtZW1vcnkgcmVxdWlyZW1lbnRzIGZ1cnRoZXIuCgo8IS0tIEFkZCBjb250ZXh0IGZvciBleHBlY3RlZCAxMHggb3V0cHV0cyBhbmQgaG93IHdlIHdvdWxkIHVzdWFsbHkgc3RhcnQgd2l0aCB0aGUgImZpbHRlcmVkIiBvdXRwdXRzIChhbmQgYWRkIGRyb3Bkb3duIG9yIGxpbmsgb3V0IHRvIDEweCByZXNvdXJjZXMgdG8gY29udHJhc3QgYmV0d2VlbiBmaWx0ZXJlZCBhbmQgcmF3IG91dHB1dHMgYW5kIHVzZSBjYXNlcyBmb3IgZWFjaCkgLS0+CgojIENyZWF0ZSBhIFNldXJhdCBvYmplY3QKClRoZSBtb3N0IHJlY2VudCByZWxlYXNlIG9mIGBTZXVyYXRgLCB2ZXJzaW9uIDUsIGluY2x1ZGVzIGNoYW5nZXMgd2hpY2ggdGFrZSBhZHZhbnRhZ2Ugb2YgdGhlIG1lbW9yeS1lZmZpY2llbnQgc3RvcmFnZSBpbXBsZW1lbnRlZCBpbiBgQlBDZWxsc2AuIFRvIHJlYWQgdGhlIGVmZmljaWVudGx5IHN0b3JlZCBkYXRhIHdpdGggYEJQQ2VsbHNgIHdlIHdpbGwgdXNlIHRoZSBgb3Blbl9tYXRyaXhfZGlyKClgIGZ1bmN0aW9uLgoKPCEtLSBJbnRyb2R1Y2UgQlBDZWxsIHBhY2thZ2UgYW5kIGhvdyBtb3N0IHJlY2VudCB2ZXJzaW9uIG9mIFNldXJhdCAoU2V1cmF0IDUpIHN1cHBvcnRzIG1vcmUgZWZmaWNpZW50IGRhdGEgc3RvcmFnZSB0byBpbXByb3ZlIHBlcmZvcm1hbmNlIGFuZCBhY2NvbW9kYXRlIGxhcmdlciBkYXRhc2V0cyAtLT4KCmBgYHtyLCByZWFkX21hdHJpeF9kaXJ9CiMgUHV0cyB0aGUgZGF0YSAib24gZGlzayIgcmF0aGVyIHRoYW4gImluIG1lbW9yeSIgIC0tLS0tLS0tLS0tLS0KZ2VvX21hdCA9IG9wZW5fbWF0cml4X2RpcihkaXIgPSAnaW5wdXRzLzEweF9tYXRfZmlsdGVyZWQnKQpgYGAKClRoaXMgcmVhZHMgaW4gdGhlIGV4cHJlc3Npb24gbWF0cml4IHdoaWNoIGhhcyBnZW5lcyBhcyByb3dzIGFuZCBjZWxscyBhcyBjb2x1bW5zLiBUaGUgZXhwcmVzc2lvbiBtYXRyaXggaXMgdGhlIHByZWN1cnNvciB0byBjcmVhdGluZyB0aGUgYFNldXJhdGAgb2JqZWN0IHVwb24gd2hpY2ggYWxsIG91ciBhbmFseXNpcyB3aWxsIGJlIGRvbmUuIFRvIGNyZWF0ZSB0aGUgYFNldXJhdGAgb2JqZWN0OgoKYGBge3IsIGNyZWF0ZV9zZXVyYXR9CiMgQ3JlYXRlIHNldXJhdCBvYmplY3QgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZ2VvX3NvID0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGdlb19tYXQsIG1pbi5jZWxscyA9IDEsIG1pbi5mZWF0dXJlcyA9IDUwKQpnZW9fc28KYGBgCgpXZSBoYXZlIHNwZWNpZmllZCBzb21lIHBhcmFtZXRlcnMgdG8gcmVtb3ZlIGdlbmVzIGFuZCBjZWxscyB3aGljaCBkbyBub3QgY29udGFpbiB2ZXJ5IG11Y2ggaW5mb3JtYXRpb24uIFNwZWNpZmljYWxseSBhIGdlbmUgaXMgcmVtb3ZlZCBpZiBpdCBpcyBleHByZXNzZWQgaW4gMSBvciBmZXdlciBjZWxscywgYW5kIGEgY2VsbCBpcyByZW1vdmVkIGlmIGl0IGNvbnRhaW5zIHJlYWRzIGZvciA1MCBvciBmZXdlciBnZW5lcy4gSW4gdGhlIGNvbnRleHQgb2YgdGhpcyB3b3Jrc2hvcCwgdGhpcyBoZWxwcyB1cyBtaW5pbWl6ZSBtZW1vcnkgdXNhZ2UuCgojIyMgUmVhZGluZyBpbiBkYXRhIGZyb20gc2NyYXRjaAo8ZGV0YWlscz4KPHN1bW1hcnk+KkhvdyB0byByZWFkIGluIGRhdGEgYW5kIGNyZWF0ZSBCUENlbGwgbWF0cml4Kjwvc3VtbWFyeT4KCkFib3ZlLCB3ZSd2ZSBkZW1vbnN0cmF0ZWQgaG93IHRvIHJlYWQgZGF0YSB0aGF0IHdhcyBhbHJlYWR5IGFycmFuZ2VkIGluIHRoZSBgQlBDZWxsc2AgbWFubmVyLiBXZSBoYXZlIGRvbmUgdGhpcyBmb3IgdGhlIHdvcmtzaG9wIHRvIHJlZHVjZSBvdXIgbWVtb3J5IGZvb3RwcmludCBmcm9tIHRoZSBzdGFydCwgYnV0IHdoZW4geW91IGdldCBkYXRhIGJhY2sgZnJvbSBBR0MsIHlvdSB3aWxsIGhhdmUgdG8gZm9sbG93IHRoZSBzdGVwcyBiZWxvdy4KCmBgYAojIFRvIGxvYWQgZGF0YSBmcm9tIHJhdyBmaWxlcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyBETyBOT1QgUlVOICMjIwoKIyBDb2xsZWN0IHRoZSBkaXJlY3RvcmllcyBjb250YWluaW5nIHRoZSAzIGZpbGVzOgojIGJhcmNvZGVzLnRzdi5neiwgZmVhdHVyZXMudHN2Lmd6LCBtYXRyaXgubXR4Lmd6CiMgVGhlIFstMV0gZHJvcHMgdGhlIHBhcmVudCBkaXJlY3RvcnkgaW5wdXRzLzEweF9hbmFseXNpcwpzYW1wbGVfZGlycyA9IGxpc3QuZGlycygnaW5wdXRzLzEweF9hbmFseXNpcycpWy0xXQoKIyBFeHRyYWN0IHRoZSBzYW1wbGUgbmFtZXMuIE5PVEUsIHRoaXMgd2lsbCBkaWZmZXIgZGVwZW5kaW5nCiMgb24gdGhlIGZvcm0gdGhlIGZvbGRlciBuYW1lcyB0YWtlLCB0aGlzIHdvcmtzIGZvciB0aGlzIGRhdGEuCiMgV2UgY3JlYXRlIGEgbmFtZWQgdmVjdG9yIApzYW1wbGVzID0gc3RyX3NwbGl0KGJhc2VuYW1lKHNhbXBsZV9kaXJzKSwgcGF0dGVybiA9ICdfJywgc2ltcGxpZnkgPSBUUlVFKVssMl0KCiMgTmFtaW5nIHRoZSBzYW1wbGVfZGlycyB2ZWN0b3IgbWFrZXMgU2V1cmF0IG5hbWUgdGhlCiMgc2FtcGxlcyBpbiB0aGUgY29ycmVzcG9uZGluZyBtYW5uZXIsIHdoaWNoIGlzIG5pY2UgZm9yIHVzLgpuYW1lcyhzYW1wbGVfZGlycykgPSBzYW1wbGVzCgojIENyZWF0ZSB0aGUgZXhwcmVzc2lvbiBtYXRyaXgKZ2VvX21hdCA9IFJlYWQxMFgoZGF0YS5kaXIgPSBzYW1wbGVfZGlycykKYGBgCgpUaGlzIHdpbGwgZ2VuZXJhdGUgdGhlIGV4cHJlc3Npb24gbWF0cml4IHRoYXQgaXMgdGhlIGlucHV0IHRvIGBDcmVhdGVTZXVyYXRPYmplY3QoKWAsIGFzIGFib3ZlLiBJZiB5b3Ugd2FudGVkIHRvIHdyaXRlIHRoaXMgZXhwcmVzc2lvbiBtYXRyaXggdXNpbmcgYEJQQ2VsbHNgIHNvIHRoYXQgaXQgY291bGQgYmUgdXNlZCBpbiB0aGUgbW9yZSBtZW1vcnktZWZmaWNpZW50IG1hbm5lciwgeW91IHdvdWxkIGRvOgoKYGBgCndyaXRlX21hdHJpeF9kaXIobWF0ID0gZ2VvX21hdCwgZGlyID0gJ2lucHV0cy8xMHhfbWF0X2ZpbHRlcmVkJykKYGBgCgpZb3UnZCB0aGVuIGhhdmUgdGhlIGl0ZW1zIG5lZWRlZCB0byBydW4gdGhlIGNvZGUgd2UndmUgYWxyZWFkeSBydW4uCjwvZGV0YWlscz4KPGJyPgoKIyMgU3RydWN0dXJlIG9mIGEgU2V1cmF0IG9iamVjdAoKVGhlIGBTZXVyYXRgIG9iamVjdCBpcyBhIGNvbXBsZXggZGF0YSB0eXBlLCBzbyBsZXQncyBnZXQgYSBiaXJkcyBleWUgdmlldyB3aXRoIGFuIGltYWdlIGZyb20gW3RoaXMgdHV0b3JpYWxdKGh0dHBzOi8vc3diaW9pbmYuZ2l0aHViLmlvL3NjUk5Bc2VxSW5SX0RvY28vc2V1cmF0b2JqZWN0Lmh0bWwjZGlzY3Vzc2lvbi10aGUtc2V1cmF0LW9iamVjdC1pbi1yKSBvbiBzaW5nbGUtY2VsbCBhbmFseXNpcy4KCiFbSW1hZ2U6IFNjaGVtYXRpYyBvZiBgU2V1cmF0YCBvYmplY3QuXShpbWFnZXMvc2V1cmF0X3NjaGVtYXRpYy9TbGlkZTEucG5nKQoKVGhlIHRocmVlIG1haW4gInNsb3RzIiBpbiB0aGUgb2JqZWN0IGFyZToKCjEuIFRoZSBgYXNzYXlzYCBzbG90IHN0b3JlcyB0aGUgZXhwcmVzc2lvbiBkYXRhIGFzIGBBc3NheWAgb2JqZWN0cy4KMi4gVGhlIGBtZXRhLmRhdGFgIHNsb3Qgd2hpY2ggc3RvcmVzIGNlbGwtbGV2ZWwgaW5mb3JtYXRpb24sIGluY2x1ZGluZyB0ZWNobmljYWwgYW5kIHBoZW5vdHlwaWMgZGF0YS4KMy4gVGhlIGByZWR1Y3Rpb25zYCBzbG90IHN0b3JlcyB0aGUgcmVzdWx0cyBvZiBkaW1lbnNpb24gcmVkdWN0aW9uIGFwcGxpZWQgdG8gdGhlIGBhc3NheXNgLgoKVGhlcmUgYXJlIG90aGVyIHNsb3RzIHdoaWNoIHN0b3JlIGluZm9ybWF0aW9uIHRoYXQgYmVjb21lcyByZWxldmFudCBhcyB3ZSBwcm9ncmVzcyB0aHJvdWdoIHRoZSBhbmFseXNpcy4gV2Ugd2lsbCBoaWdobGlnaHQgdGhlIG90aGVyIHNsb3RzIGFzIHRoZXkgY29tZSB1cC4KCkFmdGVyIHJlYWRpbmcgdGhlIGRhdGEgaW4gYW5kIGNyZWF0aW5nIHRoZSBTZXVyYXQgb2JqZWN0IGFib3ZlLCB3ZSBjYW4gaW1hZ2luZSB0aGUgZm9sbG93aW5nIHNjaGVtYXRpYyByZXByZXNlbnRpbmcgb3VyIG9iamVjdDoKCiFbSW1hZ2U6IFNjaGVtYXRpYyBhZnRlciBjcmVhdGluZyB0aGUgU2V1cmF0IG9iamVjdC5dKGltYWdlcy9zZXVyYXRfc2NoZW1hdGljL1NsaWRlMi5wbmcpCgpOb3RlIHRoZSBSTkEgYXNzYXkgY29udGFpbnMgYSBgY291bnRgIGxheWVyIGNvbnNpc3Rpbmcgb2YgYSByYXcgY291bnQgbWF0cml4IHdoZXJlIHRoZSByb3dzIGFyZSBnZW5lcyAoZmVhdHVyZXMsIG1vcmUgZ2VuZXJpY2FsbHkpLCBhbmQgdGhlIGNvbHVtbnMgYXJlIGFsbCBjZWxscyBhY3Jvc3MgYWxsIHNhbXBsZXMuIE5vdGUgYWxzbyB0aGUgcHJlc2VuY2Ugb2YgYSBgbWV0YS5kYXRhYCB0YWJsZSBnaXZpbmcgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBjZWxsLiBXZSdsbCBwYXkgY2xvc2UgYXR0ZW50aW9uIHRvIHRoaXMgYXMgd2UgcHJvY2VlZC4gVGhlIG90aGVyIHNsb3RzIGluY2x1ZGUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGBhY3RpdmUuYXNzYXlgIGFuZCBgYWN0aXZlLmlkZW50YCB3aGljaCB0ZWxsIFNldXJhdCB3aGljaCBleHByZXNzaW9uIGRhdGEgdG8gdXNlIGFuZCBob3cgdGhlIGNlbGxzIGFyZSB0byBiZSBpZGVudGlmaWVkLgoKIyMgQWNjZXNzaW5nIHBhcnRzIG9mIHRoZSBvYmplY3QKClRoZSBvbmx5IHNsb3Qgb2YgdGhlIGBTZXVyYXRgIG9iamVjdCB0aGF0IHdlJ2xsIHR5cGljYWxseSBhY2Nlc3Mgb3IgbW9kaWZ5IGJ5IGhhbmQtLXRoYXQgaXMsIHdpdGhvdXQgYSBmdW5jdGlvbiBmcm9tIHRoZSBgU2V1cmF0YCBwYWNrYWdlLS1pcyB0aGUgYG1ldGEuZGF0YWAgb2JqZWN0LiBJbiBSLCBzbG90cyBhcmUgYWNjZXNzZWQgd2l0aCB0aGUgYEBgIHN5bWJvbCwgYXMgaW46CgpgYGB7ciwgcHJldmlld19tZXRhZGF0YX0KIyBFeGFtaW5lIFNldXJhdCBvYmplY3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpoZWFkKGdlb19zb0BtZXRhLmRhdGEpCmBgYAoKSGVyZSwgZWFjaCByb3cgaXMgYSBjZWxsLCBhbmQgZWFjaCBjb2x1bW4gaXMgaW5mb3JtYXRpb24gYWJvdXQgdGhhdCBjZWxsLiBUaGUgcm93cyBvZiB0aGUgdGFibGUgYXJlIG5hbWVkIGFjY29yZGluZyB0byB0aGUgdW5pcXVlbHkgaWRlbnRpZmlhYmxlIG5hbWUgZm9yIHRoZSBjZWxsLiBJbiB0aGlzIGNhc2UsIHRoZSBkYXkgYW5kIHJlcGxpY2F0ZSwgYXMgd2VsbCBhcyB0aGUgYmFyY29kZSBmb3IgdGhhdCBjZWxsLiBBcyB3ZSBjb250aW51ZSB0aGUgd29ya3Nob3AsIHdlIHdpbGwgY2hlY2sgaW4gb24gdGhlIGBtZXRhLmRhdGFgIHNsb3QgYW5kIG9ic2VydmUgY2hhbmdlcyB3ZSB3YW50IHRvIG1ha2UsIGFuZCB0aGF0IG90aGVyIGZ1bmN0aW9ucyB3aWxsIG1ha2UuIFdlJ2xsIGFsc28gb2JzZXJ2ZSB0aGUgb3RoZXIgYXNzYXlzIGFuZCBsYXllcnMgYW5kIG5vdGUgdGhlaXIgY2hhbmdlcy4KCiMgU2F2ZSBvdXIgcHJvZ3Jlc3MKCkxldCdzIFNhdmUgb3VyIHByb2dyZXNzIGFzIGFuIFJEUyBmaWxlIHdpdGggYHNhdmVSRFMoKWA7IHRoaXMgYWxsb3dzIHVzIHRvIGhhdmUgYSBjb3B5IG9mIHRoZSBvYmplY3QgdGhhdCB3ZSBjYW4gcmVhZCBiYWNrIGludG8gb3VyIHNlc3Npb24gd2l0aCB0aGUgYHJlYWRSRFMoKWAgY29tbW1hbmQuIFBlcmlvZGljYWxseSB3ZSB3aWxsIGJlIHNhdmluZyBvdXIgU2V1cmF0IG9iamVjdCBzbyB0aGF0IHdlIGNhbiBoYXZlIGEgdmVyc2lvbiBvZiBpdCBhdCBkaWZmZXJlbnQgc3RlcHMgb2YgdGhlIGFuYWx5c2lzLiBUaGVzZSB3aWxsIGFsc28gaGVscCB1cyBnZXQgdW50YW5nbGVkIGlmIHdlIGdldCBpbnRvIGFuIG9kZCBzdGF0ZS4KCmBgYHtyLCBzYXZlX3Jkc19oaWRkZW4sIGVjaG8gPSBGQUxTRX0KaWYoIWZpbGUuZXhpc3RzKCdyZXN1bHRzL3JkYXRhL2dlb19zb191bmZpbHRlcmVkLnJkcycpKSB7CiAgc2F2ZVJEUyhnZW9fc28sIGZpbGUgPSAncmVzdWx0cy9yZGF0YS9nZW9fc29fdW5maWx0ZXJlZC5yZHMnKQp9CmBgYAoKYGBge3IsIHNhdmVfcmRzLCBldmFsID0gRkFMU0V9CiMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnNhdmVSRFMoZ2VvX3NvLCBmaWxlID0gJ3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX3VuZmlsdGVyZWQucmRzJykKYGBgCgojIFN1bW1hcnkKCkluIHRoaXMgc2VjdGlvbiB3ZToKCi0gQ3JlYXRlZCBhbiBSU3R1ZGlvIHByb2plY3QgZm9yIGFuYWx5c2lzLgotIENyZWF0ZWQgdGhlIGRpcmVjdG9yeSBzdHJ1Y3R1cmUgZm9yIGFuYWx5c2lzLgotIExlYXJuZWQgaG93IHRvIHJlYWQgMTBYIGRhdGEgaW50byBTZXVyYXQuCi0gSW50cm9kdWNlZCB0aGUgU2V1cmF0IG9iamVjdCwgYW5kIGhvdyB0byBhY2Nlc3MgcGFydHMgb2YgaXQuCgotLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgoKPGJyLz4KPGJyLz4KCi0tLS0tLS0tLS0tLS0tLQoKfCBbUHJldmlvdXMgbGVzc29uXSgwMEEtT3JpZW50aW5nT25TY1JOQVNlcS5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXSgwMEItQ2VsbFJhbmdlckluQWN0aW9uLmh0bWwpIHwKfCA6LS0tIHwgOi0tLS06IHwgLS0tOiB8Cgo=