Overview
This document reproduces the Vizgen MERSCOPE/MERFISH section of the
Seurat v5 tutorial “Analysis of Image-based Spatial Data in
Seurat” (Satija Lab), applied to the Mouse Brain
Receptor Map, Slice 2 Replicate 1 (S2R1) dataset.
The dataset contains:
- A targeted 483-gene MERFISH panel (canonical cell-type markers,
non-sensory GPCRs, and receptor tyrosine kinases).
- A cell-by-gene count matrix.
- Cell metadata with spatial centroid coordinates.
- Cell segmentation boundaries (HDF5 format, one file per spatial
tile).
- Detected-transcript (molecule-level) coordinates.
The workflow performed here is:
- Validate the local data directory.
- Load the data with
LoadVizgen().
- Inspect centroids, segmentation boundaries, and molecules.
- Run quality control.
- Normalize with
SCTransform().
- Run PCA, UMAP, neighbor-graph construction, and clustering.
- Visualize clusters and gene expression in UMAP space and in tissue
coordinates.
- Visualize individual detected molecules.
- Crop a region of interest and visualize cell segmentation
boundaries.
- Identify cluster marker genes.
- Save the processed object.
The large raw microscopy images/ directory (~23 GB
locally, considerably larger on the Vizgen server) is not
required for any step shown here and is intentionally
excluded.
load libraries
This dataset was produced using the Vizgen MERSCOPE system, which
utilizes the MERFISH technology. The total dataset is available for
public download, and contains nine samples (three full coronal slices of
the mouse brain, with three biological replicates per slice). The gene
panel consists of 483 gene targets, representing known anonical cell
type markers, nonsensory G-Protein coupled receptors (GPCRs), and
Receptor Tyrosine Kinases (RTKs). In this vignette, we analyze one of
the samples - slice 2, replicate 1. The median number of transcripts
detected in each cell is 206.
First, we read in the dataset and create a Seurat object.
We use the LoadVizgen() function, which we have written to read in
the output of the Vizgen analysis pipeline. The resulting Seurat object
contains the following information:
A count matrix, indicating the number of observed molecules for each
of the 483 transcripts in each cell. This matrix is analogous to a count
matrix in scRNA-seq, and is stored by default in the RNA assay of the
Seurat object
Loading
segmentations
Loading segmentations
is a slow process and multi processing with the future pacakge is
recommended
library(future)
plan("multisession", workers = 4)
options(future.globals.maxSize = 10 * 1024^3)
data_dir <- file.path(getwd(), "data", "vizgen", "s2r1")
vizgen.obj <- LoadVizgen(
data.dir = data_dir,
fov = "s2r1"
)
vizgen.obj
An object of class Seurat
483 features across 83546 samples within 1 assay
Active assay: Vizgen (483 features, 0 variable features)
1 layer present: counts
1 spatial field of view present: s2r1
Cell Centroids: The spatial coordinates marking the centroid for each
cell being profiled
# Get the center position of each centroid. There is one row per cell in this dataframe.
head(GetTissueCoordinates(vizgen.obj[["s2r1"]][["centroids"]]))
Cell Segmentation Boundaries: The spatial coordinates that describe
the polygon segmentation of each single cell
# Get the coordinates for each segmentation vertice. Each cell will have a variable number of
# vertices describing its shape.
head(GetTissueCoordinates(vizgen.obj[["s2r1"]][["segmentation"]]))
Molecule positions: The spatial coordinates for each individual
molecule that was detected during the multiplexed smFISH experiment.
# Fetch molecules positions for Chrm1
head(FetchData(vizgen.obj[["s2r1"]][["molecules"]], vars = "Chrm1"))
Preprocessing and
unsupervised analysis
We start by performing a standard unsupervised clustering analysis,
essentially first treating the dataset as an scRNA-seq experiment. We
use SCTransform-based normalization, though we slightly modify the
default clipping parameters to mitigate the effect of outliers that we
occasionally observe in smFISH experiments. After normalization, we can
run dimensional reduction and clustering.
vizgen.obj <- SCTransform(vizgen.obj, assay = "Vizgen", clip.range = c(-10, 10))
vizgen.obj <- RunPCA(vizgen.obj, npcs = 30, features = rownames(vizgen.obj))
vizgen.obj <- RunUMAP(vizgen.obj, dims = 1:30)
Using method 'umap'
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
vizgen.obj <- FindNeighbors(vizgen.obj, reduction = "pca", dims = 1:30)
vizgen.obj <- FindClusters(vizgen.obj, resolution = 0.3)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 83546
Number of edges: 3056263
Running Louvain algorithm...
Maximum modularity in 10 random starts: 0.9595
Number of communities: 26
Elapsed time: 19 seconds
We can then visualize the results of the clustering either in UMAP
space (with DimPlot()) or overlaid on the image with ImageDimPlot().
DimPlot(vizgen.obj, reduction = "umap")

ImageDimPlot(vizgen.obj, fov = "s2r1", cols = "polychrome", axes = TRUE)

You can also customize multiple aspect of the plot, including the
color scheme, cell border widths, and size (see below).
Customizing spatial
plots in Seurat
The ImageDimPlot() and ImageFeaturePlot() functions have a few
parameters which you can customize individual visualizations. These
include:
alpha: Ranges from 0 to 1. Sets the transparency of within-cell
coloring. size: determines the size of points representing cells, if
centroids are being plotted cols: Sets the color scheme for the internal
shading of each cell. Examples settings are polychrome, glasbey, Paired,
Set3, and parade. Default is the ggplot2 color palette shuffle.cols: In
some cases the selection of cols is more effective when the same colors
are assigned to different clusters. Set shuffle.cols = TRUE to randomly
shuffle the colors in the palette. border.size: Sets the width of the
cell segmentation borders. By default, segmentations are plotted with a
border size of 0.3 and centroids are plotted without border.
border.color: Sets the color of the cell segmentation borders
dark.background: Sets a black background color (TRUE by default) axes:
Display Since it can be difficult to visualize the spatial localization
patterns of an individual cluster when viewing them all together, we can
highlight all cells that belong to a particular cluster:
p1 <- ImageDimPlot(vizgen.obj, fov = "s2r1", cols = "red", cells = WhichCells(vizgen.obj, idents = 1))
p2 <- ImageDimPlot(vizgen.obj, fov = "s2r1", cols = "red", cells = WhichCells(vizgen.obj, idents = 15))
p1 + p2

We can find markers of individual clusters and visualize their
spatial expression pattern. We can color cells based on their quantified
expression of an individual gene, using ImageFeaturePlot(), which is
analagous to the FeaturePlot() function for visualizing expression on a
2D embedding. Since MERFISH images individual molecules, we can also
visualize the location of individual molecules.
p1 <- ImageFeaturePlot(vizgen.obj, features = "Slc17a7")
p2 <- ImageDimPlot(vizgen.obj, molecules = "Slc17a7", nmols = 10000, alpha = 0.3, mols.cols = "red")
p1 + p2

Note that the nmols parameter can be used to reduce the total number
of molecules shown to reduce overplotting. You can also use the
mols.size, mols.cols, and mols.alpha parameter to further optimize.
Plotting molecules is especially useful for visualizing co-expression
of multiple genes on the same plot.
p1 <- ImageDimPlot(vizgen.obj, fov = "s2r1", alpha = 0.3, molecules = c("Slc17a7", "Olig1"), nmols = 10000)
markers.14 <- FindMarkers(vizgen.obj, ident.1 = "14")
p2 <- ImageDimPlot(vizgen.obj, fov = "s2r1", alpha = 0.3, molecules = rownames(markers.14)[1:4],
nmols = 10000)
p1 + p2

