Last updated: 2025-05-12

Checks: 7 0

Knit directory: DigitalResearchSkillsNetwork/

This reproducible R Markdown analysis was created with workflowr (version 1.7.1). The Checks tab describes the reproducibility checks that were applied when the results were created. The Past versions tab lists the development history.


Great! Since the R Markdown file has been committed to the Git repository, you know the exact version of the code that produced these results.

Great job! The global environment was empty. Objects defined in the global environment can affect the analysis in your R Markdown file in unknown ways. For reproduciblity it’s best to always run the code in an empty environment.

The command set.seed(1337) was run prior to running the code in the R Markdown file. Setting a seed ensures that any results that rely on randomness, e.g. subsampling or permutations, are reproducible.

Great job! Recording the operating system, R version, and package versions is critical for reproducibility.

Nice! There were no cached chunks for this analysis, so you can be confident that you successfully produced the results during this run.

Great job! Using relative paths to the files within your workflowr project makes it easier to run your code on other machines.

Great! You are using Git for version control. Tracking code development and connecting the code version to the results is critical for reproducibility.

The results in this page were generated with repository version f349caf. See the Past versions tab to see a history of the changes made to the R Markdown and HTML files.

Note that you need to be careful to ensure that all relevant files for the analysis have been committed to Git prior to generating the results (you can use wflow_publish or wflow_git_commit). workflowr only checks the R Markdown file, but you know if there are other scripts or data files that it depends on. Below is the status of the Git repository when the results were generated:


Ignored files:
    Ignored:    .DS_Store
    Ignored:    .Rhistory
    Ignored:    analysis/.DS_Store
    Ignored:    analysis/.RData
    Ignored:    analysis/.Rhistory
    Ignored:    analysis/adit/.DS_Store
    Ignored:    plots/
    Ignored:    raw/

Unstaged changes:
    Modified:   analysis/202505.Rmd
    Modified:   analysis/202505_InFocus.Rmd
    Modified:   analysis/202505_Seminar.Rmd
    Modified:   workflow.R

Note that any generated files, e.g. HTML, png, CSS, etc., are not included in this status report because it is ok for generated content to have uncommitted changes.


These are the previous versions of the repository in which changes were made to the R Markdown (analysis/202505_Workshop.Rmd) and HTML (docs/202505_Workshop.html) files. If you’ve configured a remote Git repository (see ?wflow_git_remote), click on the hyperlinks in the table below to view the files as they were in that past version.

File Version Author Date Message
Rmd f349caf DrThomasOneil 2025-05-12 wflow_publish("analysis/202505_Workshop.Rmd")
html cff7923 DrThomasOneil 2025-05-07 Build site.
Rmd ff95f8f DrThomasOneil 2025-05-07 wflow_publish(files)
html 16a9613 DrThomasOneil 2025-04-30 Build site.
html 862d6b6 DrThomasOneil 2025-04-29 Build site.
Rmd b68287f DrThomasOneil 2025-04-29 wflow_publish(files)

Please try to follow the instructions below prior to attending the workshop.

Be aware!

It is difficult to debug over zoom.
If you want the best feedback and experience, make sure you attend the workshop in person!

You can download the script on the top right or from this link.

Introduction

Setup

Create Folders

├── YourFolder
│     └── raw (where we can deposit raw downloaded data)
│     └── data (where we can process data and store it)
│     └── plots (where we can store plots)
if(!dir.exists("raw")){dir.create("raw")}
if(!dir.exists("data")){dir.create("data")}
if(!dir.exists("plots")){dir.create("plots")}

Install Packages

install.packages("Seurat")
install.packages("tidyverse")
install.packages("cowplot")

BiocManager::install("GEOquery")

We have a function that will let you check the setup

source("https://github.com/DrThomasOneil/Digital-Research-Skills-Network/raw/refs/heads/main/docs/adit/checksetup.R")

checkSetup(
  cran_packages = c("Seurat", "tidyverse", "cowplot"), 
  bioc_packages=c("GEOquery") 
)

--------------------------------------

***Installing General Packages***

Seurat is loaded!
tidyverse is loaded!
cowplot is loaded!
GEOquery is loaded!

--------------------------------------
 
 All packages are loaded!

 Happy Coding! :)

set.seed(1337)

Download data

We will download the data directly from the GEO. We’ll use this dataset, which was published in 2024. Additionally, this group have published all of their methods and scripts for analysis.

# Go to the GEO page and right click on the http link under download. It should look like this

data_file <- "https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE290350&format=file"

# download the data
if(!file.exists("raw/GSE290350_RAW.tar")){download.file(url=data_file, destfile = "raw/GSE290350_RAW.tar", method='curl')}

# repeat for metadata
if(!file.exists("raw/GSE290350_metadata.csv.gz")){download.file(url='https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE290350&format=file&file=GSE290350%5Fmetadata%2Ecsv%2Egz', destfile = "raw/GSE290350_metadata.csv.gz", method='curl')}

# and the supposed processed Seurat data
if(!file.exists("raw/GSE290350_seuratObj_spatial_dist.RDS")){download.file(url="https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE290350&format=file&file=GSE290350%5FseuratObj%5Fspatial%5Fdist%2ERDS", destfile = "raw/GSE290350_seuratObj_spatial_dist.RDS", method='curl')}

Unzip and untar the raw data

untar("raw/GSE290350_RAW.tar", exdir = "../raw/GSE290350_RAW")
samples <- list.files("raw/GSE290350_RAW", pattern = "\\.tar\\.gz$", full.names = TRUE)

for (i in seq_along(samples)) {
  tar_path <- sub("\\.gz$", "", samples[i])

  # Unzip the .tar.gz file
  GEOquery::gunzip(samples[i], destname = tar_path, overwrite = TRUE)

  output_dir <- file.path("raw/GSE290350_RAW", sub("\\.tar$", "", basename(tar_path)))
  dir.create(output_dir, showWarnings = FALSE)

  # Extract tar to output directory
  untar(tar_path, exdir = output_dir)
}

Pre-processing

Here we’ll load in the data. Fortunately, this data is quite neatly organised. When present, we can load the .h5 object. Please see the Archive page of the website to find vignettes on downloading and installing GEO-sourced objects that are not as well-organised!

We will:

Load in the data

We’ll load in the supplied metadata, and then we can load 10X data quite easily using the Load10X_Spatial function, which requires a certain format. We can load in the filtered matrix, which is filtered based on whether the initial processing detected that that spot aligned with tissue. Alternatively, you can load the raw object, and filter as you wish.

meta <- read.csv("raw/GSE290350_metadata.csv.gz")
data <- Load10X_Spatial(
  data.dir="raw/GSE290350_RAW/GSM8811056_P004/P004",
  filename = "filtered_feature_bc_matrix.h5",
  filter.matrix = F
)

Add meta data

The metadata provided is not found in the object itself. So we will run a small piece of code that lets you add metadata to a Seurat object.

data$ID <- "P004"
data <- AddMetaData(data,
                    data@meta.data %>% 
                      right_join(meta[meta$ID=="P004", ], by="ID"))

QC

There are several ways to QC transcriptomic data. First, you should check and scrutinise the original manuscript.

We will assess:

  • number of genes per spot

  • number of unique genes per spot

  • mitochondrial, haemoglobin, and ribosomal percentage.

We’ll also filter the genes themselves, to remove noisy/pointless genes.

  • genes that are not present in more than x number of spots

  • high expressing spots.

Genes per spot & Unique genes per spot

This is a typical single-cell RNA analysis metric. It should be carefully considered how this data is interpretted and filtered, as this is not single cell. Spots can have double the average or half the average based on the density of cells. Indeed, this is a consideration for normalisation, but this consideration is for another workshop.

As there is no gold standard, we will demonstrate how you visualise the QC metrics and then filter.

p1=FeatureScatter(data, "nCount_Spatial", "nFeature_Spatial")+NoLegend()
# number of genes
p2=SpatialFeaturePlot(data, "nCount_Spatial", 
                   crop=F,
                   pt.size=2,
                   alpha=c(.2,1))+
  NoLegend()
# number of unique features
p3=SpatialFeaturePlot(data, "nFeature_Spatial", 
                   crop=F,
                   pt.size=2,
                   alpha=c(.2,1))+
  NoLegend()
plot_grid(p1,plot_grid(p2,p3, nrow=2))

