Workflow Overview


wayfinder

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.

Sample information

In this workshop, we will be working with single-cell RNA-seq data from mice where the goal is to understand misregulation in wound healing that can lead to bone formation in soft tissue. Of interest are the cell types and genes that are involved in that misregulation. Essentially, “Why might bone form at the injury sight when it shouldn’t?”

To investigate this, cells from day 0 (prior to injury), and days 3, 7, and 21 post-injury were subjected to scRNA-seq in quadruplicate for each day. In the interest of computational constraints in this workshop, we have omitted day 3.

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:

  1. 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.
  2. 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.
  3. 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.
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.
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.

Input data

The inputs/ folder has the data for the workshop stored in two forms. The first, inputs/10x_filtered/ is closer to what AGC would generate with Cell Ranger, where each sample has a folder, and within that folder there are three files:

  • barcodes.tsv.gz
  • features.tsv.gz
  • matrix.mtx.gz

Note, we will be using the filtered matrices.

The second, inputs/10x_mat_filtered/ is the result of the BPCells package, and is more machine-readable than human-readable. It is an efficient way of storing the same data contained in the three files above. We will use this form of the input data because it is more memory efficient.

While the full dataset we selected has time-series information from Day 0, Day 3, Day 7, and Day 21, we have removed Day 3 to reduce the memory requirements further.

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.
Image: Schematic of Seurat object.

The three main “slots” in the object are:

  1. The assays slot stores the expression data as Assay objects.
  2. The meta.data slot which stores cell-level information, including technical and phenotypic data.
  3. 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.
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.