The updated Seurat spatial framework has the option to treat cells as
individual points, or also to visualize cell boundaries (segmentations).
By default, Seurat ignores cell segmentations and treats each cell as a
point (‘centroids’). This speeds up plotting, especially when looking at
large areas, where cell boundaries are too small to visualize.
We can zoom into a region of tissue, creating a new field of view.
For example, we can zoom into a region that contains the hippocampus.
Once zoomed-in, we can set DefaultBoundary() to show cell segmentations.
You can also ‘simplify’ the cell segmentations, reducing the number of
edges in each polygon to speed up plotting.
# create a Crop
cropped.coords <- Crop(vizgen.obj[["s2r1"]], x = c(1750, 3000), y = c(3750, 5250), coords = "plot")
# set a new field of view (fov)
vizgen.obj[["hippo"]] <- cropped.coords
# visualize FOV using default settings (no cell boundaries)
p1 <- ImageDimPlot(vizgen.obj, fov = "hippo", axes = TRUE, size = 0.7, border.color = "white", cols = "polychrome",
coord.fixed = FALSE)
# visualize FOV with full cell segmentations
DefaultBoundary(vizgen.obj[["hippo"]]) <- "segmentation"
p2 <- ImageDimPlot(vizgen.obj, fov = "hippo", axes = TRUE, border.color = "white", border.size = 0.1,
cols = "polychrome", coord.fixed = FALSE)
# simplify cell segmentations
vizgen.obj[["hippo"]][["simplified.segmentations"]] <- Simplify(coords = vizgen.obj[["hippo"]][["segmentation"]],
tol = 3)
DefaultBoundary(vizgen.obj[["hippo"]]) <- "simplified.segmentations"
# visualize FOV with simplified cell segmentations
DefaultBoundary(vizgen.obj[["hippo"]]) <- "simplified.segmentations"
p3 <- ImageDimPlot(vizgen.obj, fov = "hippo", axes = TRUE, border.color = "white", border.size = 0.1,
cols = "polychrome", coord.fixed = FALSE)
p1 + p2 + p3

#What is the tol parameter? The tol parameter determines how
simplified the resulting segmentations are. A higher value of tol will
reduce the number of vertices more drastically which will speed up
plotting, but some segmentation detail will be lost. See https://rgeos.r-forge.r-project.org/reference/topo-unary-gSimplify.html
for examples using different values for tol.
We can visualize individual molecules plotted at higher resolution
after zooming-in
# Since there is nothing behind the segmentations, alpha will slightly mute colors
ImageDimPlot(vizgen.obj, fov = "hippo", molecules = rownames(markers.14)[1:4], cols = "polychrome",
mols.size = 1, alpha = 0.5, mols.cols = c("red", "blue", "yellow", "green"))

Mouse Brain: 10x
Genomics Xenium In Situ
In this section we’ll analyze data produced by the Xenium platform.
The vignette demonstrates how to load the per-transcript location data,
cell x gene matrix, cell segmentation, and cell centroid information
available in the Xenium outputs. The resulting Seurat object will
contain the gene expression profile of each cell, the centroid and
boundary of each cell, and the location of each individual detected
transcript. The per-cell gene expression profiles are similar to
standard single-cell RNA-seq and can be analyzed using the same
tools.
This uses the Tiny subset dataset from 10x Genomics provided in the
Fresh Frozen Mouse Brain for Xenium Explorer Demo which can be
downloaded as described below. These analysis steps are also compatible
with the larger Full coronal section, but will take longer to
execute.
system("wget https://cf.10xgenomics.com/samples/xenium/1.0.2/Xenium_V1_FF_Mouse_Brain_Coronal_Subset_CTX_HP/Xenium_V1_FF_Mouse_Brain_Coronal_Subset_CTX_HP_outs.zip")
url <- "https://cf.10xgenomics.com/samples/xenium/1.0.2/Xenium_V1_FF_Mouse_Brain_Coronal_Subset_CTX_HP/Xenium_V1_FF_Mouse_Brain_Coronal_Subset_CTX_HP_outs.zip"
download.file(
url,
destfile = "Xenium_V1_FF_Mouse_Brain_Coronal_Subset_CTX_HP_outs.zip",
mode = "wb"
)
First we read in the dataset and create a Seurat object. Provide the
path to the data folder for a Xenium run as the input path. The RNA data
is stored in the Xenium assay of the Seurat object. This step should
take about a minute (you can improve this by installing arrow and
hdf5r).
path <- "~/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/Spatial_Analysis/Image_Based/data/vizgen/s2r1"
# Load the Xenium data
xenium.obj <- LoadXenium(path, fov = "fov", segmentations = "cell")
# remove cells with 0 counts
xenium.obj <- subset(xenium.obj, subset = nCount_Xenium > 0)
Spatial information is loaded into slots of the Seurat object,
labelled by the name of “field of view” (FOV) being loaded. Initially
all the data is loaded into the FOV named fov. Later, we will make a
cropped FOV that zooms into a region of interest.
Standard QC plots provided by Seurat are available via the Xenium
assay. Here are violin plots of genes per cell (nFeature_Xenium) and
transcript counts per cell (nCount_Xenium)
VlnPlot(xenium.obj, features = c("nFeature_Xenium", "nCount_Xenium"), ncol = 2, pt.size = 0)

Next, we plot the positions of the pan-inhibitory neuron marker Gad1,
inhibitory neuron sub-type markers Pvalb, and Sst, and astrocyte marker
Gfap on the tissue using ImageDimPlot().
ImageDimPlot(xenium.obj, fov = "fov", molecules = c("Gad1", "Sst", "Pvalb", "Gfap"), nmols = 20000)

Here we visualize the expression level of some key layer marker genes
at the per-cell level using ImageFeaturePlot() which is analogous to the
FeaturePlot() function for visualizing expression on a 2D embedding. We
manually adjust the max.cutoff for each gene to roughly the 90th
percentile (which can be specified with max.cutoff=‘q90’) of it’s count
distribution to improve contrast.
ImageFeaturePlot(xenium.obj, features = c("Cux2", "Rorb", "Bcl11b", "Foxp2"), max.cutoff = c(25,
35, 12, 10), size = 0.75, cols = c("white", "red"))

We can zoom in on a chosen area with the Crop() function. Once
zoomed-in, we can visualize cell segmentation boundaries along with
individual molecules.
cropped.coords <- Crop(xenium.obj[["fov"]], x = c(1200, 2900), y = c(3750, 4550), coords = "plot")
xenium.obj[["zoom"]] <- cropped.coords
# visualize cropped area with cell segmentations & selected molecules
DefaultBoundary(xenium.obj[["zoom"]]) <- "segmentation"
ImageDimPlot(xenium.obj, fov = "zoom", axes = TRUE, border.color = "white", border.size = 0.1, cols = "polychrome",
coord.fixed = FALSE, molecules = c("Gad1", "Sst", "Npy2r", "Pvalb", "Nrn1"), nmols = 10000)

Next, we use SCTransform for normalization followed by standard
dimensionality reduction and clustering. This step takes about 5 minutes
from start to finish.
xenium.obj <- SCTransform(xenium.obj, assay = "Xenium")
xenium.obj <- RunPCA(xenium.obj, npcs = 30, features = rownames(xenium.obj))
xenium.obj <- RunUMAP(xenium.obj, dims = 1:30)
Using method 'umap'
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
xenium.obj <- FindNeighbors(xenium.obj, reduction = "pca", dims = 1:30)
xenium.obj <- FindClusters(xenium.obj, resolution = 0.3)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 36553
Number of edges: 1340890
Running Louvain algorithm...
Maximum modularity in 10 random starts: 0.9587
Number of communities: 28
Elapsed time: 4 seconds
We can then visualize the results of the clustering by coloring each
cell according to its cluster either in UMAP space with DimPlot() or
overlaid on the image with ImageDimPlot().
DimPlot(xenium.obj)

We can visualize the expression level of the markers we looked at
earlier on the UMAP coordinates.
FeaturePlot(xenium.obj, features = c("Cux2", "Bcl11b", "Foxp2", "Gad1", "Sst", "Gfap"))
We can now use ImageDimPlot() to color the cell positions colored by
the cluster labels determined in the previous step.
ImageDimPlot(xenium.obj, cols = "polychrome", size = 0.75)