The minimum number of Features per spot (rmin(data$nFeature_Spatial)`) and minimum number of Counts per spot (151) are greater than the paper’s cut off (presuming they did not use the filtered matrix). However, for the purposes of demonstrating how this is done:

data <- subset(data, 
               subset = nCount_Spatial > 100 & nFeature_Spatial >100)

Module Scoring

In this paper, they remove cells that are >15% Mt genes & > 10% Hb reads.

data <- PercentageFeatureSet(data, "^MT-", col.name = "percent_mito")
data <- PercentageFeatureSet(data, "^HB[^(P)]", col.name = "percent_hb")
data <- PercentageFeatureSet(data, "^RP[SL]", col.name = "percent_ribo")
feature <-  c("nCount_Spatial", "nFeature_Spatial","percent_mito","percent_hb", "percent_ribo")
sapply(data@meta.data[feature], summary) %>% 
  as_tibble(rownames = "stat") %>% 
  knitr::kable(digits = 1)
stat nCount_Spatial nFeature_Spatial percent_mito percent_hb percent_ribo
Min. 151.0 107.0 1.9 0.0 2.3
1st Qu. 1068.2 680.0 5.4 0.0 8.0
Median 2644.5 1363.0 6.5 0.0 9.6
Mean 6114.8 1934.1 6.7 0.1 9.9
3rd Qu. 6933.2 2584.2 7.8 0.1 11.8
Max. 70736.0 9935.0 14.6 12.1 20.9
p1=SpatialFeaturePlot(data, "percent_mito", 
                   crop=F,
                   pt.size=2,
                   alpha=c(.2,1))+
  NoLegend()+ggtitle("Mito")
p2=SpatialFeaturePlot(data, "percent_hb", 
                   crop=F,
                   pt.size=2,
                   alpha=c(.2,1))+
  NoLegend()+ggtitle("Hb-genes")
p3=SpatialFeaturePlot(data, "percent_ribo", 
                   crop=F,
                   pt.size=2,
                   alpha=c(.2,1))+
  NoLegend()+ggtitle("Ribo")
plot_grid(p1,p2,p3)

Filter:

data <- subset(data, subset = percent_mito<15 & percent_hb <10)
sapply(data@meta.data[feature], summary) %>% 
  as_tibble(rownames = "stat") %>% 
  knitr::kable(digits = 1)
stat nCount_Spatial nFeature_Spatial percent_mito percent_hb percent_ribo
Min. 151.0 107.0 1.9 0.0 2.3
1st Qu. 1067.5 681.0 5.4 0.0 8.0
Median 2654.0 1363.0 6.5 0.0 9.6
Mean 6118.8 1935.1 6.7 0.1 9.9
3rd Qu. 6952.5 2585.5 7.8 0.1 11.8
Max. 70736.0 9935.0 14.6 4.7 20.9

Filter genes

We’ll first remove certain genes. MALAT1 is highly expressed in long non-coding RNA and is pretty ubiquitous. So we dont want to filter and subsequently cluster according to the expression of this gene. Similarly, Hb genes are highly expressed in rbc, which may represent contamination. We can remove these too.

keep_genes <- rownames(data)[!rownames(data) %in% c(grep("MALAT1", rownames(data), value=T), grep("^HB[^(P)]", rownames(data), value=T))]
data <- subset(data, features = keep_genes)

Low genes can create noise and take up unnecessary space in the object. Lets remove genes according to the original manuscript, whereby a gene not found in > 2 spots is removed. You can adjust this as you wish.

table(rowSums(data@assays$Spatial@layers$counts>=1)>=2)

FALSE  TRUE 
17971 18617 
keep_genes <- rowSums(data@assays$Spatial@layers$counts >= 1) >= 2
#before filtering
dim(data)
[1] 36588  1255
data <- subset(data, features = rownames(data)[keep_genes])
# after filtering
dim(data)
[1] 18617  1255

We recommend taking some time to carefully consider your QC strategy. There is no universal gold standard. One common approach is to start with lenient QC, proceed with processing, and during clustering and annotation, remain aware of the relaxed filtering. If unexpected results arise, you can revisit and refine your QC. Personally, I prefer to be permissive at first, while tagging potential outliers for future consideration. For example, we initially filtered out genes not present in at least 2 spots, but I may also label genes present in fewer than 5 spots as “toQC” for later review—particularly when assessing differential expression results. Similarly, we applied a 15% mitochondrial threshold but did not filter based on ribosomal content. At a later stage, we could tighten the mitochondrial threshold to 10% and include a filter for high ribosomal spots. If unusual clusters appear, we can then assess how many of those cells would have been excluded under stricter QC.

Data Processing

Seurat is an efficient package with simple functions for standard processing.

Normalisation

Again, normalisation is quite simple.

data <- NormalizeData(data, 
                      normalization.method = "LogNormalize",
                      verbose=F)

Variable Features

We’ll find the top variable features.

data <- FindVariableFeatures(data, 
                             method = 'vst', 
                             nfeatures = 2000, 
                             verbose=F)

We can output/visualise them in several ways.

VariableFeatures(data) %>% head(50)
 [1] "PI3"        "SPRR1A"     "IGLC3"      "FLG"        "IGHA2"     
 [6] "IGLC2"      "IGHA1"      "JCHAIN"     "IGHG1"      "AC019349.1"
[11] "IGKC"       "IGLC1"      "IGHG4"      "KRT10"      "IGHM"      
[16] "MUSTN1"     "CRISP3"     "CEACAM5"    "SPINK7"     "SERPINB4"  
[21] "KRTDAP"     "SERPINB3"   "PI15"       "RPTN"       "SPRR2B"    
[26] "KLK6"       "MGST1"      "KRT6B"      "SLURP1"     "ERO1A"     
[31] "CCL14"      "KRT1"       "SPRR2E"     "RNASE7"     "TOP2A"     
[36] "CRCT1"      "MT1X"       "RRM2"       "S100A7"     "DUOXA2"    
[41] "STMN1"      "LCE3D"      "PCNA"       "FAM25A"     "IGHG3"     
[46] "C15orf48"   "OLFM4"      "PTTG1"      "YOD1"       "CEACAM7"   

LabelPoints(plot = VariableFeaturePlot(data), 
            points = head(VariableFeatures(data), 50), repel = TRUE)

Scale Data

Scaling the data is also easy!

data <- ScaleData(data, 
                  features = VariableFeatures(data), #if you want to scale all features, change this to rownames(data)
        verbose=F)

PCA & UMAP

Also very easy with Seurat. We’ll determine the number of PCs to use for the UMAP using ElbowPlot

data <- RunPCA(data,
               npcs = 50,
               verbose=F)

ElbowPlot(data)

The elbow plot drops off drastically and plateaus at ~6, meaning the variation just becomes noisy around PC6-onwards. So we’ll just use the first 6.

data <- RunUMAP(data, 
                dims=1:6, 
                verbose=F)

Visualisation

There are several visualisations we can do:

Graph:

Spatial:

UMAP

UMAP Visualisation

We’ll first cluster the data.


data <- data %>% 
  FindNeighbors(dims = 1:6, verbose=F) %>%
  FindClusters(resolution=1, verbose=F)

And now we can create a UMAP.

p1=UMAPPlot(data,
            label=T, label.box=T)+
  NoLegend()+
  NoAxes()+
  ggtitle("11 Clusters")
Open to view more Plotting options

Here are some additional arguments that change the default visualisations. Chop and change to see how they work:

UMAPPlot(data, 
         pt.size=2, 
         label=T, 
         label.size=10,
         label.box=T,
         label.color="white",
         repel=T)+
  NoLegend()+
  NoAxes()+
  scale_color_manual(values= c("blue4", 'blue2', 'lightblue', 'green2', 'green4','brown', 'orange', 'red', 'red3', 'red4', 'black'))+
  scale_fill_manual(values= c("blue4", 'blue2', 'lightblue', 'green2', 'green4','brown', 'orange', 'red', 'red3', 'red4', 'black'))

Featureplots

We can view the gene expression on the UMAP itself.

p2=FeaturePlot(data, 
            "CD3E",
            order=T,
            pt.size=2, 
            cols = c("yellow2", 'blue3'))+NoAxes()+NoLegend()
p3=FeaturePlot(data, 
            "F13A1",
            order=T,
            pt.size=2, 
            cols = c("yellow2", 'blue3'))+NoAxes()+NoLegend()
p4=FeaturePlot(data, 
            "COL1A1",
            order=T,
            pt.size=2, 
            cols = c("yellow2", 'blue3'))+NoAxes()+NoLegend()
p5=FeaturePlot(data, 
            "KRT5",
            order=T,
            pt.size=2,
            min.cutoff = 3,
            cols = c("yellow2", 'blue3'))+NoAxes()+NoLegend()
plot_grid(p1, plot_grid(p2,p3,p4,p5, ncol=2))

DE and DotPlots

And we can simply assess the clusters themselves using differential analysis and expression graphs.



markers <- FindAllMarkers(data, 
                          logfc.threshold = 0.1,#default - increasing speeds it up, but may miss weaker signals.
                          min.pct = 0.01, #default - can be increased to ensure that the minimum expression of genes are considered. 
                          min.diff.pct = -Inf, # you can adjust this too. IF you want there to be at least a 10% difference in percentage of spots expressing the gene, you'd change this to 0.1. Can ensure that its not only the level of expression, but that you're capturing highly expressed genes
                          verbose=F #change this to true if you want to track the speed at which DE is being calculated. It is off here to save it outputting into the document 
                          ) 
top10 <- markers %>% 
  group_by(cluster) %>% 
  top_n(wt=avg_log2FC, n=10)
Heatmap
DoHeatmap(AggregateExpression(ScaleData(data, rownames(data), verbose=F), return.seurat = T, verbose=F),
          features = top10$gene, 
          draw.lines = F)


DotPlot
DotPlot(data, 
        features = c("CD3E", "CLEC10A", "CD207", "KRT5", "COL1A1"), 
        cols=c("grey", "red"))

Violin
VlnPlot(data, 
        features = c("CD3E", "CLEC10A", "CD207", "KRT5", "COL1A1"))

VlnPlot(data, 
        features = c("CD3E", "CLEC10A", "CD207", "KRT5", "COL1A1"), stack=T)

Spatial

We can do a few things here.

Clusters overlaid spatially

SpatialDimPlot(data, pt.size=3)

ImageDimPlot(data, size=3)

Expressions overlaid spatially

SpatialFeaturePlot(data, 
                   features="CD3E",
                   pt.size=3)


SpatialFeaturePlot(data, 
                   features="CD3E",
                   pt.size=3)+
  scale_fill_viridis_b()


ImageFeaturePlot(data, 
                   features="CD3E",
                   size=3)


sessionInfo()
R version 4.4.0 (2024-04-24)
Platform: aarch64-apple-darwin20
Running under: macOS Sonoma 14.3

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Australia/Sydney
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] GEOquery_2.72.0     Biobase_2.64.0      BiocGenerics_0.50.0
 [4] cowplot_1.1.3       lubridate_1.9.4     forcats_1.0.0      
 [7] stringr_1.5.1       dplyr_1.1.4         purrr_1.0.4        
[10] readr_2.1.5         tidyr_1.3.1         tibble_3.2.1       
[13] ggplot2_3.5.1       tidyverse_2.0.0     Seurat_5.2.1       
[16] SeuratObject_5.0.2  sp_2.2-0            cli_3.6.4          
[19] workflowr_1.7.1    

loaded via a namespace (and not attached):
  [1] RColorBrewer_1.1-3     rstudioapi_0.17.1      jsonlite_1.9.1        
  [4] magrittr_2.0.3         ggbeeswarm_0.7.2       spatstat.utils_3.1-3  
  [7] farver_2.1.2           rmarkdown_2.29         fs_1.6.5              
 [10] vctrs_0.6.5            ROCR_1.0-11            spatstat.explore_3.3-4
 [13] htmltools_0.5.8.1      sass_0.4.9             sctransform_0.4.1     
 [16] parallelly_1.42.0      KernSmooth_2.23-26     bslib_0.9.0           
 [19] htmlwidgets_1.6.4      ica_1.0-3              plyr_1.8.9            
 [22] plotly_4.10.4          zoo_1.8-13             cachem_1.1.0          
 [25] whisker_0.4.1          igraph_2.1.4           mime_0.13             
 [28] lifecycle_1.0.4        pkgconfig_2.0.3        Matrix_1.7-3          
 [31] R6_2.6.1               fastmap_1.2.0          fitdistrplus_1.2-2    
 [34] future_1.34.0          shiny_1.10.0           digest_0.6.37         
 [37] colorspace_2.1-1       patchwork_1.3.0        ps_1.9.0              
 [40] rprojroot_2.0.4        tensor_1.5             RSpectra_0.16-2       
 [43] irlba_2.3.5.1          labeling_0.4.3         progressr_0.15.1      
 [46] timechange_0.3.0       spatstat.sparse_3.1-0  httr_1.4.7            
 [49] polyclip_1.10-7        abind_1.4-8            compiler_4.4.0        
 [52] bit64_4.6.0-1          withr_3.0.2            fastDummies_1.7.5     
 [55] MASS_7.3-65            tools_4.4.0            vipor_0.4.7           
 [58] lmtest_0.9-40          beeswarm_0.4.0         httpuv_1.6.15         
 [61] future.apply_1.11.3    goftest_1.2-3          glue_1.8.0            
 [64] callr_3.7.6            nlme_3.1-167           promises_1.3.2        
 [67] grid_4.4.0             Rtsne_0.17             getPass_0.2-4         
 [70] cluster_2.1.8.1        reshape2_1.4.4         generics_0.1.3        
 [73] hdf5r_1.3.12           gtable_0.3.6           spatstat.data_3.1-6   
 [76] tzdb_0.5.0             hms_1.1.3              data.table_1.17.0     
 [79] xml2_1.3.8             spatstat.geom_3.3-6    RcppAnnoy_0.0.22      
 [82] ggrepel_0.9.6          RANN_2.6.2             pillar_1.10.1         
 [85] limma_3.60.6           spam_2.11-1            RcppHNSW_0.6.0        
 [88] later_1.4.1            splines_4.4.0          lattice_0.22-6        
 [91] bit_4.6.0              survival_3.8-3         deldir_2.0-4          
 [94] tidyselect_1.2.1       miniUI_0.1.1.1         pbapply_1.7-2         
 [97] knitr_1.50             git2r_0.35.0           gridExtra_2.3         
[100] scattermore_1.2        xfun_0.51              statmod_1.5.0         
[103] matrixStats_1.5.0      stringi_1.8.4          lazyeval_0.2.2        
[106] yaml_2.3.10            evaluate_1.0.3         codetools_0.2-20      
[109] uwot_0.2.3             xtable_1.8-4           reticulate_1.41.0.1   
[112] munsell_0.5.1          processx_3.8.6         jquerylib_0.1.4       
[115] Rcpp_1.0.14            globals_0.16.3         spatstat.random_3.3-2 
[118] png_0.1-8              ggrastr_1.0.2          spatstat.univar_3.1-2 
[121] parallel_4.4.0         presto_1.0.0           dotCall64_1.2         
[124] listenv_0.9.1          viridisLite_0.4.2      scales_1.3.0          
[127] ggridges_0.5.6         rlang_1.1.6           
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFZpc2l1bSBBbmFseXNpcyIKYXV0aG9yOiAiQXV0aG9yIgpkYXRlOiAiMjAyNS0wMiIgCm91dHB1dDoKICB3b3JrZmxvd3I6OndmbG93X2h0bWw6CiAgICB0b2M6IGZhbHNlCiAgICBjb2RlX2ZvbGRpbmc6ICJoaWRlIgplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCiAKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgY29sbGFwc2U9VCkKYGBgCgo8ZGl2IHN0eWxlID0gInRleHQtYWxpZ246IGNlbnRlciI+Cgo8cmVhZC10aW1lPjxzcGFuIHN0eWxlPSJmb250LXNpemU6IDEuM2VtIj5QbGVhc2UgdHJ5IHRvIGZvbGxvdyB0aGUgaW5zdHJ1Y3Rpb25zIGJlbG93ICoqcHJpb3IqKiB0byBhdHRlbmRpbmcgdGhlIHdvcmtzaG9wLjxicj48YnI+PGk+KipCZSBhd2FyZSEqKjwvaT48L3NwYW4+PGJyPiBJdCBpcyBkaWZmaWN1bHQgdG8gZGVidWcgb3ZlciB6b29tLiA8YnI+SWYgeW91IHdhbnQgdGhlIGJlc3QgZmVlZGJhY2sgYW5kIGV4cGVyaWVuY2UsIG1ha2Ugc3VyZSB5b3UgYXR0ZW5kIHRoZSB3b3Jrc2hvcCBpbiBwZXJzb24hPC9yZWFkLXRpbWU+CgpZb3UgY2FuIGRvd25sb2FkIHRoZSBzY3JpcHQgb24gdGhlIHRvcCByaWdodCBvciBmcm9tIFt0aGlzIGxpbmtdKGh0dHBzOi8vZ2l0aHViLmNvbS9EclRob21hc09uZWlsL2RpZ2l0YWwtcmVzZWFyY2gtc2tpbGxzLW5ldHdvcmsvYmxvYi9tYXN0ZXIvYW5hbHlzaXMvMjAyNTA0LlJtZCkuCiAKPC9kaXY+PHdpbXI+CgoKIyBJbnRyb2R1Y3Rpb24KCiMgU2V0dXB7LnRhYnNldCAudGFic2V0LWZhZGV9CgotIENyZWF0ZSBmb2xkZXJzCgotIEluc3RhbGwgUGFja2FnZXMKCi0gRG93bmxvYWQgRGF0YQoKIyMgQ3JlYXRlIEZvbGRlcnMKCmBgYHRleHQK4pSc4pSA4pSAIFlvdXJGb2xkZXIK4pSCICAgICDilJTilIDilIAgcmF3ICh3aGVyZSB3ZSBjYW4gZGVwb3NpdCByYXcgZG93bmxvYWRlZCBkYXRhKQrilIIgICAgIOKUlOKUgOKUgCBkYXRhICh3aGVyZSB3ZSBjYW4gcHJvY2VzcyBkYXRhIGFuZCBzdG9yZSBpdCkK4pSCICAgICDilJTilIDilIAgcGxvdHMgKHdoZXJlIHdlIGNhbiBzdG9yZSBwbG90cykKYGBgCgpgYGB7ciBldmFsPVR9CmlmKCFkaXIuZXhpc3RzKCJyYXciKSl7ZGlyLmNyZWF0ZSgicmF3Iil9CmlmKCFkaXIuZXhpc3RzKCJkYXRhIikpe2Rpci5jcmVhdGUoImRhdGEiKX0KaWYoIWRpci5leGlzdHMoInBsb3RzIikpe2Rpci5jcmVhdGUoInBsb3RzIil9CmBgYAoKPHdpbXI+CgojIyBJbnN0YWxsIFBhY2thZ2VzCgpgYGB7ciwgZXZhbD1GfQppbnN0YWxsLnBhY2thZ2VzKCJTZXVyYXQiKQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQppbnN0YWxsLnBhY2thZ2VzKCJjb3dwbG90IikKCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJHRU9xdWVyeSIpCmBgYAoKV2UgaGF2ZSBhIGZ1bmN0aW9uIHRoYXQgd2lsbCBsZXQgeW91IGNoZWNrIHRoZSBzZXR1cApgYGB7cn0Kc291cmNlKCJodHRwczovL2dpdGh1Yi5jb20vRHJUaG9tYXNPbmVpbC9EaWdpdGFsLVJlc2VhcmNoLVNraWxscy1OZXR3b3JrL3Jhdy9yZWZzL2hlYWRzL21haW4vZG9jcy9hZGl0L2NoZWNrc2V0dXAuUiIpCgpjaGVja1NldHVwKAogIGNyYW5fcGFja2FnZXMgPSBjKCJTZXVyYXQiLCAidGlkeXZlcnNlIiwgImNvd3Bsb3QiKSwgCiAgYmlvY19wYWNrYWdlcz1jKCJHRU9xdWVyeSIpIAopCgpzZXQuc2VlZCgxMzM3KQpgYGAKCjx3aW1yPgoKIyMgRG93bmxvYWQgZGF0YQoKV2Ugd2lsbCBkb3dubG9hZCB0aGUgZGF0YSBkaXJlY3RseSBmcm9tIHRoZSBbR0VPXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby8pLiBXZSdsbCB1c2UgW3RoaXNdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvZ2VvL3F1ZXJ5L2FjYy5jZ2k/YWNjPUdTRTI5MDM1MCkgZGF0YXNldCwgd2hpY2ggd2FzIFtwdWJsaXNoZWQgaW4gMjAyNF0oaHR0cHM6Ly9wbWMubmNiaS5ubG0ubmloLmdvdi9hcnRpY2xlcy9QTUMxMTY0Njg1NS8pLiBBZGRpdGlvbmFsbHksIHRoaXMgZ3JvdXAgaGF2ZSBwdWJsaXNoZWQgW2FsbCBvZiB0aGVpciBtZXRob2RzIGFuZCBzY3JpcHRzIGZvciBhbmFseXNpc10oaHR0cHM6Ly9naXRodWIuY29tL3ZpbGRla2EvU3BhdGlhbF9ETVBBP3RhYj1yZWFkbWUtb3YtZmlsZSkuCgpgYGB7ciwgZXZhbD1GfQojIEdvIHRvIHRoZSBHRU8gcGFnZSBhbmQgcmlnaHQgY2xpY2sgb24gdGhlIGh0dHAgbGluayB1bmRlciBkb3dubG9hZC4gSXQgc2hvdWxkIGxvb2sgbGlrZSB0aGlzCgpkYXRhX2ZpbGUgPC0gImh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvZ2VvL2Rvd25sb2FkLz9hY2M9R1NFMjkwMzUwJmZvcm1hdD1maWxlIgoKIyBkb3dubG9hZCB0aGUgZGF0YQppZighZmlsZS5leGlzdHMoInJhdy9HU0UyOTAzNTBfUkFXLnRhciIpKXtkb3dubG9hZC5maWxlKHVybD1kYXRhX2ZpbGUsIGRlc3RmaWxlID0gInJhdy9HU0UyOTAzNTBfUkFXLnRhciIsIG1ldGhvZD0nY3VybCcpfQoKIyByZXBlYXQgZm9yIG1ldGFkYXRhCmlmKCFmaWxlLmV4aXN0cygicmF3L0dTRTI5MDM1MF9tZXRhZGF0YS5jc3YuZ3oiKSl7ZG93bmxvYWQuZmlsZSh1cmw9J2h0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvZ2VvL2Rvd25sb2FkLz9hY2M9R1NFMjkwMzUwJmZvcm1hdD1maWxlJmZpbGU9R1NFMjkwMzUwJTVGbWV0YWRhdGElMkVjc3YlMkVneicsIGRlc3RmaWxlID0gInJhdy9HU0UyOTAzNTBfbWV0YWRhdGEuY3N2Lmd6IiwgbWV0aG9kPSdjdXJsJyl9CgojIGFuZCB0aGUgc3VwcG9zZWQgcHJvY2Vzc2VkIFNldXJhdCBkYXRhCmlmKCFmaWxlLmV4aXN0cygicmF3L0dTRTI5MDM1MF9zZXVyYXRPYmpfc3BhdGlhbF9kaXN0LlJEUyIpKXtkb3dubG9hZC5maWxlKHVybD0iaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vZG93bmxvYWQvP2FjYz1HU0UyOTAzNTAmZm9ybWF0PWZpbGUmZmlsZT1HU0UyOTAzNTAlNUZzZXVyYXRPYmolNUZzcGF0aWFsJTVGZGlzdCUyRVJEUyIsIGRlc3RmaWxlID0gInJhdy9HU0UyOTAzNTBfc2V1cmF0T2JqX3NwYXRpYWxfZGlzdC5SRFMiLCBtZXRob2Q9J2N1cmwnKX0KCgpgYGAKCjx3aW1yPgoKIyMgVW56aXAgYW5kIHVudGFyIHRoZSByYXcgZGF0YQoKYGBge3IsIGV2YWw9Rn0KdW50YXIoInJhdy9HU0UyOTAzNTBfUkFXLnRhciIsIGV4ZGlyID0gIi4uL3Jhdy9HU0UyOTAzNTBfUkFXIikKYGBgCgpgYGB7ciwgZXZhbD1GfQpzYW1wbGVzIDwtIGxpc3QuZmlsZXMoInJhdy9HU0UyOTAzNTBfUkFXIiwgcGF0dGVybiA9ICJcXC50YXJcXC5neiQiLCBmdWxsLm5hbWVzID0gVFJVRSkKCmZvciAoaSBpbiBzZXFfYWxvbmcoc2FtcGxlcykpIHsKICB0YXJfcGF0aCA8LSBzdWIoIlxcLmd6JCIsICIiLCBzYW1wbGVzW2ldKQoKICAjIFVuemlwIHRoZSAudGFyLmd6IGZpbGUKICBHRU9xdWVyeTo6Z3VuemlwKHNhbXBsZXNbaV0sIGRlc3RuYW1lID0gdGFyX3BhdGgsIG92ZXJ3cml0ZSA9IFRSVUUpCgogIG91dHB1dF9kaXIgPC0gZmlsZS5wYXRoKCJyYXcvR1NFMjkwMzUwX1JBVyIsIHN1YigiXFwudGFyJCIsICIiLCBiYXNlbmFtZSh0YXJfcGF0aCkpKQogIGRpci5jcmVhdGUob3V0cHV0X2Rpciwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCgogICMgRXh0cmFjdCB0YXIgdG8gb3V0cHV0IGRpcmVjdG9yeQogIHVudGFyKHRhcl9wYXRoLCBleGRpciA9IG91dHB1dF9kaXIpCn0KYGBgCgo8d2ltcj4KCiMgUHJlLXByb2Nlc3Npbmd7LnRhYnNldCAudGFic2V0LWZhZGV9CgpIZXJlIHdlJ2xsIGxvYWQgaW4gdGhlIGRhdGEuIEZvcnR1bmF0ZWx5LCB0aGlzIGRhdGEgaXMgcXVpdGUgbmVhdGx5IG9yZ2FuaXNlZC4gV2hlbiBwcmVzZW50LCB3ZSBjYW4gbG9hZCB0aGUgYC5oNWAgb2JqZWN0LiBQbGVhc2Ugc2VlIHRoZSBgQXJjaGl2ZWAgcGFnZSBvZiB0aGUgW3dlYnNpdGVdKGh0dHBzOi8vZGlnaXRhbHJlc2VhcmNoc2tpbGxzLm5ldHdvcmsvMF9hcmNoaXZlLmh0bWwpIHRvIGZpbmQgdmlnbmV0dGVzIG9uIGRvd25sb2FkaW5nIGFuZCBpbnN0YWxsaW5nIEdFTy1zb3VyY2VkIG9iamVjdHMgdGhhdCBhcmUgbm90IGFzIHdlbGwtb3JnYW5pc2VkIQoKV2Ugd2lsbDoKCi0gTG9hZCB0aGUgZGF0YQoKLSBBZGQgdGhlIG1ldGEgZGF0YQoKLSBDaGVjayB0aGUgUUMsIGZpbHRlciBhbmQgcHJvY2VzcyB0aGUgZGF0YQoKPGRpdj4KCiMjIExvYWQgaW4gdGhlIGRhdGEgCgpXZSdsbCBsb2FkIGluIHRoZSBzdXBwbGllZCBtZXRhZGF0YSwgYW5kIHRoZW4gd2UgY2FuIGxvYWQgMTBYIGRhdGEgcXVpdGUgZWFzaWx5IHVzaW5nIHRoZSBMb2FkMTBYX1NwYXRpYWwgZnVuY3Rpb24sIHdoaWNoIHJlcXVpcmVzIGEgY2VydGFpbiBmb3JtYXQuIFdlIGNhbiBsb2FkIGluIHRoZSBmaWx0ZXJlZCBtYXRyaXgsIHdoaWNoIGlzIGZpbHRlcmVkIGJhc2VkIG9uIHdoZXRoZXIgdGhlIGluaXRpYWwgcHJvY2Vzc2luZyBkZXRlY3RlZCB0aGF0IHRoYXQgc3BvdCBhbGlnbmVkIHdpdGggdGlzc3VlLiBBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIGxvYWQgdGhlIHJhdyBvYmplY3QsIGFuZCBmaWx0ZXIgYXMgeW91IHdpc2guIAoKYGBge3J9Cm1ldGEgPC0gcmVhZC5jc3YoInJhdy9HU0UyOTAzNTBfbWV0YWRhdGEuY3N2Lmd6IikKYGBgCgpgYGB7cn0KZGF0YSA8LSBMb2FkMTBYX1NwYXRpYWwoCiAgZGF0YS5kaXI9InJhdy9HU0UyOTAzNTBfUkFXL0dTTTg4MTEwNTZfUDAwNC9QMDA0IiwKICBmaWxlbmFtZSA9ICJmaWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC5oNSIsCiAgZmlsdGVyLm1hdHJpeCA9IEYKKQpgYGAKCjx3aW1yPgoKIyMgQWRkIG1ldGEgZGF0YQoKVGhlIG1ldGFkYXRhIHByb3ZpZGVkIGlzIG5vdCBmb3VuZCBpbiB0aGUgb2JqZWN0IGl0c2VsZi4gU28gd2Ugd2lsbCBydW4gYSBzbWFsbCBwaWVjZSBvZiBjb2RlIHRoYXQgbGV0cyB5b3UgYWRkIG1ldGFkYXRhIHRvIGEgU2V1cmF0IG9iamVjdC4gCgpgYGB7cn0KZGF0YSRJRCA8LSAiUDAwNCIKZGF0YSA8LSBBZGRNZXRhRGF0YShkYXRhLAogICAgICAgICAgICAgICAgICAgIGRhdGFAbWV0YS5kYXRhICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHJpZ2h0X2pvaW4obWV0YVttZXRhJElEPT0iUDAwNCIsIF0sIGJ5PSJJRCIpKQpgYGAKCgojIyBRQ3sudGFic2V0IC50YWJzZXQtZmFkZX0KClRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gUUMgdHJhbnNjcmlwdG9taWMgZGF0YS4gRmlyc3QsIHlvdSBzaG91bGQgY2hlY2sgYW5kIHNjcnV0aW5pc2UgdGhlIFtvcmlnaW5hbCBtYW51c2NyaXB0XShodHRwczovL2dpdGh1Yi5jb20vdmlsZGVrYS9TcGF0aWFsX0RNUEE/dGFiPXJlYWRtZS1vdi1maWxlKS4KCldlIHdpbGwgYXNzZXNzOiAKCi0gbnVtYmVyIG9mIGdlbmVzIHBlciBzcG90CgotIG51bWJlciBvZiB1bmlxdWUgZ2VuZXMgcGVyIHNwb3QKCi0gbWl0b2Nob25kcmlhbCwgaGFlbW9nbG9iaW4sIGFuZCByaWJvc29tYWwgcGVyY2VudGFnZS4KCldlJ2xsIGFsc28gZmlsdGVyIHRoZSBnZW5lcyB0aGVtc2VsdmVzLCB0byByZW1vdmUgbm9pc3kvcG9pbnRsZXNzIGdlbmVzLiAKCi0gZ2VuZXMgdGhhdCBhcmUgbm90IHByZXNlbnQgaW4gbW9yZSB0aGFuIHggbnVtYmVyIG9mIHNwb3RzCgotIGhpZ2ggZXhwcmVzc2luZyBzcG90cy4KCiMjIyBHZW5lcyBwZXIgc3BvdCAmIFVuaXF1ZSBnZW5lcyBwZXIgc3BvdAoKVGhpcyBpcyBhIHR5cGljYWwgYHNpbmdsZS1jZWxsIFJOQWAgYW5hbHlzaXMgbWV0cmljLiBJdCBzaG91bGQgYmUgY2FyZWZ1bGx5IGNvbnNpZGVyZWQgaG93IHRoaXMgZGF0YSBpcyBpbnRlcnByZXR0ZWQgYW5kIGZpbHRlcmVkLCBhcyB0aGlzIGlzICoqbm90Kiogc2luZ2xlIGNlbGwuIFNwb3RzIGNhbiBoYXZlIGRvdWJsZSB0aGUgYXZlcmFnZSBvciBoYWxmIHRoZSBhdmVyYWdlIGJhc2VkIG9uIHRoZSBkZW5zaXR5IG9mIGNlbGxzLiBJbmRlZWQsIHRoaXMgaXMgYSBjb25zaWRlcmF0aW9uIGZvciBub3JtYWxpc2F0aW9uLCBidXQgdGhpcyBjb25zaWRlcmF0aW9uIGlzIGZvciBhbm90aGVyIHdvcmtzaG9wLiAKCjxpPioqQXMgdGhlcmUgaXMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+bm88L3NwYW4+IGdvbGQgc3RhbmRhcmQqKjwvaT4sIHdlIHdpbGwgZGVtb25zdHJhdGUgaG93IHlvdSB2aXN1YWxpc2UgdGhlIFFDIG1ldHJpY3MgYW5kIHRoZW4gZmlsdGVyLiAKCmBgYHtyfQpwMT1GZWF0dXJlU2NhdHRlcihkYXRhLCAibkNvdW50X1NwYXRpYWwiLCAibkZlYXR1cmVfU3BhdGlhbCIpK05vTGVnZW5kKCkKIyBudW1iZXIgb2YgZ2VuZXMKcDI9U3BhdGlhbEZlYXR1cmVQbG90KGRhdGEsICJuQ291bnRfU3BhdGlhbCIsIAogICAgICAgICAgICAgICAgICAgY3JvcD1GLAogICAgICAgICAgICAgICAgICAgcHQuc2l6ZT0yLAogICAgICAgICAgICAgICAgICAgYWxwaGE9YyguMiwxKSkrCiAgTm9MZWdlbmQoKQojIG51bWJlciBvZiB1bmlxdWUgZmVhdHVyZXMKcDM9U3BhdGlhbEZlYXR1cmVQbG90KGRhdGEsICJuRmVhdHVyZV9TcGF0aWFsIiwgCiAgICAgICAgICAgICAgICAgICBjcm9wPUYsCiAgICAgICAgICAgICAgICAgICBwdC5zaXplPTIsCiAgICAgICAgICAgICAgICAgICBhbHBoYT1jKC4yLDEpKSsKICBOb0xlZ2VuZCgpCnBsb3RfZ3JpZChwMSxwbG90X2dyaWQocDIscDMsIG5yb3c9MikpCmBgYAoKVGhlIG1pbmltdW0gbnVtYmVyIG9mIEZlYXR1cmVzIHBlciBzcG90IChgciBgbWluKGRhdGEkbkZlYXR1cmVfU3BhdGlhbClgKSBhbmQgbWluaW11bSBudW1iZXIgb2YgQ291bnRzIHBlciBzcG90IChgciBtaW4oZGF0YSRuQ291bnRfU3BhdGlhbClgKSBhcmUgZ3JlYXRlciB0aGFuIHRoZSBwYXBlcidzIGN1dCBvZmYgKHByZXN1bWluZyB0aGV5IGRpZCBub3QgdXNlIHRoZSBmaWx0ZXJlZCBtYXRyaXgpLiBIb3dldmVyLCBmb3IgdGhlIHB1cnBvc2VzIG9mIGRlbW9uc3RyYXRpbmcgaG93IHRoaXMgaXMgZG9uZToKCmBgYHtyfQpkYXRhIDwtIHN1YnNldChkYXRhLCAKICAgICAgICAgICAgICAgc3Vic2V0ID0gbkNvdW50X1NwYXRpYWwgPiAxMDAgJiBuRmVhdHVyZV9TcGF0aWFsID4xMDApCmBgYAoKIyMjIE1vZHVsZSBTY29yaW5nCgpJbiB0aGlzIHBhcGVyLCB0aGV5IHJlbW92ZSBjZWxscyB0aGF0IGFyZSA+MTUlIE10IGdlbmVzICYgPiAxMCUgSGIgcmVhZHMuIAoKYGBge3J9CmRhdGEgPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoZGF0YSwgIl5NVC0iLCBjb2wubmFtZSA9ICJwZXJjZW50X21pdG8iKQpkYXRhIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KGRhdGEsICJeSEJbXihQKV0iLCBjb2wubmFtZSA9ICJwZXJjZW50X2hiIikKZGF0YSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChkYXRhLCAiXlJQW1NMXSIsIGNvbC5uYW1lID0gInBlcmNlbnRfcmlibyIpCmBgYAoKYGBge3J9CmZlYXR1cmUgPC0gIGMoIm5Db3VudF9TcGF0aWFsIiwgIm5GZWF0dXJlX1NwYXRpYWwiLCJwZXJjZW50X21pdG8iLCJwZXJjZW50X2hiIiwgInBlcmNlbnRfcmlibyIpCnNhcHBseShkYXRhQG1ldGEuZGF0YVtmZWF0dXJlXSwgc3VtbWFyeSkgJT4lIAogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJzdGF0IikgJT4lIAogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAxKQpgYGAKCgpgYGB7cn0KcDE9U3BhdGlhbEZlYXR1cmVQbG90KGRhdGEsICJwZXJjZW50X21pdG8iLCAKICAgICAgICAgICAgICAgICAgIGNyb3A9RiwKICAgICAgICAgICAgICAgICAgIHB0LnNpemU9MiwKICAgICAgICAgICAgICAgICAgIGFscGhhPWMoLjIsMSkpKwogIE5vTGVnZW5kKCkrZ2d0aXRsZSgiTWl0byIpCnAyPVNwYXRpYWxGZWF0dXJlUGxvdChkYXRhLCAicGVyY2VudF9oYiIsIAogICAgICAgICAgICAgICAgICAgY3JvcD1GLAogICAgICAgICAgICAgICAgICAgcHQuc2l6ZT0yLAogICAgICAgICAgICAgICAgICAgYWxwaGE9YyguMiwxKSkrCiAgTm9MZWdlbmQoKStnZ3RpdGxlKCJIYi1nZW5lcyIpCnAzPVNwYXRpYWxGZWF0dXJlUGxvdChkYXRhLCAicGVyY2VudF9yaWJvIiwgCiAgICAgICAgICAgICAgICAgICBjcm9wPUYsCiAgICAgICAgICAgICAgICAgICBwdC5zaXplPTIsCiAgICAgICAgICAgICAgICAgICBhbHBoYT1jKC4yLDEpKSsKICBOb0xlZ2VuZCgpK2dndGl0bGUoIlJpYm8iKQpwbG90X2dyaWQocDEscDIscDMpCmBgYAoKRmlsdGVyOgoKYGBge3J9CmRhdGEgPC0gc3Vic2V0KGRhdGEsIHN1YnNldCA9IHBlcmNlbnRfbWl0bzwxNSAmIHBlcmNlbnRfaGIgPDEwKQpzYXBwbHkoZGF0YUBtZXRhLmRhdGFbZmVhdHVyZV0sIHN1bW1hcnkpICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAic3RhdCIpICU+JSAKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMSkKYGBgCgojIyMgRmlsdGVyIGdlbmVzCgpXZSdsbCBmaXJzdCByZW1vdmUgY2VydGFpbiBnZW5lcy4gTUFMQVQxIGlzIGhpZ2hseSBleHByZXNzZWQgaW4gbG9uZyBub24tY29kaW5nIFJOQSBhbmQgaXMgcHJldHR5IHViaXF1aXRvdXMuIFNvIHdlIGRvbnQgd2FudCB0byBmaWx0ZXIgYW5kIHN1YnNlcXVlbnRseSBjbHVzdGVyIGFjY29yZGluZyB0byB0aGUgZXhwcmVzc2lvbiBvZiB0aGlzIGdlbmUuIFNpbWlsYXJseSwgSGIgZ2VuZXMgYXJlIGhpZ2hseSBleHByZXNzZWQgaW4gcmJjLCB3aGljaCBtYXkgcmVwcmVzZW50IGNvbnRhbWluYXRpb24uIFdlIGNhbiByZW1vdmUgdGhlc2UgdG9vLiAKCmBgYHtyfQprZWVwX2dlbmVzIDwtIHJvd25hbWVzKGRhdGEpWyFyb3duYW1lcyhkYXRhKSAlaW4lIGMoZ3JlcCgiTUFMQVQxIiwgcm93bmFtZXMoZGF0YSksIHZhbHVlPVQpLCBncmVwKCJeSEJbXihQKV0iLCByb3duYW1lcyhkYXRhKSwgdmFsdWU9VCkpXQpkYXRhIDwtIHN1YnNldChkYXRhLCBmZWF0dXJlcyA9IGtlZXBfZ2VuZXMpCmBgYAoKTG93IGdlbmVzIGNhbiBjcmVhdGUgbm9pc2UgYW5kIHRha2UgdXAgdW5uZWNlc3Nhcnkgc3BhY2UgaW4gdGhlIG9iamVjdC4gTGV0cyByZW1vdmUgZ2VuZXMgYWNjb3JkaW5nIHRvIHRoZSBvcmlnaW5hbCBtYW51c2NyaXB0LCB3aGVyZWJ5IGEgZ2VuZSBub3QgZm91bmQgaW4gPiAyIHNwb3RzIGlzIHJlbW92ZWQuIFlvdSBjYW4gYWRqdXN0IHRoaXMgYXMgeW91IHdpc2guIAoKYGBge3J9CnRhYmxlKHJvd1N1bXMoZGF0YUBhc3NheXMkU3BhdGlhbEBsYXllcnMkY291bnRzPj0xKT49MikKa2VlcF9nZW5lcyA8LSByb3dTdW1zKGRhdGFAYXNzYXlzJFNwYXRpYWxAbGF5ZXJzJGNvdW50cyA+PSAxKSA+PSAyCiNiZWZvcmUgZmlsdGVyaW5nCmRpbShkYXRhKQpkYXRhIDwtIHN1YnNldChkYXRhLCBmZWF0dXJlcyA9IHJvd25hbWVzKGRhdGEpW2tlZXBfZ2VuZXNdKQojIGFmdGVyIGZpbHRlcmluZwpkaW0oZGF0YSkKYGBgCgo8L2Rpdj4KCjxocj4KCldlIHJlY29tbWVuZCB0YWtpbmcgc29tZSB0aW1lIHRvIGNhcmVmdWxseSBjb25zaWRlciB5b3VyIFFDIHN0cmF0ZWd5LiA8aT4qKlRoZXJlIGlzIG5vIHVuaXZlcnNhbCBnb2xkIHN0YW5kYXJkPC9pPioqLiBPbmUgY29tbW9uIGFwcHJvYWNoIGlzIHRvIHN0YXJ0IHdpdGggbGVuaWVudCBRQywgcHJvY2VlZCB3aXRoIHByb2Nlc3NpbmcsIGFuZCBkdXJpbmcgY2x1c3RlcmluZyBhbmQgYW5ub3RhdGlvbiwgcmVtYWluIGF3YXJlIG9mIHRoZSByZWxheGVkIGZpbHRlcmluZy4gSWYgdW5leHBlY3RlZCByZXN1bHRzIGFyaXNlLCB5b3UgY2FuIHJldmlzaXQgYW5kIHJlZmluZSB5b3VyIFFDLiBQZXJzb25hbGx5LCBJIHByZWZlciB0byBiZSBwZXJtaXNzaXZlIGF0IGZpcnN0LCB3aGlsZSB0YWdnaW5nIHBvdGVudGlhbCBvdXRsaWVycyBmb3IgZnV0dXJlIGNvbnNpZGVyYXRpb24uIEZvciBleGFtcGxlLCB3ZSBpbml0aWFsbHkgZmlsdGVyZWQgb3V0IGdlbmVzIG5vdCBwcmVzZW50IGluIGF0IGxlYXN0IDIgc3BvdHMsIGJ1dCBJIG1heSBhbHNvIGxhYmVsIGdlbmVzIHByZXNlbnQgaW4gZmV3ZXIgdGhhbiA1IHNwb3RzIGFzIOKAnHRvUUPigJ0gZm9yIGxhdGVyIHJldmlld+KAlHBhcnRpY3VsYXJseSB3aGVuIGFzc2Vzc2luZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzLiBTaW1pbGFybHksIHdlIGFwcGxpZWQgYSAxNSUgbWl0b2Nob25kcmlhbCB0aHJlc2hvbGQgYnV0IGRpZCBub3QgZmlsdGVyIGJhc2VkIG9uIHJpYm9zb21hbCBjb250ZW50LiBBdCBhIGxhdGVyIHN0YWdlLCB3ZSBjb3VsZCB0aWdodGVuIHRoZSBtaXRvY2hvbmRyaWFsIHRocmVzaG9sZCB0byAxMCUgYW5kIGluY2x1ZGUgYSBmaWx0ZXIgZm9yIGhpZ2ggcmlib3NvbWFsIHNwb3RzLiBJZiB1bnVzdWFsIGNsdXN0ZXJzIGFwcGVhciwgd2UgY2FuIHRoZW4gYXNzZXNzIGhvdyBtYW55IG9mIHRob3NlIGNlbGxzIHdvdWxkIGhhdmUgYmVlbiBleGNsdWRlZCB1bmRlciBzdHJpY3RlciBRQy4KCgojIERhdGEgUHJvY2Vzc2luZ3sudGFic2V0IC50YWJzZXQtZmFkZX0KClNldXJhdCBpcyBhbiBlZmZpY2llbnQgcGFja2FnZSB3aXRoIHNpbXBsZSBmdW5jdGlvbnMgZm9yIHN0YW5kYXJkIHByb2Nlc3NpbmcuIAoKLSBgTm9ybWFsaXplRGF0YWA6IFRoZSBkZWZhdWx0IGlzIHRvIGxvZy1ub3JtYWxpemUgKihGZWF0dXJlIGNvdW50cyBmb3IgZWFjaCBjZWxsIGFyZSBkaXZpZGVkIGJ5IHRoZSB0b3RhbCBjb3VudHMgZm9yIHRoYXQgY2VsbCBhbmQgbXVsdGlwbGllZCBieSB0aGUgc2NhbGUuZmFjdG9yLiBUaGlzIGlzIHRoZW4gbmF0dXJhbC1sb2cgdHJhbnNmb3JtZWQgdXNpbmcgbG9nMXApKi4gU3BhdGlhbCBkYXRhIG1heSBiZSBiZXR0ZXIgbm9ybWFsaXNlZCB3aXRoIEFyZWEvY2VsbCBjb3VudCBjb25zaWRlcmVkLiBBcyB3ZSBkb24ndCBoYXZlIGNlbGwgY291bnRzIHBlciBzcG90IGhlcmUsIHdlJ2xsIHN0aWNrIHRvIHRoZSBzdGFuZGFyZCBhcHByb2FjaCBhbmQgdGhhdCB0YWtlbiBieSB0aGUgYXV0aG9ycyAKCi0gYEZpbmRWYXJpYWJsZUZlYXR1cmVzYDogVGhlIHN0YW5kYXJkIGFwcHJvYWNoIGlzIDIwMDAgdG9wIHZhcmlhYmxlIGdlbmVzICsgJ3ZzdCcgc2VsZWN0aW9uIG1ldGhvZC4gKihGaXJzdCwgZml0cyBhIGxpbmUgdG8gdGhlIHJlbGF0aW9uc2hpcCBvZiBsb2codmFyaWFuY2UpIGFuZCBsb2cobWVhbikgdXNpbmcgbG9jYWwgcG9seW5vbWlhbCByZWdyZXNzaW9uIChsb2VzcykuIFRoZW4gc3RhbmRhcmRpemVzIHRoZSBmZWF0dXJlIHZhbHVlcyB1c2luZyB0aGUgb2JzZXJ2ZWQgbWVhbiBhbmQgZXhwZWN0ZWQgdmFyaWFuY2UgKGdpdmVuIGJ5IHRoZSBmaXR0ZWQgbGluZSkuIEZlYXR1cmUgdmFyaWFuY2UgaXMgdGhlbiBjYWxjdWxhdGVkIG9uIHRoZSBzdGFuZGFyZGl6ZWQgdmFsdWVzIGFmdGVyIGNsaXBwaW5nIHRvIGEgbWF4aW11bSkqLiBTdGFuZGFyZCBpcyAyMDAwIGdlbmVzCgotIGBTY2FsZURhdGFgOiBoZXJlLCB3ZSBzY2FsZSBqdXN0IHRoZSB2YXJpYWJsZSBnZW5lcyB0byBzYXZlIHNwYWNlLiBJZiB5b3UgZmluZCB5b3Vyc2VsZiB1bmFibGUgdG8gdmlzdWFsaXNlIHlvdXIgZ2VuZSBvZiBpbnRlcmVzdCBpbiBzdWJzZXF1ZW50IHZpc3VhbGlzYXRpb25zLCBpdCB3YXMgbm90IGluIHRoaXMgdG9wIHZhcmlhYmxlIGxpc3QuIEluIHRoYXQgY2FzZSwgeW91IGNhbiBzY2FsZSBhbGwgZGF0YS4gCgotIGBSdW5QQ0FgICYgYFJ1blVNQVBgOiBWaXN1YWxpc2F0aW9uIHRvb2xzLiBUaGVyZSBhcmUgYSBmZXcgdGhpbmdzIHRvIGNvbnNpZGVyIGluIHRoZSBVTUFQIGZ1bmN0aW9uLCBidXQgbW9zdCBpbXBvcnRhbnQgaXMgdGhlIGBkaW1zYCBhcmd1bWVudC4gV2UnbGwgY292ZXIgdGhpcyBiZWxvdy4gCgo8ZGl2PgoKIyMgTm9ybWFsaXNhdGlvbgoKQWdhaW4sIG5vcm1hbGlzYXRpb24gaXMgcXVpdGUgc2ltcGxlLiAKCmBgYHtyfQpkYXRhIDwtIE5vcm1hbGl6ZURhdGEoZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLAogICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1GKQpgYGAKCiMjIFZhcmlhYmxlIEZlYXR1cmVzCgpXZSdsbCBmaW5kIHRoZSB0b3AgdmFyaWFibGUgZmVhdHVyZXMuCgpgYGB7cn0KZGF0YSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhkYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAndnN0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gMjAwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1GKQpgYGAKCldlIGNhbiBvdXRwdXQvdmlzdWFsaXNlIHRoZW0gaW4gc2V2ZXJhbCB3YXlzLiAKCmBgYHtyfQpWYXJpYWJsZUZlYXR1cmVzKGRhdGEpICU+JSBoZWFkKDUwKQoKTGFiZWxQb2ludHMocGxvdCA9IFZhcmlhYmxlRmVhdHVyZVBsb3QoZGF0YSksIAogICAgICAgICAgICBwb2ludHMgPSBoZWFkKFZhcmlhYmxlRmVhdHVyZXMoZGF0YSksIDUwKSwgcmVwZWwgPSBUUlVFKQpgYGAKCiMjIFNjYWxlIERhdGEKClNjYWxpbmcgdGhlIGRhdGEgaXMgYWxzbyBlYXN5IQoKYGBge3J9CmRhdGEgPC0gU2NhbGVEYXRhKGRhdGEsIAogICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMoZGF0YSksICNpZiB5b3Ugd2FudCB0byBzY2FsZSBhbGwgZmVhdHVyZXMsIGNoYW5nZSB0aGlzIHRvIHJvd25hbWVzKGRhdGEpCiAgICAgICAgdmVyYm9zZT1GKQpgYGAKCiMjIFBDQSAmIFVNQVAKCkFsc28gdmVyeSBlYXN5IHdpdGggU2V1cmF0LiBXZSdsbCBkZXRlcm1pbmUgdGhlIG51bWJlciBvZiBQQ3MgdG8gdXNlIGZvciB0aGUgVU1BUCB1c2luZyBFbGJvd1Bsb3QKCmBgYHtyfQpkYXRhIDwtIFJ1blBDQShkYXRhLAogICAgICAgICAgICAgICBucGNzID0gNTAsCiAgICAgICAgICAgICAgIHZlcmJvc2U9RikKCkVsYm93UGxvdChkYXRhKQpgYGAKClRoZSBlbGJvdyBwbG90IGRyb3BzIG9mZiBkcmFzdGljYWxseSBhbmQgcGxhdGVhdXMgYXQgfjYsIG1lYW5pbmcgdGhlIHZhcmlhdGlvbiBqdXN0IGJlY29tZXMgbm9pc3kgYXJvdW5kIFBDNi1vbndhcmRzLiBTbyB3ZSdsbCBqdXN0IHVzZSB0aGUgZmlyc3QgNi4gCgpgYGB7cn0KZGF0YSA8LSBSdW5VTUFQKGRhdGEsIAogICAgICAgICAgICAgICAgZGltcz0xOjYsIAogICAgICAgICAgICAgICAgdmVyYm9zZT1GKQpgYGAKCjwvZGl2Pjxocj4KCiMgVmlzdWFsaXNhdGlvbnsudGFic2V0IC50YWJzZXQtZmFkZX0KClRoZXJlIGFyZSBzZXZlcmFsIHZpc3VhbGlzYXRpb25zIHdlIGNhbiBkbzoKCioqR3JhcGgqKjoKCi0gVU1BUCB2aXN1YWxpc2F0aW9uCgotIENsdXN0ZXIgYmFzZWQgZXhwcmVzc2lvbnMKCioqU3BhdGlhbCoqOgoKLSBHZW5lcyBvdmVybGFpZCBvbiB0aGUgdGlzc3VlCgotIE1vZHVsZSBzY29yZXMgb3ZlcmxhaWQgb24gdGhlIHRpc3N1ZQoKIyMgVU1BUHsudGFic2V0IC50YWJzZXQtZmFkZX0KCiMjIyBVTUFQIFZpc3VhbGlzYXRpb24KCldlJ2xsIGZpcnN0IGNsdXN0ZXIgdGhlIGRhdGEuCgpgYGB7cn0KCmRhdGEgPC0gZGF0YSAlPiUgCiAgRmluZE5laWdoYm9ycyhkaW1zID0gMTo2LCB2ZXJib3NlPUYpICU+JQogIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uPTEsIHZlcmJvc2U9RikKCmBgYAoKQW5kIG5vdyB3ZSBjYW4gY3JlYXRlIGEgVU1BUC4gCgpgYGB7cn0KcDE9VU1BUFBsb3QoZGF0YSwKICAgICAgICAgICAgbGFiZWw9VCwgbGFiZWwuYm94PVQpKwogIE5vTGVnZW5kKCkrCiAgTm9BeGVzKCkrCiAgZ2d0aXRsZSgiMTEgQ2x1c3RlcnMiKQpgYGAKCjxkZXRhaWxzPjxzdW1tYXJ5PioqT3BlbiB0byB2aWV3IG1vcmUgUGxvdHRpbmcgb3B0aW9ucyoqPC9zdW1tYXJ5PgoKSGVyZSBhcmUgc29tZSBhZGRpdGlvbmFsIGFyZ3VtZW50cyB0aGF0IGNoYW5nZSB0aGUgZGVmYXVsdCB2aXN1YWxpc2F0aW9ucy4gQ2hvcCBhbmQgY2hhbmdlIHRvIHNlZSBob3cgdGhleSB3b3JrOgoKYGBge3J9ClVNQVBQbG90KGRhdGEsIAogICAgICAgICBwdC5zaXplPTIsIAogICAgICAgICBsYWJlbD1ULCAKICAgICAgICAgbGFiZWwuc2l6ZT0xMCwKICAgICAgICAgbGFiZWwuYm94PVQsCiAgICAgICAgIGxhYmVsLmNvbG9yPSJ3aGl0ZSIsCiAgICAgICAgIHJlcGVsPVQpKwogIE5vTGVnZW5kKCkrCiAgTm9BeGVzKCkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiYmx1ZTQiLCAnYmx1ZTInLCAnbGlnaHRibHVlJywgJ2dyZWVuMicsICdncmVlbjQnLCdicm93bicsICdvcmFuZ2UnLCAncmVkJywgJ3JlZDMnLCAncmVkNCcsICdibGFjaycpKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoImJsdWU0IiwgJ2JsdWUyJywgJ2xpZ2h0Ymx1ZScsICdncmVlbjInLCAnZ3JlZW40JywnYnJvd24nLCAnb3JhbmdlJywgJ3JlZCcsICdyZWQzJywgJ3JlZDQnLCAnYmxhY2snKSkKYGBgCgo8L2RldGFpbHM+CgojIyMgRmVhdHVyZXBsb3RzCgpXZSBjYW4gdmlldyB0aGUgZ2VuZSBleHByZXNzaW9uIG9uIHRoZSBVTUFQIGl0c2VsZi4gCgpgYGB7cn0KcDI9RmVhdHVyZVBsb3QoZGF0YSwgCiAgICAgICAgICAgICJDRDNFIiwKICAgICAgICAgICAgb3JkZXI9VCwKICAgICAgICAgICAgcHQuc2l6ZT0yLCAKICAgICAgICAgICAgY29scyA9IGMoInllbGxvdzIiLCAnYmx1ZTMnKSkrTm9BeGVzKCkrTm9MZWdlbmQoKQpwMz1GZWF0dXJlUGxvdChkYXRhLCAKICAgICAgICAgICAgIkYxM0ExIiwKICAgICAgICAgICAgb3JkZXI9VCwKICAgICAgICAgICAgcHQuc2l6ZT0yLCAKICAgICAgICAgICAgY29scyA9IGMoInllbGxvdzIiLCAnYmx1ZTMnKSkrTm9BeGVzKCkrTm9MZWdlbmQoKQpwND1GZWF0dXJlUGxvdChkYXRhLCAKICAgICAgICAgICAgIkNPTDFBMSIsCiAgICAgICAgICAgIG9yZGVyPVQsCiAgICAgICAgICAgIHB0LnNpemU9MiwgCiAgICAgICAgICAgIGNvbHMgPSBjKCJ5ZWxsb3cyIiwgJ2JsdWUzJykpK05vQXhlcygpK05vTGVnZW5kKCkKcDU9RmVhdHVyZVBsb3QoZGF0YSwgCiAgICAgICAgICAgICJLUlQ1IiwKICAgICAgICAgICAgb3JkZXI9VCwKICAgICAgICAgICAgcHQuc2l6ZT0yLAogICAgICAgICAgICBtaW4uY3V0b2ZmID0gMywKICAgICAgICAgICAgY29scyA9IGMoInllbGxvdzIiLCAnYmx1ZTMnKSkrTm9BeGVzKCkrTm9MZWdlbmQoKQpwbG90X2dyaWQocDEsIHBsb3RfZ3JpZChwMixwMyxwNCxwNSwgbmNvbD0yKSkKYGBgCgojIyMgREUgYW5kIERvdFBsb3RzCgpBbmQgd2UgY2FuIHNpbXBseSBhc3Nlc3MgdGhlIGNsdXN0ZXJzIHRoZW1zZWx2ZXMgdXNpbmcgZGlmZmVyZW50aWFsIGFuYWx5c2lzIGFuZCBleHByZXNzaW9uIGdyYXBocy4gCgpgYGB7cn0KCgptYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKGRhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMSwjZGVmYXVsdCAtIGluY3JlYXNpbmcgc3BlZWRzIGl0IHVwLCBidXQgbWF5IG1pc3Mgd2Vha2VyIHNpZ25hbHMuCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnBjdCA9IDAuMDEsICNkZWZhdWx0IC0gY2FuIGJlIGluY3JlYXNlZCB0byBlbnN1cmUgdGhhdCB0aGUgbWluaW11bSBleHByZXNzaW9uIG9mIGdlbmVzIGFyZSBjb25zaWRlcmVkLiAKICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZGlmZi5wY3QgPSAtSW5mLCAjIHlvdSBjYW4gYWRqdXN0IHRoaXMgdG9vLiBJRiB5b3Ugd2FudCB0aGVyZSB0byBiZSBhdCBsZWFzdCBhIDEwJSBkaWZmZXJlbmNlIGluIHBlcmNlbnRhZ2Ugb2Ygc3BvdHMgZXhwcmVzc2luZyB0aGUgZ2VuZSwgeW91J2QgY2hhbmdlIHRoaXMgdG8gMC4xLiBDYW4gZW5zdXJlIHRoYXQgaXRzIG5vdCBvbmx5IHRoZSBsZXZlbCBvZiBleHByZXNzaW9uLCBidXQgdGhhdCB5b3UncmUgY2FwdHVyaW5nIGhpZ2hseSBleHByZXNzZWQgZ2VuZXMKICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPUYgI2NoYW5nZSB0aGlzIHRvIHRydWUgaWYgeW91IHdhbnQgdG8gdHJhY2sgdGhlIHNwZWVkIGF0IHdoaWNoIERFIGlzIGJlaW5nIGNhbGN1bGF0ZWQuIEl0IGlzIG9mZiBoZXJlIHRvIHNhdmUgaXQgb3V0cHV0dGluZyBpbnRvIHRoZSBkb2N1bWVudCAKICAgICAgICAgICAgICAgICAgICAgICAgICApIAp0b3AxMCA8LSBtYXJrZXJzICU+JSAKICBncm91cF9ieShjbHVzdGVyKSAlPiUgCiAgdG9wX24od3Q9YXZnX2xvZzJGQywgbj0xMCkKYGBgCgo8ZGV0YWlscz48c3VtbWFyeT5IZWF0bWFwPC9zdW1tYXJ5PgoKYGBge3J9CkRvSGVhdG1hcChBZ2dyZWdhdGVFeHByZXNzaW9uKFNjYWxlRGF0YShkYXRhLCByb3duYW1lcyhkYXRhKSwgdmVyYm9zZT1GKSwgcmV0dXJuLnNldXJhdCA9IFQsIHZlcmJvc2U9RiksCiAgICAgICAgICBmZWF0dXJlcyA9IHRvcDEwJGdlbmUsIAogICAgICAgICAgZHJhdy5saW5lcyA9IEYpCmBgYAoKPGhyPgoKPC9kZXRhaWxzPgoKPGRldGFpbHM+PHN1bW1hcnk+RG90UGxvdDwvc3VtbWFyeT4KCmBgYHtyfQpEb3RQbG90KGRhdGEsIAogICAgICAgIGZlYXR1cmVzID0gYygiQ0QzRSIsICJDTEVDMTBBIiwgIkNEMjA3IiwgIktSVDUiLCAiQ09MMUExIiksIAogICAgICAgIGNvbHM9YygiZ3JleSIsICJyZWQiKSkKYGBgCgo8L2RldGFpbHM+Cgo8ZGV0YWlscz48c3VtbWFyeT5WaW9saW48L3N1bW1hcnk+CgpgYGB7cn0KVmxuUGxvdChkYXRhLCAKICAgICAgICBmZWF0dXJlcyA9IGMoIkNEM0UiLCAiQ0xFQzEwQSIsICJDRDIwNyIsICJLUlQ1IiwgIkNPTDFBMSIpKQpWbG5QbG90KGRhdGEsIAogICAgICAgIGZlYXR1cmVzID0gYygiQ0QzRSIsICJDTEVDMTBBIiwgIkNEMjA3IiwgIktSVDUiLCAiQ09MMUExIiksIHN0YWNrPVQpCmBgYAoKPC9kZXRhaWxzPgoKIyMgU3BhdGlhbHsudGFic2V0IC50YWJzZXQtZmFkZX0KCldlIGNhbiBkbyBhIGZldyB0aGluZ3MgaGVyZS4gCgojIyMgQ2x1c3RlcnMgb3ZlcmxhaWQgc3BhdGlhbGx5CgpgYGB7cn0KU3BhdGlhbERpbVBsb3QoZGF0YSwgcHQuc2l6ZT0zKQpJbWFnZURpbVBsb3QoZGF0YSwgc2l6ZT0zKQpgYGAKCiMjIyBFeHByZXNzaW9ucyBvdmVybGFpZCBzcGF0aWFsbHkKCmBgYHtyfQpTcGF0aWFsRmVhdHVyZVBsb3QoZGF0YSwgCiAgICAgICAgICAgICAgICAgICBmZWF0dXJlcz0iQ0QzRSIsCiAgICAgICAgICAgICAgICAgICBwdC5zaXplPTMpCgpTcGF0aWFsRmVhdHVyZVBsb3QoZGF0YSwgCiAgICAgICAgICAgICAgICAgICBmZWF0dXJlcz0iQ0QzRSIsCiAgICAgICAgICAgICAgICAgICBwdC5zaXplPTMpKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19iKCkKCkltYWdlRmVhdHVyZVBsb3QoZGF0YSwgCiAgICAgICAgICAgICAgICAgICBmZWF0dXJlcz0iQ0QzRSIsCiAgICAgICAgICAgICAgICAgICBzaXplPTMpCmBgYAoKCgoKCgoKCgoKCg==