Previous lesson Top of this lesson Next lesson
LS0tCnRpdGxlOiAiR2V0dGluZyBTdGFydGVkIHdpdGggU2V1cmF0IgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIHRvYzogdHJ1ZQogICAgICAgICAgICB0b2NfZGVwdGg6IDQKICAgICAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogICBmb250LXNpemU6IDE4cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTJweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTJweAp9Cgp0YWJsZXsKICAgd2lkdGg6MTAwJTsKfQo8L3N0eWxlPgoKYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9CmtsaXBweTo6a2xpcHB5KGxhbmcgPSBjKCJyIiwgIm1hcmtkb3duIiwgImJhc2giKSwgcG9zaXRpb24gPSBjKCJ0b3AiLCAicmlnaHQiKSkKYGBgCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpzb3VyY2UoIi4uL2Jpbi9jaHVuay1vcHRpb25zLlIiKQprbml0cl9maWdfcGF0aCgiMDEtR2V0dGluZ1N0YXJ0ZWQvMDEtIikKYGBgCgojIFdvcmtmbG93IE92ZXJ2aWV3IHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cgo8YnIvPgo8aW1nIHNyYz0iaW1hZ2VzL3dheWZpbmRlci93YXlmaW5kZXIucG5nIiBhbHQ9IndheWZpbmRlciIgc3R5bGU9ImhlaWdodDogNDAwcHg7Ii8+Cjxici8+Cjxici8+CgojIEludHJvZHVjdGlvbgoKVGhlIGZpZWxkIG9mIHNpbmdsZS1jZWxsIGV4cHJlc3Npb24gbWVhc3VyZW1lbnRzIGlzIHN0aWxsIHJlbGF0aXZlbHkgbmV3IDwhLS0gYWRkIGxpbmtzIHRvIGVhcmx5IHB1YmxpY2F0aW9ucyAmIGluZm9ybWF0aW9uIHJlZ2FyZGluZyAxMHggY29tbWVyY2lhbGl6YXRpb24gLS0+IGFuZCB0aGVyZWZvcmUgIGJlc3QgcHJhY3RpY2VzIGZvciBhbmFseXNpcyBhbmQgc3RhbmRhcmRzIGZvciBwdWJsaWNhdGlvbiBhcmUgY29udGludWluZyB0byBldm9sdmUuIAoKT25lIG9mIHRoZSBnb2FscyBvZiB0aGlzIHdvcmtzaG9wIGlzIHRvIG5vdCBvbmx5IHByb3ZpZGUgYW4gZXhhbXBsZSBvZiBhIHN0YXJ0IHRvIGVuZCBzaW5nbGUtY2VsbCB3b3JrZmxvdyBidXQgYWxzbyBleHBsb3JlIHNvbWUgb2YgdGhlIHJlbGF0aXZlbHkgYXJiaXRyYXJ5IGRlY2lzaW9uIHBvaW50cyBhbmQgdGhlIHJhdGlvbmFsZSBmb3IgdGhlIGNob2ljZXMgd2UgaW5jbHVkZWQgaW4gb3VyIGFuYWx5c2lzLgoKIyMgU2FtcGxlIGluZm9ybWF0aW9uCgpJbiB0aGlzIHdvcmtzaG9wLCB3ZSB3aWxsIGJlIHdvcmtpbmcgd2l0aCBzaW5nbGUtY2VsbCBSTkEtc2VxIGRhdGEgZnJvbSBtaWNlIHdoZXJlIHRoZSBnb2FsIGlzIHRvIHVuZGVyc3RhbmQgbWlzcmVndWxhdGlvbiBpbiB3b3VuZCBoZWFsaW5nIHRoYXQgY2FuIGxlYWQgdG8gYm9uZSBmb3JtYXRpb24gaW4gc29mdCB0aXNzdWUuIE9mIGludGVyZXN0IGFyZSB0aGUgY2VsbCB0eXBlcyBhbmQgZ2VuZXMgdGhhdCBhcmUgaW52b2x2ZWQgaW4gdGhhdCBtaXNyZWd1bGF0aW9uLiBFc3NlbnRpYWxseSwgIldoeSBtaWdodCBib25lIGZvcm0gYXQgdGhlIGluanVyeSBzaWdodCB3aGVuIGl0IHNob3VsZG4ndD8iCgpUbyBpbnZlc3RpZ2F0ZSB0aGlzLCBjZWxscyBmcm9tIGRheSAwIChwcmlvciB0byBpbmp1cnkpLCBhbmQgZGF5cyAzLCA3LCBhbmQgMjEgcG9zdC1pbmp1cnkgd2VyZSBzdWJqZWN0ZWQgdG8gc2NSTkEtc2VxIGluIHF1YWRydXBsaWNhdGUgZm9yIGVhY2ggZGF5LiBJbiB0aGUgaW50ZXJlc3Qgb2YgY29tcHV0YXRpb25hbCBjb25zdHJhaW50cyBpbiB0aGlzIHdvcmtzaG9wLCB3ZSBoYXZlIG9taXR0ZWQgZGF5IDMuCgohW10oLi9pbWFnZXMvY3VycmljdWx1bS9leHBlcmltZW50YWxfZGVzaWduLmpwZykKCiMjIE9iamVjdGl2ZXMKCi0gT3JpZW50IG9uIFJTdHVkaW8uCi0gQ3JlYXRlIFJTdHVkaW8gcHJvamVjdCBmb3IgYW5hbHlzaXMuCi0gQ3JlYXRlIGRpcmVjdG9yeSBzdHJ1Y3R1cmUgZm9yIGFuYWx5c2lzLgotIExlYXJuIGhvdyB0byByZWFkIDEwWCBkYXRhIGludG8gU2V1cmF0LgotIEludHJvZHVjZSB0aGUgU2V1cmF0IG9iamVjdCwgYW5kIGhvdyB0byBhY2Nlc3MgcGFydHMgb2YgaXQuCgotLS0KCiMgT3JpZW50aW5nIG9uIFJTdHVkaW8KClRvIG9yaWVudCBvdXJzZWx2ZXMgb24gdGhlIGludGVyZmFjZSBvZiBSU3R1ZGlvLCBsb29rIGluIHRoZSBsb3dlciByaWdodCBwYW5lLCB0aGF0IGhhcyBhIGxpc3Qgb2YgZmlsZXMgYW5kIGZvbGRlcnMuIENsaWNrIG9uIHRoZSBgd2VsY29tZS5SYCBzY3JpcHQuCgpUaGVyZSBzaG91bGQgbm93IGJlIGZvdXIgcGFuZXMgaW4geW91ciBSU3R1ZGlvIHdpbmRvdywgd2l0aCB0aGUgYHdlbGNvbWUuUmAgc2NyaXB0IGluIHRoZSB1cHBlciBsZWZ0IHBhbmUsIHRoaXMgaXMgdGhlICJTb3VyY2UiIG9yICJTY3JpcHRzIiBwYW5lLiBJdCBkaXNwbGF5cyB0aGUgY29kZSB0aGF0IHdlIHdpbGwgd3JpdGUgdG8gcGVyZm9ybSBvdXIgYW5hbHlzaXMuCgo8IS0tIEluc2VydCBzY3JlZW5zaG90IHdpdGggd2VsY29tZS5SIGluIHNjcmlwdCBwYW5lLiAtLT4KCkluIHRoZSBTY3JpcHRzIHBhbmUsIHRoZXJlIGlzIGEgbGluZSBvZiBpY29ucywgdG93YXJkcyB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgcGFuZSB0aGVyZSBpcyBhICJSdW4iIGJ1dHRvbi4gRmlyc3QgaGlnaGxpZ2h0IGFsbCB0aGUgY29kZSBpbiB0aGUgU291cmNlIHBhbmUsIGFuZCB0aGVuIGNsaWNrICJSdW4iLgoKPCEtLSBJbnNlcnQgc2NyZWVuc2hvdCBvZiB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgd2VsY29tZS5SIC0tPgoKKipDaGVja3BvaW50KioKCkEgbnVtYmVyIG9mIHRoaW5ncyBqdXN0IGhhcHBlbmVkIGluIGFsbCB0aGUgcGFuZXM6CgoxLiBUaGVyZSBpcyBhIHJlY29yZCBvZiB0aGUgY29kZSB0aGF0IHdhcyBydW4gYWxvbmcgd2l0aCB0aGUgcmVzdWx0cyBpbiB0aGUgbG93ZXIgbGVmdCBwYW5lLiBUaGlzIGlzIHRoZSAiQ29uc29sZSIgcGFuZWwsIGFuZCBoZXJlIGlzIHdoZXJlIHRoZSBvdXRwdXQgb2YgZXhlY3V0aW5nIGNvZGUgaXMgZGlzcGxheWVkLiBZb3UgY2FuIGFsc28gdHlwZSBkaXJlY3RseSBpbiB0aGUgcHJvbXB0IGFuZCBleGVjdXRlIGNvZGUsIGJ1dCBpdCBpcyBub3Qgc2F2ZWQgaW4gdGhlIHNjcmlwdC4KMi4gVGhlcmUgaXMgYSBgcGhlbm90eXBlc19ieV9zcGVjaWVzYCBvYmplY3QgbGlzdGVkIGluIHRoZSB1cHBlciByaWdodCBwYW5lLiBUaGlzIGlzIHRoZSAiRW52aXJvbm1lbnQiIHBhbmVsLCBhbmQgaXQgc2hvd3MgYWxsIHRoZSBleGlzdGluZyB2YXJpYWJsZXMgaW4gdGhlIHNlc3Npb24sIGFzIHdlbGwgYXMgYSBicmllZiBkZXNjcmlwdGlvbi4KMy4gVGhlcmUgaXMgYSBwbG90IGluIHRoZSBsb3dlciByaWdodCBwYW5lIHdoZXJlIHRoZSBmaWxlIHN0cnVjdHVyZSB3YXMgcHJldmlvdXNseSBsaXN0ZWQuIFRoaXMgaXMgdGhlICJGaWxlcyAvIFBsb3RzIC8gSGVscCIgcGFuZSwgYW5kIGl0IGNhbiBzaG93IHRoZSBmaWxlIGJyb3dzZXIgdGhhdCB3ZSBzYXcgYXQgdGhlIHN0YXJ0LCBkaXNwbGF5IHBsb3RzIHRoYXQgYXJlIGV4ZWN1dGVkIGluIHRoZSBDb25zb2xlLCBvciBkaXNwbGF5IGhlbHAgcGFnZXMgYXMgd2UnbGwgc2VlIGxhdGVyLgoKIyBQcm9qZWN0IHNldHVwCgpUbyBzdGFydCwgd2Ugd2lsbCBhY2Nlc3Mgb3VyIHNoYXJlZCBSU3R1ZGlvIHNlcnZlciBieSBvcGVuaW5nIGEgd2ViIGJyb3dzZXIgdG8gdGhlIGZvbGxvd2luZyBVUkw6CgpbaHR0cHM6Ly9iZngtd29ya3Nob3AwMi5tZWQudW1pY2guZWR1L10oaHR0cHM6Ly9iZngtd29ya3Nob3AwMi5tZWQudW1pY2guZWR1LykKCllvdSBzaG91bGQgbm93IGJlIGxvb2tpbmcgYXQgYSBwYWdlIHRoYXQgd2lsbCBhbGxvdyB5b3UgdG8gbG9naW4gdG8gdGhlIFJTdHVkaW8gc2VydmVyOgoKIVtJbWFnZTogTG9naW4gcGFnZSBmb3IgUlN0dWRpbyBzZXJ2ZXIuXShpbWFnZXMvY3VycmljdWx1bS9zZXJ2ZXJfbG9naW4ucG5nKQoKRW50ZXIgeW91ciB1c2VyIGNyZWRlbnRpYWxzIGFuZCBjbGljayA8a2JkPlNpZ24gSW48L2tiZD4uIFlvdXIgdXNlcm5hbWUgYW5kIHBhc3N3b3JkIHdlcmUgcHJvdmlkZWQgdmlhIGVtYWlsLCBidXQgaWYgeW91IG5lZWQgaGVscCwgYSBoZWxwZXIgY2FuIHJldHJpZXZlIGl0IGZvciB5b3UgaWYgeW91IGFzayBpbiBTbGFjay4gT25jZSBsb2dnZWQgaW4sIHlvdSBzaG91bGQgbm93IHNlZSB0aGUgUlN0dWRpbyBpbnRlcmZhY2U6CgohW0ltYWdlOiBMYW5kaW5nIHBhZ2UgYWZ0ZXIgc3VjY2Vzc2Z1bCBsb2dpbiB0byBSU3R1ZGlvIHNlcnZlci5dKGltYWdlcy9jdXJyaWN1bHVtL3JzdHVkaW9fbGFuZGluZy5wbmcpCgo8IS0tIGNvbnNpZGVyIGFkZGluZyB3ZWxjb21lIHNjcmlwdCBvciBleGFtcGxlIG9mIGZpbmFsIGdvYWw/LS0+CgojIyBDcmVhdGluZyBhIHByb2plY3QKCldlIHdpbGwgY3JlYXRlIGFuIFJTdHVkaW8gUHJvamVjdCB0aGF0IHdpbGwgaGVscCB1cyBrZWVwIG91ciBmaWxlcyBvcmdhbml6ZWQuIFNlZSB0aGUgW1Byb2plY3RzXShodHRwczovL3I0ZHMuaGFkbGV5Lm56L3dvcmtmbG93LXNjcmlwdHMuaHRtbCNwcm9qZWN0cykgc2VjdGlvbiBvZiBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkbGV5Lm56LykgZm9yIGEgbW9yZSBpbi1kZXB0aCBkZXNjcmlwdGlvbiBvZiB3aGF0IGEgcHJvamVjdCBpcyBhbmQgaG93IGl0J3MgaGVscGZ1bC4KClRvIGNyZWF0ZSBhIFByb2plY3QgZm9yIHRoaXMgd29ya3Nob3AsIGNsaWNrIDxrYmQ+RmlsZTwva2JkPiB0aGVuIDxrYmQ+TmV3IFByb2plY3QuLi48L2tiZD4uIEluIHRoZSBOZXcgUHJvamVjdCBXaXphcmQgd2luZG93IHRoYXQgb3BlbnMsIHNlbGVjdCA8a2JkPkV4aXN0aW5nIERpcmVjdG9yeTwva2JkPiwgdGhlbiA8a2JkPkJyb3dzZS4uLjwva2JkPi4gSW4gdGhlIENob29zZSBEaXJlY3Rvcnkgd2luZG93LCBzZWxlY3QgdGhlIGBJU0NfUmAgZm9sZGVyIGJ5IGNsaWNraW5nIGl0IG9uY2UsIGFuZCB0aGVuIGNsaWNrIHRoZSA8a2JkPkNob29zZTwva2JkPiBidXR0b24uIEZpbmFsbHksIGNsaWNrIDxrYmQ+Q3JlYXRlIFByb2plY3Q8L2tiZD4uCgpPbmNlIHdlIGRvIHRoaXMsIFJTdHVkaW8gd2lsbCByZXN0YXJ0IGFuZCB0aGUgRmlsZXMgcGFuZSAobG93ZXIgcmlnaHQpIHNob3VsZCBwdXQgdXMgaW4gdGhlIGB+L0lTQ19SYCBmb2xkZXIgd2hlcmUgdGhlcmUgaXMgYW4gYGlucHV0cy9gIGZvbGRlciBhbmQgYW4gYElTQ19SLlJwcm9qYCBmaWxlLgoKIyMgRGlyZWN0b3J5IHN0cnVjdHVyZQoKV2UgaGF2ZSBpbmNsdWRlZCB0aGUgZGF0YSB0byBiZSB1c2VkIGluIHRoZSB3b3Jrc2hvcCBpbiB0aGUgYGlucHV0cy9gIGZvbGRlci4gSG93ZXZlciwgdGhlIHByb2plY3Qgd2lsbCBuZWVkIHRvIGluY2x1ZGUgZm9sZGVycyBmb3Igb3VyIGFuYWx5c2lzIGFuZCBvdXIgYW5hbHlzaXMgc2NyaXB0cy4gTGV0J3MgY3JlYXRlIHRoYXQgZGlyZWN0b3J5IHN0cnVjdHVyZSB3aXRoIHRoZSBgZGlyLmNyZWF0ZSgpYCBmdW5jdGlvbi4KCmBgYHtyLCBkaXJfY3JlYXRlfQojIyMjIyBEYXkgMSAtIEdldHRpbmcgU3RhcnRlZCB3aXRoIFNldXJhdAoKIyBDcmVhdGUgcHJvamVjdCBkaXJlY3RvcmllcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmRpci5jcmVhdGUoJ3NjcmlwdHMnLCBzaG93V2FybmluZ3MgPSBGQUxTRSwgcmVjdXJzaXZlID0gVFJVRSkKZGlyLmNyZWF0ZSgncmVzdWx0cy9maWd1cmVzJywgc2hvd1dhcm5pbmdzID0gRkFMU0UsIHJlY3Vyc2l2ZSA9IFRSVUUpCmRpci5jcmVhdGUoJ3Jlc3VsdHMvdGFibGVzJywgc2hvd1dhcm5pbmdzID0gRkFMU0UsIHJlY3Vyc2l2ZSA9IFRSVUUpCmRpci5jcmVhdGUoJ3Jlc3VsdHMvcmRhdGEnLCBzaG93V2FybmluZ3MgPSBGQUxTRSwgcmVjdXJzaXZlID0gVFJVRSkKYGBgCgpJbiB0aGUgRmlsZXMgcGFuZSB3ZSBzaG91bGQgc2VlIHRoZSBuZXcgYHJlc3VsdHMvYCBhbmQgYHNjcmlwdHMvYCBmb2xkZXJzLiAKCiMjIEFuYWx5c2lzIHNjcmlwdAoKVGhlIHR3byBtb3N0IGltcG9ydGFudCBhcnRpZmFjdHMgb2Ygb3VyIGFuYWx5c2lzIGFyZSB0aGUgZGF0YSBmcm9tIENlbGwgUmFuZ2VyLCBhbmQgdGhlIHNjcmlwdCB0byBhbmFseXplIHRoZSBkYXRhLiBUaGVyZSB3aWxsIGJlIG91dHB1dHMgaW4gYHJlc3VsdHMvYCwgYW5kIHRoZXNlIHdpbGwgYmUgaW1wb3J0YW50LCBidXQgaWYgdGhlIGNvbnRlbnRzIG9mIGByZXN1bHRzL2AgYXJlIGV2ZXIgbG9zdCwgdGhlIHNjcmlwdCB3aWxsIGJlIGFibGUgdG8gcmUtZ2VuZXJhdGUgdGhlbSBpZiB3ZSd2ZSBjYXB0dXJlZCBhbGwgb3VyIHN0ZXBzIGFzIGNvZGUsIHdoaWNoIHdlIGFpbSB0byBkby4KClRvIGNyZWF0ZSB0aGUgYW5hbHlzaXMgc2NyaXB0LCBjbGljayA8a2JkPkZpbGU8L2tiZD4sIGhvdmVyIG92ZXIgPGtiZD5OZXcgRmlsZTwva2JkPiwgYW5kIGNsaWNrIG9uIDxrYmQ+UiBTY3JpcHQ8L2tiZD4uIEEgbmV3IHBhbmUgaW4gdGhlIHVwcGVyIGxlZnQgc2xpZGVzIGludG8gdmlldyBhbmQgaXMgdGhlIFVudGl0bGVkIHNjcmlwdCBmaWxlLiBTYXZlIHRoaXMgZmlsZSwgYW5kIG5hbWUgaXQsIGJ5IGNsaWNraW5nIDxrYmQ+RmlsZTwva2JkPiB0aGVuIDxrYmQ+U2F2ZTwva2JkPi4gRG91YmxlIGNsaWNrIHRoZSBgc2NyaXB0cy9gIGZvbGRlciwgYW5kIGluIHRoZSBGaWxlIG5hbWU6IHRleHQgYm94IHR5cGUgImFuYWx5c2lzLlIiLiBUaGVuIGNsaWNrIDxrYmQ+U2F2ZTwva2JkPi4KCkFzIHdlIHByb2NlZWQgdGhyb3VnaCB0aGUgd29ya3Nob3AsIHdlIHNob3VsZCBzYXZlIHRoaXMgZmlsZSAoYnkgY2xpY2tpbmcgdGhlIEZsb3BweSBkaXNrLCBjbGlja2luZyA8a2JkPkZpbGU8L2tiZD4gdGhlbiA8a2JkPlNhdmU8L2tiZD4sIG9yIGJ5IHR5cGluZyA8a2JkPkNvbnRyb2wgKyBTPC9rYmQ+KS4KCj4gKipHb29kIHNjcmlwdGluZyBwcmFjdGljZXMqKgo+IAo+IEluIGFueSBhbmFseXNpcyBzY3JpcHQsIHdlIHJlY29tbWVuZCB1c2luZyBjb21tZW50cyAobGluZXMgcHJlY2VkZWQgYnkgYSAiIyIpIHRvIHByb3ZpZGUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCBjb2RlIHRoYXQgbWF5IG5vdCBiZSBzZWxmLWV2aWRlbnQuIFRoaXMgaXMgdG8gdGhlIGJlbmVmaXQgb2Ygb3RoZXJzIHRoYXQgbWF5IGxvb2sgYXQgdGhlIGNvZGUsIGJ1dCBhbHNvIHRvIHlvdXIgZnV0dXJlLXNlbGYuCjxicj4KCiMgQW5hbHlzaXMgaW5pdGlhbGl6YXRpb24KCldlIGJlZ2luIG91ciBhbmFseXNpcyBzY3JpcHQgYnkgbG9hZGluZyB0aGUgbGlicmFyaWVzIHdlIGV4cGVjdCB0byB1c2UuIEl0J3MgZ2VuZXJhbGx5IGdvb2QgcHJhY3RpY2UgdG8gaW5jbHVkZSBhbGwgYGxpYnJhcnkoKWAgY2FsbHMgYXQgdGhlIHRvcCBvZiBhIHNjcmlwdCBmb3IgdmlzaWJpbGl0eS4KCmBgYHtyLCBsb2FkX2xpYnJhcmllcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgTG9hZCBsaWJyYXJpZXMgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoQlBDZWxscykKbGlicmFyeSh0aWR5dmVyc2UpCgpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSAxZTkpCmBgYAoKVGhlIGxpYnJhcmllcyB0aGF0IHdlIGFyZSBsb2FkaW5nIGFyZToKCi0gVGhlIGBTZXVyYXRgIGxpYnJhcnksIGRldmVsb3BlZCBieSB0aGUgW1NhdGlqYSBsYWJdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy8pLCB3aGljaCB3aWxsIHByb3ZpZGUgdGhlIGVzc2VudGlhbCBmdW5jdGlvbnMgdXNlZCBpbiBvdXIgc2luZ2xlLWNlbGwgYW5hbHlzaXMuIFRoZSBbU2V1cmF0IGRvY3VtZW50YXRpb25dKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKSBpcyBleHRlbnNpdmUgYW5kIGluZGlzcGVuc2libGUuCi0gVGhlIGBCUENlbGxzYCBsaWJyYXJ5LCBkZXZlbG9wZWQgYnkgW0JlbmphbWluIFBhcmtzXShodHRwczovL2JucHJrcy5naXRodWIuaW8vQlBDZWxscy9hdXRob3JzLmh0bWwjY2l0YXRpb24pLCBpcyBhIHJlY2VudCBwYWNrYWdlIHdpdGggdGhlIHByaW1hcnkgZ29hbCBvZiBlZmZpY2llbnRseSBzdG9yaW5nIHNpbmdsZS1jZWxsIGRhdGEgdG8gcmVkdWNlIGl0cyBtZW1vcnkgZm9vdHByaW50LiBUaGUgW0JQQ2VsbHMgZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9ibnBya3MuZ2l0aHViLmlvL0JQQ2VsbHMvaW5kZXguaHRtbCkgaW5jbHVkZXMgbWFueSB1c2VmdWwgdHV0b3JpYWxzLgotIFRoZSBgdGlkeXZlcnNlYCBsaWJyYXJ5LCBkZXZlbG9wZWQgYnkgUG9zaXQsIGlzIGFuIGVzc2VudGlhbCBwYWNrYWdlIGZvciBkYXRhIG1hbmlwdWxhdGlvbiBhbmQgcGxvdHRpbmcuIFRoZSBbdGlkeXZlcnNlIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKSBpcyBlc3NlbnRpYWwgZm9yIGdldHRpbmcgYSBoYW5kbGUgb24gdGhlIGFycmF5IG9mIGZ1bmN0aW9ucyBpbiB0aGUgbWFueSBwYWNrYWdlcyBjb250YWluZWQgdGhlcmVpbi4KCiMgSW5wdXQgZGF0YQoKVGhlIGBpbnB1dHMvYCBmb2xkZXIgaGFzIHRoZSBkYXRhIGZvciB0aGUgd29ya3Nob3Agc3RvcmVkIGluIHR3byBmb3Jtcy4gVGhlIGZpcnN0LCBgaW5wdXRzLzEweF9maWx0ZXJlZC9gIGlzIGNsb3NlciB0byB3aGF0IEFHQyB3b3VsZCBnZW5lcmF0ZSB3aXRoIENlbGwgUmFuZ2VyLCB3aGVyZSBlYWNoIHNhbXBsZSBoYXMgYSBmb2xkZXIsIGFuZCB3aXRoaW4gdGhhdCBmb2xkZXIgdGhlcmUgYXJlIHRocmVlIGZpbGVzOgoKLSBgYmFyY29kZXMudHN2Lmd6YAotIGBmZWF0dXJlcy50c3YuZ3pgCi0gYG1hdHJpeC5tdHguZ3pgCgpOb3RlLCB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBmaWx0ZXJlZCBtYXRyaWNlcy4KCjwhLS0gV2VyZSB0aGVzZSBmaWxlcyBleHBsYWluZWQgZWFybGllcj8gSWYgbm90LCBicmllZmx5IGV4cGxhaW4gdGhlbSBoZXJlIC0tPgoKVGhlIHNlY29uZCwgYGlucHV0cy8xMHhfbWF0X2ZpbHRlcmVkL2AgaXMgdGhlIHJlc3VsdCBvZiB0aGUgYEJQQ2VsbHNgIHBhY2thZ2UsIGFuZCBpcyBtb3JlIG1hY2hpbmUtcmVhZGFibGUgdGhhbiBodW1hbi1yZWFkYWJsZS4gSXQgaXMgYW4gZWZmaWNpZW50IHdheSBvZiBzdG9yaW5nIHRoZSBzYW1lIGRhdGEgY29udGFpbmVkIGluIHRoZSB0aHJlZSBmaWxlcyBhYm92ZS4gV2Ugd2lsbCB1c2UgdGhpcyBmb3JtIG9mIHRoZSBpbnB1dCBkYXRhIGJlY2F1c2UgaXQgaXMgbW9yZSBtZW1vcnkgZWZmaWNpZW50LgoKV2hpbGUgdGhlIGZ1bGwgZGF0YXNldCB3ZSBzZWxlY3RlZCBoYXMgdGltZS1zZXJpZXMgaW5mb3JtYXRpb24gZnJvbSBEYXkgMCwgRGF5IDMsIERheSA3LCBhbmQgRGF5IDIxLCB3ZSBoYXZlIHJlbW92ZWQgRGF5IDMgdG8gcmVkdWNlIHRoZSBtZW1vcnkgcmVxdWlyZW1lbnRzIGZ1cnRoZXIuCgo8IS0tIEFkZCBjb250ZXh0IGZvciBleHBlY3RlZCAxMHggb3V0cHV0cyBhbmQgaG93IHdlIHdvdWxkIHVzdWFsbHkgc3RhcnQgd2l0aCB0aGUgImZpbHRlcmVkIiBvdXRwdXRzIChhbmQgYWRkIGRyb3Bkb3duIG9yIGxpbmsgb3V0IHRvIDEweCByZXNvdXJjZXMgdG8gY29udHJhc3QgYmV0d2VlbiBmaWx0ZXJlZCBhbmQgcmF3IG91dHB1dHMgYW5kIHVzZSBjYXNlcyBmb3IgZWFjaCkgLS0+CgojIENyZWF0ZSBhIFNldXJhdCBvYmplY3QKClRoZSBtb3N0IHJlY2VudCByZWxlYXNlIG9mIGBTZXVyYXRgLCB2ZXJzaW9uIDUsIGluY2x1ZGVzIGNoYW5nZXMgd2hpY2ggdGFrZSBhZHZhbnRhZ2Ugb2YgdGhlIG1lbW9yeS1lZmZpY2llbnQgc3RvcmFnZSBpbXBsZW1lbnRlZCBpbiBgQlBDZWxsc2AuIFRvIHJlYWQgdGhlIGVmZmljaWVudGx5IHN0b3JlZCBkYXRhIHdpdGggYEJQQ2VsbHNgIHdlIHdpbGwgdXNlIHRoZSBgb3Blbl9tYXRyaXhfZGlyKClgIGZ1bmN0aW9uLgoKPCEtLSBJbnRyb2R1Y2UgQlBDZWxsIHBhY2thZ2UgYW5kIGhvdyBtb3N0IHJlY2VudCB2ZXJzaW9uIG9mIFNldXJhdCAoU2V1cmF0IDUpIHN1cHBvcnRzIG1vcmUgZWZmaWNpZW50IGRhdGEgc3RvcmFnZSB0byBpbXByb3ZlIHBlcmZvcm1hbmNlIGFuZCBhY2NvbW9kYXRlIGxhcmdlciBkYXRhc2V0cyAtLT4KCmBgYHtyLCByZWFkX21hdHJpeF9kaXJ9CiMgUHV0cyB0aGUgZGF0YSAib24gZGlzayIgcmF0aGVyIHRoYW4gImluIG1lbW9yeSIgIC0tLS0tLS0tLS0tLS0KZ2VvX21hdCA9IG9wZW5fbWF0cml4X2RpcihkaXIgPSAnaW5wdXRzLzEweF9tYXRfZmlsdGVyZWQnKQpgYGAKClRoaXMgcmVhZHMgaW4gdGhlIGV4cHJlc3Npb24gbWF0cml4IHdoaWNoIGhhcyBnZW5lcyBhcyByb3dzIGFuZCBjZWxscyBhcyBjb2x1bW5zLiBUaGUgZXhwcmVzc2lvbiBtYXRyaXggaXMgdGhlIHByZWN1cnNvciB0byBjcmVhdGluZyB0aGUgYFNldXJhdGAgb2JqZWN0IHVwb24gd2hpY2ggYWxsIG91ciBhbmFseXNpcyB3aWxsIGJlIGRvbmUuIFRvIGNyZWF0ZSB0aGUgYFNldXJhdGAgb2JqZWN0OgoKYGBge3IsIGNyZWF0ZV9zZXVyYXR9CiMgQ3JlYXRlIHNldXJhdCBvYmplY3QgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZ2VvX3NvID0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGdlb19tYXQsIG1pbi5jZWxscyA9IDEsIG1pbi5mZWF0dXJlcyA9IDUwKQpnZW9fc28KYGBgCgpXZSBoYXZlIHNwZWNpZmllZCBzb21lIHBhcmFtZXRlcnMgdG8gcmVtb3ZlIGdlbmVzIGFuZCBjZWxscyB3aGljaCBkbyBub3QgY29udGFpbiB2ZXJ5IG11Y2ggaW5mb3JtYXRpb24uIFNwZWNpZmljYWxseSBhIGdlbmUgaXMgcmVtb3ZlZCBpZiBpdCBpcyBleHByZXNzZWQgaW4gMSBvciBmZXdlciBjZWxscywgYW5kIGEgY2VsbCBpcyByZW1vdmVkIGlmIGl0IGNvbnRhaW5zIHJlYWRzIGZvciA1MCBvciBmZXdlciBnZW5lcy4gSW4gdGhlIGNvbnRleHQgb2YgdGhpcyB3b3Jrc2hvcCwgdGhpcyBoZWxwcyB1cyBtaW5pbWl6ZSBtZW1vcnkgdXNhZ2UuCgojIyMgUmVhZGluZyBpbiBkYXRhIGZyb20gc2NyYXRjaAo8ZGV0YWlscz4KPHN1bW1hcnk+KkhvdyB0byByZWFkIGluIGRhdGEgYW5kIGNyZWF0ZSBCUENlbGwgbWF0cml4Kjwvc3VtbWFyeT4KCkFib3ZlLCB3ZSd2ZSBkZW1vbnN0cmF0ZWQgaG93IHRvIHJlYWQgZGF0YSB0aGF0IHdhcyBhbHJlYWR5IGFycmFuZ2VkIGluIHRoZSBgQlBDZWxsc2AgbWFubmVyLiBXZSBoYXZlIGRvbmUgdGhpcyBmb3IgdGhlIHdvcmtzaG9wIHRvIHJlZHVjZSBvdXIgbWVtb3J5IGZvb3RwcmludCBmcm9tIHRoZSBzdGFydCwgYnV0IHdoZW4geW91IGdldCBkYXRhIGJhY2sgZnJvbSBBR0MsIHlvdSB3aWxsIGhhdmUgdG8gZm9sbG93IHRoZSBzdGVwcyBiZWxvdy4KCmBgYAojIFRvIGxvYWQgZGF0YSBmcm9tIHJhdyBmaWxlcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyBETyBOT1QgUlVOICMjIwoKIyBDb2xsZWN0IHRoZSBkaXJlY3RvcmllcyBjb250YWluaW5nIHRoZSAzIGZpbGVzOgojIGJhcmNvZGVzLnRzdi5neiwgZmVhdHVyZXMudHN2Lmd6LCBtYXRyaXgubXR4Lmd6CiMgVGhlIFstMV0gZHJvcHMgdGhlIHBhcmVudCBkaXJlY3RvcnkgaW5wdXRzLzEweF9hbmFseXNpcwpzYW1wbGVfZGlycyA9IGxpc3QuZGlycygnaW5wdXRzLzEweF9hbmFseXNpcycpWy0xXQoKIyBFeHRyYWN0IHRoZSBzYW1wbGUgbmFtZXMuIE5PVEUsIHRoaXMgd2lsbCBkaWZmZXIgZGVwZW5kaW5nCiMgb24gdGhlIGZvcm0gdGhlIGZvbGRlciBuYW1lcyB0YWtlLCB0aGlzIHdvcmtzIGZvciB0aGlzIGRhdGEuCiMgV2UgY3JlYXRlIGEgbmFtZWQgdmVjdG9yIApzYW1wbGVzID0gc3RyX3NwbGl0KGJhc2VuYW1lKHNhbXBsZV9kaXJzKSwgcGF0dGVybiA9ICdfJywgc2ltcGxpZnkgPSBUUlVFKVssMl0KCiMgTmFtaW5nIHRoZSBzYW1wbGVfZGlycyB2ZWN0b3IgbWFrZXMgU2V1cmF0IG5hbWUgdGhlCiMgc2FtcGxlcyBpbiB0aGUgY29ycmVzcG9uZGluZyBtYW5uZXIsIHdoaWNoIGlzIG5pY2UgZm9yIHVzLgpuYW1lcyhzYW1wbGVfZGlycykgPSBzYW1wbGVzCgojIENyZWF0ZSB0aGUgZXhwcmVzc2lvbiBtYXRyaXgKZ2VvX21hdCA9IFJlYWQxMFgoZGF0YS5kaXIgPSBzYW1wbGVfZGlycykKYGBgCgpUaGlzIHdpbGwgZ2VuZXJhdGUgdGhlIGV4cHJlc3Npb24gbWF0cml4IHRoYXQgaXMgdGhlIGlucHV0IHRvIGBDcmVhdGVTZXVyYXRPYmplY3QoKWAsIGFzIGFib3ZlLiBJZiB5b3Ugd2FudGVkIHRvIHdyaXRlIHRoaXMgZXhwcmVzc2lvbiBtYXRyaXggdXNpbmcgYEJQQ2VsbHNgIHNvIHRoYXQgaXQgY291bGQgYmUgdXNlZCBpbiB0aGUgbW9yZSBtZW1vcnktZWZmaWNpZW50IG1hbm5lciwgeW91IHdvdWxkIGRvOgoKYGBgCndyaXRlX21hdHJpeF9kaXIobWF0ID0gZ2VvX21hdCwgZGlyID0gJ2lucHV0cy8xMHhfbWF0X2ZpbHRlcmVkJykKYGBgCgpZb3UnZCB0aGVuIGhhdmUgdGhlIGl0ZW1zIG5lZWRlZCB0byBydW4gdGhlIGNvZGUgd2UndmUgYWxyZWFkeSBydW4uCjwvZGV0YWlscz4KPGJyPgoKIyMgU3RydWN0dXJlIG9mIGEgU2V1cmF0IG9iamVjdAoKVGhlIGBTZXVyYXRgIG9iamVjdCBpcyBhIGNvbXBsZXggZGF0YSB0eXBlLCBzbyBsZXQncyBnZXQgYSBiaXJkcyBleWUgdmlldyB3aXRoIGFuIGltYWdlIGZyb20gW3RoaXMgdHV0b3JpYWxdKGh0dHBzOi8vc3diaW9pbmYuZ2l0aHViLmlvL3NjUk5Bc2VxSW5SX0RvY28vc2V1cmF0b2JqZWN0Lmh0bWwjZGlzY3Vzc2lvbi10aGUtc2V1cmF0LW9iamVjdC1pbi1yKSBvbiBzaW5nbGUtY2VsbCBhbmFseXNpcy4KCiFbSW1hZ2U6IFNjaGVtYXRpYyBvZiBgU2V1cmF0YCBvYmplY3QuXShpbWFnZXMvc2V1cmF0X3NjaGVtYXRpYy9TbGlkZTEucG5nKQoKVGhlIHRocmVlIG1haW4gInNsb3RzIiBpbiB0aGUgb2JqZWN0IGFyZToKCjEuIFRoZSBgYXNzYXlzYCBzbG90IHN0b3JlcyB0aGUgZXhwcmVzc2lvbiBkYXRhIGFzIGBBc3NheWAgb2JqZWN0cy4KMi4gVGhlIGBtZXRhLmRhdGFgIHNsb3Qgd2hpY2ggc3RvcmVzIGNlbGwtbGV2ZWwgaW5mb3JtYXRpb24sIGluY2x1ZGluZyB0ZWNobmljYWwgYW5kIHBoZW5vdHlwaWMgZGF0YS4KMy4gVGhlIGByZWR1Y3Rpb25zYCBzbG90IHN0b3JlcyB0aGUgcmVzdWx0cyBvZiBkaW1lbnNpb24gcmVkdWN0aW9uIGFwcGxpZWQgdG8gdGhlIGBhc3NheXNgLgoKVGhlcmUgYXJlIG90aGVyIHNsb3RzIHdoaWNoIHN0b3JlIGluZm9ybWF0aW9uIHRoYXQgYmVjb21lcyByZWxldmFudCBhcyB3ZSBwcm9ncmVzcyB0aHJvdWdoIHRoZSBhbmFseXNpcy4gV2Ugd2lsbCBoaWdobGlnaHQgdGhlIG90aGVyIHNsb3RzIGFzIHRoZXkgY29tZSB1cC4KCkFmdGVyIHJlYWRpbmcgdGhlIGRhdGEgaW4gYW5kIGNyZWF0aW5nIHRoZSBTZXVyYXQgb2JqZWN0IGFib3ZlLCB3ZSBjYW4gaW1hZ2luZSB0aGUgZm9sbG93aW5nIHNjaGVtYXRpYyByZXByZXNlbnRpbmcgb3VyIG9iamVjdDoKCiFbSW1hZ2U6IFNjaGVtYXRpYyBhZnRlciBjcmVhdGluZyB0aGUgU2V1cmF0IG9iamVjdC5dKGltYWdlcy9zZXVyYXRfc2NoZW1hdGljL1NsaWRlMi5wbmcpCgpOb3RlIHRoZSBSTkEgYXNzYXkgY29udGFpbnMgYSBgY291bnRgIGxheWVyIGNvbnNpc3Rpbmcgb2YgYSByYXcgY291bnQgbWF0cml4IHdoZXJlIHRoZSByb3dzIGFyZSBnZW5lcyAoZmVhdHVyZXMsIG1vcmUgZ2VuZXJpY2FsbHkpLCBhbmQgdGhlIGNvbHVtbnMgYXJlIGFsbCBjZWxscyBhY3Jvc3MgYWxsIHNhbXBsZXMuIE5vdGUgYWxzbyB0aGUgcHJlc2VuY2Ugb2YgYSBgbWV0YS5kYXRhYCB0YWJsZSBnaXZpbmcgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBjZWxsLiBXZSdsbCBwYXkgY2xvc2UgYXR0ZW50aW9uIHRvIHRoaXMgYXMgd2UgcHJvY2VlZC4gVGhlIG90aGVyIHNsb3RzIGluY2x1ZGUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGBhY3RpdmUuYXNzYXlgIGFuZCBgYWN0aXZlLmlkZW50YCB3aGljaCB0ZWxsIFNldXJhdCB3aGljaCBleHByZXNzaW9uIGRhdGEgdG8gdXNlIGFuZCBob3cgdGhlIGNlbGxzIGFyZSB0byBiZSBpZGVudGlmaWVkLgoKIyMgQWNjZXNzaW5nIHBhcnRzIG9mIHRoZSBvYmplY3QKClRoZSBvbmx5IHNsb3Qgb2YgdGhlIGBTZXVyYXRgIG9iamVjdCB0aGF0IHdlJ2xsIHR5cGljYWxseSBhY2Nlc3Mgb3IgbW9kaWZ5IGJ5IGhhbmQtLXRoYXQgaXMsIHdpdGhvdXQgYSBmdW5jdGlvbiBmcm9tIHRoZSBgU2V1cmF0YCBwYWNrYWdlLS1pcyB0aGUgYG1ldGEuZGF0YWAgb2JqZWN0LiBJbiBSLCBzbG90cyBhcmUgYWNjZXNzZWQgd2l0aCB0aGUgYEBgIHN5bWJvbCwgYXMgaW46CgpgYGB7ciwgcHJldmlld19tZXRhZGF0YX0KIyBFeGFtaW5lIFNldXJhdCBvYmplY3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpoZWFkKGdlb19zb0BtZXRhLmRhdGEpCmBgYAoKSGVyZSwgZWFjaCByb3cgaXMgYSBjZWxsLCBhbmQgZWFjaCBjb2x1bW4gaXMgaW5mb3JtYXRpb24gYWJvdXQgdGhhdCBjZWxsLiBUaGUgcm93cyBvZiB0aGUgdGFibGUgYXJlIG5hbWVkIGFjY29yZGluZyB0byB0aGUgdW5pcXVlbHkgaWRlbnRpZmlhYmxlIG5hbWUgZm9yIHRoZSBjZWxsLiBJbiB0aGlzIGNhc2UsIHRoZSBkYXkgYW5kIHJlcGxpY2F0ZSwgYXMgd2VsbCBhcyB0aGUgYmFyY29kZSBmb3IgdGhhdCBjZWxsLiBBcyB3ZSBjb250aW51ZSB0aGUgd29ya3Nob3AsIHdlIHdpbGwgY2hlY2sgaW4gb24gdGhlIGBtZXRhLmRhdGFgIHNsb3QgYW5kIG9ic2VydmUgY2hhbmdlcyB3ZSB3YW50IHRvIG1ha2UsIGFuZCB0aGF0IG90aGVyIGZ1bmN0aW9ucyB3aWxsIG1ha2UuIFdlJ2xsIGFsc28gb2JzZXJ2ZSB0aGUgb3RoZXIgYXNzYXlzIGFuZCBsYXllcnMgYW5kIG5vdGUgdGhlaXIgY2hhbmdlcy4KCiMgU2F2ZSBvdXIgcHJvZ3Jlc3MKCkxldCdzIFNhdmUgb3VyIHByb2dyZXNzIGFzIGFuIFJEUyBmaWxlIHdpdGggYHNhdmVSRFMoKWA7IHRoaXMgYWxsb3dzIHVzIHRvIGhhdmUgYSBjb3B5IG9mIHRoZSBvYmplY3QgdGhhdCB3ZSBjYW4gcmVhZCBiYWNrIGludG8gb3VyIHNlc3Npb24gd2l0aCB0aGUgYHJlYWRSRFMoKWAgY29tbW1hbmQuIFBlcmlvZGljYWxseSB3ZSB3aWxsIGJlIHNhdmluZyBvdXIgU2V1cmF0IG9iamVjdCBzbyB0aGF0IHdlIGNhbiBoYXZlIGEgdmVyc2lvbiBvZiBpdCBhdCBkaWZmZXJlbnQgc3RlcHMgb2YgdGhlIGFuYWx5c2lzLiBUaGVzZSB3aWxsIGFsc28gaGVscCB1cyBnZXQgdW50YW5nbGVkIGlmIHdlIGdldCBpbnRvIGFuIG9kZCBzdGF0ZS4KCmBgYHtyLCBzYXZlX3Jkc19oaWRkZW4sIGVjaG8gPSBGQUxTRX0KaWYoIWZpbGUuZXhpc3RzKCdyZXN1bHRzL3JkYXRhL2dlb19zb191bmZpbHRlcmVkLnJkcycpKSB7CiAgc2F2ZVJEUyhnZW9fc28sIGZpbGUgPSAncmVzdWx0cy9yZGF0YS9nZW9fc29fdW5maWx0ZXJlZC5yZHMnKQp9CmBgYAoKYGBge3IsIHNhdmVfcmRzLCBldmFsID0gRkFMU0V9CiMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnNhdmVSRFMoZ2VvX3NvLCBmaWxlID0gJ3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX3VuZmlsdGVyZWQucmRzJykKYGBgCgojIFN1bW1hcnkKCkluIHRoaXMgc2VjdGlvbiB3ZToKCi0gQ3JlYXRlZCBhbiBSU3R1ZGlvIHByb2plY3QgZm9yIGFuYWx5c2lzLgotIENyZWF0ZWQgdGhlIGRpcmVjdG9yeSBzdHJ1Y3R1cmUgZm9yIGFuYWx5c2lzLgotIExlYXJuZWQgaG93IHRvIHJlYWQgMTBYIGRhdGEgaW50byBTZXVyYXQuCi0gSW50cm9kdWNlZCB0aGUgU2V1cmF0IG9iamVjdCwgYW5kIGhvdyB0byBhY2Nlc3MgcGFydHMgb2YgaXQuCgotLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgoKPGJyLz4KPGJyLz4KCi0tLS0tLS0tLS0tLS0tLQoKfCBbUHJldmlvdXMgbGVzc29uXSgwMEEtT3JpZW50aW5nT25TY1JOQVNlcS5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXSgwMEItQ2VsbFJhbmdlckluQWN0aW9uLmh0bWwpIHwKfCA6LS0tIHwgOi0tLS06IHwgLS0tOiB8Cgo=