Using the positional information of each cell, we compute spatial
niches. We use a cortex reference from the the Allen Brain Institute to
annotate cells, so we first crop the dataset to the cortex. The Allen
Brain reference can be installed here.
Below, we use Slc17a7 expression to help determine the cortical
region.
#To be continued………………..
.............................
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgSW1hZ2UtQmFzZWQgU3BhdGlhbCBEYXRhIGluIFNldXJhdDogVml6Z2VuIE1FUkZJU0ggTW91c2UgQnJhaW4gKFMyUjEpIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgojIE92ZXJ2aWV3CgpUaGlzIGRvY3VtZW50IHJlcHJvZHVjZXMgdGhlIFZpemdlbiBNRVJTQ09QRS9NRVJGSVNIIHNlY3Rpb24gb2YgdGhlIFNldXJhdCB2NSB0dXRvcmlhbCAqKiJBbmFseXNpcyBvZiBJbWFnZS1iYXNlZCBTcGF0aWFsIERhdGEgaW4gU2V1cmF0IioqIChTYXRpamEgTGFiKSwgYXBwbGllZCB0byB0aGUgKipNb3VzZSBCcmFpbiBSZWNlcHRvciBNYXAsIFNsaWNlIDIgUmVwbGljYXRlIDEgKFMyUjEpKiogZGF0YXNldC4KClRoZSBkYXRhc2V0IGNvbnRhaW5zOgoKLSBBIHRhcmdldGVkIDQ4My1nZW5lIE1FUkZJU0ggcGFuZWwgKGNhbm9uaWNhbCBjZWxsLXR5cGUgbWFya2Vycywgbm9uLXNlbnNvcnkgR1BDUnMsIGFuZCByZWNlcHRvciB0eXJvc2luZSBraW5hc2VzKS4KLSBBIGNlbGwtYnktZ2VuZSBjb3VudCBtYXRyaXguCi0gQ2VsbCBtZXRhZGF0YSB3aXRoIHNwYXRpYWwgY2VudHJvaWQgY29vcmRpbmF0ZXMuCi0gQ2VsbCBzZWdtZW50YXRpb24gYm91bmRhcmllcyAoSERGNSBmb3JtYXQsIG9uZSBmaWxlIHBlciBzcGF0aWFsIHRpbGUpLgotIERldGVjdGVkLXRyYW5zY3JpcHQgKG1vbGVjdWxlLWxldmVsKSBjb29yZGluYXRlcy4KClRoZSB3b3JrZmxvdyBwZXJmb3JtZWQgaGVyZSBpczoKCjEuIFZhbGlkYXRlIHRoZSBsb2NhbCBkYXRhIGRpcmVjdG9yeS4KMi4gTG9hZCB0aGUgZGF0YSB3aXRoIGBMb2FkVml6Z2VuKClgLgozLiBJbnNwZWN0IGNlbnRyb2lkcywgc2VnbWVudGF0aW9uIGJvdW5kYXJpZXMsIGFuZCBtb2xlY3VsZXMuCjQuIFJ1biBxdWFsaXR5IGNvbnRyb2wuCjUuIE5vcm1hbGl6ZSB3aXRoIGBTQ1RyYW5zZm9ybSgpYC4KNi4gUnVuIFBDQSwgVU1BUCwgbmVpZ2hib3ItZ3JhcGggY29uc3RydWN0aW9uLCBhbmQgY2x1c3RlcmluZy4KNy4gVmlzdWFsaXplIGNsdXN0ZXJzIGFuZCBnZW5lIGV4cHJlc3Npb24gaW4gVU1BUCBzcGFjZSBhbmQgaW4gdGlzc3VlIGNvb3JkaW5hdGVzLgo4LiBWaXN1YWxpemUgaW5kaXZpZHVhbCBkZXRlY3RlZCBtb2xlY3VsZXMuCjkuIENyb3AgYSByZWdpb24gb2YgaW50ZXJlc3QgYW5kIHZpc3VhbGl6ZSBjZWxsIHNlZ21lbnRhdGlvbiBib3VuZGFyaWVzLgoxMC4gSWRlbnRpZnkgY2x1c3RlciBtYXJrZXIgZ2VuZXMuCjExLiBTYXZlIHRoZSBwcm9jZXNzZWQgb2JqZWN0LgoKVGhlIGxhcmdlIHJhdyBtaWNyb3Njb3B5IGBpbWFnZXMvYCBkaXJlY3RvcnkgKH4yMyBHQiBsb2NhbGx5LCBjb25zaWRlcmFibHkgbGFyZ2VyIG9uIHRoZSBWaXpnZW4gc2VydmVyKSBpcyAqKm5vdCByZXF1aXJlZCoqIGZvciBhbnkgc3RlcCBzaG93biBoZXJlIGFuZCBpcyBpbnRlbnRpb25hbGx5IGV4Y2x1ZGVkLgoKLS0tCgojIGxvYWQgbGlicmFyaWVzCmBgYHtyICwgaW5jbHVkZT1GQUxTRX0KcmVxdWlyZWRfcGFja2FnZXMgPC0gYygKICAiU2V1cmF0IiwKICAiU2V1cmF0T2JqZWN0IiwKICAiZnV0dXJlIiwKICAiZ2dwbG90MiIsCiAgInBhdGNod29yayIsCiAgImRwbHlyIgopCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDEwICogMTAyNF4zKQpgYGAKCgpUaGlzIGRhdGFzZXQgd2FzIHByb2R1Y2VkIHVzaW5nIHRoZSBWaXpnZW4gTUVSU0NPUEUgc3lzdGVtLCB3aGljaCB1dGlsaXplcyB0aGUgTUVSRklTSCB0ZWNobm9sb2d5LiBUaGUgdG90YWwgZGF0YXNldCBpcyBhdmFpbGFibGUgZm9yIHB1YmxpYyBkb3dubG9hZCwgYW5kIGNvbnRhaW5zIG5pbmUgc2FtcGxlcyAodGhyZWUgZnVsbCBjb3JvbmFsIHNsaWNlcyBvZiB0aGUgbW91c2UgYnJhaW4sIHdpdGggdGhyZWUgYmlvbG9naWNhbCByZXBsaWNhdGVzIHBlciBzbGljZSkuIFRoZSBnZW5lIHBhbmVsIGNvbnNpc3RzIG9mIDQ4MyBnZW5lIHRhcmdldHMsIHJlcHJlc2VudGluZyBrbm93biBhbm9uaWNhbCBjZWxsIHR5cGUgbWFya2Vycywgbm9uc2Vuc29yeSBHLVByb3RlaW4gY291cGxlZCByZWNlcHRvcnMgKEdQQ1JzKSwgYW5kIFJlY2VwdG9yIFR5cm9zaW5lIEtpbmFzZXMgKFJUS3MpLiBJbiB0aGlzIHZpZ25ldHRlLCB3ZSBhbmFseXplIG9uZSBvZiB0aGUgc2FtcGxlcyAtIHNsaWNlIDIsIHJlcGxpY2F0ZSAxLiBUaGUgbWVkaWFuIG51bWJlciBvZiB0cmFuc2NyaXB0cyBkZXRlY3RlZCBpbiBlYWNoIGNlbGwgaXMgMjA2LgoKRmlyc3QsIHdlIHJlYWQgaW4gdGhlIGRhdGFzZXQgYW5kIGNyZWF0ZSBhIFNldXJhdCBvYmplY3QuCgpXZSB1c2UgdGhlIExvYWRWaXpnZW4oKSBmdW5jdGlvbiwgd2hpY2ggd2UgaGF2ZSB3cml0dGVuIHRvIHJlYWQgaW4gdGhlIG91dHB1dCBvZiB0aGUgVml6Z2VuIGFuYWx5c2lzIHBpcGVsaW5lLiBUaGUgcmVzdWx0aW5nIFNldXJhdCBvYmplY3QgY29udGFpbnMgdGhlIGZvbGxvd2luZyBpbmZvcm1hdGlvbjoKCkEgY291bnQgbWF0cml4LCBpbmRpY2F0aW5nIHRoZSBudW1iZXIgb2Ygb2JzZXJ2ZWQgbW9sZWN1bGVzIGZvciBlYWNoIG9mIHRoZSA0ODMgdHJhbnNjcmlwdHMgaW4gZWFjaCBjZWxsLiBUaGlzIG1hdHJpeCBpcyBhbmFsb2dvdXMgdG8gYSBjb3VudCBtYXRyaXggaW4gc2NSTkEtc2VxLCBhbmQgaXMgc3RvcmVkIGJ5IGRlZmF1bHQgaW4gdGhlIFJOQSBhc3NheSBvZiB0aGUgU2V1cmF0IG9iamVjdAoKCmBgYHtyICwgaW5jbHVkZT1GQUxTRX0KZ2V0d2QoKQoKcHJvamVjdF9kaXIgPC0gbm9ybWFsaXplUGF0aChnZXR3ZCgpKQpkYXRhX2RpciA8LSBmaWxlLnBhdGgocHJvamVjdF9kaXIsICJkYXRhIiwgInZpemdlbiIsICJzMnIxIikKCmRpci5leGlzdHMoZGF0YV9kaXIpCmxpc3QuZmlsZXMoZGF0YV9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCmBgYAojIExvYWRpbmcgc2VnbWVudGF0aW9ucwojIExvYWRpbmcgc2VnbWVudGF0aW9ucyBpcyBhIHNsb3cgcHJvY2VzcyBhbmQgbXVsdGkgcHJvY2Vzc2luZyB3aXRoIHRoZSBmdXR1cmUgcGFjYWtnZSBpcwojIHJlY29tbWVuZGVkCmBgYHtyfQpsaWJyYXJ5KGZ1dHVyZSkKCnBsYW4oIm11bHRpc2Vzc2lvbiIsIHdvcmtlcnMgPSA0KQpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSAxMCAqIDEwMjReMykKCmRhdGFfZGlyIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAiZGF0YSIsICJ2aXpnZW4iLCAiczJyMSIpCgp2aXpnZW4ub2JqIDwtIExvYWRWaXpnZW4oCiAgZGF0YS5kaXIgPSBkYXRhX2RpciwKICBmb3YgPSAiczJyMSIKKQoKdml6Z2VuLm9iagpgYGAKCkNlbGwgQ2VudHJvaWRzOiBUaGUgc3BhdGlhbCBjb29yZGluYXRlcyBtYXJraW5nIHRoZSBjZW50cm9pZCBmb3IgZWFjaCBjZWxsIGJlaW5nIHByb2ZpbGVkCmBgYHtyfQojIEdldCB0aGUgY2VudGVyIHBvc2l0aW9uIG9mIGVhY2ggY2VudHJvaWQuIFRoZXJlIGlzIG9uZSByb3cgcGVyIGNlbGwgaW4gdGhpcyBkYXRhZnJhbWUuCmhlYWQoR2V0VGlzc3VlQ29vcmRpbmF0ZXModml6Z2VuLm9ialtbInMycjEiXV1bWyJjZW50cm9pZHMiXV0pKQpgYGAKCkNlbGwgU2VnbWVudGF0aW9uIEJvdW5kYXJpZXM6IFRoZSBzcGF0aWFsIGNvb3JkaW5hdGVzIHRoYXQgZGVzY3JpYmUgdGhlIHBvbHlnb24gc2VnbWVudGF0aW9uIG9mIGVhY2ggc2luZ2xlIGNlbGwKYGBge3J9CiMgR2V0IHRoZSBjb29yZGluYXRlcyBmb3IgZWFjaCBzZWdtZW50YXRpb24gdmVydGljZS4gRWFjaCBjZWxsIHdpbGwgaGF2ZSBhIHZhcmlhYmxlIG51bWJlciBvZgojIHZlcnRpY2VzIGRlc2NyaWJpbmcgaXRzIHNoYXBlLgpoZWFkKEdldFRpc3N1ZUNvb3JkaW5hdGVzKHZpemdlbi5vYmpbWyJzMnIxIl1dW1sic2VnbWVudGF0aW9uIl1dKSkKYGBgCgoKTW9sZWN1bGUgcG9zaXRpb25zOiBUaGUgc3BhdGlhbCBjb29yZGluYXRlcyBmb3IgZWFjaCBpbmRpdmlkdWFsIG1vbGVjdWxlIHRoYXQgd2FzIGRldGVjdGVkIGR1cmluZyB0aGUgbXVsdGlwbGV4ZWQgc21GSVNIIGV4cGVyaW1lbnQuCmBgYHtyfQojIEZldGNoIG1vbGVjdWxlcyBwb3NpdGlvbnMgZm9yIENocm0xCmhlYWQoRmV0Y2hEYXRhKHZpemdlbi5vYmpbWyJzMnIxIl1dW1sibW9sZWN1bGVzIl1dLCB2YXJzID0gIkNocm0xIikpCmBgYAoKIyBQcmVwcm9jZXNzaW5nIGFuZCB1bnN1cGVydmlzZWQgYW5hbHlzaXMKCldlIHN0YXJ0IGJ5IHBlcmZvcm1pbmcgYSBzdGFuZGFyZCB1bnN1cGVydmlzZWQgY2x1c3RlcmluZyBhbmFseXNpcywgZXNzZW50aWFsbHkgZmlyc3QgdHJlYXRpbmcgdGhlIGRhdGFzZXQgYXMgYW4gc2NSTkEtc2VxIGV4cGVyaW1lbnQuIFdlIHVzZSBTQ1RyYW5zZm9ybS1iYXNlZCBub3JtYWxpemF0aW9uLCB0aG91Z2ggd2Ugc2xpZ2h0bHkgbW9kaWZ5IHRoZSBkZWZhdWx0IGNsaXBwaW5nIHBhcmFtZXRlcnMgdG8gbWl0aWdhdGUgdGhlIGVmZmVjdCBvZiBvdXRsaWVycyB0aGF0IHdlIG9jY2FzaW9uYWxseSBvYnNlcnZlIGluIHNtRklTSCBleHBlcmltZW50cy4gQWZ0ZXIgbm9ybWFsaXphdGlvbiwgd2UgY2FuIHJ1biBkaW1lbnNpb25hbCByZWR1Y3Rpb24gYW5kIGNsdXN0ZXJpbmcuCgpgYGB7cn0Kdml6Z2VuLm9iaiA8LSBTQ1RyYW5zZm9ybSh2aXpnZW4ub2JqLCBhc3NheSA9ICJWaXpnZW4iLCBjbGlwLnJhbmdlID0gYygtMTAsIDEwKSkKdml6Z2VuLm9iaiA8LSBSdW5QQ0Eodml6Z2VuLm9iaiwgbnBjcyA9IDMwLCBmZWF0dXJlcyA9IHJvd25hbWVzKHZpemdlbi5vYmopKQp2aXpnZW4ub2JqIDwtIFJ1blVNQVAodml6Z2VuLm9iaiwgZGltcyA9IDE6MzApCnZpemdlbi5vYmogPC0gRmluZE5laWdoYm9ycyh2aXpnZW4ub2JqLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MzApCnZpemdlbi5vYmogPC0gRmluZENsdXN0ZXJzKHZpemdlbi5vYmosIHJlc29sdXRpb24gPSAwLjMpCmBgYAoKV2UgY2FuIHRoZW4gdmlzdWFsaXplIHRoZSByZXN1bHRzIG9mIHRoZSBjbHVzdGVyaW5nIGVpdGhlciBpbiBVTUFQIHNwYWNlICh3aXRoIERpbVBsb3QoKSkgb3Igb3ZlcmxhaWQgb24gdGhlIGltYWdlIHdpdGggSW1hZ2VEaW1QbG90KCkuCmBgYHtyfQpEaW1QbG90KHZpemdlbi5vYmosIHJlZHVjdGlvbiA9ICJ1bWFwIikKYGBgCgpgYGB7cn0KSW1hZ2VEaW1QbG90KHZpemdlbi5vYmosIGZvdiA9ICJzMnIxIiwgY29scyA9ICJwb2x5Y2hyb21lIiwgYXhlcyA9IFRSVUUpCmBgYApZb3UgY2FuIGFsc28gY3VzdG9taXplIG11bHRpcGxlIGFzcGVjdCBvZiB0aGUgcGxvdCwgaW5jbHVkaW5nIHRoZSBjb2xvciBzY2hlbWUsIGNlbGwgYm9yZGVyIHdpZHRocywgYW5kIHNpemUgKHNlZSBiZWxvdykuCgojIEN1c3RvbWl6aW5nIHNwYXRpYWwgcGxvdHMgaW4gU2V1cmF0ClRoZSBJbWFnZURpbVBsb3QoKSBhbmQgSW1hZ2VGZWF0dXJlUGxvdCgpIGZ1bmN0aW9ucyBoYXZlIGEgZmV3IHBhcmFtZXRlcnMgd2hpY2ggeW91IGNhbiBjdXN0b21pemUgaW5kaXZpZHVhbCB2aXN1YWxpemF0aW9ucy4gVGhlc2UgaW5jbHVkZToKCmFscGhhOiBSYW5nZXMgZnJvbSAwIHRvIDEuIFNldHMgdGhlIHRyYW5zcGFyZW5jeSBvZiB3aXRoaW4tY2VsbCBjb2xvcmluZy4Kc2l6ZTogZGV0ZXJtaW5lcyB0aGUgc2l6ZSBvZiBwb2ludHMgcmVwcmVzZW50aW5nIGNlbGxzLCBpZiBjZW50cm9pZHMgYXJlIGJlaW5nIHBsb3R0ZWQKY29sczogU2V0cyB0aGUgY29sb3Igc2NoZW1lIGZvciB0aGUgaW50ZXJuYWwgc2hhZGluZyBvZiBlYWNoIGNlbGwuIEV4YW1wbGVzIHNldHRpbmdzIGFyZSBwb2x5Y2hyb21lLCBnbGFzYmV5LCBQYWlyZWQsIFNldDMsIGFuZCBwYXJhZGUuIERlZmF1bHQgaXMgdGhlIGdncGxvdDIgY29sb3IgcGFsZXR0ZQpzaHVmZmxlLmNvbHM6IEluIHNvbWUgY2FzZXMgdGhlIHNlbGVjdGlvbiBvZiBjb2xzIGlzIG1vcmUgZWZmZWN0aXZlIHdoZW4gdGhlIHNhbWUgY29sb3JzIGFyZSBhc3NpZ25lZCB0byBkaWZmZXJlbnQgY2x1c3RlcnMuIFNldCBzaHVmZmxlLmNvbHMgPSBUUlVFIHRvIHJhbmRvbWx5IHNodWZmbGUgdGhlIGNvbG9ycyBpbiB0aGUgcGFsZXR0ZS4KYm9yZGVyLnNpemU6IFNldHMgdGhlIHdpZHRoIG9mIHRoZSBjZWxsIHNlZ21lbnRhdGlvbiBib3JkZXJzLiBCeSBkZWZhdWx0LCBzZWdtZW50YXRpb25zIGFyZSBwbG90dGVkIHdpdGggYSBib3JkZXIgc2l6ZSBvZiAwLjMgYW5kIGNlbnRyb2lkcyBhcmUgcGxvdHRlZCB3aXRob3V0IGJvcmRlci4KYm9yZGVyLmNvbG9yOiBTZXRzIHRoZSBjb2xvciBvZiB0aGUgY2VsbCBzZWdtZW50YXRpb24gYm9yZGVycwpkYXJrLmJhY2tncm91bmQ6IFNldHMgYSBibGFjayBiYWNrZ3JvdW5kIGNvbG9yIChUUlVFIGJ5IGRlZmF1bHQpCmF4ZXM6IERpc3BsYXkKU2luY2UgaXQgY2FuIGJlIGRpZmZpY3VsdCB0byB2aXN1YWxpemUgdGhlIHNwYXRpYWwgbG9jYWxpemF0aW9uIHBhdHRlcm5zIG9mIGFuIGluZGl2aWR1YWwgY2x1c3RlciB3aGVuIHZpZXdpbmcgdGhlbSBhbGwgdG9nZXRoZXIsIHdlIGNhbiBoaWdobGlnaHQgYWxsIGNlbGxzIHRoYXQgYmVsb25nIHRvIGEgcGFydGljdWxhciBjbHVzdGVyOgoKYGBge3IsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQ9IDZ9CnAxIDwtIEltYWdlRGltUGxvdCh2aXpnZW4ub2JqLCBmb3YgPSAiczJyMSIsIGNvbHMgPSAicmVkIiwgY2VsbHMgPSBXaGljaENlbGxzKHZpemdlbi5vYmosIGlkZW50cyA9IDEpKQpwMiA8LSBJbWFnZURpbVBsb3Qodml6Z2VuLm9iaiwgZm92ID0gInMycjEiLCBjb2xzID0gInJlZCIsIGNlbGxzID0gV2hpY2hDZWxscyh2aXpnZW4ub2JqLCBpZGVudHMgPSAxNSkpCnAxICsgcDIKYGBgCldlIGNhbiBmaW5kIG1hcmtlcnMgb2YgaW5kaXZpZHVhbCBjbHVzdGVycyBhbmQgdmlzdWFsaXplIHRoZWlyIHNwYXRpYWwgZXhwcmVzc2lvbiBwYXR0ZXJuLiBXZSBjYW4gY29sb3IgY2VsbHMgYmFzZWQgb24gdGhlaXIgcXVhbnRpZmllZCBleHByZXNzaW9uIG9mIGFuIGluZGl2aWR1YWwgZ2VuZSwgdXNpbmcgSW1hZ2VGZWF0dXJlUGxvdCgpLCB3aGljaCBpcyBhbmFsYWdvdXMgdG8gdGhlIEZlYXR1cmVQbG90KCkgZnVuY3Rpb24gZm9yIHZpc3VhbGl6aW5nIGV4cHJlc3Npb24gb24gYSAyRCBlbWJlZGRpbmcuIFNpbmNlIE1FUkZJU0ggaW1hZ2VzIGluZGl2aWR1YWwgbW9sZWN1bGVzLCB3ZSBjYW4gYWxzbyB2aXN1YWxpemUgdGhlIGxvY2F0aW9uIG9mIGluZGl2aWR1YWwgbW9sZWN1bGVzLgpgYGB7ciwgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD0gNn0KcDEgPC0gSW1hZ2VGZWF0dXJlUGxvdCh2aXpnZW4ub2JqLCBmZWF0dXJlcyA9ICJTbGMxN2E3IikKcDIgPC0gSW1hZ2VEaW1QbG90KHZpemdlbi5vYmosIG1vbGVjdWxlcyA9ICJTbGMxN2E3Iiwgbm1vbHMgPSAxMDAwMCwgYWxwaGEgPSAwLjMsIG1vbHMuY29scyA9ICJyZWQiKQpwMSArIHAyCmBgYAoKTm90ZSB0aGF0IHRoZSBubW9scyBwYXJhbWV0ZXIgY2FuIGJlIHVzZWQgdG8gcmVkdWNlIHRoZSB0b3RhbCBudW1iZXIgb2YgbW9sZWN1bGVzIHNob3duIHRvIHJlZHVjZSBvdmVycGxvdHRpbmcuIFlvdSBjYW4gYWxzbyB1c2UgdGhlIG1vbHMuc2l6ZSwgbW9scy5jb2xzLCBhbmQgbW9scy5hbHBoYSBwYXJhbWV0ZXIgdG8gZnVydGhlciBvcHRpbWl6ZS4KClBsb3R0aW5nIG1vbGVjdWxlcyBpcyBlc3BlY2lhbGx5IHVzZWZ1bCBmb3IgdmlzdWFsaXppbmcgY28tZXhwcmVzc2lvbiBvZiBtdWx0aXBsZSBnZW5lcyBvbiB0aGUgc2FtZSBwbG90LgpgYGB7ciwgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD0gNn0KcDEgPC0gSW1hZ2VEaW1QbG90KHZpemdlbi5vYmosIGZvdiA9ICJzMnIxIiwgYWxwaGEgPSAwLjMsIG1vbGVjdWxlcyA9IGMoIlNsYzE3YTciLCAiT2xpZzEiKSwgbm1vbHMgPSAxMDAwMCkKbWFya2Vycy4xNCA8LSBGaW5kTWFya2Vycyh2aXpnZW4ub2JqLCBpZGVudC4xID0gIjE0IikKcDIgPC0gSW1hZ2VEaW1QbG90KHZpemdlbi5vYmosIGZvdiA9ICJzMnIxIiwgYWxwaGEgPSAwLjMsIG1vbGVjdWxlcyA9IHJvd25hbWVzKG1hcmtlcnMuMTQpWzE6NF0sCiAgICBubW9scyA9IDEwMDAwKQpwMSArIHAyCmBgYAoKVGhlIHVwZGF0ZWQgU2V1cmF0IHNwYXRpYWwgZnJhbWV3b3JrIGhhcyB0aGUgb3B0aW9uIHRvIHRyZWF0IGNlbGxzIGFzIGluZGl2aWR1YWwgcG9pbnRzLCBvciBhbHNvIHRvIHZpc3VhbGl6ZSBjZWxsIGJvdW5kYXJpZXMgKHNlZ21lbnRhdGlvbnMpLiBCeSBkZWZhdWx0LCBTZXVyYXQgaWdub3JlcyBjZWxsIHNlZ21lbnRhdGlvbnMgYW5kIHRyZWF0cyBlYWNoIGNlbGwgYXMgYSBwb2ludCAo4oCYY2VudHJvaWRz4oCZKS4gVGhpcyBzcGVlZHMgdXAgcGxvdHRpbmcsIGVzcGVjaWFsbHkgd2hlbiBsb29raW5nIGF0IGxhcmdlIGFyZWFzLCB3aGVyZSBjZWxsIGJvdW5kYXJpZXMgYXJlIHRvbyBzbWFsbCB0byB2aXN1YWxpemUuCgpXZSBjYW4gem9vbSBpbnRvIGEgcmVnaW9uIG9mIHRpc3N1ZSwgY3JlYXRpbmcgYSBuZXcgZmllbGQgb2Ygdmlldy4gRm9yIGV4YW1wbGUsIHdlIGNhbiB6b29tIGludG8gYSByZWdpb24gdGhhdCBjb250YWlucyB0aGUgaGlwcG9jYW1wdXMuIE9uY2Ugem9vbWVkLWluLCB3ZSBjYW4gc2V0IERlZmF1bHRCb3VuZGFyeSgpIHRvIHNob3cgY2VsbCBzZWdtZW50YXRpb25zLiBZb3UgY2FuIGFsc28g4oCYc2ltcGxpZnnigJkgdGhlIGNlbGwgc2VnbWVudGF0aW9ucywgcmVkdWNpbmcgdGhlIG51bWJlciBvZiBlZGdlcyBpbiBlYWNoIHBvbHlnb24gdG8gc3BlZWQgdXAgcGxvdHRpbmcuCmBgYHtyLCBmaWcud2lkdGg9IDEyLCBmaWcuaGVpZ2h0PSA2fQojIGNyZWF0ZSBhIENyb3AKY3JvcHBlZC5jb29yZHMgPC0gQ3JvcCh2aXpnZW4ub2JqW1siczJyMSJdXSwgeCA9IGMoMTc1MCwgMzAwMCksIHkgPSBjKDM3NTAsIDUyNTApLCBjb29yZHMgPSAicGxvdCIpCiMgc2V0IGEgbmV3IGZpZWxkIG9mIHZpZXcgKGZvdikKdml6Z2VuLm9ialtbImhpcHBvIl1dIDwtIGNyb3BwZWQuY29vcmRzCgojIHZpc3VhbGl6ZSBGT1YgdXNpbmcgZGVmYXVsdCBzZXR0aW5ncyAobm8gY2VsbCBib3VuZGFyaWVzKQpwMSA8LSBJbWFnZURpbVBsb3Qodml6Z2VuLm9iaiwgZm92ID0gImhpcHBvIiwgYXhlcyA9IFRSVUUsIHNpemUgPSAwLjcsIGJvcmRlci5jb2xvciA9ICJ3aGl0ZSIsIGNvbHMgPSAicG9seWNocm9tZSIsCiAgICBjb29yZC5maXhlZCA9IEZBTFNFKQoKIyB2aXN1YWxpemUgRk9WIHdpdGggZnVsbCBjZWxsIHNlZ21lbnRhdGlvbnMKRGVmYXVsdEJvdW5kYXJ5KHZpemdlbi5vYmpbWyJoaXBwbyJdXSkgPC0gInNlZ21lbnRhdGlvbiIKcDIgPC0gSW1hZ2VEaW1QbG90KHZpemdlbi5vYmosIGZvdiA9ICJoaXBwbyIsIGF4ZXMgPSBUUlVFLCBib3JkZXIuY29sb3IgPSAid2hpdGUiLCBib3JkZXIuc2l6ZSA9IDAuMSwKICAgIGNvbHMgPSAicG9seWNocm9tZSIsIGNvb3JkLmZpeGVkID0gRkFMU0UpCgojIHNpbXBsaWZ5IGNlbGwgc2VnbWVudGF0aW9ucwp2aXpnZW4ub2JqW1siaGlwcG8iXV1bWyJzaW1wbGlmaWVkLnNlZ21lbnRhdGlvbnMiXV0gPC0gU2ltcGxpZnkoY29vcmRzID0gdml6Z2VuLm9ialtbImhpcHBvIl1dW1sic2VnbWVudGF0aW9uIl1dLAogICAgdG9sID0gMykKRGVmYXVsdEJvdW5kYXJ5KHZpemdlbi5vYmpbWyJoaXBwbyJdXSkgPC0gInNpbXBsaWZpZWQuc2VnbWVudGF0aW9ucyIKCiMgdmlzdWFsaXplIEZPViB3aXRoIHNpbXBsaWZpZWQgY2VsbCBzZWdtZW50YXRpb25zCkRlZmF1bHRCb3VuZGFyeSh2aXpnZW4ub2JqW1siaGlwcG8iXV0pIDwtICJzaW1wbGlmaWVkLnNlZ21lbnRhdGlvbnMiCnAzIDwtIEltYWdlRGltUGxvdCh2aXpnZW4ub2JqLCBmb3YgPSAiaGlwcG8iLCBheGVzID0gVFJVRSwgYm9yZGVyLmNvbG9yID0gIndoaXRlIiwgYm9yZGVyLnNpemUgPSAwLjEsCiAgICBjb2xzID0gInBvbHljaHJvbWUiLCBjb29yZC5maXhlZCA9IEZBTFNFKQoKcDEgKyBwMiArIHAzCmBgYAoKCiNXaGF0IGlzIHRoZSB0b2wgcGFyYW1ldGVyPwpUaGUgdG9sIHBhcmFtZXRlciBkZXRlcm1pbmVzIGhvdyBzaW1wbGlmaWVkIHRoZSByZXN1bHRpbmcgc2VnbWVudGF0aW9ucyBhcmUuIEEgaGlnaGVyIHZhbHVlIG9mIHRvbCB3aWxsIHJlZHVjZSB0aGUgbnVtYmVyIG9mIHZlcnRpY2VzIG1vcmUgZHJhc3RpY2FsbHkgd2hpY2ggd2lsbCBzcGVlZCB1cCBwbG90dGluZywgYnV0IHNvbWUgc2VnbWVudGF0aW9uIGRldGFpbCB3aWxsIGJlIGxvc3QuIFNlZSBodHRwczovL3JnZW9zLnItZm9yZ2Uuci1wcm9qZWN0Lm9yZy9yZWZlcmVuY2UvdG9wby11bmFyeS1nU2ltcGxpZnkuaHRtbCBmb3IgZXhhbXBsZXMgdXNpbmcgZGlmZmVyZW50IHZhbHVlcyBmb3IgdG9sLgoKV2UgY2FuIHZpc3VhbGl6ZSBpbmRpdmlkdWFsIG1vbGVjdWxlcyBwbG90dGVkIGF0IGhpZ2hlciByZXNvbHV0aW9uIGFmdGVyIHpvb21pbmctaW4KYGBge3IsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQ9IDZ9CiMgU2luY2UgdGhlcmUgaXMgbm90aGluZyBiZWhpbmQgdGhlIHNlZ21lbnRhdGlvbnMsIGFscGhhIHdpbGwgc2xpZ2h0bHkgbXV0ZSBjb2xvcnMKSW1hZ2VEaW1QbG90KHZpemdlbi5vYmosIGZvdiA9ICJoaXBwbyIsIG1vbGVjdWxlcyA9IHJvd25hbWVzKG1hcmtlcnMuMTQpWzE6NF0sIGNvbHMgPSAicG9seWNocm9tZSIsCiAgICBtb2xzLnNpemUgPSAxLCBhbHBoYSA9IDAuNSwgbW9scy5jb2xzID0gYygicmVkIiwgImJsdWUiLCAieWVsbG93IiwgImdyZWVuIikpCmBgYAoKIyBNb3VzZSBCcmFpbjogMTB4IEdlbm9taWNzIFhlbml1bSBJbiBTaXR1CkluIHRoaXMgc2VjdGlvbiB3ZeKAmWxsIGFuYWx5emUgZGF0YSBwcm9kdWNlZCBieSB0aGUgWGVuaXVtIHBsYXRmb3JtLiBUaGUgdmlnbmV0dGUgZGVtb25zdHJhdGVzIGhvdyB0byBsb2FkIHRoZSBwZXItdHJhbnNjcmlwdCBsb2NhdGlvbiBkYXRhLCBjZWxsIHggZ2VuZSBtYXRyaXgsIGNlbGwgc2VnbWVudGF0aW9uLCBhbmQgY2VsbCBjZW50cm9pZCBpbmZvcm1hdGlvbiBhdmFpbGFibGUgaW4gdGhlIFhlbml1bSBvdXRwdXRzLiBUaGUgcmVzdWx0aW5nIFNldXJhdCBvYmplY3Qgd2lsbCBjb250YWluIHRoZSBnZW5lIGV4cHJlc3Npb24gcHJvZmlsZSBvZiBlYWNoIGNlbGwsIHRoZSBjZW50cm9pZCBhbmQgYm91bmRhcnkgb2YgZWFjaCBjZWxsLCBhbmQgdGhlIGxvY2F0aW9uIG9mIGVhY2ggaW5kaXZpZHVhbCBkZXRlY3RlZCB0cmFuc2NyaXB0LiBUaGUgcGVyLWNlbGwgZ2VuZSBleHByZXNzaW9uIHByb2ZpbGVzIGFyZSBzaW1pbGFyIHRvIHN0YW5kYXJkIHNpbmdsZS1jZWxsIFJOQS1zZXEgYW5kIGNhbiBiZSBhbmFseXplZCB1c2luZyB0aGUgc2FtZSB0b29scy4KClRoaXMgdXNlcyB0aGUgVGlueSBzdWJzZXQgZGF0YXNldCBmcm9tIDEweCBHZW5vbWljcyBwcm92aWRlZCBpbiB0aGUgRnJlc2ggRnJvemVuIE1vdXNlIEJyYWluIGZvciBYZW5pdW0gRXhwbG9yZXIgRGVtbyB3aGljaCBjYW4gYmUgZG93bmxvYWRlZCBhcyBkZXNjcmliZWQgYmVsb3cuIFRoZXNlIGFuYWx5c2lzIHN0ZXBzIGFyZSBhbHNvIGNvbXBhdGlibGUgd2l0aCB0aGUgbGFyZ2VyIEZ1bGwgY29yb25hbCBzZWN0aW9uLCBidXQgd2lsbCB0YWtlIGxvbmdlciB0byBleGVjdXRlLgoKYGBge3IsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQ9IDZ9CnN5c3RlbSgid2dldCBodHRwczovL2NmLjEweGdlbm9taWNzLmNvbS9zYW1wbGVzL3hlbml1bS8xLjAuMi9YZW5pdW1fVjFfRkZfTW91c2VfQnJhaW5fQ29yb25hbF9TdWJzZXRfQ1RYX0hQL1hlbml1bV9WMV9GRl9Nb3VzZV9CcmFpbl9Db3JvbmFsX1N1YnNldF9DVFhfSFBfb3V0cy56aXAiKQoKdXJsIDwtICJodHRwczovL2NmLjEweGdlbm9taWNzLmNvbS9zYW1wbGVzL3hlbml1bS8xLjAuMi9YZW5pdW1fVjFfRkZfTW91c2VfQnJhaW5fQ29yb25hbF9TdWJzZXRfQ1RYX0hQL1hlbml1bV9WMV9GRl9Nb3VzZV9CcmFpbl9Db3JvbmFsX1N1YnNldF9DVFhfSFBfb3V0cy56aXAiCgpkb3dubG9hZC5maWxlKAogIHVybCwKICBkZXN0ZmlsZSA9ICJYZW5pdW1fVjFfRkZfTW91c2VfQnJhaW5fQ29yb25hbF9TdWJzZXRfQ1RYX0hQX291dHMuemlwIiwKICBtb2RlID0gIndiIgopCmBgYAoKRmlyc3Qgd2UgcmVhZCBpbiB0aGUgZGF0YXNldCBhbmQgY3JlYXRlIGEgU2V1cmF0IG9iamVjdC4gUHJvdmlkZSB0aGUgcGF0aCB0byB0aGUgZGF0YSBmb2xkZXIgZm9yIGEgWGVuaXVtIHJ1biBhcyB0aGUgaW5wdXQgcGF0aC4gVGhlIFJOQSBkYXRhIGlzIHN0b3JlZCBpbiB0aGUgWGVuaXVtIGFzc2F5IG9mIHRoZSBTZXVyYXQgb2JqZWN0LiBUaGlzIHN0ZXAgc2hvdWxkIHRha2UgYWJvdXQgYSBtaW51dGUgKHlvdSBjYW4gaW1wcm92ZSB0aGlzIGJ5IGluc3RhbGxpbmcgYXJyb3cgYW5kIGhkZjVyKS4KCgpgYGB7ciwgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD0gNn0KcGF0aCA8LSAifi8xLVRoZXNpc19GaW5hbF9ZZWFyXzIwMjUvMjAyNS1ZZWFyM19BbmFseXNpcy8xLXNjUk5BX1JFU1VMVFMtMTktMTEtMjAyNS9TcGF0aWFsX0FuYWx5c2lzL0ltYWdlX0Jhc2VkL2RhdGEvdml6Z2VuL3MycjEiCiMgTG9hZCB0aGUgWGVuaXVtIGRhdGEKeGVuaXVtLm9iaiA8LSBMb2FkWGVuaXVtKHBhdGgsIGZvdiA9ICJmb3YiLCBzZWdtZW50YXRpb25zID0gImNlbGwiKQojIHJlbW92ZSBjZWxscyB3aXRoIDAgY291bnRzCnhlbml1bS5vYmogPC0gc3Vic2V0KHhlbml1bS5vYmosIHN1YnNldCA9IG5Db3VudF9YZW5pdW0gPiAwKQpgYGAKU3BhdGlhbCBpbmZvcm1hdGlvbiBpcyBsb2FkZWQgaW50byBzbG90cyBvZiB0aGUgU2V1cmF0IG9iamVjdCwgbGFiZWxsZWQgYnkgdGhlIG5hbWUgb2Yg4oCcZmllbGQgb2Ygdmlld+KAnSAoRk9WKSBiZWluZyBsb2FkZWQuIEluaXRpYWxseSBhbGwgdGhlIGRhdGEgaXMgbG9hZGVkIGludG8gdGhlIEZPViBuYW1lZCBmb3YuIExhdGVyLCB3ZSB3aWxsIG1ha2UgYSBjcm9wcGVkIEZPViB0aGF0IHpvb21zIGludG8gYSByZWdpb24gb2YgaW50ZXJlc3QuCgpTdGFuZGFyZCBRQyBwbG90cyBwcm92aWRlZCBieSBTZXVyYXQgYXJlIGF2YWlsYWJsZSB2aWEgdGhlIFhlbml1bSBhc3NheS4gSGVyZSBhcmUgdmlvbGluIHBsb3RzIG9mIGdlbmVzIHBlciBjZWxsIChuRmVhdHVyZV9YZW5pdW0pIGFuZCB0cmFuc2NyaXB0IGNvdW50cyBwZXIgY2VsbCAobkNvdW50X1hlbml1bSkKYGBge3IsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQ9IDZ9ClZsblBsb3QoeGVuaXVtLm9iaiwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9YZW5pdW0iLCAibkNvdW50X1hlbml1bSIpLCBuY29sID0gMiwgcHQuc2l6ZSA9IDApCmBgYApOZXh0LCB3ZSBwbG90IHRoZSBwb3NpdGlvbnMgb2YgdGhlIHBhbi1pbmhpYml0b3J5IG5ldXJvbiBtYXJrZXIgR2FkMSwgaW5oaWJpdG9yeSBuZXVyb24gc3ViLXR5cGUgbWFya2VycyBQdmFsYiwgYW5kIFNzdCwgYW5kIGFzdHJvY3l0ZSBtYXJrZXIgR2ZhcCBvbiB0aGUgdGlzc3VlIHVzaW5nIEltYWdlRGltUGxvdCgpLgpgYGB7ciwgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD0gNn0KSW1hZ2VEaW1QbG90KHhlbml1bS5vYmosIGZvdiA9ICJmb3YiLCBtb2xlY3VsZXMgPSBjKCJHYWQxIiwgIlNzdCIsICJQdmFsYiIsICJHZmFwIiksIG5tb2xzID0gMjAwMDApCmBgYApIZXJlIHdlIHZpc3VhbGl6ZSB0aGUgZXhwcmVzc2lvbiBsZXZlbCBvZiBzb21lIGtleSBsYXllciBtYXJrZXIgZ2VuZXMgYXQgdGhlIHBlci1jZWxsIGxldmVsIHVzaW5nIEltYWdlRmVhdHVyZVBsb3QoKSB3aGljaCBpcyBhbmFsb2dvdXMgdG8gdGhlIEZlYXR1cmVQbG90KCkgZnVuY3Rpb24gZm9yIHZpc3VhbGl6aW5nIGV4cHJlc3Npb24gb24gYSAyRCBlbWJlZGRpbmcuIFdlIG1hbnVhbGx5IGFkanVzdCB0aGUgbWF4LmN1dG9mZiBmb3IgZWFjaCBnZW5lIHRvIHJvdWdobHkgdGhlIDkwdGggcGVyY2VudGlsZSAod2hpY2ggY2FuIGJlIHNwZWNpZmllZCB3aXRoIG1heC5jdXRvZmY9J3E5MCcpIG9mIGl04oCZcyBjb3VudCBkaXN0cmlidXRpb24gdG8gaW1wcm92ZSBjb250cmFzdC4KYGBge3IsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQ9IDZ9CkltYWdlRmVhdHVyZVBsb3QoeGVuaXVtLm9iaiwgZmVhdHVyZXMgPSBjKCJDdXgyIiwgIlJvcmIiLCAiQmNsMTFiIiwgIkZveHAyIiksIG1heC5jdXRvZmYgPSBjKDI1LAogICAgMzUsIDEyLCAxMCksIHNpemUgPSAwLjc1LCBjb2xzID0gYygid2hpdGUiLCAicmVkIikpCmBgYAoKV2UgY2FuIHpvb20gaW4gb24gYSBjaG9zZW4gYXJlYSB3aXRoIHRoZSBDcm9wKCkgZnVuY3Rpb24uIE9uY2Ugem9vbWVkLWluLCB3ZSBjYW4gdmlzdWFsaXplIGNlbGwgc2VnbWVudGF0aW9uIGJvdW5kYXJpZXMgYWxvbmcgd2l0aCBpbmRpdmlkdWFsIG1vbGVjdWxlcy4KYGBge3IsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQ9IDZ9CmNyb3BwZWQuY29vcmRzIDwtIENyb3AoeGVuaXVtLm9ialtbImZvdiJdXSwgeCA9IGMoMTIwMCwgMjkwMCksIHkgPSBjKDM3NTAsIDQ1NTApLCBjb29yZHMgPSAicGxvdCIpCnhlbml1bS5vYmpbWyJ6b29tIl1dIDwtIGNyb3BwZWQuY29vcmRzCiMgdmlzdWFsaXplIGNyb3BwZWQgYXJlYSB3aXRoIGNlbGwgc2VnbWVudGF0aW9ucyAmIHNlbGVjdGVkIG1vbGVjdWxlcwpEZWZhdWx0Qm91bmRhcnkoeGVuaXVtLm9ialtbInpvb20iXV0pIDwtICJzZWdtZW50YXRpb24iCkltYWdlRGltUGxvdCh4ZW5pdW0ub2JqLCBmb3YgPSAiem9vbSIsIGF4ZXMgPSBUUlVFLCBib3JkZXIuY29sb3IgPSAid2hpdGUiLCBib3JkZXIuc2l6ZSA9IDAuMSwgY29scyA9ICJwb2x5Y2hyb21lIiwKICAgIGNvb3JkLmZpeGVkID0gRkFMU0UsIG1vbGVjdWxlcyA9IGMoIkdhZDEiLCAiU3N0IiwgIk5weTJyIiwgIlB2YWxiIiwgIk5ybjEiKSwgbm1vbHMgPSAxMDAwMCkKYGBgCgpOZXh0LCB3ZSB1c2UgU0NUcmFuc2Zvcm0gZm9yIG5vcm1hbGl6YXRpb24gZm9sbG93ZWQgYnkgc3RhbmRhcmQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGFuZCBjbHVzdGVyaW5nLiBUaGlzIHN0ZXAgdGFrZXMgYWJvdXQgNSBtaW51dGVzIGZyb20gc3RhcnQgdG8gZmluaXNoLgpgYGB7ciwgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD0gNn0KeGVuaXVtLm9iaiA8LSBTQ1RyYW5zZm9ybSh4ZW5pdW0ub2JqLCBhc3NheSA9ICJYZW5pdW0iKQp4ZW5pdW0ub2JqIDwtIFJ1blBDQSh4ZW5pdW0ub2JqLCBucGNzID0gMzAsIGZlYXR1cmVzID0gcm93bmFtZXMoeGVuaXVtLm9iaikpCnhlbml1bS5vYmogPC0gUnVuVU1BUCh4ZW5pdW0ub2JqLCBkaW1zID0gMTozMCkKeGVuaXVtLm9iaiA8LSBGaW5kTmVpZ2hib3JzKHhlbml1bS5vYmosIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMTozMCkKeGVuaXVtLm9iaiA8LSBGaW5kQ2x1c3RlcnMoeGVuaXVtLm9iaiwgcmVzb2x1dGlvbiA9IDAuMykKYGBgCgpXZSBjYW4gdGhlbiB2aXN1YWxpemUgdGhlIHJlc3VsdHMgb2YgdGhlIGNsdXN0ZXJpbmcgYnkgY29sb3JpbmcgZWFjaCBjZWxsIGFjY29yZGluZyB0byBpdHMgY2x1c3RlciBlaXRoZXIgaW4gVU1BUCBzcGFjZSB3aXRoIERpbVBsb3QoKSBvciBvdmVybGFpZCBvbiB0aGUgaW1hZ2Ugd2l0aCBJbWFnZURpbVBsb3QoKS4KYGBge3IsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQ9IDZ9CkRpbVBsb3QoeGVuaXVtLm9iaikKYGBgCgpXZSBjYW4gdmlzdWFsaXplIHRoZSBleHByZXNzaW9uIGxldmVsIG9mIHRoZSBtYXJrZXJzIHdlIGxvb2tlZCBhdCBlYXJsaWVyIG9uIHRoZSBVTUFQIGNvb3JkaW5hdGVzLgpgYGB7ciwgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD0gNn0KRmVhdHVyZVBsb3QoeGVuaXVtLm9iaiwgZmVhdHVyZXMgPSBjKCJDdXgyIiwgIkJjbDExYiIsICJGb3hwMiIsICJHYWQxIiwgIlNzdCIsICJHZmFwIikpCmBgYAoKV2UgY2FuIG5vdyB1c2UgSW1hZ2VEaW1QbG90KCkgdG8gY29sb3IgdGhlIGNlbGwgcG9zaXRpb25zIGNvbG9yZWQgYnkgdGhlIGNsdXN0ZXIgbGFiZWxzIGRldGVybWluZWQgaW4gdGhlIHByZXZpb3VzIHN0ZXAuCmBgYHtyLCBmaWcud2lkdGg9IDEyLCBmaWcuaGVpZ2h0PSA2fQpJbWFnZURpbVBsb3QoeGVuaXVtLm9iaiwgY29scyA9ICJwb2x5Y2hyb21lIiwgc2l6ZSA9IDAuNzUpCmBgYAoKClVzaW5nIHRoZSBwb3NpdGlvbmFsIGluZm9ybWF0aW9uIG9mIGVhY2ggY2VsbCwgd2UgY29tcHV0ZSBzcGF0aWFsIG5pY2hlcy4gV2UgdXNlIGEgY29ydGV4IHJlZmVyZW5jZSBmcm9tIHRoZSB0aGUgQWxsZW4gQnJhaW4gSW5zdGl0dXRlIHRvIGFubm90YXRlIGNlbGxzLCBzbyB3ZSBmaXJzdCBjcm9wIHRoZSBkYXRhc2V0IHRvIHRoZSBjb3J0ZXguIFRoZSBBbGxlbiBCcmFpbiByZWZlcmVuY2UgY2FuIGJlIGluc3RhbGxlZCBoZXJlLgoKQmVsb3csIHdlIHVzZSBTbGMxN2E3IGV4cHJlc3Npb24gdG8gaGVscCBkZXRlcm1pbmUgdGhlIGNvcnRpY2FsIHJlZ2lvbi4KCiNUbyBiZSBjb250aW51ZWQuLi4uLi4uLi4uLi4uLi4uLi4uLgpgYGB7ciwgZmlnLndpZHRoPSAxMiwgZmlnLmhlaWdodD0gNn0KLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4KYGBgCg==