tissue_of_interest = "Liver"
library(here)
source("/restricted/projectnb/waxmanlab/kkarri/scRNAseq_data_integration/boilerplate.R")
#tiss = load_tissue_droplet(tissue_of_interest)
#library(scater)
library(dplyr)
library(Seurat)
library(cowplot)
#library(MAST)
########## function load_tissue_droplet############
metadata_chow <- read.csv("/net/waxman-server/mnt/data/waxmanlabvm_home/kkarri/NASH/data/Chow_metadata.csv", sep=",", header = TRUE)
colnames(metadata_chow)[1] <- "channel"
tissue_metadata_chow = filter(metadata_chow, tissue == tissue_of_interest)[,c('channel','tissue','subtissue','mouse.sex', 'mouse.id')]

raw.data <- Read10X("/net/waxman-server/mnt/data/waxmanlabvm_home/kkarri/NASH/data/Liver_Chow1/")
colnames(raw.data) <- lapply(colnames(raw.data), function(x) paste0(tissue_metadata_chow$channel[1],'_',x))
  meta.data = data.frame(row.names = colnames(raw.data))
  meta.data['channel'] = tissue_metadata_chow$channel[1]

    if (length(tissue_metadata_chow$channel) > 1){
    # Some tissues, like Thymus and Heart had only one channel
    for(i in 2:nrow(tissue_metadata_chow)){
subfolder = paste0("/net/waxman-server/mnt/data/waxmanlabvm_home/kkarri/NASH/data/",tissue_of_interest, '_', tissue_metadata_chow$channel[i])
      new.data <- Read10X(data.dir = subfolder)
      colnames(new.data) <- lapply(colnames(new.data), function(x) paste0(tissue_metadata_chow$channel[i],'_', x))
      new.metadata = data.frame(row.names = colnames(new.data))
      new.metadata['channel'] = tissue_metadata_chow$channel[i]

      raw.data = cbind(raw.data, new.data)
      meta.data = rbind(meta.data, new.metadata) }}
  
  
  rnames = row.names(meta.data)
  meta.data <- merge(meta.data, tissue_metadata_chow, sort = F)
  row.names(meta.data) <- rnames
   
  # Order the cells alphabetically to ensure consistency.
  ordered_cell_names = order(colnames(raw.data))
  raw.data = raw.data[,ordered_cell_names]
  meta.data = meta.data[ordered_cell_names,]
  # Find ERCC's, compute the percent ERCC, and drop them from the raw data.
  erccs <- grep(pattern = "^ERCC-", x = rownames(x = raw.data), value = TRUE)
  percent.ercc <- Matrix::colSums(raw.data[erccs, ])/Matrix::colSums(raw.data)
  ercc.index <- grep(pattern = "^ERCC-", x = rownames(x = raw.data), value = FALSE)
  raw.data <- raw.data[-ercc.index,]
  
  ncRNA.genes <- grep(pattern = "^ncRNA", x = rownames(x = raw.data), value = TRUE)
  percent.ncRNA <- Matrix::colSums(raw.data[ncRNA.genes, ])/Matrix::colSums(raw.data)
 
  # Create the Seurat object with all the data
  droplet <- CreateSeuratObject(raw.data)   # dropseq
  droplet <- AddMetaData(object = droplet, meta.data) 
  droplet@meta.data$tech <- "Chow"
#droplet <- SubsetData(droplet,subset.names = c("nGene", "nUMI"), low.thresholds = c(500, 1000))  # old version of seurat
droplet <-  subset(droplet, subset = nFeature_RNA > 500 & nCount_RNA > 1000)
droplet <- NormalizeData(droplet, verbose = FALSE)
droplet <- FindVariableFeatures(droplet, selection.method = "vst", nfeatures = 2000)
droplet$stim <- "Chow"

### run chow umap separatly
droplet <- ScaleData(droplet, verbose = FALSE)
droplet <- RunPCA(droplet, npcs = 30, verbose = FALSE)
droplet <- RunUMAP(droplet, reduction = "pca", dims = 1:25)
droplet <- FindNeighbors(droplet, reduction = "pca", dims = 1:25)
droplet <- FindClusters(droplet, resolution = 0.07 )   
chowp1<- UMAPPlot(droplet, reduction = "umap", group.by = "channel", label=TRUE, label.size=5)
chowp4 <- UMAPPlot(droplet, label=TRUE, label.size=6)
plot_grid(chowp1, chowp4) 

chow_samples <- droplet
chow_samples$label_channel <- paste(Idents(chow_samples), chow_samples$channel, sep = "_")
chow_samples$seurat_clusters <- Idents(chow_samples)
Idents(chow_samples) <- "label_channel_cluster"


### this is tranformation option for develpment SCtransform program #######
droplet <- SCTransform(droplet,verbose =TRUE)


####################### NASH #######################################
metadata_nash <- read.csv("/net/waxman-server/mnt/data/waxmanlabvm_home/kkarri/NASH/data/Nash_metadata.csv", sep=",", header = TRUE)
colnames(metadata_nash)[1] <- "channel"
tissue_metadata_nash = filter(metadata_nash, tissue == tissue_of_interest)[,c('channel','tissue','subtissue','mouse.sex', 'mouse.id')]

raw.data1 <- Read10X("/net/waxman-server/mnt/data/waxmanlabvm_home/kkarri/NASH/data/Liver-10X_Nash1/")
colnames(raw.data1) <- lapply(colnames(raw.data1), function(x) paste0(tissue_metadata_nash$channel[1],'_',x))
  meta.data1 = data.frame(row.names = colnames(raw.data1))
  meta.data1['channel'] = tissue_metadata_nash$channel[1]

    if (length(tissue_metadata_nash$channel) > 1){
    # Some tissues, like Thymus and Heart had only one channel
    for(i in 2:nrow(tissue_metadata_nash)){
subfolder = paste0("/net/waxman-server/mnt/data/waxmanlabvm_home/kkarri/NASH/data/",tissue_of_interest, '-', tissue_metadata_nash$channel[i])
      new.data1 <- Read10X(data.dir = subfolder)
      colnames(new.data1) <- lapply(colnames(new.data1), function(x) paste0(tissue_metadata_nash$channel[i],'_', x))
      new.metadata1 = data.frame(row.names = colnames(new.data1))
      new.metadata1['channel'] = tissue_metadata_nash$channel[i]
      raw.data1 = cbind(raw.data1, new.data1)
      meta.data1 = rbind(meta.data1, new.metadata1) }}
  
  rnames = row.names(meta.data1)
  meta.data1 <- merge(meta.data1, tissue_metadata_nash, sort = F)
  row.names(meta.data1) <- rnames
   
  # Order the cells alphabetically to ensure consistency.
  ordered_cell_names = order(colnames(raw.data1))
  raw.data1 = raw.data1[,ordered_cell_names]
  meta.data1 = meta.data1[ordered_cell_names,]
  # Find ERCC's, compute the percent ERCC, and drop them from the raw data.
  erccs <- grep(pattern = "^ERCC-", x = rownames(x = raw.data1), value = TRUE)
  percent.ercc <- Matrix::colSums(raw.data1[erccs, ])/Matrix::colSums(raw.data1)
  ercc.index <- grep(pattern = "^ERCC-", x = rownames(x = raw.data1), value = FALSE)
  raw.data1 <- raw.data1[-ercc.index,]
  ncRNA.genes <- grep(pattern = "^ncRNA", x = rownames(x = raw.data1), value = TRUE)
  percent.ncRNA <- Matrix::colSums(raw.data1[ncRNA.genes, ])/Matrix::colSums(raw.data1)
 
  # Create the Seurat object with all the data
  droplet1 <- CreateSeuratObject(raw.data1)   # dropseq
  droplet1 <- AddMetaData(object = droplet1, meta.data1) 
  droplet1 <- subset(droplet1, subset = nFeature_RNA > 500 & nCount_RNA > 1000)
  droplet1@meta.data$tech <- "Nash"
  droplet1 <- NormalizeData(droplet1, verbose = FALSE)
  droplet1 <- FindVariableFeatures(droplet1, selection.method = "vst", nfeatures = 2000)
  droplet1$stim <- "Nash"

  
droplet1 <- ScaleData(droplet1, verbose = FALSE)
droplet1 <- RunPCA(droplet1, npcs = 30, verbose = FALSE)
droplet1 <- RunUMAP(droplet1, reduction = "pca", dims = 1:25)
droplet1 <- FindNeighbors(droplet1, reduction = "pca", dims = 1:25)
droplet1 <- FindClusters(droplet1, resolution = 0.07 )   
nashp1<- UMAPPlot(droplet1, reduction = "umap", group.by = "stim", label=TRUE, label.size=5)
nashp4 <- UMAPPlot(droplet1, label=TRUE, label.size=6)
plot_grid(nashp1, nashp4)
  
  droplet <- SCTransform(droplet,verbose =TRUE)
  droplet1 <- SCTransform(droplet1,verbose =TRUE)


#######################3 option 2 standard workflow merging ################################
chow.list <- SplitObject(droplet, split.by = "channel")
#for (i in names(pbmc.list)) {
    #pbmc.list[[i]] <- SCTransform(pbmc.list[[i]], verbose = FALSE)
#}
nash.list <- SplitObject(droplet1, split.by = "channel")

chow_nash <- c(chow.list, nash.list)
features <- SelectIntegrationFeatures(object.list = chow_nash, nfeatures = 3000)
chow_nash <- PrepSCTIntegration(object.list = c(droplet,droplet1), anchor.features = features)
# This command returns dataset 5.  We can also specify multiple refs. (i.e. c(5,6))
reference_dataset <- which(names(chow_nash) == "Chow")
anchors <- FindIntegrationAnchors(object.list = chow_nash, normalization.method = "SCT", 
    anchor.features = features, reference = reference_dataset)
integrated <- IntegrateData(anchorset = anchors, normalization.method = "SCT")
DefaultAssay(integrated) <- "integrated"
integrated <- ScaleData(integrated, verbose = FALSE)
integrated <- RunPCA(object = integrated, npcs=30,verbose = FALSE)
integrated <- RunUMAP(object = integrated, dims = 1:25)
integrated <- FindNeighbors(integrated, reduction = "pca", dims = 1:25)
integrated <- FindClusters(integrated, resolution = 0.5 )   


##### label transfer appraoches #####33
for (i in 1:length(chow.list)) {
    chow.list[[i]] <- NormalizeData(chow.list[[i]], verbose = FALSE)
    chow.list[[i]] <- FindVariableFeatures(chow.list[[i]], selection.method = "vst", 
        nfeatures = 2000, verbose = FALSE)
}

ref.features <- SelectIntegrationFeatures(object.list = chow.list, nfeatures = 3000)

ref.anchors <- FindIntegrationAnchors(object.list = chow.list,  
    anchor.features =ref.features, verbose = TRUE)
ref.integrated <- IntegrateData(anchorset = ref.anchors, normalization.method = "LogNormalize", 
    verbose = FALSE)


DefaultAssay(ref.integrated) <- "integrated"
# Run the standard workflow for visualization and clustering
ref.integrated <- ScaleData(ref.integrated, verbose = FALSE)
ref.integrated <- RunPCA(ref.integrated, npcs = 30, verbose = FALSE)
ref.integrated <- RunUMAP(ref.integrated, reduction = "pca", dims = 1:30)
ref.integrated <- FindNeighbors(ref.integrated, reduction = "pca", dims = 1:30)
ref.integrated <- FindClusters(ref.integrated, resolution = 0.07 )   


p1 <- DimPlot(ref.integrated, reduction = "umap", group.by = "channel")
p2 <- DimPlot(ref.integrated, reduction = "umap",  label = TRUE, repel = TRUE) 
plot_grid(p1, p2)




transfer.anchors <- FindTransferAnchors(reference = ref.integrated, query =droplet1, dims = 1:30)
predictions <- TransferData(anchorset = transfer.anchors, refdata = ref.integrated$seurat_clusters,  dims = 1:30)
droplet1 <- AddMetaData(droplet1, metadata = predictions)
droplet1$prediction.match <- droplet1$predicted.id == droplet1$
table(pancreas.query$prediction.match)
                                                   
####### old apprach for integration without reference-based  t-SNE and Clustering ########################
anchors <- FindIntegrationAnchors(object.list = list(droplet, droplet1), dims = 1:50) # defeault anchor.feature=2000
#anchors <- FindIntegrationAnchors(object.list = list(lnc5998, droplet1), dims = 1:50, anchor.features = 3000)
combined <- IntegrateData(anchorset = anchors, dims = 1:50)    
DefaultAssay(combined) <- "integrated"
# Run the standard workflow for visualization and clustering
combined <- ScaleData(combined, verbose = FALSE)
combined <- RunPCA(combined, npcs = 30, verbose = FALSE)
                                                   
# t-SNE and Clustering
combined <- RunUMAP(combined, reduction = "pca", dims = 1:25)
combined <- FindNeighbors(combined, reduction = "pca", dims = 1:25)
combined <- FindClusters(combined, resolution = 0.07 )   
#combined <- RunTSNE(combined, reduction = "pca", dims = 1:20)
 # Visualization
p1 <- UMAPPlot(combined, reduction = "umap", group.by = "stim", label=TRUE, label.size=5)
p2 <- UMAPPlot(combined, reduction = "umap", group.by = "mouse.sex")
p3 <- UMAPPlot(combined, reduction = "umap", label = TRUE, label.size=3)
p4 <- UMAPPlot(combined, label=TRUE, label.size=6)
plot_grid(p1, p4) 
DimPlot(combined, reduction = "umap", split.by = "stim")   


## tsne based visualisation 
p5 <- DimPlot(combined, reduction = "tsne", group.by = "stim")
p6 <- DimPlot(combined, reduction = "tsne", group.by = "mouse.sex")
p7 <- DimPlot(combined, reduction = "tsne", label = TRUE)
p8 <- TSNEPlot(combined, label =T)
plot_grid(p6, p7,p8) 
DimPlot(combined, reduction = "tsne", split.by = "stim") 





 # New Visualization
p1 <- UMAPPlot(integrated, reduction = "umap", group.by = "stim", label=TRUE, label.size=5)
p2 <- UMAPPlot(integrated, reduction = "umap", group.by = "mouse.sex")
p3 <- UMAPPlot(integrated, reduction = "umap", label = TRUE, label.size=3)
p4 <- UMAPPlot(integrated, label=TRUE, label.size=6)
plot_grid(p1, p4) 
DimPlot(integrated, reduction = "umap", split.by = "stim")   


  

diffusion plt code

# Before running MDS, we first calculate a distance matrix between all pairs of cells.  Here we
# use a simple euclidean distance metric on all genes, using scale.data as input
d <- dist(t(GetAssayData(combined, slot = "scale.data")))
# Run the MDS procedure, k determines the number of dimensions
mds <- cmdscale(d = d, k = 2)
# cmdscale returns the cell embeddings, we first label the columns to ensure downstream
# consistency
colnames(mds) <- paste0("MDS_", 1:2)
# We will now store this as a custom dimensional reduction called 'mds'
combined[["mds"]] <- CreateDimReducObject(embeddings = mds, key = "MDS_", assay = DefaultAssay(combined))

# We can now use this as you would any other dimensional reduction in all downstream functions
DimPlot(combined, reduction = "mds", pt.size = 0.5)
genes_hep_main =c('Alb', 'Ttr', 'Apoa1', 'Serpina1c')
genes_hep = c('Alb', 'Ttr', 'Apoa1', 'Serpina1c',
                   'Cyp2e1', 'Glul', 'Oat', 'Gulo',
                   'Ass1', 'Hamp', 'Gstp1', 'Ubb',
                   'Cyp2f2', 'Pck1', 'Hal', 'Cdh1')
genes_endo = c('Pecam1', 'Nrp1', 'Kdr','Oit3')
genes_kuppfer = c( 'Clec4f', 'Cd68')
genes_nk = c('Il2rb', 'Nkg7', 'Cxcr6', 'Gzma')
genes_b = c('Cd79a', 'Cd79b')
genes_bec = c('Epcam', 'Krt19', 'Krt7')
genes_immune = 'Ptprc'
HSC = c("Dcn","Lama1","Nes")
Dividing = "Top2a"
Bplasma= "Jchain"
Mac= "Csf1r"
Chol="Sox9"

all_genes = c(genes_hep, genes_endo, genes_kuppfer, Mac,Chol,genes_nk, genes_b,Bplasma, genes_bec, genes_immune, HSC, Dividing)
genes_bec_b_immune  = c(genes_bec,genes_b,genes_immune)
genes_zones = c('Cyp2e1', 'Glul', 'Oat', 'Gulo',
              'Ass1', 'Hamp', 'Gstp1', 'Ubb',
              'Cyp2f2', 'Pck1', 'Hal', 'Cdh1')

receptor_KO <- c("ncRNA-inter-chr7-5998","Cyp2b10","Nr1i2","Nr1i3","Ppara","Pparg","Ppargc1b","Ppard")

cell <- c("Stab2","Csf1r","Cd3g","Ebf1","Irf8","Sox9","Apoc3","Top2a","Dcn")
DefaultAssay(droplet) <- "RNA"
#droplet <- NormalizeData(combined, verbose = TRUE, normalization.method = "RC", scale.factor = 1e6)
combined <- NormalizeData(combined, verbose = TRUE)


DotPlot(combined, features = all_genes)
FeaturePlot(combined, features = genes_hep_main, min.cutoff = "q9")
#hepatocytes 
subtissplot <- DotPlot(combined, features = c(genes_hep_main, genes_endo, genes_bec_b_immune, genes_kuppfer, genes_nk))
PC <- DotPlot(combined, features = c(genes_hep_main,genes_zones))
NPC <- DotPlot(combined, features = c(genes_endo,genes_kuppfer, genes_nk))
all <- DotPlot(combined, features=c(all_genes))

### coexpression plots####

f1 <- FeaturePlot(KO.cells, features = c('Cyp2b10','ncRNA-inter-chr7-5998'), reduction = "mds", order = TRUE,split.by = "stim", blend = TRUE,sort.cell = TRUE, max.cutoff = 0.5)


########## this is exact averaging formula ###############33
  x <- (AverageExpression(KO.cells, verbose = TRUE, assays = "RNA" ,slot="counts")$RNA)
   x["ncRNA-inter-chr7-5998",]
#                         G171B    G171C
#ncRNA-inter-chr7-5998 1.871795 1.091463
########
#Idents(combined) <- factor(Idents(combined), levels = c(0,1,12))
markers.to.plot <- c("Alb","ncRNA-inter-chr7-5998")
DotPlot(combined, features = rev(markers.to.plot), cols = c("blue", "red"), dot.scale = 8, 
    split.by = "stim") + RotatedAxis()

FeaturePlot(combined, features = c("Alb", "ncRNA-inter-chr7-5998","Cyp2b10","dSaCas9","KRAB","AAV8-mCherry"), split.by = "stim", max.cutoff = 3, cols = c("grey", "red"))


######################### vlnplot ##########################

plots <- VlnPlot(combined, features = c("Alb", "ncRNA-inter-chr7-5998","Cyp2b10","dSaCas9"), split.by = "stim", group.by = "seurat_clusters", pt.size = 0, combine = FALSE)
CombinePlots(plots = plots, ncol = 1)

plots <- VlnPlot(combined, features = c("Lhx4","Dtna","Fam189a1","Galnt16","Kalrn"), split.by = "stim", group.by = "seurat_clusters", pt.size = 0, combine = FALSE)
CombinePlots(plots = plots, ncol = 1)


#endothelial
DotPlot(combined, features = genes_endo)

#zones
zones <- DotPlot(combined, features = genes_zones)

f1 <- FeaturePlot(combined, features = c('Cyp2e1','Cyp2f2','Ass1'), min.cutoff = "q9", reduction = "tsne")

DimPlot(combined, label = TRUE)

save(combined, file="Seurat_smart-drop_integrated.Robj")


################# save raw counts from cluster #####################

Idents(combined) <- "stim"

### to avergae out the matrix from KO cells 

combined.raw.data.0.1 <- as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 0,idents = "stim")])
combined.raw.data.1 <- as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 0)])
combined.raw.data.2 <- as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 0)])

#combined.raw.data.[i] <- as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 1)])
#combined.raw.data.12 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 12)])
#combined.raw.data.1 <- as.matrix(GetAssayData(combined, slot = "counts"))
x <- AverageExpression(test.combined,assays = "RNA",add.ident = "stim", slot = "data",use.scale = FALSE, use.counts = FALSE)$RNA

#}######## CAR data 
avg.combined.cells <- (AverageExpression(combined, verbose = FALSE)$RNA) 
avg.combined.cells$gene <- rownames(avg.combined.cells)

CAR_FP <- FeaturePlot(combined, features = c('Cyp2b10','Nr1i3'), reduction = "umap", order = TRUE,split.by = "stim", blend = TRUE,sort.cell = TRUE, max.cutoff = 1, min.cutoff = 0, pt.size = 0.5, repel = TRUE)
CAR_DOT_NR <- DotPlot(combined, features = 'Nr1i3', col.min = 0)

Cyp2b10_FP <- FeaturePlot(combined, features = 'Cyp2b10', reduction = "umap", min.cutoff = 0)
########### tSNE #################################
combined <- NormalizeData(object = combined)
combined <- FindVariableFeatures(combined, selection.method = "vst", nfeatures = 2000)

KO.cells <- subset(combined, idents = c("0","1","12"))
Idents(KO.cells) <- "stim"



### to avergae out the matrix from KO cells 
raw.data.0 <- as.matrix(GetAssayData(combined, slot = c("counts","data"))[, WhichCells(combined, ident = 0)])
raw.data.1 <- as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 1)])
raw.data.2 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 2)])
raw.data.3 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 3)])
raw.data.4 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 4)])
raw.data.5 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 5)])
raw.data.6 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 6)])
raw.data.7 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 7)])
raw.data.8 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 8)])
raw.data.9 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 9)])
raw.data.10 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 10)])
raw.data.11 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 11)])
raw.data.12 <-as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 12)])


TPMcount0<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 0)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 0)]))

TPMcount1<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 1)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 1)]))

TPMcount2<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 2)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 2)]))

TPMcount3<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 3)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 3)]))

TPMcount4<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 4)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 4)]))

TPMcount5<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 5)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 5)]))

TPMcount6<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 6)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 6)]))

TPMcount7<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 7)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 7)]))

TPMcount8<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 8)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 8)]))

TPMcount9<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 9)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 9)]))

TPMcount10<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 10)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 10)]))

TPMcount11<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 11)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 11)]))

TPMcount12<- cbind(as.matrix(GetAssayData(combined, slot = "counts")[, WhichCells(combined, ident = 12)]), as.matrix(GetAssayData(combined, slot = "data")[, WhichCells(combined, ident = 12)]))




write.csv(TPMcount0, "CountResult/counts.TPMcount0.csv")
write.csv(TPMcount1, "CountResult/counts.TPMcount1.csv")
write.csv(TPMcount2, "CountResult/counts.TPMcount2.csv")
write.csv(TPMcount3, "CountResult/counts.TPMcount3.csv")
write.csv(TPMcount4, "CountResult/counts.TPMcount4.csv")
write.csv(TPMcount5, "CountResult/counts.TPMcount5.csv")
write.csv(TPMcount6, "CountResult/counts.TPMcount6.csv")
write.csv(TPMcount7, "CountResult/counts.TPMcount7.csv")
write.csv(TPMcount8, "CountResult/counts.TPMcount8.csv")
write.csv(TPMcount9, "CountResult/counts.TPMcount9.csv")
write.csv(TPMcount10, "CountResult/counts.TPMcount10.csv")
write.csv(TPMcount11, "CountResult/counts.TPMcount11.csv")
write.csv(TPMcount12, "CountResult/counts.TPMcount12.csv")



#avg.KO.cells <- log1p(AverageExpression(KO.cells, verbose = FALSE)$RNA)  #original code log transformed
avg.KO.cells <- (AverageExpression(KO.cells, verbose = FALSE)$RNA) 
avg.KO.cells$gene <- rownames(avg.KO.cells)

genes.to.label= ("ncRNA-inter-chr7-5998")
#genes.to.label = c("ISG15", "LY6E", "IFI6", "ISG20", "MX1", "IFIT2", "IFIT1", "CXCL10", "CCL8")
p1 <- ggplot(avg.KO.cells, aes(CTRL, STIM)) + geom_point() + ggtitle("CD4 Naive T Cells")
p1 <- LabelPoints(plot = p1, points = genes.to.label, repel = TRUE)
p2 <- ggplot(avg.cd14.mono, aes(CTRL, STIM)) + geom_point() + ggtitle("CD14 Monocytes")
p2 <- LabelPoints(plot = p2, points = genes.to.label, repel = TRUE)
plot_grid(p1, p2)
KO.cells <- RunUMAP(KO.cells, reduction = "pca", dims = 1:20 )
KO.cells <- FindNeighbors(KO.cells, reduction = "pca", dims = 1:20)
KO.cells <- FindClusters(KO.cells, resolution = 0.5 )   
KO.cells <- RunTSNE(KO.cells, reduction = "pca", dims = 1:20)
 

Hep.cells <- RunUMAP(Hep.cells, reduction = "pca", dims = 1:20 )
Hep.cells <- FindNeighbors(Hep.cells, reduction = "pca", dims = 1:20)
Hep.cells <- FindClusters(Hep.cells, resolution = 0.5 )   
Hep.cells <- RunTSNE(Hep.cells, reduction = "pca", dims = 1:20)
 

   
 # Visualization
p1 <- UMAPPlot(KO.cells, reduction = "umap", group.by = "stim")
p2 <- UMAPPlot(KO.cells, reduction = "umap", group.by = "mouse.sex")
p3 <- UMAPPlot(KO.cells, reduction = "umap", label = TRUE)
p4 <- UMAPPlot(KO.cells, label=TRUE)
plot_grid(p1,p4) 
DimPlot(KO.cells, reduction = "umap", split.by = "stim")   


#hepatocyte cells

p5 <- UMAPPlot(Hep.cells, reduction = "umap", group.by = "stim")
p6 <- UMAPPlot(Hep.cells, reduction = "umap", group.by = "mouse.sex")
p7 <- UMAPPlot(Hep.cells, reduction = "umap", label = TRUE)
p8 <- UMAPPlot(Hep.cells, label=TRUE)
plot_grid(p5,p8) 
DimPlot(KO.cells, reduction = "umap", split.by = "stim")   


raw.data.KO.0 <- as.matrix(GetAssayData(KO.cells, slot = "counts")[, WhichCells(KO.cells, ident = 0)])
raw.data.KO.1 <- as.matrix(GetAssayData(KO.cells, slot = "counts")[, WhichCells(KO.cells, ident = 1)])
raw.data.KO.2 <-as.matrix(GetAssayData(KO.cells, slot = "counts")[, WhichCells(KO.cells, ident = 2)])
raw.data.KO.3 <-as.matrix(GetAssayData(KO.cells, slot = "counts")[, WhichCells(KO.cells, ident = 3)])
raw.data.KO.4 <-as.matrix(GetAssayData(KO.cells, slot = "counts")[, WhichCells(KO.cells, ident = 4)])
raw.data.KO.5 <-as.matrix(GetAssayData(KO.cells, slot = "counts")[, WhichCells(KO.cells, ident = 5)])


write.csv(raw.data.KO.0, "CountResult/Markers/raw.data.KO.0")
write.csv(raw.data.KO.1, "CountResult/Markers/raw.data.KO.1")
write.csv(raw.data.KO.2, "CountResult/Markers/raw.data.KO.2")
write.csv(raw.data.KO.3, "CountResult/Markers/raw.data.KO.3")
write.csv(raw.data.KO.4, "CountResult/Markers/raw.data.KO.4")
write.csv(raw.data.KO.5, "CountResult/Markers/raw.data.KO.5")



f1 <- FeaturePlot(KO.cells, features = c('ncRNA-inter-chr7-5998'),  reduction = "umap", split.by = "stim")
plot_grid(f1,p1,p4) 

DefaultAssay(KO.cells) <- "RNA"
KO.cells <- NormalizeData(KO.cells, verbose = FALSE)

plots <- VlnPlot(KO.cells, features = c("Alb", "ncRNA-inter-chr7-5998","Cyp2b10","Cyp2e1","Cyp2f2"), split.by = "stim", group.by = "seurat_clusters", pt.size = 0, combine = FALSE)
CombinePlots(plots = plots, ncol = 1)

Three_five_six <- subset(KO.cells, idents = c("5","6"))

Three_five_six <- RunUMAP(Three_five_six, reduction = "pca", dims = 1:20 )
Three_five_six <- FindNeighbors(Three_five_six, reduction = "pca", dims = 1:20)
Three_five_six <- FindClusters(Three_five_six, resolution = 1 )   
Three_five_six <- RunTSNE(Three_five_six, reduction = "pca", dims = 1:20)

p1 <- UMAPPlot(Three_five_six, reduction = "umap", group.by = "stim")
p2 <- UMAPPlot(Three_five_six, reduction = "umap", group.by = "mouse.sex")
p3 <- UMAPPlot(Three_five_six, reduction = "umap", label = TRUE)
p4 <- UMAPPlot(Three_five_six, label=TRUE)
plot_grid(p1,p4) 
DimPlot(Three_five_six, reduction = "umap", split.by = "stim")   

f1 <- FeaturePlot(Three_five_six, features = c('ncRNA-inter-chr7-5998'),  reduction = "umap", split.by = "stim")


lnc5998 <- subset(combined, cells = lnc5998.cells, idents = "1")

lnc5998 <- RunUMAP(lnc5998, reduction = "pca", dims = 1:20 )
lnc5998 <- FindNeighbors(lnc5998, reduction = "pca", dims = 1:20)
lnc5998 <- FindClusters(lnc5998, resolution = 1 )   
lnc5998 <- RunTSNE(lnc5998, reduction = "pca", dims = 1:20)
    
 # Visualization
p1 <- UMAPPlot(lnc5998, reduction = "umap", group.by = "stim")
p2 <- UMAPPlot(lnc5998, reduction = "umap", group.by = "mouse.sex")
p3 <- UMAPPlot(lnc5998, reduction = "umap", label = TRUE)
p4 <- UMAPPlot(lnc5998, label=TRUE)
plot_grid(p1,p4) 
DimPlot(lnc5998, reduction = "umap", split.by = "stim")   

lnc5998.cells <- WhichCells(object = combined, expression = "ncRNA-inter-chr7-5998" > 1)
FeaturePlot(lnc5998, features = c("ncRNA-inter-chr7-5998"), split.by = "stim",  
+             cols = c("grey", "red"), cells = lnc5998.cells,min.cutoff = 0.5)


DefaultAssay(lnc5998) <- "RNA"
lnc5998 <- NormalizeData(lnc5998, verbose = FALSE)

plots <- VlnPlot(lnc5998, features = c("Alb", "ncRNA-inter-chr7-5998","Cyp2b10","Cyp2e1","Cyp2f2"), split.by = "stim", group.by = "seurat_clusters", pt.size = 0, combine = FALSE)
CombinePlots(plots = plots, ncol = 1)

 
d <- dist(t(GetAssayData(KO.cells, slot = "scale.data")))
# Run the MDS procedure, k determines the number of dimensions
mds <- cmdscale(d = d, k = 2)
# cmdscale returns the cell embeddings, we first label the columns to ensure downstream
# consistency
colnames(mds) <- paste0("MDS_", 1:2)
# We will now store this as a custom dimensional reduction called 'mds'
KO.cells[["mds"]] <- CreateDimReducObject(embeddings = mds, key = "MDS_", assay = DefaultAssay(KO.cells))

# We can now use this as you would any other dimensional reduction in all downstream functions
DimPlot(KO.cells, reduction = "mds", pt.size = 0.5)

Find differential markers

KO.cells$celltype.stim <- paste(Idents(KO.cells), KO.cells$stim, sep = "_")
KO.cells$celltype <- Idents(KO.cells)
Idents(KO.cells) <- "celltype.stim"
response3 <- FindMarkers(KO.cells, ident.1 = c("1_G171B","0_G171B","2_G171B"), ident.2 = c("1_G171C", "0_G171C","2_G171C"), verbose = TRUE, test.use = "MAST", logfc.threshold = FALSE,min.pct = FALSE)
head(response3, n = 15)


#DE1: Compare cluster 0+1+12 (that expressed lnc5998) with Other hepatocyte clusters (2+5+8)
#DE2: compare cluster 1 (showed major effects in the KD) vs Cluster 0 (that showed little KD)
#DE3: For KO.cells that formed five subcluster, compare clusters 3+4+5+1 vs 2+0
#DE4: for KO.cells that formed five clusters. Comapre cluster 4 vs. 2

DE0112.258 <- FindMarkers(combined, ident.1 = c("0","1","12" ), ident.2 = c("2","5","8"),verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DE0112.All <- FindMarkers(combined, ident.1 = c("0","1","12" ), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DE1.2 <- FindMarkers(combined, ident.1 = c("1" ), ident.2 = c("2"),verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DE1.5 <- FindMarkers(combined, ident.1 = c("1" ), ident.2 = c("5"),verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DE1.8 <- FindMarkers(combined, ident.1 = c("1" ), ident.2 = c("8"),verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)


DE2.5 <- FindMarkers(combined, ident.1 = c("2" ), ident.2 = c("5"),verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DE2.8 <- FindMarkers(combined, ident.1 = c("2" ), ident.2 = c("8"),verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DE5.8 <- FindMarkers(combined, ident.1 = c("5" ), ident.2 = c("8"),verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)


DEK4351.20 <- FindMarkers(KO.cells, ident.1 = c("3","4","5","1" ), ident.2 = c("2","0"),verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)

DEK4.3 <- FindMarkers(KO.cells, ident.1 = "4", ident.2 = "3",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK4.2 <- FindMarkers(KO.cells, ident.1 = "4", ident.2 = "2",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK4.0 <- FindMarkers(KO.cells, ident.1 = "4", ident.2 = "0",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK4.1 <- FindMarkers(KO.cells, ident.1 = "4", ident.2 = "1",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK4.5 <- FindMarkers(KO.cells, ident.1 = "4", ident.2 = "5",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)



##### comaprison between G171B vs G171C for KO.cell clusters of 0+1+12 ######33

DEK4_C.B <- FindMarkers(KO.cells, ident.1 = "4_G171C", ident.2 = "4_G171B",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK3_C.B <- FindMarkers(KO.cells, ident.1 = "3_G171C", ident.2 = "3_G171B",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK1_C.B <- FindMarkers(KO.cells, ident.1 = "1_G171C", ident.2 = "1_G171B",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK2_C.B <- FindMarkers(KO.cells, ident.1 = "2_G171C", ident.2 = "2_G171B",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK0_C.B <- FindMarkers(KO.cells, ident.1 = "0_G171C", ident.2 = "0_G171B",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
DEK5_C.B <- FindMarkers(KO.cells, ident.1 = "5_G171C", ident.2 = "5_G171B",verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)


################################### Write the results #########################
write.csv(DE0112.258, "CountResult/Markers/DE0112.258")
write.csv(DE0112.All, "CountResult/Markers/DE0112.All")
write.csv(DE1.2, "CountResult/Markers/DE1.2")
write.csv(DE1.5, "CountResult/Markers/DE1.5")
write.csv(DE1.8, "CountResult/Markers/DE1.8")
write.csv(DE2.5, "CountResult/Markers/DE2.5")
write.csv(DE2.8, "CountResult/Markers/DE2.8")
write.csv(DE5.8, "CountResult/Markers/DE5.8")

write.csv(DEK4351.20, "CountResult/Markers/DEK4351.20")
write.csv(DEK4.3, "CountResult/Markers/DEK4.3")
write.csv(DEK4.2, "CountResult/Markers/DEK4.2")
write.csv(DEK4.0, "CountResult/Markers/DEK4.0")
write.csv(DEK4.1, "CountResult/Markers/DEK4.1")
write.csv(DEK4.5, "CountResult/Markers/DEK4.5")


write.csv(DEK4_C.B, "CountResult/Markers/DEK4_C.B")
write.csv(DEK3_C.B, "CountResult/Markers/DEK3_C.B")
write.csv(DEK1_C.B, "CountResult/Markers/DEK1_C.B")
write.csv(DEK2_C.B, "CountResult/Markers/DEK2_C.B")
write.csv(DEK0_C.B, "CountResult/Markers/DEK0_C.B")
write.csv(DEK5_C.B, "CountResult/Markers/DEK5_C.B")




combined$celltype.stim <- paste(Idents(combined), combined$stim, sep = "_")
combined$celltype <- Idents(combined)
Idents(combined) <- "celltype.stim"


test.combined$celltype.stim <- paste(Idents(test.combined), test.combined$stim, sep = "_")
test.combined$celltype <- Idents(test.combined)
Idents(test.combined) <- "celltype.stim"




Combined_G171B_vs_G171C <- FindMarkers(combined, ident.1 = c("0_G171B","1_G171B","2_G171B","3_G171B","4_G171B","5_G171B","6_G171B","7_G171B","8_G171B","9_G171B","10_G171B","11_G171B","12_G171B" ), ident.2 = c("0_G171C","1_G171C","2_G171C","3_G171C","4_G171C","5_G171C","6_G171C","7_G171C","8_G171C","9_G171C","10_G171C","11_G171C","12_G171C" ), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)


Combined_G171C_vs_G171B_Clust1 <- FindMarkers(combined, ident.1 = c("1_G171C"), ident.2 = c("1_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust0 <- FindMarkers(combined, ident.1 = c("0_G171C"), ident.2 = c("0_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust12 <- FindMarkers(combined, ident.1 = c("12_G171C"), ident.2 = c("12_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust2 <- FindMarkers(combined, ident.1 = c("2_G171C"), ident.2 = c("2_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust5 <- FindMarkers(combined, ident.1 = c("5_G171C"), ident.2 = c("5_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust8 <- FindMarkers(combined, ident.1 = c("8_G171C"), ident.2 = c("8_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)


Combined_G171C_vs_G171B_Clust3 <- FindMarkers(combined, ident.1 = c("3_G171C"), ident.2 = c("3_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust4 <- FindMarkers(combined, ident.1 = c("4_G171C"), ident.2 = c("4_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust6 <- FindMarkers(combined, ident.1 = c("6_G171C"), ident.2 = c("6_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust7 <- FindMarkers(combined, ident.1 = c("7_G171C"), ident.2 = c("7_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust9 <- FindMarkers(combined, ident.1 = c("9_G171C"), ident.2 = c("9_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust10 <- FindMarkers(combined, ident.1 = c("10_G171C"), ident.2 = c("10_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)
Combined_G171C_vs_G171B_Clust11 <- FindMarkers(combined, ident.1 = c("11_G171C"), ident.2 = c("11_G171B"), verbose = TRUE, logfc.threshold = FALSE,min.pct = FALSE)





write.csv(Combined_G171C_vs_G171B_Clust1, "CountResult/Markers/Combined_G171C_vs_G171B_Clust1")
write.csv(Combined_G171C_vs_G171B_Clust0, "CountResult/Markers/Combined_G171C_vs_G171B_Clust0")
write.csv(Combined_G171C_vs_G171B_Clust12, "CountResult/Markers/Combined_G171C_vs_G171B_Clust12")
write.csv(Combined_G171C_vs_G171B_Clust2, "CountResult/Markers/Combined_G171C_vs_G171B_Clust2")
write.csv(Combined_G171C_vs_G171B_Clust5, "CountResult/Markers/Combined_G171C_vs_G171B_Clust5")
write.csv(Combined_G171C_vs_G171B_Clust8, "CountResult/Markers/Combined_G171C_vs_G171B_Clust8")

write.csv(Combined_G171C_vs_G171B_Clust3, "CountResult/Markers/Combined_G171C_vs_G171B_Clust3")
write.csv(Combined_G171C_vs_G171B_Clust4, "CountResult/Markers/Combined_G171C_vs_G171B_Clust4")
write.csv(Combined_G171C_vs_G171B_Clust6, "CountResult/Markers/Combined_G171C_vs_G171B_Clust6")
write.csv(Combined_G171C_vs_G171B_Clust7, "CountResult/Markers/Combined_G171C_vs_G171B_Clust7")
write.csv(Combined_G171C_vs_G171B_Clust9, "CountResult/Markers/Combined_G171C_vs_G171B_Clust9")
write.csv(Combined_G171C_vs_G171B_Clust10, "CountResult/Markers/Combined_G171C_vs_G171B_Clust10")
write.csv(Combined_G171C_vs_G171B_Clust11, "CountResult/Markers/Combined_G171C_vs_G171B_Clust11")





PC_vs_PP_G171B <- FindMarkers(KO.cells, ident.1 = c("4_G171B","3_G171B"), ident.2 = c("0_G171B","2_G171B"), verbose = TRUE, test.use = "MAST", logfc.threshold = FALSE,min.pct = FALSE)
head(PC_vs_PP_G171B, n = 15)


PC_vs_PP_G171C <- FindMarkers(KO.cells, ident.1 = c("4_G171C","3_G171C"), ident.2 = c("0_G171C","2_G171C"), verbose = TRUE, test.use = "MAST", logfc.threshold = FALSE,min.pct = FALSE)
head(PC_vs_PP_G171C, n = 15)


PC_vs_PP_G171BC <- FindMarkers(KO.cells, ident.1 = c("4_G171B","3_G171B","4_G171C","3_G171C"), ident.2 = c("0_G171B","2_G171B","0_G171C","2_G171C"), verbose = TRUE, test.use = "MAST", logfc.threshold = FALSE,min.pct = FALSE)
head(PC_vs_PP_G171C, n = 15)


lnc5998_KO_DE_1 <- FindMarkers(KO.cells, ident.1 = c("4_G171B","3_G171B","5_G171B"), ident.2 = c("4_G171C","3_G171C","5_G171C"), verbose = TRUE, test.use = "MAST", logfc.threshold = FALSE,min.pct = FALSE)
head(lnc5998_KO_DE, n = 15)


lnc5998_KO_DE_2 <- FindMarkers(KO.cells, ident.1 = c("4_G171C","3_G171C","5_G171C"), ident.2 =c("4_G171B","3_G171B","5_G171B") , verbose = TRUE, test.use = "MAST", logfc.threshold = FALSE,min.pct = FALSE)
head(lnc5998_KO_DE, n = 15)

cell.type.genes <- (PC_vs_PP_G171BC[1]) # Takes all the unique cell type specific genes
GOterms = topGOterms(fg.genes = cell.type.genes, bg.genes = rownames(KO.cells@assays$RNA@dataKO.cells@assays$RNA@data), organism = "Mouse")

cell.type.genes <- (PC_vs_PP_G171BC[1]) # Takes all the unique cell type specific genes
GOterms = topGOterms(fg.genes = rownames(cell.type.genes), bg.genes = rownames(KO.cells@assays$RNA@data), organism = "Mouse")
 

AvergeExpression2 <- function (object, assays = NULL, features = NULL, return.seurat = FALSE, 
          add.ident = NULL, slot = "data", use.scale = FALSE, use.counts = FALSE, 
          verbose = TRUE, ...) 
{
    
    fxn.average <- switch(EXPR = slot, data = function(x) {
        return(mean(x = x))
    }, mean)
    object.assays <- FilterObjects(object = object, classes.keep = "Assay")
    assays <- assays %||% object.assays
    ident.orig <- Idents(object = object)
    orig.levels <- levels(x = Idents(object = object))
    ident.new <- c()
    if (!all(assays %in% object.assays)) {
        assays <- assays[assays %in% object.assays]
        if (length(assays) == 0) {
            stop("None of the requested assays are present in the object")
        }
        else {
            warning("Requested assays that do not exist in object. Proceeding with existing assays only.")
        }
    }
    if (!is.null(x = add.ident)) {
        new.data <- FetchData(object = object, vars = add.ident)
        new.ident <- paste(Idents(object)[rownames(x = new.data)], 
                           new.data[, 1], sep = "_")
        Idents(object, cells = rownames(new.data)) <- new.ident
    }
    data.return <- list()
    for (i in 1:length(x = assays)) {
        data.use <- GetAssayData(object = object, assay = assays[i], 
                                 slot = slot)
        features.assay <- features
        if (length(x = intersect(x = features, y = rownames(x = data.use))) < 
            1) {
            features.assay <- rownames(x = data.use)
        }
        data.all <- data.frame(row.names = features.assay)
        for (j in levels(x = Idents(object))) {
            temp.cells <- WhichCells(object = object, idents = j)
            features.assay <- unique(x = intersect(x = features.assay, 
                                                   y = rownames(x = data.use)))
            if (length(x = temp.cells) == 1) {
                data.temp <- (data.use[features.assay, temp.cells])
                if (slot == "data") {
                    data.temp <-  data.temp
                }
            }
            if (length(x = temp.cells) > 1) {
                data.temp <- apply(X = data.use[features.assay, 
                                                temp.cells, drop = FALSE], MARGIN = 1, FUN = fxn.average)
            }
            data.all <- cbind(data.all, data.temp)
            colnames(x = data.all)[ncol(x = data.all)] <- j
            if (verbose) {
                message(paste("Finished averaging", assays[i], 
                              "for cluster", j))
            }
            if (i == 1) {
                ident.new <- c(ident.new, as.character(x = ident.orig[temp.cells[1]]))
            }
        }
        names(x = ident.new) <- levels(x = Idents(object))
        data.return[[i]] <- data.all
        names(x = data.return)[i] <- assays[[i]]
    }
    if (return.seurat) {
        toRet <- CreateSeuratObject(counts = data.return[[1]], 
                                    project = "Average", assay = names(x = data.return)[1], 
                                    ...)
        if (length(x = data.return) > 1) {
            for (i in 2:length(x = data.return)) {
                toRet[[names(x = data.return)[i]]] <- CreateAssayObject(counts = data.return[[i]])
            }
        }
        if (DefaultAssay(object = object) %in% names(x = data.return)) {
            DefaultAssay(object = toRet) <- DefaultAssay(object = object)
        }
        Idents(toRet, cells = colnames(x = toRet)) <- ident.new[colnames(x = toRet)]
        Idents(object = toRet) <- factor(x = Idents(object = toRet), 
                                         levels = as.character(x = orig.levels), ordered = TRUE)
        toRet <- NormalizeData(object = toRet, verbose = verbose)
        toRet <- ScaleData(object = toRet, verbose = verbose)
        return(toRet)
    }
    else {
        return(data.return)
    }
}

Find differential expression markers

combined.markers <- FindAllMarkers(object = combined, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)
combined.markers %>% group_by(cluster) %>% top_n(2, avg_logFC)

KO.markers <- FindAllMarkers(object = KO.cells, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)

response3 <- FindMarkers(combined, ident.1 = c("1_G171B","0_G171B","2_G171B"), ident.2 = c("1_G171C", "0_G171C","2_G171C"), verbose = TRUE, test.use = "MAST", logfc.threshold = FALSE,min.pct = FALSE)
head(response3, n = 15)

Visualize top genes in principal components

Later on (in FindClusters and TSNE) you will pick a number of principal components to use. This has the effect of keeping the major directions of variation in the data and, ideally, supressing noise. There is no correct answer to the number to use, but a decent rule of thumb is to go until the plot plateaus.

PCElbowPlot(object = tiss1)

Choose the number of principal components to use.

# Set number of principal components. 
n.pcs = 10

The clustering is performed based on a nearest neighbors graph. Cells that have similar expression will be joined together. The Louvain algorithm looks for groups of cells with high modularity–more connections within the group than between groups. The resolution parameter determines the scale. Higher resolution will give more clusters, lower resolution will give fewer.

For the top-level clustering, aim to under-cluster instead of over-cluster. It will be easy to subset groups and further analyze them below.

# Set resolution 
res.used <- 4
tiss1 <- FindClusters(object = tiss1, reduction.type = "pca", dims.use = 1:n.pcs, 
    resolution = res.used, print.output = 0, save.SNN = TRUE, force.recalc = TRUE)

We use TSNE solely to visualize the data.

# If cells are too spread out, you can raise the perplexity. If you have few cells, try a lower perplexity (but never less than 10).
tiss1 <- RunTSNE(object = tiss1, dims.use = 1:n.pcs, seed.use = 10, perplexity=30)
TSNEPlot(object = tiss1, do.label = T, pt.size = 1.2, label.size = 4)

Compare to previous annotations

previous_annotation = read.csv("/Users/kkarri/Documents/Lab/Single_cell_project/dropseq/Liver_droplet_annotation.csv", stringsAsFactors = FALSE)
cols = c('free_annotation', 'cell_ontology_class')
    for (col in cols){
      previous_col = paste0('previous_', col)
      tiss1@meta.data[, previous_col] <- "NA"
      tiss1@meta.data[as.character(previous_annotation$X), previous_col] <- previous_annotation[, col]
      print(table(tiss1@meta.data[, previous_col]))
      print(table(tiss1@meta.data[, previous_col], tiss@ident))
      
    }
    
tiss1 = compare_previous_annotation(tiss1, tissue_of_interest, "droplet")
TSNEPlot(object = tiss1, do.return = TRUE, group.by = "previous_cell_ontology_class")
table(tiss1@meta.data[, "previous_cell_ontology_class"], tiss@ident)
tiss1 = compare_previous_annotation(tiss1, tissue_of_interest, "droplet")
TSNEPlot(object = tiss1, do.return = TRUE, group.by = "previous_cell_ontology_class")
table(tiss1@meta.data[, "previous_cell_ontology_class"], tiss1@ident)
TSNEPlot(tiss1, group.by="mouse.sex")
TSNEPlot(tiss1, group.by="mouse.id")

Significant genes:

hepatocyte: Alb, Ttr, Apoa1, and Serpina1c pericentral: Cyp2e1, Glul, Oat, Gulo midlobular: Ass1, Hamp, Gstp1, Ubb periportal: Cyp2f2, Pck1, Hal, Cdh1

endothelial cells: Pecam1, Nrp1, Kdr+ and Oit3+ Kuppfer cells: Emr1, Clec4f, Cd68, Irf7 NK/NKT cells: Zap70, Il2rb, Nkg7, Cxcr6, Klr1c, Gzma B cells: Cd79a, Cd79b, Cd74 and Cd19 Immune cells: Ptprc

Dotplots let you see the intensity of exppression and the fraction of cells expressing for each of your genes of interest. The radius shows you the percent of cells in that cluster with at least one read sequenced from that gene. The color level indicates the average Z-score of gene expression for cells in that cluster, where the scaling is done over taken over all cells in the sample.

We have various immune cell types in the last cluster

Using the markers above, we can confidentaly label many of the clusters:

19: endothelial cells 20: bile duct epithelial cells 21: immune cells rest are hepatocytes

We will add those cell_ontology_classes to the dataset.

tiss1 <- StashIdent(object = tiss1, save.name = "cluster.ids")
cluster.ids <- c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
free_annotation <- c(
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  NA,
  "bile duct epithelial cells",
  "endothelial cell of hepatic sinusoid",
  NA
  )
cell_ontology_class <- c(
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "hepatocyte",
  "duct epithelial cell",
  "endothelial cell of hepatic sinusoid",
  "hepatocyte")
tiss1 = stash_annotations(tiss1, cluster.ids, free_annotation, cell_ontology_class)

Checking for batch effects

Color by metadata, like plate barcode, to check for batch effects.

TSNEPlot(object = tiss1, do.return = TRUE, group.by = "channel")
TSNEPlot(object = tiss1, do.return = TRUE, group.by = "free_annotation")

Subcluster

Let’s drill down on the hepatocytes.

subtiss1 = SubsetData(tiss1, ident.use = c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,20))
subtiss1 <- subtiss1 %>% ScaleData() %>%
  FindVariableGenes(do.plot = FALSE, x.high.cutoff = Inf, y.cutoff = 0.5) %>%
  RunPCA(do.print = FALSE)
PCHeatmap(object = subtiss1, pc.use = 1:3, cells.use = 20, do.balanced = TRUE, label.columns = FALSE, num.genes = 8)
PCElbowPlot(subtiss1)
sub.n.pcs = 8
sub.res.use = 0.5
subtiss1 <- subtiss1 %>% FindClusters(reduction.type = "pca", dims.use = 1:sub.n.pcs,
    resolution = sub.res.use, print.output = 0, save.SNN = TRUE, force.recalc = TRUE) %>%
    RunTSNE(dims.use = 1:sub.n.pcs, seed.use = 10, perplexity=8)
TSNEPlot(object = subtiss1, do.label = T, pt.size = 1, label.size = 4)
BuildClusterTree(subtiss1)

From these genes, it appears that the clusters represent:

0: midlobular male 1: pericentral female 2: periportal female 3: periportal male 4: midlobular male 5: pericentral male 6: midlobular female 7: midlobular female

The multitude of clusters of each type correspond mostly to individual animals/sexes.

table(FetchData(subtiss1, c('mouse.sex','ident')) %>% droplevels())
sub.cluster.ids <- c(0, 1, 2, 3, 4, 5, 6, 7)
sub.free_annotation <- c("periportal female", "midlobular male", "pericentral female", "periportal male", "midlobular male", "pericentral male", "midlobular female", "midlobular female")
sub.cell_ontology_class <- c("hepatocyte", "hepatocyte", "hepatocyte", "hepatocyte", "hepatocyte", "hepatocyte", "hepatocyte", "hepatocyte")
subtiss1 = stash_annotations(subtiss1, sub.cluster.ids, sub.free_annotation, sub.cell_ontology_class)
tiss1 = stash_subtiss_in_tiss(tiss1, subtiss1)

Liver zonation markers

genes_zones = c('Cyp2e1', 'Glul', 'Oat', 'Gulo',
              'Ass1', 'Hamp', 'Gstp1', 'Ubb',
              'Cyp2f2', 'Pck1', 'Hal', 'Cdh1')

FeaturePlot(subtiss1,c(genes_zones),cols.use = c("grey", "red"), pt.size = 1, nCol = 4)

DotPlot(subtiss1,c(genes_zones), plot.legend = T, col.max = 2.5, do.return = T) + coord_flip()


TSNEPlot(object = subtiss1, do.label = T, pt.size = 1, label.size = 4, group.by="free_annotation")

TSNEPlot(object = tiss1, do.label = T, pt.size = 1, label.size = 4, group.by="free_annotation")

Find cluster markers for lncRNAs


MIN_LOGFOLD_CHANGE = 1 # set to minimum required average log fold change in gene expression.
MIN_PCT_CELLS_EXPR_GENE = 0.1

all.markers = FindAllMarkers(tiss1,
                             min.pct = MIN_PCT_CELLS_EXPR_GENE,
                             logfc.threshold = MIN_LOGFOLD_CHANGE,
                             only.pos = TRUE,
                             test.use="bimod") # likelihood ratio test
lnc_all_markers <- grep(pattern = "^ncRNA", x= rownames(all.markers), value = TRUE)
lnc_all_markers

#[1] "ncRNA_inter_chr10_92081" "ncRNA_intra_chr16_13383" "ncRNA_inter_chr17_13605" "ncRNA_inter_chr14_11815"
#[5] "ncRNA_inter_chr18_14344"

FeaturePlot(subtiss1,c(lnc_all_markers),cols.use = c("grey", "red"), pt.size = 1, nCol = 4)

######################### lncRNA markers- CELL TYPE MARKER ############
markers.hep <- FindMarkers(object = tiss1, ident.1 = c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,20), ident.2 = c(18,19),only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)
lnc_markers_hep <- grep(pattern = "^ncRNA", x= rownames(markers.hep), value = TRUE)
lnc_markers_hep
FeaturePlot(tiss1,c(lnc_markers_hep),cols.use = c("grey", "red"), pt.size = 1, nCol = 4)
DotPlot(tiss1,lnc_markers_hep, plot.legend = T, col.max = 2.5, do.return = T) + coord_flip()
#[1] "ncRNA_as_chr11_9423"     "ncRNA_as_chr7_6166"      "ncRNA_inter_chr4_3295"   "ncRNA_inter_chr17_14026"
#[5] "ncRNA_inter_chr3_2915"   "ncRNA_inter_chr5_4547"   "ncRNA_inter_chr15_12684"


markers.hep.MAST <- FindMarkers(object = tiss1, ident.1 = c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,20), ident.2 = c(18,19),only.pos = TRUE, test.use = "MAST")
lnc_markers_hep_MAST_TABLE <- subset(markers.hep.MAST, grepl("^ncRNA", rownames(markers.hep.MAST)))
lnc_markers_hep_MAST <- grep(pattern = "^ncRNA", x= rownames(markers.hep.MAST), value = TRUE)
lnc_markers_hep_MAST



markers.endo <- FindMarkers(object = tiss1, ident.1 = c(18,19),  only.pos = TRUE, min.pct = 0.25, thresh.use = 0.5)
lnc_markers_endo <- grep(pattern = "^ncRNA", x= rownames(markers.endo), value = TRUE)
lnc_markers_endo
FeaturePlot(tiss1,c(lnc_markers_endo),cols.use = c("grey", "red"), pt.size = 1, nCol = 4)
DotPlot(tiss1,lnc_markers_endo, plot.legend = T, col.max = 2.5, do.return = T) + coord_flip()


#"ncRNA_inter_chr15_12770", "ncRNA_inter_chr12_10817", "ncRNA_as_chr13_11451",


markers.endo.MAST <- FindMarkers(object = tiss1, ident.1 = 19, test.use = "MAST" ,only.pos = TRUE)
lnc_markers_endo_MAST_TABLE <- subset(markers.endo.MAST, grepl("^ncRNA", rownames(markers.endo.MAST)))
lnc_markers_endo_MAST <- grep(pattern = "^ncRNA", x= rownames(markers.endo.MAST), value = TRUE)
lnc_markers_endo_MAST


################## lncRNA expression ########################3

# "ncRNA_inter_chr17_13605" , "ncRNA_intra_chr16_13383"

########## Periporal markers- zonation markers ############
markers.pc <- FindMarkers(object = subtiss1, ident.1 = c(2,5), 
                              only.pos = FALSE, min.pct = 0.001, thresh.use = 0.001, test.use = "bimod" )

markers.pc.MAST <- FindMarkers(object = subtiss1, ident.1 = c(2,5), ident.2 = c(0,3), test.use = "MAST" ,only.pos = TRUE)
lnc_markers_pc <- subset(markers.pc, grepl("^ncRNA", rownames(markers.pc)))
lnc_markers_pc <- grep(pattern = "^ncRNA", x= rownames(markers.pc), value = TRUE)
lnc_markers_pc 

markers.pc.MAST <- FindMarkers(object = subtiss1, ident.1 = c(2,5), ident.2 = c(0,3), test.use = "MAST" ,only.pos = TRUE)
lnc_markers_pc_MAST <- subset(markers.pc.MAST, grepl("^ncRNA", rownames(markers.pc.MAST)))
lnc_markers_pc_MAST <- grep(pattern = "^ncRNA", x= rownames(markers.pc.MAST), value = TRUE)
lnc_markers_pc_MAST

DotPlot(tiss1, lnc_markers_pc, plot.legend = T, col.max = 2.5, do.return = T, group.by="free_annotation") + coord_flip()
FeaturePlot(subtiss1,c(lnc_markers_pc),cols.use = c("grey", "red"), pt.size = 1, nCol = 4)


############################### midlobular genes #############

markers.mid <- FindMarkers(object = subtiss1, ident.1 = c(1,4,6,7), 
                              only.pos = FALSE, min.pct = 0.001, thresh.use = 0.05)

lnc_markers_mid <- subset(markers.mid, grepl("^ncRNA", rownames(markers.mid)))
lnc_markers_mid <- grep(pattern = "^ncRNA", x= rownames(markers.mid), value = TRUE)
lnc_markers_mid
DotPlot(tiss1, lnc_markers_mid, plot.legend = T, col.max = 2.5, do.return = T, group.by="free_annotation") + coord_flip()


FeaturePlot(subtiss1,c(lnc_markers_mid),cols.use = c("grey", "red"), pt.size = 1, nCol = 4)



markers.mid.MAST <- FindMarkers(object = subtiss1, ident.1 = c(1,4,6,7),test.use = "MAST",only.pos = TRUE )

lnc_markers_mid_MAST_TABLE <- subset(markers.mid.MAST, grepl("^ncRNA", rownames(markers.mid.MAST)))
lnc_markers_mid_MAST <- grep(pattern = "^ncRNA", x= rownames(markers.mid.MAST), value = TRUE)
lnc_markers_mid_MAST


#####3 periportalmarker genes############3

markers.pp <- FindMarkers(object = subtiss1, ident.1 = c(0,3),
                              only.pos = FALSE, min.pct = 0.001, thresh.use = 0.05)


lnc_markers_pp <- subset(markers.pp, grepl("^ncRNA", rownames(markers.pp)))
lnc_markers_pp <- grep(pattern = "^ncRNA", x= rownames(markers.pp), value = TRUE)
lnc_markers_pp

markers.pp.MAST <- FindMarkers(object = subtiss1, ident.1 = c(0,3), ident.2 = c(2,5),test.use = "MAST",only.pos = TRUE )

lnc_markers_pp_MAST_TABLE <- subset(markers.pp.MAST, grepl("^ncRNA", rownames(markers.pp.MAST)))
lnc_markers_pp_MAST <- grep(pattern = "^ncRNA", x= rownames(markers.pp.MAST), value = TRUE)
lnc_markers_pp_MAST

FeaturePlot(subtiss1,c(lnc_markers_pp),cols.use = c("grey", "red"), pt.size = 1, nCol = 4, max.cutoff = 1)
DotPlot(tiss1, c(lnc_markers_pp,"Cyp2e1","Cyp2f2"), plot.legend = T, col.max = 2.5, do.return = T, group.by= "free_annotation") + coord_flip()


################## amle and female specific ############################

markers.female <- FindMarkers(object = subtiss1, ident.1 = c(0,2,6,7),
                              only.pos = TRUE, min.pct = 0.1, logfc.threshold = 1)

lnc_markers_female <- subset(markers.female, grepl("^ncRNA", rownames(markers.female)))
lnc_markers_female <- grep(pattern = "^ncRNA", x= rownames(markers.female), value = TRUE)
lnc_markers_female

FeaturePlot(subtiss1,c(lnc_markers_female),cols.use = c("grey", "red"), pt.size = 1, nCol = 4, max.cutoff = 1)
DotPlot(tiss1, c(lnc_markers_female,"Cyp2e1","Cyp2f2"), plot.legend = T, col.max = 2.5, do.return = T, group.by= "free_annotation") + coord_flip()



markers.male <- FindMarkers(object = subtiss1, ident.1 = c(1,3,4,5),
                              only.pos = TRUE, min.pct = 0.001, thresh.use = 0.05)

lnc_markers_male <- subset(markers.male, grepl("^ncRNA", rownames(markers.male)))
lnc_markers_male <- grep(pattern = "^ncRNA", x= rownames(markers.male), value = TRUE)
lnc_markers_male

FeaturePlot(subtiss1,c(lnc_markers_male),cols.use = c("grey", "red"), pt.size = 1, nCol = 4, max.cutoff = 1)
DotPlot(tiss1, c(lnc_markers_male,"Cyp2e1","Cyp2f2"), plot.legend = T, col.max = 2.5, do.return = T, group.by= "free_annotation") + coord_flip()


############################ Female zonate specific genes ###################################

markers.pericentral.female <- FindMarkers(object = tiss1, ident.1 = c(6,11,14,20), test.use = "MAST",
                            only.pos = TRUE, min.pct = 0.1, ident.2 = c(2,3,15,12,13,8,5,16), logfc.threshold = 1)

markers.periportal.female <- FindMarkers(object = tiss1, ident.1 = c(2,3,15),
                            only.pos = TRUE, min.pct = 0.1, ident.2 = c(6,11,14,20,12,13,8,5,16), logfc.threshold = 1)


markers.pericentral.male <- FindMarkers(object = tiss1, ident.1 = c(13,12), test.use = "MAST",
                            only.pos = TRUE, min.pct = 0.1, ident.2 = c(2,3,15,8,5,16,6,11,14,20), logfc.threshold = 1)


markers.periportal.male <- FindMarkers(object = tiss1, ident.1 = c(8,5,16), test.use = "MAST",
                            only.pos = TRUE, min.pct = 0.1, ident.2 = c(2,3,15,13,12,6,11,14,20), logfc.threshold = 1)




############### xeno-lncs CAR?RXR ##################

FeaturePlot(tiss1,c("ncRNA_inter_chr15_12684","ncRNA_inter_chr8_7430","ncRNA_inter_chr7_6222"),cols.use = c("grey", "red"), pt.size = 1, nCol = 4, max.cutoff = 1)






#######################################################################

markers.endo.2 <- FindMarkers(object = seurat_drop, logfc.threshold = 2,ident.1 = "Endothelial", 
                              only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)

lnc.endo.2 <- grep(pattern = "^ncRNA", x= rownames(markers.endo.2), value = TRUE)
lnc.endo.2

Zonated lncRNAs

pp_zontaed <- c('ncRNA_inter_chr14_12016','ncRNA_as_chr19_15090','ncRNA_inter_chr10_9351','ncRNA_inter_chr16_13170',
'ncRNA_inter_chr3_2697','ncRNA_inter_chr1_274','ncRNA_as_chr6_5518','ncRNA_inter_chr14_12066','ncRNA_intra_chr12_10871',
'ncRNA_inter_chr16_13510','ncRNA_inter_chr3_2314','ncRNA_inter_chr10_9264,'ncRNA_inter_chr9_8122')

Checking for batch effects

Color by metadata, like plate barcode, to check for batch effects.

TSNEPlot(object = subtiss1, do.return = TRUE, group.by = "mouse.sex")

Final coloring

Color by cell ontology class on the original TSNE.

TSNEPlot(object = tiss1, do.return = TRUE, group.by = "cell_ontology_class")

Save the Robject for later

filename = here('00_data_ingest', '04_tiss1ue_robj_generated', 
                     paste0("droplet_", tiss1ue_of_interest, "refinedcells_seurat_tiss1.Robj"))
print(filename)
save(tiss1, file=filename)
# To reload a saved object
filename = here('00_data_ingest', '04_tiss1ue_robj_generated',
                      paste0("droplet_", tissue_of_interest, "seurat_smartdrop-integrated-8272019.Robj"))
load(file=filename)

Export the final metadata

save_annotation_csv(tiss1, tiss1ue_of_interest, "droplet")
LS0tCiB0aXRsZTogIkxpdmVyIERyb3BsZXQtIFJhdyBkYXRhIE5vdGVib29rIgogb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCgoKYGBge3J9CnRpc3N1ZV9vZl9pbnRlcmVzdCA9ICJMaXZlciIKbGlicmFyeShoZXJlKQpzb3VyY2UoIi9yZXN0cmljdGVkL3Byb2plY3RuYi93YXhtYW5sYWIva2thcnJpL3NjUk5Bc2VxX2RhdGFfaW50ZWdyYXRpb24vYm9pbGVycGxhdGUuUiIpCiN0aXNzID0gbG9hZF90aXNzdWVfZHJvcGxldCh0aXNzdWVfb2ZfaW50ZXJlc3QpCiNsaWJyYXJ5KHNjYXRlcikKbGlicmFyeShkcGx5cikKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoY293cGxvdCkKI2xpYnJhcnkoTUFTVCkKIyMjIyMjIyMjIyBmdW5jdGlvbiBsb2FkX3Rpc3N1ZV9kcm9wbGV0IyMjIyMjIyMjIyMjCm1ldGFkYXRhX2Nob3cgPC0gcmVhZC5jc3YoIi9uZXQvd2F4bWFuLXNlcnZlci9tbnQvZGF0YS93YXhtYW5sYWJ2bV9ob21lL2trYXJyaS9OQVNIL2RhdGEvQ2hvd19tZXRhZGF0YS5jc3YiLCBzZXA9IiwiLCBoZWFkZXIgPSBUUlVFKQpjb2xuYW1lcyhtZXRhZGF0YV9jaG93KVsxXSA8LSAiY2hhbm5lbCIKdGlzc3VlX21ldGFkYXRhX2Nob3cgPSBmaWx0ZXIobWV0YWRhdGFfY2hvdywgdGlzc3VlID09IHRpc3N1ZV9vZl9pbnRlcmVzdClbLGMoJ2NoYW5uZWwnLCd0aXNzdWUnLCdzdWJ0aXNzdWUnLCdtb3VzZS5zZXgnLCAnbW91c2UuaWQnKV0KCnJhdy5kYXRhIDwtIFJlYWQxMFgoIi9uZXQvd2F4bWFuLXNlcnZlci9tbnQvZGF0YS93YXhtYW5sYWJ2bV9ob21lL2trYXJyaS9OQVNIL2RhdGEvTGl2ZXJfQ2hvdzEvIikKY29sbmFtZXMocmF3LmRhdGEpIDwtIGxhcHBseShjb2xuYW1lcyhyYXcuZGF0YSksIGZ1bmN0aW9uKHgpIHBhc3RlMCh0aXNzdWVfbWV0YWRhdGFfY2hvdyRjaGFubmVsWzFdLCdfJyx4KSkKICBtZXRhLmRhdGEgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKHJhdy5kYXRhKSkKICBtZXRhLmRhdGFbJ2NoYW5uZWwnXSA9IHRpc3N1ZV9tZXRhZGF0YV9jaG93JGNoYW5uZWxbMV0KCiAgICBpZiAobGVuZ3RoKHRpc3N1ZV9tZXRhZGF0YV9jaG93JGNoYW5uZWwpID4gMSl7CiAgICAjIFNvbWUgdGlzc3VlcywgbGlrZSBUaHltdXMgYW5kIEhlYXJ0IGhhZCBvbmx5IG9uZSBjaGFubmVsCiAgICBmb3IoaSBpbiAyOm5yb3codGlzc3VlX21ldGFkYXRhX2Nob3cpKXsKc3ViZm9sZGVyID0gcGFzdGUwKCIvbmV0L3dheG1hbi1zZXJ2ZXIvbW50L2RhdGEvd2F4bWFubGFidm1faG9tZS9ra2FycmkvTkFTSC9kYXRhLyIsdGlzc3VlX29mX2ludGVyZXN0LCAnXycsIHRpc3N1ZV9tZXRhZGF0YV9jaG93JGNoYW5uZWxbaV0pCiAgICAgIG5ldy5kYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSBzdWJmb2xkZXIpCiAgICAgIGNvbG5hbWVzKG5ldy5kYXRhKSA8LSBsYXBwbHkoY29sbmFtZXMobmV3LmRhdGEpLCBmdW5jdGlvbih4KSBwYXN0ZTAodGlzc3VlX21ldGFkYXRhX2Nob3ckY2hhbm5lbFtpXSwnXycsIHgpKQogICAgICBuZXcubWV0YWRhdGEgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKG5ldy5kYXRhKSkKICAgICAgbmV3Lm1ldGFkYXRhWydjaGFubmVsJ10gPSB0aXNzdWVfbWV0YWRhdGFfY2hvdyRjaGFubmVsW2ldCgogICAgICByYXcuZGF0YSA9IGNiaW5kKHJhdy5kYXRhLCBuZXcuZGF0YSkKICAgICAgbWV0YS5kYXRhID0gcmJpbmQobWV0YS5kYXRhLCBuZXcubWV0YWRhdGEpIH19CiAgCiAgCiAgcm5hbWVzID0gcm93Lm5hbWVzKG1ldGEuZGF0YSkKICBtZXRhLmRhdGEgPC0gbWVyZ2UobWV0YS5kYXRhLCB0aXNzdWVfbWV0YWRhdGFfY2hvdywgc29ydCA9IEYpCiAgcm93Lm5hbWVzKG1ldGEuZGF0YSkgPC0gcm5hbWVzCiAgIAogICMgT3JkZXIgdGhlIGNlbGxzIGFscGhhYmV0aWNhbGx5IHRvIGVuc3VyZSBjb25zaXN0ZW5jeS4KICBvcmRlcmVkX2NlbGxfbmFtZXMgPSBvcmRlcihjb2xuYW1lcyhyYXcuZGF0YSkpCiAgcmF3LmRhdGEgPSByYXcuZGF0YVssb3JkZXJlZF9jZWxsX25hbWVzXQogIG1ldGEuZGF0YSA9IG1ldGEuZGF0YVtvcmRlcmVkX2NlbGxfbmFtZXMsXQogICMgRmluZCBFUkNDJ3MsIGNvbXB1dGUgdGhlIHBlcmNlbnQgRVJDQywgYW5kIGRyb3AgdGhlbSBmcm9tIHRoZSByYXcgZGF0YS4KICBlcmNjcyA8LSBncmVwKHBhdHRlcm4gPSAiXkVSQ0MtIiwgeCA9IHJvd25hbWVzKHggPSByYXcuZGF0YSksIHZhbHVlID0gVFJVRSkKICBwZXJjZW50LmVyY2MgPC0gTWF0cml4Ojpjb2xTdW1zKHJhdy5kYXRhW2VyY2NzLCBdKS9NYXRyaXg6OmNvbFN1bXMocmF3LmRhdGEpCiAgZXJjYy5pbmRleCA8LSBncmVwKHBhdHRlcm4gPSAiXkVSQ0MtIiwgeCA9IHJvd25hbWVzKHggPSByYXcuZGF0YSksIHZhbHVlID0gRkFMU0UpCiAgcmF3LmRhdGEgPC0gcmF3LmRhdGFbLWVyY2MuaW5kZXgsXQogIAogIG5jUk5BLmdlbmVzIDwtIGdyZXAocGF0dGVybiA9ICJebmNSTkEiLCB4ID0gcm93bmFtZXMoeCA9IHJhdy5kYXRhKSwgdmFsdWUgPSBUUlVFKQogIHBlcmNlbnQubmNSTkEgPC0gTWF0cml4Ojpjb2xTdW1zKHJhdy5kYXRhW25jUk5BLmdlbmVzLCBdKS9NYXRyaXg6OmNvbFN1bXMocmF3LmRhdGEpCiAKICAjIENyZWF0ZSB0aGUgU2V1cmF0IG9iamVjdCB3aXRoIGFsbCB0aGUgZGF0YQogIGRyb3BsZXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KHJhdy5kYXRhKSAgICMgZHJvcHNlcQogIGRyb3BsZXQgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gZHJvcGxldCwgbWV0YS5kYXRhKSAKICBkcm9wbGV0QG1ldGEuZGF0YSR0ZWNoIDwtICJDaG93IgojZHJvcGxldCA8LSBTdWJzZXREYXRhKGRyb3BsZXQsc3Vic2V0Lm5hbWVzID0gYygibkdlbmUiLCAiblVNSSIpLCBsb3cudGhyZXNob2xkcyA9IGMoNTAwLCAxMDAwKSkgICMgb2xkIHZlcnNpb24gb2Ygc2V1cmF0CmRyb3BsZXQgPC0gIHN1YnNldChkcm9wbGV0LCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiA1MDAgJiBuQ291bnRfUk5BID4gMTAwMCkKZHJvcGxldCA8LSBOb3JtYWxpemVEYXRhKGRyb3BsZXQsIHZlcmJvc2UgPSBGQUxTRSkKZHJvcGxldCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhkcm9wbGV0LCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDApCmRyb3BsZXQkc3RpbSA8LSAiQ2hvdyIKCiMjIyBydW4gY2hvdyB1bWFwIHNlcGFyYXRseQpkcm9wbGV0IDwtIFNjYWxlRGF0YShkcm9wbGV0LCB2ZXJib3NlID0gRkFMU0UpCmRyb3BsZXQgPC0gUnVuUENBKGRyb3BsZXQsIG5wY3MgPSAzMCwgdmVyYm9zZSA9IEZBTFNFKQpkcm9wbGV0IDwtIFJ1blVNQVAoZHJvcGxldCwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjI1KQpkcm9wbGV0IDwtIEZpbmROZWlnaGJvcnMoZHJvcGxldCwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjI1KQpkcm9wbGV0IDwtIEZpbmRDbHVzdGVycyhkcm9wbGV0LCByZXNvbHV0aW9uID0gMC4wNyApICAgCmNob3dwMTwtIFVNQVBQbG90KGRyb3BsZXQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiY2hhbm5lbCIsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemU9NSkKY2hvd3A0IDwtIFVNQVBQbG90KGRyb3BsZXQsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemU9NikKcGxvdF9ncmlkKGNob3dwMSwgY2hvd3A0KSAKCmNob3dfc2FtcGxlcyA8LSBkcm9wbGV0CmNob3dfc2FtcGxlcyRsYWJlbF9jaGFubmVsIDwtIHBhc3RlKElkZW50cyhjaG93X3NhbXBsZXMpLCBjaG93X3NhbXBsZXMkY2hhbm5lbCwgc2VwID0gIl8iKQpjaG93X3NhbXBsZXMkc2V1cmF0X2NsdXN0ZXJzIDwtIElkZW50cyhjaG93X3NhbXBsZXMpCklkZW50cyhjaG93X3NhbXBsZXMpIDwtICJsYWJlbF9jaGFubmVsX2NsdXN0ZXIiCgoKIyMjIHRoaXMgaXMgdHJhbmZvcm1hdGlvbiBvcHRpb24gZm9yIGRldmVscG1lbnQgU0N0cmFuc2Zvcm0gcHJvZ3JhbSAjIyMjIyMjCmRyb3BsZXQgPC0gU0NUcmFuc2Zvcm0oZHJvcGxldCx2ZXJib3NlID1UUlVFKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIE5BU0ggIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm1ldGFkYXRhX25hc2ggPC0gcmVhZC5jc3YoIi9uZXQvd2F4bWFuLXNlcnZlci9tbnQvZGF0YS93YXhtYW5sYWJ2bV9ob21lL2trYXJyaS9OQVNIL2RhdGEvTmFzaF9tZXRhZGF0YS5jc3YiLCBzZXA9IiwiLCBoZWFkZXIgPSBUUlVFKQpjb2xuYW1lcyhtZXRhZGF0YV9uYXNoKVsxXSA8LSAiY2hhbm5lbCIKdGlzc3VlX21ldGFkYXRhX25hc2ggPSBmaWx0ZXIobWV0YWRhdGFfbmFzaCwgdGlzc3VlID09IHRpc3N1ZV9vZl9pbnRlcmVzdClbLGMoJ2NoYW5uZWwnLCd0aXNzdWUnLCdzdWJ0aXNzdWUnLCdtb3VzZS5zZXgnLCAnbW91c2UuaWQnKV0KCnJhdy5kYXRhMSA8LSBSZWFkMTBYKCIvbmV0L3dheG1hbi1zZXJ2ZXIvbW50L2RhdGEvd2F4bWFubGFidm1faG9tZS9ra2FycmkvTkFTSC9kYXRhL0xpdmVyLTEwWF9OYXNoMS8iKQpjb2xuYW1lcyhyYXcuZGF0YTEpIDwtIGxhcHBseShjb2xuYW1lcyhyYXcuZGF0YTEpLCBmdW5jdGlvbih4KSBwYXN0ZTAodGlzc3VlX21ldGFkYXRhX25hc2gkY2hhbm5lbFsxXSwnXycseCkpCiAgbWV0YS5kYXRhMSA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXMocmF3LmRhdGExKSkKICBtZXRhLmRhdGExWydjaGFubmVsJ10gPSB0aXNzdWVfbWV0YWRhdGFfbmFzaCRjaGFubmVsWzFdCgogICAgaWYgKGxlbmd0aCh0aXNzdWVfbWV0YWRhdGFfbmFzaCRjaGFubmVsKSA+IDEpewogICAgIyBTb21lIHRpc3N1ZXMsIGxpa2UgVGh5bXVzIGFuZCBIZWFydCBoYWQgb25seSBvbmUgY2hhbm5lbAogICAgZm9yKGkgaW4gMjpucm93KHRpc3N1ZV9tZXRhZGF0YV9uYXNoKSl7CnN1YmZvbGRlciA9IHBhc3RlMCgiL25ldC93YXhtYW4tc2VydmVyL21udC9kYXRhL3dheG1hbmxhYnZtX2hvbWUva2thcnJpL05BU0gvZGF0YS8iLHRpc3N1ZV9vZl9pbnRlcmVzdCwgJy0nLCB0aXNzdWVfbWV0YWRhdGFfbmFzaCRjaGFubmVsW2ldKQogICAgICBuZXcuZGF0YTEgPC0gUmVhZDEwWChkYXRhLmRpciA9IHN1YmZvbGRlcikKICAgICAgY29sbmFtZXMobmV3LmRhdGExKSA8LSBsYXBwbHkoY29sbmFtZXMobmV3LmRhdGExKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHRpc3N1ZV9tZXRhZGF0YV9uYXNoJGNoYW5uZWxbaV0sJ18nLCB4KSkKICAgICAgbmV3Lm1ldGFkYXRhMSA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXMobmV3LmRhdGExKSkKICAgICAgbmV3Lm1ldGFkYXRhMVsnY2hhbm5lbCddID0gdGlzc3VlX21ldGFkYXRhX25hc2gkY2hhbm5lbFtpXQogICAgICByYXcuZGF0YTEgPSBjYmluZChyYXcuZGF0YTEsIG5ldy5kYXRhMSkKICAgICAgbWV0YS5kYXRhMSA9IHJiaW5kKG1ldGEuZGF0YTEsIG5ldy5tZXRhZGF0YTEpIH19CiAgCiAgcm5hbWVzID0gcm93Lm5hbWVzKG1ldGEuZGF0YTEpCiAgbWV0YS5kYXRhMSA8LSBtZXJnZShtZXRhLmRhdGExLCB0aXNzdWVfbWV0YWRhdGFfbmFzaCwgc29ydCA9IEYpCiAgcm93Lm5hbWVzKG1ldGEuZGF0YTEpIDwtIHJuYW1lcwogICAKICAjIE9yZGVyIHRoZSBjZWxscyBhbHBoYWJldGljYWxseSB0byBlbnN1cmUgY29uc2lzdGVuY3kuCiAgb3JkZXJlZF9jZWxsX25hbWVzID0gb3JkZXIoY29sbmFtZXMocmF3LmRhdGExKSkKICByYXcuZGF0YTEgPSByYXcuZGF0YTFbLG9yZGVyZWRfY2VsbF9uYW1lc10KICBtZXRhLmRhdGExID0gbWV0YS5kYXRhMVtvcmRlcmVkX2NlbGxfbmFtZXMsXQogICMgRmluZCBFUkNDJ3MsIGNvbXB1dGUgdGhlIHBlcmNlbnQgRVJDQywgYW5kIGRyb3AgdGhlbSBmcm9tIHRoZSByYXcgZGF0YS4KICBlcmNjcyA8LSBncmVwKHBhdHRlcm4gPSAiXkVSQ0MtIiwgeCA9IHJvd25hbWVzKHggPSByYXcuZGF0YTEpLCB2YWx1ZSA9IFRSVUUpCiAgcGVyY2VudC5lcmNjIDwtIE1hdHJpeDo6Y29sU3VtcyhyYXcuZGF0YTFbZXJjY3MsIF0pL01hdHJpeDo6Y29sU3VtcyhyYXcuZGF0YTEpCiAgZXJjYy5pbmRleCA8LSBncmVwKHBhdHRlcm4gPSAiXkVSQ0MtIiwgeCA9IHJvd25hbWVzKHggPSByYXcuZGF0YTEpLCB2YWx1ZSA9IEZBTFNFKQogIHJhdy5kYXRhMSA8LSByYXcuZGF0YTFbLWVyY2MuaW5kZXgsXQogIG5jUk5BLmdlbmVzIDwtIGdyZXAocGF0dGVybiA9ICJebmNSTkEiLCB4ID0gcm93bmFtZXMoeCA9IHJhdy5kYXRhMSksIHZhbHVlID0gVFJVRSkKICBwZXJjZW50Lm5jUk5BIDwtIE1hdHJpeDo6Y29sU3VtcyhyYXcuZGF0YTFbbmNSTkEuZ2VuZXMsIF0pL01hdHJpeDo6Y29sU3VtcyhyYXcuZGF0YTEpCiAKICAjIENyZWF0ZSB0aGUgU2V1cmF0IG9iamVjdCB3aXRoIGFsbCB0aGUgZGF0YQogIGRyb3BsZXQxIDwtIENyZWF0ZVNldXJhdE9iamVjdChyYXcuZGF0YTEpICAgIyBkcm9wc2VxCiAgZHJvcGxldDEgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gZHJvcGxldDEsIG1ldGEuZGF0YTEpIAogIGRyb3BsZXQxIDwtIHN1YnNldChkcm9wbGV0MSwgc3Vic2V0ID0gbkZlYXR1cmVfUk5BID4gNTAwICYgbkNvdW50X1JOQSA+IDEwMDApCiAgZHJvcGxldDFAbWV0YS5kYXRhJHRlY2ggPC0gIk5hc2giCiAgZHJvcGxldDEgPC0gTm9ybWFsaXplRGF0YShkcm9wbGV0MSwgdmVyYm9zZSA9IEZBTFNFKQogIGRyb3BsZXQxIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKGRyb3BsZXQxLCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDApCiAgZHJvcGxldDEkc3RpbSA8LSAiTmFzaCIKCiAgCmRyb3BsZXQxIDwtIFNjYWxlRGF0YShkcm9wbGV0MSwgdmVyYm9zZSA9IEZBTFNFKQpkcm9wbGV0MSA8LSBSdW5QQ0EoZHJvcGxldDEsIG5wY3MgPSAzMCwgdmVyYm9zZSA9IEZBTFNFKQpkcm9wbGV0MSA8LSBSdW5VTUFQKGRyb3BsZXQxLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjUpCmRyb3BsZXQxIDwtIEZpbmROZWlnaGJvcnMoZHJvcGxldDEsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyNSkKZHJvcGxldDEgPC0gRmluZENsdXN0ZXJzKGRyb3BsZXQxLCByZXNvbHV0aW9uID0gMC4wNyApICAgCm5hc2hwMTwtIFVNQVBQbG90KGRyb3BsZXQxLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInN0aW0iLCBsYWJlbD1UUlVFLCBsYWJlbC5zaXplPTUpCm5hc2hwNCA8LSBVTUFQUGxvdChkcm9wbGV0MSwgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZT02KQpwbG90X2dyaWQobmFzaHAxLCBuYXNocDQpCiAgCiAgZHJvcGxldCA8LSBTQ1RyYW5zZm9ybShkcm9wbGV0LHZlcmJvc2UgPVRSVUUpCiAgZHJvcGxldDEgPC0gU0NUcmFuc2Zvcm0oZHJvcGxldDEsdmVyYm9zZSA9VFJVRSkKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIzMgb3B0aW9uIDIgc3RhbmRhcmQgd29ya2Zsb3cgbWVyZ2luZyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpjaG93Lmxpc3QgPC0gU3BsaXRPYmplY3QoZHJvcGxldCwgc3BsaXQuYnkgPSAiY2hhbm5lbCIpCiNmb3IgKGkgaW4gbmFtZXMocGJtYy5saXN0KSkgewogICAgI3BibWMubGlzdFtbaV1dIDwtIFNDVHJhbnNmb3JtKHBibWMubGlzdFtbaV1dLCB2ZXJib3NlID0gRkFMU0UpCiN9Cm5hc2gubGlzdCA8LSBTcGxpdE9iamVjdChkcm9wbGV0MSwgc3BsaXQuYnkgPSAiY2hhbm5lbCIpCgpjaG93X25hc2ggPC0gYyhjaG93Lmxpc3QsIG5hc2gubGlzdCkKZmVhdHVyZXMgPC0gU2VsZWN0SW50ZWdyYXRpb25GZWF0dXJlcyhvYmplY3QubGlzdCA9IGNob3dfbmFzaCwgbmZlYXR1cmVzID0gMzAwMCkKY2hvd19uYXNoIDwtIFByZXBTQ1RJbnRlZ3JhdGlvbihvYmplY3QubGlzdCA9IGMoZHJvcGxldCxkcm9wbGV0MSksIGFuY2hvci5mZWF0dXJlcyA9IGZlYXR1cmVzKQojIFRoaXMgY29tbWFuZCByZXR1cm5zIGRhdGFzZXQgNS4gIFdlIGNhbiBhbHNvIHNwZWNpZnkgbXVsdGlwbGUgcmVmcy4gKGkuZS4gYyg1LDYpKQpyZWZlcmVuY2VfZGF0YXNldCA8LSB3aGljaChuYW1lcyhjaG93X25hc2gpID09ICJDaG93IikKYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gY2hvd19uYXNoLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiLCAKICAgIGFuY2hvci5mZWF0dXJlcyA9IGZlYXR1cmVzLCByZWZlcmVuY2UgPSByZWZlcmVuY2VfZGF0YXNldCkKaW50ZWdyYXRlZCA8LSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIpCkRlZmF1bHRBc3NheShpbnRlZ3JhdGVkKSA8LSAiaW50ZWdyYXRlZCIKaW50ZWdyYXRlZCA8LSBTY2FsZURhdGEoaW50ZWdyYXRlZCwgdmVyYm9zZSA9IEZBTFNFKQppbnRlZ3JhdGVkIDwtIFJ1blBDQShvYmplY3QgPSBpbnRlZ3JhdGVkLCBucGNzPTMwLHZlcmJvc2UgPSBGQUxTRSkKaW50ZWdyYXRlZCA8LSBSdW5VTUFQKG9iamVjdCA9IGludGVncmF0ZWQsIGRpbXMgPSAxOjI1KQppbnRlZ3JhdGVkIDwtIEZpbmROZWlnaGJvcnMoaW50ZWdyYXRlZCwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjI1KQppbnRlZ3JhdGVkIDwtIEZpbmRDbHVzdGVycyhpbnRlZ3JhdGVkLCByZXNvbHV0aW9uID0gMC41ICkgICAKCgojIyMjIyBsYWJlbCB0cmFuc2ZlciBhcHByYW9jaGVzICMjIyMjMzMKZm9yIChpIGluIDE6bGVuZ3RoKGNob3cubGlzdCkpIHsKICAgIGNob3cubGlzdFtbaV1dIDwtIE5vcm1hbGl6ZURhdGEoY2hvdy5saXN0W1tpXV0sIHZlcmJvc2UgPSBGQUxTRSkKICAgIGNob3cubGlzdFtbaV1dIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKGNob3cubGlzdFtbaV1dLCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIAogICAgICAgIG5mZWF0dXJlcyA9IDIwMDAsIHZlcmJvc2UgPSBGQUxTRSkKfQoKcmVmLmZlYXR1cmVzIDwtIFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMob2JqZWN0Lmxpc3QgPSBjaG93Lmxpc3QsIG5mZWF0dXJlcyA9IDMwMDApCgpyZWYuYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gY2hvdy5saXN0LCAgCiAgICBhbmNob3IuZmVhdHVyZXMgPXJlZi5mZWF0dXJlcywgdmVyYm9zZSA9IFRSVUUpCnJlZi5pbnRlZ3JhdGVkIDwtIEludGVncmF0ZURhdGEoYW5jaG9yc2V0ID0gcmVmLmFuY2hvcnMsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIAogICAgdmVyYm9zZSA9IEZBTFNFKQoKCkRlZmF1bHRBc3NheShyZWYuaW50ZWdyYXRlZCkgPC0gImludGVncmF0ZWQiCiMgUnVuIHRoZSBzdGFuZGFyZCB3b3JrZmxvdyBmb3IgdmlzdWFsaXphdGlvbiBhbmQgY2x1c3RlcmluZwpyZWYuaW50ZWdyYXRlZCA8LSBTY2FsZURhdGEocmVmLmludGVncmF0ZWQsIHZlcmJvc2UgPSBGQUxTRSkKcmVmLmludGVncmF0ZWQgPC0gUnVuUENBKHJlZi5pbnRlZ3JhdGVkLCBucGNzID0gMzAsIHZlcmJvc2UgPSBGQUxTRSkKcmVmLmludGVncmF0ZWQgPC0gUnVuVU1BUChyZWYuaW50ZWdyYXRlZCwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjMwKQpyZWYuaW50ZWdyYXRlZCA8LSBGaW5kTmVpZ2hib3JzKHJlZi5pbnRlZ3JhdGVkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MzApCnJlZi5pbnRlZ3JhdGVkIDwtIEZpbmRDbHVzdGVycyhyZWYuaW50ZWdyYXRlZCwgcmVzb2x1dGlvbiA9IDAuMDcgKSAgIAoKCnAxIDwtIERpbVBsb3QocmVmLmludGVncmF0ZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiY2hhbm5lbCIpCnAyIDwtIERpbVBsb3QocmVmLmludGVncmF0ZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUUlVFKSAKcGxvdF9ncmlkKHAxLCBwMikKCgoKCnRyYW5zZmVyLmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSByZWYuaW50ZWdyYXRlZCwgcXVlcnkgPWRyb3BsZXQxLCBkaW1zID0gMTozMCkKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IHRyYW5zZmVyLmFuY2hvcnMsIHJlZmRhdGEgPSByZWYuaW50ZWdyYXRlZCRzZXVyYXRfY2x1c3RlcnMsICBkaW1zID0gMTozMCkKZHJvcGxldDEgPC0gQWRkTWV0YURhdGEoZHJvcGxldDEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCmRyb3BsZXQxJHByZWRpY3Rpb24ubWF0Y2ggPC0gZHJvcGxldDEkcHJlZGljdGVkLmlkID09IGRyb3BsZXQxJAp0YWJsZShwYW5jcmVhcy5xdWVyeSRwcmVkaWN0aW9uLm1hdGNoKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKIyMjIyMjIyBvbGQgYXBwcmFjaCBmb3IgaW50ZWdyYXRpb24gd2l0aG91dCByZWZlcmVuY2UtYmFzZWQgIHQtU05FIGFuZCBDbHVzdGVyaW5nICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwphbmNob3JzIDwtIEZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMob2JqZWN0Lmxpc3QgPSBsaXN0KGRyb3BsZXQsIGRyb3BsZXQxKSwgZGltcyA9IDE6NTApICMgZGVmZWF1bHQgYW5jaG9yLmZlYXR1cmU9MjAwMAojYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gbGlzdChsbmM1OTk4LCBkcm9wbGV0MSksIGRpbXMgPSAxOjUwLCBhbmNob3IuZmVhdHVyZXMgPSAzMDAwKQpjb21iaW5lZCA8LSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIGRpbXMgPSAxOjUwKSAgICAKRGVmYXVsdEFzc2F5KGNvbWJpbmVkKSA8LSAiaW50ZWdyYXRlZCIKIyBSdW4gdGhlIHN0YW5kYXJkIHdvcmtmbG93IGZvciB2aXN1YWxpemF0aW9uIGFuZCBjbHVzdGVyaW5nCmNvbWJpbmVkIDwtIFNjYWxlRGF0YShjb21iaW5lZCwgdmVyYm9zZSA9IEZBTFNFKQpjb21iaW5lZCA8LSBSdW5QQ0EoY29tYmluZWQsIG5wY3MgPSAzMCwgdmVyYm9zZSA9IEZBTFNFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKIyB0LVNORSBhbmQgQ2x1c3RlcmluZwpjb21iaW5lZCA8LSBSdW5VTUFQKGNvbWJpbmVkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjUpCmNvbWJpbmVkIDwtIEZpbmROZWlnaGJvcnMoY29tYmluZWQsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyNSkKY29tYmluZWQgPC0gRmluZENsdXN0ZXJzKGNvbWJpbmVkLCByZXNvbHV0aW9uID0gMC4wNyApICAgCiNjb21iaW5lZCA8LSBSdW5UU05FKGNvbWJpbmVkLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjApCiAjIFZpc3VhbGl6YXRpb24KcDEgPC0gVU1BUFBsb3QoY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAic3RpbSIsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemU9NSkKcDIgPC0gVU1BUFBsb3QoY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAibW91c2Uuc2V4IikKcDMgPC0gVU1BUFBsb3QoY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplPTMpCnA0IDwtIFVNQVBQbG90KGNvbWJpbmVkLCBsYWJlbD1UUlVFLCBsYWJlbC5zaXplPTYpCnBsb3RfZ3JpZChwMSwgcDQpIApEaW1QbG90KGNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIHNwbGl0LmJ5ID0gInN0aW0iKSAgIAoKCiMjIHRzbmUgYmFzZWQgdmlzdWFsaXNhdGlvbiAKcDUgPC0gRGltUGxvdChjb21iaW5lZCwgcmVkdWN0aW9uID0gInRzbmUiLCBncm91cC5ieSA9ICJzdGltIikKcDYgPC0gRGltUGxvdChjb21iaW5lZCwgcmVkdWN0aW9uID0gInRzbmUiLCBncm91cC5ieSA9ICJtb3VzZS5zZXgiKQpwNyA8LSBEaW1QbG90KGNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidHNuZSIsIGxhYmVsID0gVFJVRSkKcDggPC0gVFNORVBsb3QoY29tYmluZWQsIGxhYmVsID1UKQpwbG90X2dyaWQocDYsIHA3LHA4KSAKRGltUGxvdChjb21iaW5lZCwgcmVkdWN0aW9uID0gInRzbmUiLCBzcGxpdC5ieSA9ICJzdGltIikgCgoKCgoKICMgTmV3IFZpc3VhbGl6YXRpb24KcDEgPC0gVU1BUFBsb3QoaW50ZWdyYXRlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJzdGltIiwgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZT01KQpwMiA8LSBVTUFQUGxvdChpbnRlZ3JhdGVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIm1vdXNlLnNleCIpCnAzIDwtIFVNQVBQbG90KGludGVncmF0ZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplPTMpCnA0IDwtIFVNQVBQbG90KGludGVncmF0ZWQsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemU9NikKcGxvdF9ncmlkKHAxLCBwNCkgCkRpbVBsb3QoaW50ZWdyYXRlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBzcGxpdC5ieSA9ICJzdGltIikgICAKCgogIAoKYGBgCgoKCmRpZmZ1c2lvbiBwbHQgY29kZSAKCmBgYHtyfQojIEJlZm9yZSBydW5uaW5nIE1EUywgd2UgZmlyc3QgY2FsY3VsYXRlIGEgZGlzdGFuY2UgbWF0cml4IGJldHdlZW4gYWxsIHBhaXJzIG9mIGNlbGxzLiAgSGVyZSB3ZQojIHVzZSBhIHNpbXBsZSBldWNsaWRlYW4gZGlzdGFuY2UgbWV0cmljIG9uIGFsbCBnZW5lcywgdXNpbmcgc2NhbGUuZGF0YSBhcyBpbnB1dApkIDwtIGRpc3QodChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAic2NhbGUuZGF0YSIpKSkKIyBSdW4gdGhlIE1EUyBwcm9jZWR1cmUsIGsgZGV0ZXJtaW5lcyB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMKbWRzIDwtIGNtZHNjYWxlKGQgPSBkLCBrID0gMikKIyBjbWRzY2FsZSByZXR1cm5zIHRoZSBjZWxsIGVtYmVkZGluZ3MsIHdlIGZpcnN0IGxhYmVsIHRoZSBjb2x1bW5zIHRvIGVuc3VyZSBkb3duc3RyZWFtCiMgY29uc2lzdGVuY3kKY29sbmFtZXMobWRzKSA8LSBwYXN0ZTAoIk1EU18iLCAxOjIpCiMgV2Ugd2lsbCBub3cgc3RvcmUgdGhpcyBhcyBhIGN1c3RvbSBkaW1lbnNpb25hbCByZWR1Y3Rpb24gY2FsbGVkICdtZHMnCmNvbWJpbmVkW1sibWRzIl1dIDwtIENyZWF0ZURpbVJlZHVjT2JqZWN0KGVtYmVkZGluZ3MgPSBtZHMsIGtleSA9ICJNRFNfIiwgYXNzYXkgPSBEZWZhdWx0QXNzYXkoY29tYmluZWQpKQoKIyBXZSBjYW4gbm93IHVzZSB0aGlzIGFzIHlvdSB3b3VsZCBhbnkgb3RoZXIgZGltZW5zaW9uYWwgcmVkdWN0aW9uIGluIGFsbCBkb3duc3RyZWFtIGZ1bmN0aW9ucwpEaW1QbG90KGNvbWJpbmVkLCByZWR1Y3Rpb24gPSAibWRzIiwgcHQuc2l6ZSA9IDAuNSkKYGBgCgoKCmBgYHtyfQpnZW5lc19oZXBfbWFpbiA9YygnQWxiJywgJ1R0cicsICdBcG9hMScsICdTZXJwaW5hMWMnKQpnZW5lc19oZXAgPSBjKCdBbGInLCAnVHRyJywgJ0Fwb2ExJywgJ1NlcnBpbmExYycsCiAgICAgICAgICAgICAgICAgICAnQ3lwMmUxJywgJ0dsdWwnLCAnT2F0JywgJ0d1bG8nLAogICAgICAgICAgICAgICAgICAgJ0FzczEnLCAnSGFtcCcsICdHc3RwMScsICdVYmInLAogICAgICAgICAgICAgICAgICAgJ0N5cDJmMicsICdQY2sxJywgJ0hhbCcsICdDZGgxJykKZ2VuZXNfZW5kbyA9IGMoJ1BlY2FtMScsICdOcnAxJywgJ0tkcicsJ09pdDMnKQpnZW5lc19rdXBwZmVyID0gYyggJ0NsZWM0ZicsICdDZDY4JykKZ2VuZXNfbmsgPSBjKCdJbDJyYicsICdOa2c3JywgJ0N4Y3I2JywgJ0d6bWEnKQpnZW5lc19iID0gYygnQ2Q3OWEnLCAnQ2Q3OWInKQpnZW5lc19iZWMgPSBjKCdFcGNhbScsICdLcnQxOScsICdLcnQ3JykKZ2VuZXNfaW1tdW5lID0gJ1B0cHJjJwpIU0MgPSBjKCJEY24iLCJMYW1hMSIsIk5lcyIpCkRpdmlkaW5nID0gIlRvcDJhIgpCcGxhc21hPSAiSmNoYWluIgpNYWM9ICJDc2YxciIKQ2hvbD0iU294OSIKCmFsbF9nZW5lcyA9IGMoZ2VuZXNfaGVwLCBnZW5lc19lbmRvLCBnZW5lc19rdXBwZmVyLCBNYWMsQ2hvbCxnZW5lc19uaywgZ2VuZXNfYixCcGxhc21hLCBnZW5lc19iZWMsIGdlbmVzX2ltbXVuZSwgSFNDLCBEaXZpZGluZykKZ2VuZXNfYmVjX2JfaW1tdW5lICA9IGMoZ2VuZXNfYmVjLGdlbmVzX2IsZ2VuZXNfaW1tdW5lKQpnZW5lc196b25lcyA9IGMoJ0N5cDJlMScsICdHbHVsJywgJ09hdCcsICdHdWxvJywKICAgICAgICAgICAgICAnQXNzMScsICdIYW1wJywgJ0dzdHAxJywgJ1ViYicsCiAgICAgICAgICAgICAgJ0N5cDJmMicsICdQY2sxJywgJ0hhbCcsICdDZGgxJykKCnJlY2VwdG9yX0tPIDwtIGMoIm5jUk5BLWludGVyLWNocjctNTk5OCIsIkN5cDJiMTAiLCJOcjFpMiIsIk5yMWkzIiwiUHBhcmEiLCJQcGFyZyIsIlBwYXJnYzFiIiwiUHBhcmQiKQoKY2VsbCA8LSBjKCJTdGFiMiIsIkNzZjFyIiwiQ2QzZyIsIkViZjEiLCJJcmY4IiwiU294OSIsIkFwb2MzIiwiVG9wMmEiLCJEY24iKQpgYGAKCgoKYGBge3J9CkRlZmF1bHRBc3NheShkcm9wbGV0KSA8LSAiUk5BIgojZHJvcGxldCA8LSBOb3JtYWxpemVEYXRhKGNvbWJpbmVkLCB2ZXJib3NlID0gVFJVRSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiUkMiLCBzY2FsZS5mYWN0b3IgPSAxZTYpCmNvbWJpbmVkIDwtIE5vcm1hbGl6ZURhdGEoY29tYmluZWQsIHZlcmJvc2UgPSBUUlVFKQoKCkRvdFBsb3QoY29tYmluZWQsIGZlYXR1cmVzID0gYWxsX2dlbmVzKQpGZWF0dXJlUGxvdChjb21iaW5lZCwgZmVhdHVyZXMgPSBnZW5lc19oZXBfbWFpbiwgbWluLmN1dG9mZiA9ICJxOSIpCiNoZXBhdG9jeXRlcyAKc3VidGlzc3Bsb3QgPC0gRG90UGxvdChjb21iaW5lZCwgZmVhdHVyZXMgPSBjKGdlbmVzX2hlcF9tYWluLCBnZW5lc19lbmRvLCBnZW5lc19iZWNfYl9pbW11bmUsIGdlbmVzX2t1cHBmZXIsIGdlbmVzX25rKSkKUEMgPC0gRG90UGxvdChjb21iaW5lZCwgZmVhdHVyZXMgPSBjKGdlbmVzX2hlcF9tYWluLGdlbmVzX3pvbmVzKSkKTlBDIDwtIERvdFBsb3QoY29tYmluZWQsIGZlYXR1cmVzID0gYyhnZW5lc19lbmRvLGdlbmVzX2t1cHBmZXIsIGdlbmVzX25rKSkKYWxsIDwtIERvdFBsb3QoY29tYmluZWQsIGZlYXR1cmVzPWMoYWxsX2dlbmVzKSkKCiMjIyBjb2V4cHJlc3Npb24gcGxvdHMjIyMjCgpmMSA8LSBGZWF0dXJlUGxvdChLTy5jZWxscywgZmVhdHVyZXMgPSBjKCdDeXAyYjEwJywnbmNSTkEtaW50ZXItY2hyNy01OTk4JyksIHJlZHVjdGlvbiA9ICJtZHMiLCBvcmRlciA9IFRSVUUsc3BsaXQuYnkgPSAic3RpbSIsIGJsZW5kID0gVFJVRSxzb3J0LmNlbGwgPSBUUlVFLCBtYXguY3V0b2ZmID0gMC41KQoKCiMjIyMjIyMjIyMgdGhpcyBpcyBleGFjdCBhdmVyYWdpbmcgZm9ybXVsYSAjIyMjIyMjIyMjIyMjIyMzMwogIHggPC0gKEF2ZXJhZ2VFeHByZXNzaW9uKEtPLmNlbGxzLCB2ZXJib3NlID0gVFJVRSwgYXNzYXlzID0gIlJOQSIgLHNsb3Q9ImNvdW50cyIpJFJOQSkKICAgeFsibmNSTkEtaW50ZXItY2hyNy01OTk4IixdCiMgICAgICAgICAgICAgICAgICAgICAgICAgRzE3MUIgICAgRzE3MUMKI25jUk5BLWludGVyLWNocjctNTk5OCAxLjg3MTc5NSAxLjA5MTQ2MwojIyMjIyMjIwojSWRlbnRzKGNvbWJpbmVkKSA8LSBmYWN0b3IoSWRlbnRzKGNvbWJpbmVkKSwgbGV2ZWxzID0gYygwLDEsMTIpKQptYXJrZXJzLnRvLnBsb3QgPC0gYygiQWxiIiwibmNSTkEtaW50ZXItY2hyNy01OTk4IikKRG90UGxvdChjb21iaW5lZCwgZmVhdHVyZXMgPSByZXYobWFya2Vycy50by5wbG90KSwgY29scyA9IGMoImJsdWUiLCAicmVkIiksIGRvdC5zY2FsZSA9IDgsIAogICAgc3BsaXQuYnkgPSAic3RpbSIpICsgUm90YXRlZEF4aXMoKQoKRmVhdHVyZVBsb3QoY29tYmluZWQsIGZlYXR1cmVzID0gYygiQWxiIiwgIm5jUk5BLWludGVyLWNocjctNTk5OCIsIkN5cDJiMTAiLCJkU2FDYXM5IiwiS1JBQiIsIkFBVjgtbUNoZXJyeSIpLCBzcGxpdC5ieSA9ICJzdGltIiwgbWF4LmN1dG9mZiA9IDMsIGNvbHMgPSBjKCJncmV5IiwgInJlZCIpKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgdmxucGxvdCAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKcGxvdHMgPC0gVmxuUGxvdChjb21iaW5lZCwgZmVhdHVyZXMgPSBjKCJBbGIiLCAibmNSTkEtaW50ZXItY2hyNy01OTk4IiwiQ3lwMmIxMCIsImRTYUNhczkiKSwgc3BsaXQuYnkgPSAic3RpbSIsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIHB0LnNpemUgPSAwLCBjb21iaW5lID0gRkFMU0UpCkNvbWJpbmVQbG90cyhwbG90cyA9IHBsb3RzLCBuY29sID0gMSkKCnBsb3RzIDwtIFZsblBsb3QoY29tYmluZWQsIGZlYXR1cmVzID0gYygiTGh4NCIsIkR0bmEiLCJGYW0xODlhMSIsIkdhbG50MTYiLCJLYWxybiIpLCBzcGxpdC5ieSA9ICJzdGltIiwgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwgcHQuc2l6ZSA9IDAsIGNvbWJpbmUgPSBGQUxTRSkKQ29tYmluZVBsb3RzKHBsb3RzID0gcGxvdHMsIG5jb2wgPSAxKQoKCiNlbmRvdGhlbGlhbApEb3RQbG90KGNvbWJpbmVkLCBmZWF0dXJlcyA9IGdlbmVzX2VuZG8pCgojem9uZXMKem9uZXMgPC0gRG90UGxvdChjb21iaW5lZCwgZmVhdHVyZXMgPSBnZW5lc196b25lcykKCmYxIDwtIEZlYXR1cmVQbG90KGNvbWJpbmVkLCBmZWF0dXJlcyA9IGMoJ0N5cDJlMScsJ0N5cDJmMicsJ0FzczEnKSwgbWluLmN1dG9mZiA9ICJxOSIsIHJlZHVjdGlvbiA9ICJ0c25lIikKCkRpbVBsb3QoY29tYmluZWQsIGxhYmVsID0gVFJVRSkKCnNhdmUoY29tYmluZWQsIGZpbGU9IlNldXJhdF9zbWFydC1kcm9wX2ludGVncmF0ZWQuUm9iaiIpCgoKIyMjIyMjIyMjIyMjIyMjIyMgc2F2ZSByYXcgY291bnRzIGZyb20gY2x1c3RlciAjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCklkZW50cyhjb21iaW5lZCkgPC0gInN0aW0iCgojIyMgdG8gYXZlcmdhZSBvdXQgdGhlIG1hdHJpeCBmcm9tIEtPIGNlbGxzIAoKY29tYmluZWQucmF3LmRhdGEuMC4xIDwtIGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiZGF0YSIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAwLGlkZW50cyA9ICJzdGltIildKQpjb21iaW5lZC5yYXcuZGF0YS4xIDwtIGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiY291bnRzIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDApXSkKY29tYmluZWQucmF3LmRhdGEuMiA8LSBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAwKV0pCgojY29tYmluZWQucmF3LmRhdGEuW2ldIDwtIGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiY291bnRzIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDEpXSkKI2NvbWJpbmVkLnJhdy5kYXRhLjEyIDwtYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJjb3VudHMiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gMTIpXSkKI2NvbWJpbmVkLnJhdy5kYXRhLjEgPC0gYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJjb3VudHMiKSkKeCA8LSBBdmVyYWdlRXhwcmVzc2lvbih0ZXN0LmNvbWJpbmVkLGFzc2F5cyA9ICJSTkEiLGFkZC5pZGVudCA9ICJzdGltIiwgc2xvdCA9ICJkYXRhIix1c2Uuc2NhbGUgPSBGQUxTRSwgdXNlLmNvdW50cyA9IEZBTFNFKSRSTkEKCiN9IyMjIyMjIyMgQ0FSIGRhdGEgCmF2Zy5jb21iaW5lZC5jZWxscyA8LSAoQXZlcmFnZUV4cHJlc3Npb24oY29tYmluZWQsIHZlcmJvc2UgPSBGQUxTRSkkUk5BKSAKYXZnLmNvbWJpbmVkLmNlbGxzJGdlbmUgPC0gcm93bmFtZXMoYXZnLmNvbWJpbmVkLmNlbGxzKQoKQ0FSX0ZQIDwtIEZlYXR1cmVQbG90KGNvbWJpbmVkLCBmZWF0dXJlcyA9IGMoJ0N5cDJiMTAnLCdOcjFpMycpLCByZWR1Y3Rpb24gPSAidW1hcCIsIG9yZGVyID0gVFJVRSxzcGxpdC5ieSA9ICJzdGltIiwgYmxlbmQgPSBUUlVFLHNvcnQuY2VsbCA9IFRSVUUsIG1heC5jdXRvZmYgPSAxLCBtaW4uY3V0b2ZmID0gMCwgcHQuc2l6ZSA9IDAuNSwgcmVwZWwgPSBUUlVFKQpDQVJfRE9UX05SIDwtIERvdFBsb3QoY29tYmluZWQsIGZlYXR1cmVzID0gJ05yMWkzJywgY29sLm1pbiA9IDApCgpDeXAyYjEwX0ZQIDwtIEZlYXR1cmVQbG90KGNvbWJpbmVkLCBmZWF0dXJlcyA9ICdDeXAyYjEwJywgcmVkdWN0aW9uID0gInVtYXAiLCBtaW4uY3V0b2ZmID0gMCkKIyMjIyMjIyMjIyMgdFNORSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKY29tYmluZWQgPC0gTm9ybWFsaXplRGF0YShvYmplY3QgPSBjb21iaW5lZCkKY29tYmluZWQgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoY29tYmluZWQsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgbmZlYXR1cmVzID0gMjAwMCkKCmBgYAoKCmBgYHtyfQoKS08uY2VsbHMgPC0gc3Vic2V0KGNvbWJpbmVkLCBpZGVudHMgPSBjKCIwIiwiMSIsIjEyIikpCklkZW50cyhLTy5jZWxscykgPC0gInN0aW0iCgoKCiMjIyB0byBhdmVyZ2FlIG91dCB0aGUgbWF0cml4IGZyb20gS08gY2VsbHMgCnJhdy5kYXRhLjAgPC0gYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9IGMoImNvdW50cyIsImRhdGEiKSlbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDApXSkKcmF3LmRhdGEuMSA8LSBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAxKV0pCnJhdy5kYXRhLjIgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAyKV0pCnJhdy5kYXRhLjMgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAzKV0pCnJhdy5kYXRhLjQgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA0KV0pCnJhdy5kYXRhLjUgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA1KV0pCnJhdy5kYXRhLjYgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA2KV0pCnJhdy5kYXRhLjcgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA3KV0pCnJhdy5kYXRhLjggPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA4KV0pCnJhdy5kYXRhLjkgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA5KV0pCnJhdy5kYXRhLjEwIDwtYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJjb3VudHMiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gMTApXSkKcmF3LmRhdGEuMTEgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAxMSldKQpyYXcuZGF0YS4xMiA8LWFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiY291bnRzIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDEyKV0pCgoKVFBNY291bnQwPC0gY2JpbmQoYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJjb3VudHMiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gMCldKSwgYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJkYXRhIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDApXSkpCgpUUE1jb3VudDE8LSBjYmluZChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAxKV0pLCBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImRhdGEiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gMSldKSkKClRQTWNvdW50MjwtIGNiaW5kKGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiY291bnRzIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDIpXSksIGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiZGF0YSIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAyKV0pKQoKVFBNY291bnQzPC0gY2JpbmQoYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJjb3VudHMiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gMyldKSwgYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJkYXRhIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDMpXSkpCgpUUE1jb3VudDQ8LSBjYmluZChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA0KV0pLCBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImRhdGEiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gNCldKSkKClRQTWNvdW50NTwtIGNiaW5kKGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiY291bnRzIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDUpXSksIGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiZGF0YSIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA1KV0pKQoKVFBNY291bnQ2PC0gY2JpbmQoYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJjb3VudHMiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gNildKSwgYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJkYXRhIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDYpXSkpCgpUUE1jb3VudDc8LSBjYmluZChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA3KV0pLCBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImRhdGEiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gNyldKSkKClRQTWNvdW50ODwtIGNiaW5kKGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiY291bnRzIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDgpXSksIGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiZGF0YSIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSA4KV0pKQoKVFBNY291bnQ5PC0gY2JpbmQoYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJjb3VudHMiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gOSldKSwgYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJkYXRhIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDkpXSkpCgpUUE1jb3VudDEwPC0gY2JpbmQoYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJjb3VudHMiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gMTApXSksIGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiZGF0YSIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAxMCldKSkKClRQTWNvdW50MTE8LSBjYmluZChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhjb21iaW5lZCwgaWRlbnQgPSAxMSldKSwgYXMubWF0cml4KEdldEFzc2F5RGF0YShjb21iaW5lZCwgc2xvdCA9ICJkYXRhIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDExKV0pKQoKVFBNY291bnQxMjwtIGNiaW5kKGFzLm1hdHJpeChHZXRBc3NheURhdGEoY29tYmluZWQsIHNsb3QgPSAiY291bnRzIilbLCBXaGljaENlbGxzKGNvbWJpbmVkLCBpZGVudCA9IDEyKV0pLCBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGNvbWJpbmVkLCBzbG90ID0gImRhdGEiKVssIFdoaWNoQ2VsbHMoY29tYmluZWQsIGlkZW50ID0gMTIpXSkpCgoKCgp3cml0ZS5jc3YoVFBNY291bnQwLCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50MC5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQxLCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50MS5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQyLCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50Mi5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQzLCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50My5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQ0LCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50NC5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQ1LCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50NS5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQ2LCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50Ni5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQ3LCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50Ny5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQ4LCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50OC5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQ5LCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50OS5jc3YiKQp3cml0ZS5jc3YoVFBNY291bnQxMCwgIkNvdW50UmVzdWx0L2NvdW50cy5UUE1jb3VudDEwLmNzdiIpCndyaXRlLmNzdihUUE1jb3VudDExLCAiQ291bnRSZXN1bHQvY291bnRzLlRQTWNvdW50MTEuY3N2IikKd3JpdGUuY3N2KFRQTWNvdW50MTIsICJDb3VudFJlc3VsdC9jb3VudHMuVFBNY291bnQxMi5jc3YiKQoKCgojYXZnLktPLmNlbGxzIDwtIGxvZzFwKEF2ZXJhZ2VFeHByZXNzaW9uKEtPLmNlbGxzLCB2ZXJib3NlID0gRkFMU0UpJFJOQSkgICNvcmlnaW5hbCBjb2RlIGxvZyB0cmFuc2Zvcm1lZAphdmcuS08uY2VsbHMgPC0gKEF2ZXJhZ2VFeHByZXNzaW9uKEtPLmNlbGxzLCB2ZXJib3NlID0gRkFMU0UpJFJOQSkgCmF2Zy5LTy5jZWxscyRnZW5lIDwtIHJvd25hbWVzKGF2Zy5LTy5jZWxscykKCmdlbmVzLnRvLmxhYmVsPSAoIm5jUk5BLWludGVyLWNocjctNTk5OCIpCiNnZW5lcy50by5sYWJlbCA9IGMoIklTRzE1IiwgIkxZNkUiLCAiSUZJNiIsICJJU0cyMCIsICJNWDEiLCAiSUZJVDIiLCAiSUZJVDEiLCAiQ1hDTDEwIiwgIkNDTDgiKQpwMSA8LSBnZ3Bsb3QoYXZnLktPLmNlbGxzLCBhZXMoQ1RSTCwgU1RJTSkpICsgZ2VvbV9wb2ludCgpICsgZ2d0aXRsZSgiQ0Q0IE5haXZlIFQgQ2VsbHMiKQpwMSA8LSBMYWJlbFBvaW50cyhwbG90ID0gcDEsIHBvaW50cyA9IGdlbmVzLnRvLmxhYmVsLCByZXBlbCA9IFRSVUUpCnAyIDwtIGdncGxvdChhdmcuY2QxNC5tb25vLCBhZXMoQ1RSTCwgU1RJTSkpICsgZ2VvbV9wb2ludCgpICsgZ2d0aXRsZSgiQ0QxNCBNb25vY3l0ZXMiKQpwMiA8LSBMYWJlbFBvaW50cyhwbG90ID0gcDIsIHBvaW50cyA9IGdlbmVzLnRvLmxhYmVsLCByZXBlbCA9IFRSVUUpCnBsb3RfZ3JpZChwMSwgcDIpCmBgYAoKCmBgYHtyfQpLTy5jZWxscyA8LSBSdW5VTUFQKEtPLmNlbGxzLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjAgKQpLTy5jZWxscyA8LSBGaW5kTmVpZ2hib3JzKEtPLmNlbGxzLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjApCktPLmNlbGxzIDwtIEZpbmRDbHVzdGVycyhLTy5jZWxscywgcmVzb2x1dGlvbiA9IDAuNSApICAgCktPLmNlbGxzIDwtIFJ1blRTTkUoS08uY2VsbHMsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyMCkKIAoKSGVwLmNlbGxzIDwtIFJ1blVNQVAoSGVwLmNlbGxzLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjAgKQpIZXAuY2VsbHMgPC0gRmluZE5laWdoYm9ycyhIZXAuY2VsbHMsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyMCkKSGVwLmNlbGxzIDwtIEZpbmRDbHVzdGVycyhIZXAuY2VsbHMsIHJlc29sdXRpb24gPSAwLjUgKSAgIApIZXAuY2VsbHMgPC0gUnVuVFNORShIZXAuY2VsbHMsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyMCkKIAoKICAgCiAjIFZpc3VhbGl6YXRpb24KcDEgPC0gVU1BUFBsb3QoS08uY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAic3RpbSIpCnAyIDwtIFVNQVBQbG90KEtPLmNlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIm1vdXNlLnNleCIpCnAzIDwtIFVNQVBQbG90KEtPLmNlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSkKcDQgPC0gVU1BUFBsb3QoS08uY2VsbHMsIGxhYmVsPVRSVUUpCnBsb3RfZ3JpZChwMSxwNCkgCkRpbVBsb3QoS08uY2VsbHMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgc3BsaXQuYnkgPSAic3RpbSIpICAgCgoKI2hlcGF0b2N5dGUgY2VsbHMKCnA1IDwtIFVNQVBQbG90KEhlcC5jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJzdGltIikKcDYgPC0gVU1BUFBsb3QoSGVwLmNlbGxzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIm1vdXNlLnNleCIpCnA3IDwtIFVNQVBQbG90KEhlcC5jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUpCnA4IDwtIFVNQVBQbG90KEhlcC5jZWxscywgbGFiZWw9VFJVRSkKcGxvdF9ncmlkKHA1LHA4KSAKRGltUGxvdChLTy5jZWxscywgcmVkdWN0aW9uID0gInVtYXAiLCBzcGxpdC5ieSA9ICJzdGltIikgICAKCgpyYXcuZGF0YS5LTy4wIDwtIGFzLm1hdHJpeChHZXRBc3NheURhdGEoS08uY2VsbHMsIHNsb3QgPSAiY291bnRzIilbLCBXaGljaENlbGxzKEtPLmNlbGxzLCBpZGVudCA9IDApXSkKcmF3LmRhdGEuS08uMSA8LSBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKEtPLmNlbGxzLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhLTy5jZWxscywgaWRlbnQgPSAxKV0pCnJhdy5kYXRhLktPLjIgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKEtPLmNlbGxzLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhLTy5jZWxscywgaWRlbnQgPSAyKV0pCnJhdy5kYXRhLktPLjMgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKEtPLmNlbGxzLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhLTy5jZWxscywgaWRlbnQgPSAzKV0pCnJhdy5kYXRhLktPLjQgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKEtPLmNlbGxzLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhLTy5jZWxscywgaWRlbnQgPSA0KV0pCnJhdy5kYXRhLktPLjUgPC1hcy5tYXRyaXgoR2V0QXNzYXlEYXRhKEtPLmNlbGxzLCBzbG90ID0gImNvdW50cyIpWywgV2hpY2hDZWxscyhLTy5jZWxscywgaWRlbnQgPSA1KV0pCgoKd3JpdGUuY3N2KHJhdy5kYXRhLktPLjAsICJDb3VudFJlc3VsdC9NYXJrZXJzL3Jhdy5kYXRhLktPLjAiKQp3cml0ZS5jc3YocmF3LmRhdGEuS08uMSwgIkNvdW50UmVzdWx0L01hcmtlcnMvcmF3LmRhdGEuS08uMSIpCndyaXRlLmNzdihyYXcuZGF0YS5LTy4yLCAiQ291bnRSZXN1bHQvTWFya2Vycy9yYXcuZGF0YS5LTy4yIikKd3JpdGUuY3N2KHJhdy5kYXRhLktPLjMsICJDb3VudFJlc3VsdC9NYXJrZXJzL3Jhdy5kYXRhLktPLjMiKQp3cml0ZS5jc3YocmF3LmRhdGEuS08uNCwgIkNvdW50UmVzdWx0L01hcmtlcnMvcmF3LmRhdGEuS08uNCIpCndyaXRlLmNzdihyYXcuZGF0YS5LTy41LCAiQ291bnRSZXN1bHQvTWFya2Vycy9yYXcuZGF0YS5LTy41IikKCgoKZjEgPC0gRmVhdHVyZVBsb3QoS08uY2VsbHMsIGZlYXR1cmVzID0gYygnbmNSTkEtaW50ZXItY2hyNy01OTk4JyksICByZWR1Y3Rpb24gPSAidW1hcCIsIHNwbGl0LmJ5ID0gInN0aW0iKQpwbG90X2dyaWQoZjEscDEscDQpIAoKRGVmYXVsdEFzc2F5KEtPLmNlbGxzKSA8LSAiUk5BIgpLTy5jZWxscyA8LSBOb3JtYWxpemVEYXRhKEtPLmNlbGxzLCB2ZXJib3NlID0gRkFMU0UpCgpwbG90cyA8LSBWbG5QbG90KEtPLmNlbGxzLCBmZWF0dXJlcyA9IGMoIkFsYiIsICJuY1JOQS1pbnRlci1jaHI3LTU5OTgiLCJDeXAyYjEwIiwiQ3lwMmUxIiwiQ3lwMmYyIiksIHNwbGl0LmJ5ID0gInN0aW0iLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBwdC5zaXplID0gMCwgY29tYmluZSA9IEZBTFNFKQpDb21iaW5lUGxvdHMocGxvdHMgPSBwbG90cywgbmNvbCA9IDEpCgpUaHJlZV9maXZlX3NpeCA8LSBzdWJzZXQoS08uY2VsbHMsIGlkZW50cyA9IGMoIjUiLCI2IikpCgpUaHJlZV9maXZlX3NpeCA8LSBSdW5VTUFQKFRocmVlX2ZpdmVfc2l4LCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjAgKQpUaHJlZV9maXZlX3NpeCA8LSBGaW5kTmVpZ2hib3JzKFRocmVlX2ZpdmVfc2l4LCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjApClRocmVlX2ZpdmVfc2l4IDwtIEZpbmRDbHVzdGVycyhUaHJlZV9maXZlX3NpeCwgcmVzb2x1dGlvbiA9IDEgKSAgIApUaHJlZV9maXZlX3NpeCA8LSBSdW5UU05FKFRocmVlX2ZpdmVfc2l4LCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MjApCgpwMSA8LSBVTUFQUGxvdChUaHJlZV9maXZlX3NpeCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJzdGltIikKcDIgPC0gVU1BUFBsb3QoVGhyZWVfZml2ZV9zaXgsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAibW91c2Uuc2V4IikKcDMgPC0gVU1BUFBsb3QoVGhyZWVfZml2ZV9zaXgsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFKQpwNCA8LSBVTUFQUGxvdChUaHJlZV9maXZlX3NpeCwgbGFiZWw9VFJVRSkKcGxvdF9ncmlkKHAxLHA0KSAKRGltUGxvdChUaHJlZV9maXZlX3NpeCwgcmVkdWN0aW9uID0gInVtYXAiLCBzcGxpdC5ieSA9ICJzdGltIikgICAKCmYxIDwtIEZlYXR1cmVQbG90KFRocmVlX2ZpdmVfc2l4LCBmZWF0dXJlcyA9IGMoJ25jUk5BLWludGVyLWNocjctNTk5OCcpLCAgcmVkdWN0aW9uID0gInVtYXAiLCBzcGxpdC5ieSA9ICJzdGltIikKCgpsbmM1OTk4IDwtIHN1YnNldChjb21iaW5lZCwgY2VsbHMgPSBsbmM1OTk4LmNlbGxzLCBpZGVudHMgPSAiMSIpCgpsbmM1OTk4IDwtIFJ1blVNQVAobG5jNTk5OCwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjIwICkKbG5jNTk5OCA8LSBGaW5kTmVpZ2hib3JzKGxuYzU5OTgsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyMCkKbG5jNTk5OCA8LSBGaW5kQ2x1c3RlcnMobG5jNTk5OCwgcmVzb2x1dGlvbiA9IDEgKSAgIApsbmM1OTk4IDwtIFJ1blRTTkUobG5jNTk5OCwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjIwKQogICAgCiAjIFZpc3VhbGl6YXRpb24KcDEgPC0gVU1BUFBsb3QobG5jNTk5OCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJzdGltIikKcDIgPC0gVU1BUFBsb3QobG5jNTk5OCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJtb3VzZS5zZXgiKQpwMyA8LSBVTUFQUGxvdChsbmM1OTk4LCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSkKcDQgPC0gVU1BUFBsb3QobG5jNTk5OCwgbGFiZWw9VFJVRSkKcGxvdF9ncmlkKHAxLHA0KSAKRGltUGxvdChsbmM1OTk4LCByZWR1Y3Rpb24gPSAidW1hcCIsIHNwbGl0LmJ5ID0gInN0aW0iKSAgIAoKbG5jNTk5OC5jZWxscyA8LSBXaGljaENlbGxzKG9iamVjdCA9IGNvbWJpbmVkLCBleHByZXNzaW9uID0gIm5jUk5BLWludGVyLWNocjctNTk5OCIgPiAxKQpGZWF0dXJlUGxvdChsbmM1OTk4LCBmZWF0dXJlcyA9IGMoIm5jUk5BLWludGVyLWNocjctNTk5OCIpLCBzcGxpdC5ieSA9ICJzdGltIiwgIAorICAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5IiwgInJlZCIpLCBjZWxscyA9IGxuYzU5OTguY2VsbHMsbWluLmN1dG9mZiA9IDAuNSkKCgpEZWZhdWx0QXNzYXkobG5jNTk5OCkgPC0gIlJOQSIKbG5jNTk5OCA8LSBOb3JtYWxpemVEYXRhKGxuYzU5OTgsIHZlcmJvc2UgPSBGQUxTRSkKCnBsb3RzIDwtIFZsblBsb3QobG5jNTk5OCwgZmVhdHVyZXMgPSBjKCJBbGIiLCAibmNSTkEtaW50ZXItY2hyNy01OTk4IiwiQ3lwMmIxMCIsIkN5cDJlMSIsIkN5cDJmMiIpLCBzcGxpdC5ieSA9ICJzdGltIiwgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwgcHQuc2l6ZSA9IDAsIGNvbWJpbmUgPSBGQUxTRSkKQ29tYmluZVBsb3RzKHBsb3RzID0gcGxvdHMsIG5jb2wgPSAxKQoKIApgYGAKCmBgYHtyfQpkIDwtIGRpc3QodChHZXRBc3NheURhdGEoS08uY2VsbHMsIHNsb3QgPSAic2NhbGUuZGF0YSIpKSkKIyBSdW4gdGhlIE1EUyBwcm9jZWR1cmUsIGsgZGV0ZXJtaW5lcyB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMKbWRzIDwtIGNtZHNjYWxlKGQgPSBkLCBrID0gMikKIyBjbWRzY2FsZSByZXR1cm5zIHRoZSBjZWxsIGVtYmVkZGluZ3MsIHdlIGZpcnN0IGxhYmVsIHRoZSBjb2x1bW5zIHRvIGVuc3VyZSBkb3duc3RyZWFtCiMgY29uc2lzdGVuY3kKY29sbmFtZXMobWRzKSA8LSBwYXN0ZTAoIk1EU18iLCAxOjIpCiMgV2Ugd2lsbCBub3cgc3RvcmUgdGhpcyBhcyBhIGN1c3RvbSBkaW1lbnNpb25hbCByZWR1Y3Rpb24gY2FsbGVkICdtZHMnCktPLmNlbGxzW1sibWRzIl1dIDwtIENyZWF0ZURpbVJlZHVjT2JqZWN0KGVtYmVkZGluZ3MgPSBtZHMsIGtleSA9ICJNRFNfIiwgYXNzYXkgPSBEZWZhdWx0QXNzYXkoS08uY2VsbHMpKQoKIyBXZSBjYW4gbm93IHVzZSB0aGlzIGFzIHlvdSB3b3VsZCBhbnkgb3RoZXIgZGltZW5zaW9uYWwgcmVkdWN0aW9uIGluIGFsbCBkb3duc3RyZWFtIGZ1bmN0aW9ucwpEaW1QbG90KEtPLmNlbGxzLCByZWR1Y3Rpb24gPSAibWRzIiwgcHQuc2l6ZSA9IDAuNSkKYGBgCgpGaW5kIGRpZmZlcmVudGlhbCBtYXJrZXJzCgpgYGB7cn0KS08uY2VsbHMkY2VsbHR5cGUuc3RpbSA8LSBwYXN0ZShJZGVudHMoS08uY2VsbHMpLCBLTy5jZWxscyRzdGltLCBzZXAgPSAiXyIpCktPLmNlbGxzJGNlbGx0eXBlIDwtIElkZW50cyhLTy5jZWxscykKSWRlbnRzKEtPLmNlbGxzKSA8LSAiY2VsbHR5cGUuc3RpbSIKcmVzcG9uc2UzIDwtIEZpbmRNYXJrZXJzKEtPLmNlbGxzLCBpZGVudC4xID0gYygiMV9HMTcxQiIsIjBfRzE3MUIiLCIyX0cxNzFCIiksIGlkZW50LjIgPSBjKCIxX0cxNzFDIiwgIjBfRzE3MUMiLCIyX0cxNzFDIiksIHZlcmJvc2UgPSBUUlVFLCB0ZXN0LnVzZSA9ICJNQVNUIiwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpoZWFkKHJlc3BvbnNlMywgbiA9IDE1KQoKCiNERTE6IENvbXBhcmUgY2x1c3RlciAwKzErMTIgKHRoYXQgZXhwcmVzc2VkIGxuYzU5OTgpIHdpdGggT3RoZXIgaGVwYXRvY3l0ZSBjbHVzdGVycyAoMis1KzgpCiNERTI6IGNvbXBhcmUgY2x1c3RlciAxIChzaG93ZWQgbWFqb3IgZWZmZWN0cyBpbiB0aGUgS0QpIHZzIENsdXN0ZXIgMCAodGhhdCBzaG93ZWQgbGl0dGxlIEtEKQojREUzOiBGb3IgS08uY2VsbHMgdGhhdCBmb3JtZWQgZml2ZSBzdWJjbHVzdGVyLCBjb21wYXJlIGNsdXN0ZXJzIDMrNCs1KzEgdnMgMiswCiNERTQ6IGZvciBLTy5jZWxscyB0aGF0IGZvcm1lZCBmaXZlIGNsdXN0ZXJzLiBDb21hcHJlIGNsdXN0ZXIgNCB2cy4gMgoKREUwMTEyLjI1OCA8LSBGaW5kTWFya2Vycyhjb21iaW5lZCwgaWRlbnQuMSA9IGMoIjAiLCIxIiwiMTIiICksIGlkZW50LjIgPSBjKCIyIiwiNSIsIjgiKSx2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpERTAxMTIuQWxsIDwtIEZpbmRNYXJrZXJzKGNvbWJpbmVkLCBpZGVudC4xID0gYygiMCIsIjEiLCIxMiIgKSwgdmVyYm9zZSA9IFRSVUUsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKREUxLjIgPC0gRmluZE1hcmtlcnMoY29tYmluZWQsIGlkZW50LjEgPSBjKCIxIiApLCBpZGVudC4yID0gYygiMiIpLHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCkRFMS41IDwtIEZpbmRNYXJrZXJzKGNvbWJpbmVkLCBpZGVudC4xID0gYygiMSIgKSwgaWRlbnQuMiA9IGMoIjUiKSx2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpERTEuOCA8LSBGaW5kTWFya2Vycyhjb21iaW5lZCwgaWRlbnQuMSA9IGMoIjEiICksIGlkZW50LjIgPSBjKCI4IiksdmVyYm9zZSA9IFRSVUUsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKCgpERTIuNSA8LSBGaW5kTWFya2Vycyhjb21iaW5lZCwgaWRlbnQuMSA9IGMoIjIiICksIGlkZW50LjIgPSBjKCI1IiksdmVyYm9zZSA9IFRSVUUsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKREUyLjggPC0gRmluZE1hcmtlcnMoY29tYmluZWQsIGlkZW50LjEgPSBjKCIyIiApLCBpZGVudC4yID0gYygiOCIpLHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCkRFNS44IDwtIEZpbmRNYXJrZXJzKGNvbWJpbmVkLCBpZGVudC4xID0gYygiNSIgKSwgaWRlbnQuMiA9IGMoIjgiKSx2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQoKCkRFSzQzNTEuMjAgPC0gRmluZE1hcmtlcnMoS08uY2VsbHMsIGlkZW50LjEgPSBjKCIzIiwiNCIsIjUiLCIxIiApLCBpZGVudC4yID0gYygiMiIsIjAiKSx2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQoKREVLNC4zIDwtIEZpbmRNYXJrZXJzKEtPLmNlbGxzLCBpZGVudC4xID0gIjQiLCBpZGVudC4yID0gIjMiLHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCkRFSzQuMiA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9ICI0IiwgaWRlbnQuMiA9ICIyIix2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpERUs0LjAgPC0gRmluZE1hcmtlcnMoS08uY2VsbHMsIGlkZW50LjEgPSAiNCIsIGlkZW50LjIgPSAiMCIsdmVyYm9zZSA9IFRSVUUsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKREVLNC4xIDwtIEZpbmRNYXJrZXJzKEtPLmNlbGxzLCBpZGVudC4xID0gIjQiLCBpZGVudC4yID0gIjEiLHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCkRFSzQuNSA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9ICI0IiwgaWRlbnQuMiA9ICI1Iix2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQoKCgojIyMjIyBjb21hcHJpc29uIGJldHdlZW4gRzE3MUIgdnMgRzE3MUMgZm9yIEtPLmNlbGwgY2x1c3RlcnMgb2YgMCsxKzEyICMjIyMjIzMzCgpERUs0X0MuQiA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9ICI0X0cxNzFDIiwgaWRlbnQuMiA9ICI0X0cxNzFCIix2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpERUszX0MuQiA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9ICIzX0cxNzFDIiwgaWRlbnQuMiA9ICIzX0cxNzFCIix2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpERUsxX0MuQiA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9ICIxX0cxNzFDIiwgaWRlbnQuMiA9ICIxX0cxNzFCIix2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpERUsyX0MuQiA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9ICIyX0cxNzFDIiwgaWRlbnQuMiA9ICIyX0cxNzFCIix2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpERUswX0MuQiA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9ICIwX0cxNzFDIiwgaWRlbnQuMiA9ICIwX0cxNzFCIix2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpERUs1X0MuQiA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9ICI1X0cxNzFDIiwgaWRlbnQuMiA9ICI1X0cxNzFCIix2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIFdyaXRlIHRoZSByZXN1bHRzICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKd3JpdGUuY3N2KERFMDExMi4yNTgsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFMDExMi4yNTgiKQp3cml0ZS5jc3YoREUwMTEyLkFsbCwgIkNvdW50UmVzdWx0L01hcmtlcnMvREUwMTEyLkFsbCIpCndyaXRlLmNzdihERTEuMiwgIkNvdW50UmVzdWx0L01hcmtlcnMvREUxLjIiKQp3cml0ZS5jc3YoREUxLjUsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFMS41IikKd3JpdGUuY3N2KERFMS44LCAiQ291bnRSZXN1bHQvTWFya2Vycy9ERTEuOCIpCndyaXRlLmNzdihERTIuNSwgIkNvdW50UmVzdWx0L01hcmtlcnMvREUyLjUiKQp3cml0ZS5jc3YoREUyLjgsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFMi44IikKd3JpdGUuY3N2KERFNS44LCAiQ291bnRSZXN1bHQvTWFya2Vycy9ERTUuOCIpCgp3cml0ZS5jc3YoREVLNDM1MS4yMCwgIkNvdW50UmVzdWx0L01hcmtlcnMvREVLNDM1MS4yMCIpCndyaXRlLmNzdihERUs0LjMsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFSzQuMyIpCndyaXRlLmNzdihERUs0LjIsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFSzQuMiIpCndyaXRlLmNzdihERUs0LjAsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFSzQuMCIpCndyaXRlLmNzdihERUs0LjEsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFSzQuMSIpCndyaXRlLmNzdihERUs0LjUsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFSzQuNSIpCgoKd3JpdGUuY3N2KERFSzRfQy5CLCAiQ291bnRSZXN1bHQvTWFya2Vycy9ERUs0X0MuQiIpCndyaXRlLmNzdihERUszX0MuQiwgIkNvdW50UmVzdWx0L01hcmtlcnMvREVLM19DLkIiKQp3cml0ZS5jc3YoREVLMV9DLkIsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFSzFfQy5CIikKd3JpdGUuY3N2KERFSzJfQy5CLCAiQ291bnRSZXN1bHQvTWFya2Vycy9ERUsyX0MuQiIpCndyaXRlLmNzdihERUswX0MuQiwgIkNvdW50UmVzdWx0L01hcmtlcnMvREVLMF9DLkIiKQp3cml0ZS5jc3YoREVLNV9DLkIsICJDb3VudFJlc3VsdC9NYXJrZXJzL0RFSzVfQy5CIikKCgoKCmNvbWJpbmVkJGNlbGx0eXBlLnN0aW0gPC0gcGFzdGUoSWRlbnRzKGNvbWJpbmVkKSwgY29tYmluZWQkc3RpbSwgc2VwID0gIl8iKQpjb21iaW5lZCRjZWxsdHlwZSA8LSBJZGVudHMoY29tYmluZWQpCklkZW50cyhjb21iaW5lZCkgPC0gImNlbGx0eXBlLnN0aW0iCgoKdGVzdC5jb21iaW5lZCRjZWxsdHlwZS5zdGltIDwtIHBhc3RlKElkZW50cyh0ZXN0LmNvbWJpbmVkKSwgdGVzdC5jb21iaW5lZCRzdGltLCBzZXAgPSAiXyIpCnRlc3QuY29tYmluZWQkY2VsbHR5cGUgPC0gSWRlbnRzKHRlc3QuY29tYmluZWQpCklkZW50cyh0ZXN0LmNvbWJpbmVkKSA8LSAiY2VsbHR5cGUuc3RpbSIKCgoKCkNvbWJpbmVkX0cxNzFCX3ZzX0cxNzFDIDwtIEZpbmRNYXJrZXJzKGNvbWJpbmVkLCBpZGVudC4xID0gYygiMF9HMTcxQiIsIjFfRzE3MUIiLCIyX0cxNzFCIiwiM19HMTcxQiIsIjRfRzE3MUIiLCI1X0cxNzFCIiwiNl9HMTcxQiIsIjdfRzE3MUIiLCI4X0cxNzFCIiwiOV9HMTcxQiIsIjEwX0cxNzFCIiwiMTFfRzE3MUIiLCIxMl9HMTcxQiIgKSwgaWRlbnQuMiA9IGMoIjBfRzE3MUMiLCIxX0cxNzFDIiwiMl9HMTcxQyIsIjNfRzE3MUMiLCI0X0cxNzFDIiwiNV9HMTcxQyIsIjZfRzE3MUMiLCI3X0cxNzFDIiwiOF9HMTcxQyIsIjlfRzE3MUMiLCIxMF9HMTcxQyIsIjExX0cxNzFDIiwiMTJfRzE3MUMiICksIHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCgoKQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QxIDwtIEZpbmRNYXJrZXJzKGNvbWJpbmVkLCBpZGVudC4xID0gYygiMV9HMTcxQyIpLCBpZGVudC4yID0gYygiMV9HMTcxQiIpLCB2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpDb21iaW5lZF9HMTcxQ192c19HMTcxQl9DbHVzdDAgPC0gRmluZE1hcmtlcnMoY29tYmluZWQsIGlkZW50LjEgPSBjKCIwX0cxNzFDIiksIGlkZW50LjIgPSBjKCIwX0cxNzFCIiksIHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCkNvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0MTIgPC0gRmluZE1hcmtlcnMoY29tYmluZWQsIGlkZW50LjEgPSBjKCIxMl9HMTcxQyIpLCBpZGVudC4yID0gYygiMTJfRzE3MUIiKSwgdmVyYm9zZSA9IFRSVUUsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QyIDwtIEZpbmRNYXJrZXJzKGNvbWJpbmVkLCBpZGVudC4xID0gYygiMl9HMTcxQyIpLCBpZGVudC4yID0gYygiMl9HMTcxQiIpLCB2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpDb21iaW5lZF9HMTcxQ192c19HMTcxQl9DbHVzdDUgPC0gRmluZE1hcmtlcnMoY29tYmluZWQsIGlkZW50LjEgPSBjKCI1X0cxNzFDIiksIGlkZW50LjIgPSBjKCI1X0cxNzFCIiksIHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCkNvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0OCA8LSBGaW5kTWFya2Vycyhjb21iaW5lZCwgaWRlbnQuMSA9IGMoIjhfRzE3MUMiKSwgaWRlbnQuMiA9IGMoIjhfRzE3MUIiKSwgdmVyYm9zZSA9IFRSVUUsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKCgpDb21iaW5lZF9HMTcxQ192c19HMTcxQl9DbHVzdDMgPC0gRmluZE1hcmtlcnMoY29tYmluZWQsIGlkZW50LjEgPSBjKCIzX0cxNzFDIiksIGlkZW50LjIgPSBjKCIzX0cxNzFCIiksIHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCkNvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0NCA8LSBGaW5kTWFya2Vycyhjb21iaW5lZCwgaWRlbnQuMSA9IGMoIjRfRzE3MUMiKSwgaWRlbnQuMiA9IGMoIjRfRzE3MUIiKSwgdmVyYm9zZSA9IFRSVUUsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3Q2IDwtIEZpbmRNYXJrZXJzKGNvbWJpbmVkLCBpZGVudC4xID0gYygiNl9HMTcxQyIpLCBpZGVudC4yID0gYygiNl9HMTcxQiIpLCB2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpDb21iaW5lZF9HMTcxQ192c19HMTcxQl9DbHVzdDcgPC0gRmluZE1hcmtlcnMoY29tYmluZWQsIGlkZW50LjEgPSBjKCI3X0cxNzFDIiksIGlkZW50LjIgPSBjKCI3X0cxNzFCIiksIHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCkNvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0OSA8LSBGaW5kTWFya2Vycyhjb21iaW5lZCwgaWRlbnQuMSA9IGMoIjlfRzE3MUMiKSwgaWRlbnQuMiA9IGMoIjlfRzE3MUIiKSwgdmVyYm9zZSA9IFRSVUUsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QxMCA8LSBGaW5kTWFya2Vycyhjb21iaW5lZCwgaWRlbnQuMSA9IGMoIjEwX0cxNzFDIiksIGlkZW50LjIgPSBjKCIxMF9HMTcxQiIpLCB2ZXJib3NlID0gVFJVRSwgbG9nZmMudGhyZXNob2xkID0gRkFMU0UsbWluLnBjdCA9IEZBTFNFKQpDb21iaW5lZF9HMTcxQ192c19HMTcxQl9DbHVzdDExIDwtIEZpbmRNYXJrZXJzKGNvbWJpbmVkLCBpZGVudC4xID0gYygiMTFfRzE3MUMiKSwgaWRlbnQuMiA9IGMoIjExX0cxNzFCIiksIHZlcmJvc2UgPSBUUlVFLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCgoKCgoKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0MSwgIkNvdW50UmVzdWx0L01hcmtlcnMvQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QxIikKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0MCwgIkNvdW50UmVzdWx0L01hcmtlcnMvQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QwIikKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0MTIsICJDb3VudFJlc3VsdC9NYXJrZXJzL0NvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0MTIiKQp3cml0ZS5jc3YoQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QyLCAiQ291bnRSZXN1bHQvTWFya2Vycy9Db21iaW5lZF9HMTcxQ192c19HMTcxQl9DbHVzdDIiKQp3cml0ZS5jc3YoQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3Q1LCAiQ291bnRSZXN1bHQvTWFya2Vycy9Db21iaW5lZF9HMTcxQ192c19HMTcxQl9DbHVzdDUiKQp3cml0ZS5jc3YoQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3Q4LCAiQ291bnRSZXN1bHQvTWFya2Vycy9Db21iaW5lZF9HMTcxQ192c19HMTcxQl9DbHVzdDgiKQoKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0MywgIkNvdW50UmVzdWx0L01hcmtlcnMvQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QzIikKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0NCwgIkNvdW50UmVzdWx0L01hcmtlcnMvQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3Q0IikKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0NiwgIkNvdW50UmVzdWx0L01hcmtlcnMvQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3Q2IikKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0NywgIkNvdW50UmVzdWx0L01hcmtlcnMvQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3Q3IikKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0OSwgIkNvdW50UmVzdWx0L01hcmtlcnMvQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3Q5IikKd3JpdGUuY3N2KENvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0MTAsICJDb3VudFJlc3VsdC9NYXJrZXJzL0NvbWJpbmVkX0cxNzFDX3ZzX0cxNzFCX0NsdXN0MTAiKQp3cml0ZS5jc3YoQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QxMSwgIkNvdW50UmVzdWx0L01hcmtlcnMvQ29tYmluZWRfRzE3MUNfdnNfRzE3MUJfQ2x1c3QxMSIpCgoKCgoKUENfdnNfUFBfRzE3MUIgPC0gRmluZE1hcmtlcnMoS08uY2VsbHMsIGlkZW50LjEgPSBjKCI0X0cxNzFCIiwiM19HMTcxQiIpLCBpZGVudC4yID0gYygiMF9HMTcxQiIsIjJfRzE3MUIiKSwgdmVyYm9zZSA9IFRSVUUsIHRlc3QudXNlID0gIk1BU1QiLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCmhlYWQoUENfdnNfUFBfRzE3MUIsIG4gPSAxNSkKCgpQQ192c19QUF9HMTcxQyA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9IGMoIjRfRzE3MUMiLCIzX0cxNzFDIiksIGlkZW50LjIgPSBjKCIwX0cxNzFDIiwiMl9HMTcxQyIpLCB2ZXJib3NlID0gVFJVRSwgdGVzdC51c2UgPSAiTUFTVCIsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKaGVhZChQQ192c19QUF9HMTcxQywgbiA9IDE1KQoKClBDX3ZzX1BQX0cxNzFCQyA8LSBGaW5kTWFya2VycyhLTy5jZWxscywgaWRlbnQuMSA9IGMoIjRfRzE3MUIiLCIzX0cxNzFCIiwiNF9HMTcxQyIsIjNfRzE3MUMiKSwgaWRlbnQuMiA9IGMoIjBfRzE3MUIiLCIyX0cxNzFCIiwiMF9HMTcxQyIsIjJfRzE3MUMiKSwgdmVyYm9zZSA9IFRSVUUsIHRlc3QudXNlID0gIk1BU1QiLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCmhlYWQoUENfdnNfUFBfRzE3MUMsIG4gPSAxNSkKCgpsbmM1OTk4X0tPX0RFXzEgPC0gRmluZE1hcmtlcnMoS08uY2VsbHMsIGlkZW50LjEgPSBjKCI0X0cxNzFCIiwiM19HMTcxQiIsIjVfRzE3MUIiKSwgaWRlbnQuMiA9IGMoIjRfRzE3MUMiLCIzX0cxNzFDIiwiNV9HMTcxQyIpLCB2ZXJib3NlID0gVFJVRSwgdGVzdC51c2UgPSAiTUFTVCIsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKaGVhZChsbmM1OTk4X0tPX0RFLCBuID0gMTUpCgoKbG5jNTk5OF9LT19ERV8yIDwtIEZpbmRNYXJrZXJzKEtPLmNlbGxzLCBpZGVudC4xID0gYygiNF9HMTcxQyIsIjNfRzE3MUMiLCI1X0cxNzFDIiksIGlkZW50LjIgPWMoIjRfRzE3MUIiLCIzX0cxNzFCIiwiNV9HMTcxQiIpICwgdmVyYm9zZSA9IFRSVUUsIHRlc3QudXNlID0gIk1BU1QiLCBsb2dmYy50aHJlc2hvbGQgPSBGQUxTRSxtaW4ucGN0ID0gRkFMU0UpCmhlYWQobG5jNTk5OF9LT19ERSwgbiA9IDE1KQoKY2VsbC50eXBlLmdlbmVzIDwtIChQQ192c19QUF9HMTcxQkNbMV0pICMgVGFrZXMgYWxsIHRoZSB1bmlxdWUgY2VsbCB0eXBlIHNwZWNpZmljIGdlbmVzCkdPdGVybXMgPSB0b3BHT3Rlcm1zKGZnLmdlbmVzID0gY2VsbC50eXBlLmdlbmVzLCBiZy5nZW5lcyA9IHJvd25hbWVzKEtPLmNlbGxzQGFzc2F5cyRSTkFAZGF0YUtPLmNlbGxzQGFzc2F5cyRSTkFAZGF0YSksIG9yZ2FuaXNtID0gIk1vdXNlIikKCmNlbGwudHlwZS5nZW5lcyA8LSAoUENfdnNfUFBfRzE3MUJDWzFdKSAjIFRha2VzIGFsbCB0aGUgdW5pcXVlIGNlbGwgdHlwZSBzcGVjaWZpYyBnZW5lcwpHT3Rlcm1zID0gdG9wR090ZXJtcyhmZy5nZW5lcyA9IHJvd25hbWVzKGNlbGwudHlwZS5nZW5lcyksIGJnLmdlbmVzID0gcm93bmFtZXMoS08uY2VsbHNAYXNzYXlzJFJOQUBkYXRhKSwgb3JnYW5pc20gPSAiTW91c2UiKQogCmBgYAoKYGBge3J9CgpBdmVyZ2VFeHByZXNzaW9uMiA8LSBmdW5jdGlvbiAob2JqZWN0LCBhc3NheXMgPSBOVUxMLCBmZWF0dXJlcyA9IE5VTEwsIHJldHVybi5zZXVyYXQgPSBGQUxTRSwgCiAgICAgICAgICBhZGQuaWRlbnQgPSBOVUxMLCBzbG90ID0gImRhdGEiLCB1c2Uuc2NhbGUgPSBGQUxTRSwgdXNlLmNvdW50cyA9IEZBTFNFLCAKICAgICAgICAgIHZlcmJvc2UgPSBUUlVFLCAuLi4pIAp7CiAgICAKICAgIGZ4bi5hdmVyYWdlIDwtIHN3aXRjaChFWFBSID0gc2xvdCwgZGF0YSA9IGZ1bmN0aW9uKHgpIHsKICAgICAgICByZXR1cm4obWVhbih4ID0geCkpCiAgICB9LCBtZWFuKQogICAgb2JqZWN0LmFzc2F5cyA8LSBGaWx0ZXJPYmplY3RzKG9iamVjdCA9IG9iamVjdCwgY2xhc3Nlcy5rZWVwID0gIkFzc2F5IikKICAgIGFzc2F5cyA8LSBhc3NheXMgJXx8JSBvYmplY3QuYXNzYXlzCiAgICBpZGVudC5vcmlnIDwtIElkZW50cyhvYmplY3QgPSBvYmplY3QpCiAgICBvcmlnLmxldmVscyA8LSBsZXZlbHMoeCA9IElkZW50cyhvYmplY3QgPSBvYmplY3QpKQogICAgaWRlbnQubmV3IDwtIGMoKQogICAgaWYgKCFhbGwoYXNzYXlzICVpbiUgb2JqZWN0LmFzc2F5cykpIHsKICAgICAgICBhc3NheXMgPC0gYXNzYXlzW2Fzc2F5cyAlaW4lIG9iamVjdC5hc3NheXNdCiAgICAgICAgaWYgKGxlbmd0aChhc3NheXMpID09IDApIHsKICAgICAgICAgICAgc3RvcCgiTm9uZSBvZiB0aGUgcmVxdWVzdGVkIGFzc2F5cyBhcmUgcHJlc2VudCBpbiB0aGUgb2JqZWN0IikKICAgICAgICB9CiAgICAgICAgZWxzZSB7CiAgICAgICAgICAgIHdhcm5pbmcoIlJlcXVlc3RlZCBhc3NheXMgdGhhdCBkbyBub3QgZXhpc3QgaW4gb2JqZWN0LiBQcm9jZWVkaW5nIHdpdGggZXhpc3RpbmcgYXNzYXlzIG9ubHkuIikKICAgICAgICB9CiAgICB9CiAgICBpZiAoIWlzLm51bGwoeCA9IGFkZC5pZGVudCkpIHsKICAgICAgICBuZXcuZGF0YSA8LSBGZXRjaERhdGEob2JqZWN0ID0gb2JqZWN0LCB2YXJzID0gYWRkLmlkZW50KQogICAgICAgIG5ldy5pZGVudCA8LSBwYXN0ZShJZGVudHMob2JqZWN0KVtyb3duYW1lcyh4ID0gbmV3LmRhdGEpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5kYXRhWywgMV0sIHNlcCA9ICJfIikKICAgICAgICBJZGVudHMob2JqZWN0LCBjZWxscyA9IHJvd25hbWVzKG5ldy5kYXRhKSkgPC0gbmV3LmlkZW50CiAgICB9CiAgICBkYXRhLnJldHVybiA8LSBsaXN0KCkKICAgIGZvciAoaSBpbiAxOmxlbmd0aCh4ID0gYXNzYXlzKSkgewogICAgICAgIGRhdGEudXNlIDwtIEdldEFzc2F5RGF0YShvYmplY3QgPSBvYmplY3QsIGFzc2F5ID0gYXNzYXlzW2ldLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xvdCA9IHNsb3QpCiAgICAgICAgZmVhdHVyZXMuYXNzYXkgPC0gZmVhdHVyZXMKICAgICAgICBpZiAobGVuZ3RoKHggPSBpbnRlcnNlY3QoeCA9IGZlYXR1cmVzLCB5ID0gcm93bmFtZXMoeCA9IGRhdGEudXNlKSkpIDwgCiAgICAgICAgICAgIDEpIHsKICAgICAgICAgICAgZmVhdHVyZXMuYXNzYXkgPC0gcm93bmFtZXMoeCA9IGRhdGEudXNlKQogICAgICAgIH0KICAgICAgICBkYXRhLmFsbCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGZlYXR1cmVzLmFzc2F5KQogICAgICAgIGZvciAoaiBpbiBsZXZlbHMoeCA9IElkZW50cyhvYmplY3QpKSkgewogICAgICAgICAgICB0ZW1wLmNlbGxzIDwtIFdoaWNoQ2VsbHMob2JqZWN0ID0gb2JqZWN0LCBpZGVudHMgPSBqKQogICAgICAgICAgICBmZWF0dXJlcy5hc3NheSA8LSB1bmlxdWUoeCA9IGludGVyc2VjdCh4ID0gZmVhdHVyZXMuYXNzYXksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcm93bmFtZXMoeCA9IGRhdGEudXNlKSkpCiAgICAgICAgICAgIGlmIChsZW5ndGgoeCA9IHRlbXAuY2VsbHMpID09IDEpIHsKICAgICAgICAgICAgICAgIGRhdGEudGVtcCA8LSAoZGF0YS51c2VbZmVhdHVyZXMuYXNzYXksIHRlbXAuY2VsbHNdKQogICAgICAgICAgICAgICAgaWYgKHNsb3QgPT0gImRhdGEiKSB7CiAgICAgICAgICAgICAgICAgICAgZGF0YS50ZW1wIDwtICBkYXRhLnRlbXAKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAobGVuZ3RoKHggPSB0ZW1wLmNlbGxzKSA+IDEpIHsKICAgICAgICAgICAgICAgIGRhdGEudGVtcCA8LSBhcHBseShYID0gZGF0YS51c2VbZmVhdHVyZXMuYXNzYXksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZW1wLmNlbGxzLCBkcm9wID0gRkFMU0VdLCBNQVJHSU4gPSAxLCBGVU4gPSBmeG4uYXZlcmFnZSkKICAgICAgICAgICAgfQogICAgICAgICAgICBkYXRhLmFsbCA8LSBjYmluZChkYXRhLmFsbCwgZGF0YS50ZW1wKQogICAgICAgICAgICBjb2xuYW1lcyh4ID0gZGF0YS5hbGwpW25jb2woeCA9IGRhdGEuYWxsKV0gPC0gagogICAgICAgICAgICBpZiAodmVyYm9zZSkgewogICAgICAgICAgICAgICAgbWVzc2FnZShwYXN0ZSgiRmluaXNoZWQgYXZlcmFnaW5nIiwgYXNzYXlzW2ldLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZvciBjbHVzdGVyIiwgaikpCiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKGkgPT0gMSkgewogICAgICAgICAgICAgICAgaWRlbnQubmV3IDwtIGMoaWRlbnQubmV3LCBhcy5jaGFyYWN0ZXIoeCA9IGlkZW50Lm9yaWdbdGVtcC5jZWxsc1sxXV0pKQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIG5hbWVzKHggPSBpZGVudC5uZXcpIDwtIGxldmVscyh4ID0gSWRlbnRzKG9iamVjdCkpCiAgICAgICAgZGF0YS5yZXR1cm5bW2ldXSA8LSBkYXRhLmFsbAogICAgICAgIG5hbWVzKHggPSBkYXRhLnJldHVybilbaV0gPC0gYXNzYXlzW1tpXV0KICAgIH0KICAgIGlmIChyZXR1cm4uc2V1cmF0KSB7CiAgICAgICAgdG9SZXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGRhdGEucmV0dXJuW1sxXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0ID0gIkF2ZXJhZ2UiLCBhc3NheSA9IG5hbWVzKHggPSBkYXRhLnJldHVybilbMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi4pCiAgICAgICAgaWYgKGxlbmd0aCh4ID0gZGF0YS5yZXR1cm4pID4gMSkgewogICAgICAgICAgICBmb3IgKGkgaW4gMjpsZW5ndGgoeCA9IGRhdGEucmV0dXJuKSkgewogICAgICAgICAgICAgICAgdG9SZXRbW25hbWVzKHggPSBkYXRhLnJldHVybilbaV1dXSA8LSBDcmVhdGVBc3NheU9iamVjdChjb3VudHMgPSBkYXRhLnJldHVybltbaV1dKQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGlmIChEZWZhdWx0QXNzYXkob2JqZWN0ID0gb2JqZWN0KSAlaW4lIG5hbWVzKHggPSBkYXRhLnJldHVybikpIHsKICAgICAgICAgICAgRGVmYXVsdEFzc2F5KG9iamVjdCA9IHRvUmV0KSA8LSBEZWZhdWx0QXNzYXkob2JqZWN0ID0gb2JqZWN0KQogICAgICAgIH0KICAgICAgICBJZGVudHModG9SZXQsIGNlbGxzID0gY29sbmFtZXMoeCA9IHRvUmV0KSkgPC0gaWRlbnQubmV3W2NvbG5hbWVzKHggPSB0b1JldCldCiAgICAgICAgSWRlbnRzKG9iamVjdCA9IHRvUmV0KSA8LSBmYWN0b3IoeCA9IElkZW50cyhvYmplY3QgPSB0b1JldCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGFzLmNoYXJhY3Rlcih4ID0gb3JpZy5sZXZlbHMpLCBvcmRlcmVkID0gVFJVRSkKICAgICAgICB0b1JldCA8LSBOb3JtYWxpemVEYXRhKG9iamVjdCA9IHRvUmV0LCB2ZXJib3NlID0gdmVyYm9zZSkKICAgICAgICB0b1JldCA8LSBTY2FsZURhdGEob2JqZWN0ID0gdG9SZXQsIHZlcmJvc2UgPSB2ZXJib3NlKQogICAgICAgIHJldHVybih0b1JldCkKICAgIH0KICAgIGVsc2UgewogICAgICAgIHJldHVybihkYXRhLnJldHVybikKICAgIH0KfQpgYGAKCgoKRmluZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBtYXJrZXJzCgpgYGB7cn0KY29tYmluZWQubWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhvYmplY3QgPSBjb21iaW5lZCwgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgdGhyZXNoLnVzZSA9IDAuMjUpCmNvbWJpbmVkLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbigyLCBhdmdfbG9nRkMpCgpLTy5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKG9iamVjdCA9IEtPLmNlbGxzLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjI1LCB0aHJlc2gudXNlID0gMC4yNSkKCnJlc3BvbnNlMyA8LSBGaW5kTWFya2Vycyhjb21iaW5lZCwgaWRlbnQuMSA9IGMoIjFfRzE3MUIiLCIwX0cxNzFCIiwiMl9HMTcxQiIpLCBpZGVudC4yID0gYygiMV9HMTcxQyIsICIwX0cxNzFDIiwiMl9HMTcxQyIpLCB2ZXJib3NlID0gVFJVRSwgdGVzdC51c2UgPSAiTUFTVCIsIGxvZ2ZjLnRocmVzaG9sZCA9IEZBTFNFLG1pbi5wY3QgPSBGQUxTRSkKaGVhZChyZXNwb25zZTMsIG4gPSAxNSkKCmBgYAoKCgoKVmlzdWFsaXplIHRvcCBnZW5lcyBpbiBwcmluY2lwYWwgY29tcG9uZW50cwoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTh9ClBDSGVhdG1hcChvYmplY3QgPSB0aXNzMSwgcGMudXNlID0gMTozLCBjZWxscy51c2UgPSA1MDAsIGRvLmJhbGFuY2VkID0gVFJVRSwgbGFiZWwuY29sdW1ucyA9IEZBTFNFLCBudW0uZ2VuZXMgPSA4KQpgYGAKCkxhdGVyIG9uIChpbiBGaW5kQ2x1c3RlcnMgYW5kIFRTTkUpIHlvdSB3aWxsIHBpY2sgYSBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMgdG8gdXNlLiBUaGlzIGhhcyB0aGUgZWZmZWN0IG9mIGtlZXBpbmcgdGhlIG1ham9yIGRpcmVjdGlvbnMgb2YgdmFyaWF0aW9uIGluIHRoZSBkYXRhIGFuZCwgaWRlYWxseSwgc3VwcmVzc2luZyBub2lzZS4gVGhlcmUgaXMgbm8gY29ycmVjdCBhbnN3ZXIgdG8gdGhlIG51bWJlciB0byB1c2UsIGJ1dCBhIGRlY2VudCBydWxlIG9mIHRodW1iIGlzIHRvIGdvIHVudGlsIHRoZSBwbG90IHBsYXRlYXVzLgoKYGBge3J9ClBDRWxib3dQbG90KG9iamVjdCA9IHRpc3MxKQpgYGAKCkNob29zZSB0aGUgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHVzZS4KYGBge3J9CiMgU2V0IG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cy4gCm4ucGNzID0gMTAKYGBgCgpUaGUgY2x1c3RlcmluZyBpcyBwZXJmb3JtZWQgYmFzZWQgb24gYSBuZWFyZXN0IG5laWdoYm9ycyBncmFwaC4gQ2VsbHMgdGhhdCBoYXZlIHNpbWlsYXIgZXhwcmVzc2lvbiB3aWxsIGJlIGpvaW5lZCB0b2dldGhlci4gVGhlIExvdXZhaW4gYWxnb3JpdGhtIGxvb2tzIGZvciBncm91cHMgb2YgY2VsbHMgd2l0aCBoaWdoIG1vZHVsYXJpdHktLW1vcmUgY29ubmVjdGlvbnMgd2l0aGluIHRoZSBncm91cCB0aGFuIGJldHdlZW4gZ3JvdXBzLiBUaGUgcmVzb2x1dGlvbiBwYXJhbWV0ZXIgZGV0ZXJtaW5lcyB0aGUgc2NhbGUuIEhpZ2hlciByZXNvbHV0aW9uIHdpbGwgZ2l2ZSBtb3JlIGNsdXN0ZXJzLCBsb3dlciByZXNvbHV0aW9uIHdpbGwgZ2l2ZSBmZXdlci4KCkZvciB0aGUgdG9wLWxldmVsIGNsdXN0ZXJpbmcsIGFpbSB0byB1bmRlci1jbHVzdGVyIGluc3RlYWQgb2Ygb3Zlci1jbHVzdGVyLiBJdCB3aWxsIGJlIGVhc3kgdG8gc3Vic2V0IGdyb3VwcyBhbmQgZnVydGhlciBhbmFseXplIHRoZW0gYmVsb3cuCgpgYGB7cn0KIyBTZXQgcmVzb2x1dGlvbiAKcmVzLnVzZWQgPC0gNAp0aXNzMSA8LSBGaW5kQ2x1c3RlcnMob2JqZWN0ID0gdGlzczEsIHJlZHVjdGlvbi50eXBlID0gInBjYSIsIGRpbXMudXNlID0gMTpuLnBjcywgCiAgICByZXNvbHV0aW9uID0gcmVzLnVzZWQsIHByaW50Lm91dHB1dCA9IDAsIHNhdmUuU05OID0gVFJVRSwgZm9yY2UucmVjYWxjID0gVFJVRSkKYGBgCgpXZSB1c2UgVFNORSBzb2xlbHkgdG8gdmlzdWFsaXplIHRoZSBkYXRhLgpgYGB7cn0KIyBJZiBjZWxscyBhcmUgdG9vIHNwcmVhZCBvdXQsIHlvdSBjYW4gcmFpc2UgdGhlIHBlcnBsZXhpdHkuIElmIHlvdSBoYXZlIGZldyBjZWxscywgdHJ5IGEgbG93ZXIgcGVycGxleGl0eSAoYnV0IG5ldmVyIGxlc3MgdGhhbiAxMCkuCnRpc3MxIDwtIFJ1blRTTkUob2JqZWN0ID0gdGlzczEsIGRpbXMudXNlID0gMTpuLnBjcywgc2VlZC51c2UgPSAxMCwgcGVycGxleGl0eT0zMCkKYGBgCgpgYGB7cn0KVFNORVBsb3Qob2JqZWN0ID0gdGlzczEsIGRvLmxhYmVsID0gVCwgcHQuc2l6ZSA9IDEuMiwgbGFiZWwuc2l6ZSA9IDQpCmBgYAojIyBDb21wYXJlIHRvIHByZXZpb3VzIGFubm90YXRpb25zCmBgYHtyfQpwcmV2aW91c19hbm5vdGF0aW9uID0gcmVhZC5jc3YoIi9Vc2Vycy9ra2FycmkvRG9jdW1lbnRzL0xhYi9TaW5nbGVfY2VsbF9wcm9qZWN0L2Ryb3BzZXEvTGl2ZXJfZHJvcGxldF9hbm5vdGF0aW9uLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKY29scyA9IGMoJ2ZyZWVfYW5ub3RhdGlvbicsICdjZWxsX29udG9sb2d5X2NsYXNzJykKICAgIGZvciAoY29sIGluIGNvbHMpewogICAgICBwcmV2aW91c19jb2wgPSBwYXN0ZTAoJ3ByZXZpb3VzXycsIGNvbCkKICAgICAgdGlzczFAbWV0YS5kYXRhWywgcHJldmlvdXNfY29sXSA8LSAiTkEiCiAgICAgIHRpc3MxQG1ldGEuZGF0YVthcy5jaGFyYWN0ZXIocHJldmlvdXNfYW5ub3RhdGlvbiRYKSwgcHJldmlvdXNfY29sXSA8LSBwcmV2aW91c19hbm5vdGF0aW9uWywgY29sXQogICAgICBwcmludCh0YWJsZSh0aXNzMUBtZXRhLmRhdGFbLCBwcmV2aW91c19jb2xdKSkKICAgICAgcHJpbnQodGFibGUodGlzczFAbWV0YS5kYXRhWywgcHJldmlvdXNfY29sXSwgdGlzc0BpZGVudCkpCiAgICAgIAogICAgfQogICAgCnRpc3MxID0gY29tcGFyZV9wcmV2aW91c19hbm5vdGF0aW9uKHRpc3MxLCB0aXNzdWVfb2ZfaW50ZXJlc3QsICJkcm9wbGV0IikKVFNORVBsb3Qob2JqZWN0ID0gdGlzczEsIGRvLnJldHVybiA9IFRSVUUsIGdyb3VwLmJ5ID0gInByZXZpb3VzX2NlbGxfb250b2xvZ3lfY2xhc3MiKQp0YWJsZSh0aXNzMUBtZXRhLmRhdGFbLCAicHJldmlvdXNfY2VsbF9vbnRvbG9neV9jbGFzcyJdLCB0aXNzQGlkZW50KQpgYGAKCgpgYGB7cn0KdGlzczEgPSBjb21wYXJlX3ByZXZpb3VzX2Fubm90YXRpb24odGlzczEsIHRpc3N1ZV9vZl9pbnRlcmVzdCwgImRyb3BsZXQiKQpUU05FUGxvdChvYmplY3QgPSB0aXNzMSwgZG8ucmV0dXJuID0gVFJVRSwgZ3JvdXAuYnkgPSAicHJldmlvdXNfY2VsbF9vbnRvbG9neV9jbGFzcyIpCnRhYmxlKHRpc3MxQG1ldGEuZGF0YVssICJwcmV2aW91c19jZWxsX29udG9sb2d5X2NsYXNzIl0sIHRpc3MxQGlkZW50KQpgYGAKCgpgYGB7cn0KVFNORVBsb3QodGlzczEsIGdyb3VwLmJ5PSJtb3VzZS5zZXgiKQpUU05FUGxvdCh0aXNzMSwgZ3JvdXAuYnk9Im1vdXNlLmlkIikKYGBgCgoKU2lnbmlmaWNhbnQgZ2VuZXM6CgpoZXBhdG9jeXRlOiBBbGIsIFR0ciwgQXBvYTEsIGFuZCBTZXJwaW5hMWMKcGVyaWNlbnRyYWw6IEN5cDJlMSwgR2x1bCwgT2F0LCBHdWxvCm1pZGxvYnVsYXI6IEFzczEsIEhhbXAsIEdzdHAxLCBVYmIKcGVyaXBvcnRhbDogQ3lwMmYyLCBQY2sxLCBIYWwsIENkaDEKCmVuZG90aGVsaWFsIGNlbGxzOiBQZWNhbTEsIE5ycDEsIEtkcisgYW5kIE9pdDMrCkt1cHBmZXIgY2VsbHM6IEVtcjEsIENsZWM0ZiwgQ2Q2OCwgSXJmNwpOSy9OS1QgY2VsbHM6IFphcDcwLCBJbDJyYiwgTmtnNywgQ3hjcjYsIEtscjFjLCBHem1hCkIgY2VsbHM6IENkNzlhLCBDZDc5YiwgQ2Q3NCBhbmQgQ2QxOQpJbW11bmUgY2VsbHM6IFB0cHJjCgoKCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD0xNiwgZmlnLndpZHRoPTEyfQojIEhlcGF0aWMgbWFya2VyCkZlYXR1cmVQbG90KHRpc3MxLCBjKGdlbmVzX2hlcCksIHB0LnNpemUgPSAxLCBuQ29sID0gNCwgY29scy51c2UgPSBjKCJncmV5IiwgInJlZCIpKQoKIyBFbmRvdGhlbGlhbCBtYXJrZXJzCkZlYXR1cmVQbG90KHRpc3MxLCBjKGdlbmVzX2VuZG8pLCBwdC5zaXplID0gMSwgbkNvbCA9IDQsIGNvbHMudXNlID0gYygiZ3JleSIsICJyZWQiKSkKCiMgS3VwZmZlciBjZWxscwpGZWF0dXJlUGxvdCh0aXNzMSwgYyhnZW5lc19rdXBwZmVyKSwgcHQuc2l6ZSA9IDEsIG5Db2wgPSA0LCBjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIikpCgojIGdlbmVzX25rCkZlYXR1cmVQbG90KHRpc3MxLCBjKGdlbmVzX25rKSwgcHQuc2l6ZSA9IDEsIG5Db2wgPSA0LCBjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIikpCgojIGdlbmVzX2IKRmVhdHVyZVBsb3QodGlzczEsIGMoZ2VuZXNfYiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCwgY29scy51c2UgPSBjKCJncmV5IiwgInJlZCIpKQoKIyBnZW5lcyBiaWxlIGR1Y3QgZW5kbyBjZWxscwpGZWF0dXJlUGxvdCh0aXNzMSwgYyhnZW5lc19iZWMpLCBwdC5zaXplID0gMSwgbkNvbCA9IDQsIGNvbHMudXNlID0gYygiZ3JleSIsICJyZWQiKSkKCiMgZ2VuZXMgaW1tdW5lCkZlYXR1cmVQbG90KHRpc3MxLCBjKGdlbmVzX2ltbXVuZSksIHB0LnNpemUgPSAxLCBuQ29sID0gNCwgY29scy51c2UgPSBjKCJncmV5IiwgInJlZCIpKQoKCmBgYAoKRG90cGxvdHMgbGV0IHlvdSBzZWUgdGhlIGludGVuc2l0eSBvZiBleHBwcmVzc2lvbiBhbmQgdGhlIGZyYWN0aW9uIG9mIGNlbGxzIGV4cHJlc3NpbmcgZm9yIGVhY2ggb2YgeW91ciBnZW5lcyBvZiBpbnRlcmVzdC4KVGhlIHJhZGl1cyBzaG93cyB5b3UgdGhlIHBlcmNlbnQgb2YgY2VsbHMgaW4gdGhhdCBjbHVzdGVyIHdpdGggYXQgbGVhc3Qgb25lIHJlYWQgc2VxdWVuY2VkIGZyb20gdGhhdCBnZW5lLiBUaGUgY29sb3IgbGV2ZWwgaW5kaWNhdGVzIHRoZSBhdmVyYWdlClotc2NvcmUgb2YgZ2VuZSBleHByZXNzaW9uIGZvciBjZWxscyBpbiB0aGF0IGNsdXN0ZXIsIHdoZXJlIHRoZSBzY2FsaW5nIGlzIGRvbmUgb3ZlciB0YWtlbiBvdmVyIGFsbCBjZWxscyBpbiB0aGUgc2FtcGxlLgoKI1dlIGhhdmUgdmFyaW91cyBpbW11bmUgY2VsbCB0eXBlcyBpbiB0aGUgbGFzdCBjbHVzdGVyCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMH0KRG90UGxvdCh0aXNzMSwgYyhnZW5lc19rdXBwZmVyLCBnZW5lc19uaywgZ2VuZXNfYiwgIlB0cHJjIiksIHBsb3QubGVnZW5kID0gVCwgY29sLm1heCA9IDIuNSwgZG8ucmV0dXJuID0gVCkgKyBjb29yZF9mbGlwKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CkRvdFBsb3QodGlzczEsIGMoZ2VuZXNfaGVwX21haW4sIGdlbmVzX2VuZG8sIGdlbmVzX25rLCBnZW5lc19rdXBwZmVyLCBnZW5lc19iZWNfYl9pbW11bmUpLCBwbG90LmxlZ2VuZCA9IFQsIGNvbC5tYXggPSAyLjUsIGRvLnJldHVybiA9IFQpICsgY29vcmRfZmxpcCgpCmBgYAoKVXNpbmcgdGhlIG1hcmtlcnMgYWJvdmUsIHdlIGNhbiBjb25maWRlbnRhbHkgbGFiZWwgbWFueSBvZiB0aGUgY2x1c3RlcnM6CgoxOTogZW5kb3RoZWxpYWwgY2VsbHMKMjA6IGJpbGUgZHVjdCBlcGl0aGVsaWFsIGNlbGxzCjIxOiBpbW11bmUgY2VsbHMKcmVzdCBhcmUgaGVwYXRvY3l0ZXMKCldlIHdpbGwgYWRkIHRob3NlIGNlbGxfb250b2xvZ3lfY2xhc3NlcyB0byB0aGUgZGF0YXNldC4KCmBgYHtyfQp0aXNzMSA8LSBTdGFzaElkZW50KG9iamVjdCA9IHRpc3MxLCBzYXZlLm5hbWUgPSAiY2x1c3Rlci5pZHMiKQpjbHVzdGVyLmlkcyA8LSBjKDAsMSwyLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQsMTUsMTYsMTcsMTgsMTksMjApCmZyZWVfYW5ub3RhdGlvbiA8LSBjKAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogIE5BLAogICJiaWxlIGR1Y3QgZXBpdGhlbGlhbCBjZWxscyIsCiAgImVuZG90aGVsaWFsIGNlbGwgb2YgaGVwYXRpYyBzaW51c29pZCIsCiAgTkEKICApCmNlbGxfb250b2xvZ3lfY2xhc3MgPC0gYygKICAiaGVwYXRvY3l0ZSIsCiAgImhlcGF0b2N5dGUiLAogICJoZXBhdG9jeXRlIiwKICAiaGVwYXRvY3l0ZSIsCiAgImhlcGF0b2N5dGUiLAogICJoZXBhdG9jeXRlIiwKICAiaGVwYXRvY3l0ZSIsCiAgImhlcGF0b2N5dGUiLAogICJoZXBhdG9jeXRlIiwKICAiaGVwYXRvY3l0ZSIsCiAgImhlcGF0b2N5dGUiLAogICJoZXBhdG9jeXRlIiwKICAiaGVwYXRvY3l0ZSIsCiAgImhlcGF0b2N5dGUiLAogICJoZXBhdG9jeXRlIiwKICAiaGVwYXRvY3l0ZSIsCiAgImhlcGF0b2N5dGUiLAogICJoZXBhdG9jeXRlIiwKICAiZHVjdCBlcGl0aGVsaWFsIGNlbGwiLAogICJlbmRvdGhlbGlhbCBjZWxsIG9mIGhlcGF0aWMgc2ludXNvaWQiLAogICJoZXBhdG9jeXRlIikKdGlzczEgPSBzdGFzaF9hbm5vdGF0aW9ucyh0aXNzMSwgY2x1c3Rlci5pZHMsIGZyZWVfYW5ub3RhdGlvbiwgY2VsbF9vbnRvbG9neV9jbGFzcykKYGBgCgojIyBDaGVja2luZyBmb3IgYmF0Y2ggZWZmZWN0cwoKQ29sb3IgYnkgbWV0YWRhdGEsIGxpa2UgcGxhdGUgYmFyY29kZSwgdG8gY2hlY2sgZm9yIGJhdGNoIGVmZmVjdHMuCmBgYHtyfQpUU05FUGxvdChvYmplY3QgPSB0aXNzMSwgZG8ucmV0dXJuID0gVFJVRSwgZ3JvdXAuYnkgPSAiY2hhbm5lbCIpClRTTkVQbG90KG9iamVjdCA9IHRpc3MxLCBkby5yZXR1cm4gPSBUUlVFLCBncm91cC5ieSA9ICJmcmVlX2Fubm90YXRpb24iKQoKYGBgCgojIyBTdWJjbHVzdGVyCgpMZXQncyBkcmlsbCBkb3duIG9uIHRoZSBoZXBhdG9jeXRlcy4KCmBgYHtyfQpzdWJ0aXNzMSA9IFN1YnNldERhdGEodGlzczEsIGlkZW50LnVzZSA9IGMoMCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywyMCkpCgoKYGBgCgpgYGB7cn0Kc3VidGlzczEgPC0gc3VidGlzczEgJT4lIFNjYWxlRGF0YSgpICU+JQogIEZpbmRWYXJpYWJsZUdlbmVzKGRvLnBsb3QgPSBGQUxTRSwgeC5oaWdoLmN1dG9mZiA9IEluZiwgeS5jdXRvZmYgPSAwLjUpICU+JQogIFJ1blBDQShkby5wcmludCA9IEZBTFNFKQpgYGAKCmBgYHtyfQpQQ0hlYXRtYXAob2JqZWN0ID0gc3VidGlzczEsIHBjLnVzZSA9IDE6MywgY2VsbHMudXNlID0gMjAsIGRvLmJhbGFuY2VkID0gVFJVRSwgbGFiZWwuY29sdW1ucyA9IEZBTFNFLCBudW0uZ2VuZXMgPSA4KQpQQ0VsYm93UGxvdChzdWJ0aXNzMSkKYGBgCgoKYGBge3J9CnN1Yi5uLnBjcyA9IDgKc3ViLnJlcy51c2UgPSAwLjUKc3VidGlzczEgPC0gc3VidGlzczEgJT4lIEZpbmRDbHVzdGVycyhyZWR1Y3Rpb24udHlwZSA9ICJwY2EiLCBkaW1zLnVzZSA9IDE6c3ViLm4ucGNzLAogICAgcmVzb2x1dGlvbiA9IHN1Yi5yZXMudXNlLCBwcmludC5vdXRwdXQgPSAwLCBzYXZlLlNOTiA9IFRSVUUsIGZvcmNlLnJlY2FsYyA9IFRSVUUpICU+JQogICAgUnVuVFNORShkaW1zLnVzZSA9IDE6c3ViLm4ucGNzLCBzZWVkLnVzZSA9IDEwLCBwZXJwbGV4aXR5PTgpClRTTkVQbG90KG9iamVjdCA9IHN1YnRpc3MxLCBkby5sYWJlbCA9IFQsIHB0LnNpemUgPSAxLCBsYWJlbC5zaXplID0gNCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD0yNSwgZmlnLndpZHRoPTI1fQpGZWF0dXJlUGxvdChzdWJ0aXNzMSwgZ2VuZXNfaGVwLGNvbHMudXNlID0gYygiZ3JleSIsICJyZWQiKSwgcHQuc2l6ZSA9IDQsIG5Db2wgPSA0KQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KRG90UGxvdChzdWJ0aXNzMSwgYWxsX2dlbmVzLCBjb2wubWF4ID0gMi41LCBwbG90LmxlZ2VuZCA9IFQsIGRvLnJldHVybiA9IFQpICsgY29vcmRfZmxpcCgpCmBgYAoKYGBge3J9CkJ1aWxkQ2x1c3RlclRyZWUoc3VidGlzczEpCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD04fQojZmVtYWxlIGdlbmVzIGhhdmUgbG93ZXIgZXhwcmVzc2lvbiBpbiBjbHVzdGVyIDYgcmVsYXRpdmUgdG8gb3RoZXIgZmVtYWxlIGNsdXN0ZXJzLCBlc3BlY2FsbHkgWGlzdApGZWF0dXJlUGxvdChzdWJ0aXNzMSxjKCdNdXAyMCcsICdNdXAxJywnTXVwMTInLCAnTXVwMjEnLCAnQ3lwMmQ5JywgJ1hpc3QnLCAnQTFiZycsICdDeXAyYzY5JyksY29scy51c2UgPSBjKCJncmV5IiwgInJlZCIpLCBwdC5zaXplID0gMywgbkNvbCA9IDIpCgpEb3RQbG90KHRpc3MxLGMoJ011cDIwJywgJ011cDEnLCdNdXAxMicsICdNdXAyMScsICdDeXAyZDknLCAnWGlzdCcsICdBMWJnJywgJ0N5cDJjNjknKSwgcGxvdC5sZWdlbmQgPSBULCBjb2wubWF4ID0gMi41LCBkby5yZXR1cm4gPSBUKSArIGNvb3JkX2ZsaXAoKQoKYGBgCgoKRnJvbSB0aGVzZSBnZW5lcywgaXQgYXBwZWFycyB0aGF0IHRoZSBjbHVzdGVycyByZXByZXNlbnQ6CgowOiBtaWRsb2J1bGFyIG1hbGUKMTogcGVyaWNlbnRyYWwgZmVtYWxlCjI6IHBlcmlwb3J0YWwgZmVtYWxlCjM6IHBlcmlwb3J0YWwgbWFsZQo0OiBtaWRsb2J1bGFyIG1hbGUKNTogcGVyaWNlbnRyYWwgbWFsZQo2OiBtaWRsb2J1bGFyIGZlbWFsZQo3OiBtaWRsb2J1bGFyIGZlbWFsZQoKVGhlIG11bHRpdHVkZSBvZiBjbHVzdGVycyBvZiBlYWNoIHR5cGUgY29ycmVzcG9uZCBtb3N0bHkgdG8gaW5kaXZpZHVhbCBhbmltYWxzL3NleGVzLgoKYGBge3J9CnRhYmxlKEZldGNoRGF0YShzdWJ0aXNzMSwgYygnbW91c2Uuc2V4JywnaWRlbnQnKSkgJT4lIGRyb3BsZXZlbHMoKSkKYGBgCgpgYGB7cn0Kc3ViLmNsdXN0ZXIuaWRzIDwtIGMoMCwgMSwgMiwgMywgNCwgNSwgNiwgNykKc3ViLmZyZWVfYW5ub3RhdGlvbiA8LSBjKCJwZXJpcG9ydGFsIGZlbWFsZSIsICJtaWRsb2J1bGFyIG1hbGUiLCAicGVyaWNlbnRyYWwgZmVtYWxlIiwgInBlcmlwb3J0YWwgbWFsZSIsICJtaWRsb2J1bGFyIG1hbGUiLCAicGVyaWNlbnRyYWwgbWFsZSIsICJtaWRsb2J1bGFyIGZlbWFsZSIsICJtaWRsb2J1bGFyIGZlbWFsZSIpCnN1Yi5jZWxsX29udG9sb2d5X2NsYXNzIDwtIGMoImhlcGF0b2N5dGUiLCAiaGVwYXRvY3l0ZSIsICJoZXBhdG9jeXRlIiwgImhlcGF0b2N5dGUiLCAiaGVwYXRvY3l0ZSIsICJoZXBhdG9jeXRlIiwgImhlcGF0b2N5dGUiLCAiaGVwYXRvY3l0ZSIpCnN1YnRpc3MxID0gc3Rhc2hfYW5ub3RhdGlvbnMoc3VidGlzczEsIHN1Yi5jbHVzdGVyLmlkcywgc3ViLmZyZWVfYW5ub3RhdGlvbiwgc3ViLmNlbGxfb250b2xvZ3lfY2xhc3MpCnRpc3MxID0gc3Rhc2hfc3VidGlzc19pbl90aXNzKHRpc3MxLCBzdWJ0aXNzMSkKYGBgCgpMaXZlciB6b25hdGlvbiBtYXJrZXJzCgpgYGB7cn0KZ2VuZXNfem9uZXMgPSBjKCdDeXAyZTEnLCAnR2x1bCcsICdPYXQnLCAnR3VsbycsCiAgICAgICAgICAgICAgJ0FzczEnLCAnSGFtcCcsICdHc3RwMScsICdVYmInLAogICAgICAgICAgICAgICdDeXAyZjInLCAnUGNrMScsICdIYWwnLCAnQ2RoMScpCgpGZWF0dXJlUGxvdChzdWJ0aXNzMSxjKGdlbmVzX3pvbmVzKSxjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCkKCkRvdFBsb3Qoc3VidGlzczEsYyhnZW5lc196b25lcyksIHBsb3QubGVnZW5kID0gVCwgY29sLm1heCA9IDIuNSwgZG8ucmV0dXJuID0gVCkgKyBjb29yZF9mbGlwKCkKCgpUU05FUGxvdChvYmplY3QgPSBzdWJ0aXNzMSwgZG8ubGFiZWwgPSBULCBwdC5zaXplID0gMSwgbGFiZWwuc2l6ZSA9IDQsIGdyb3VwLmJ5PSJmcmVlX2Fubm90YXRpb24iKQoKVFNORVBsb3Qob2JqZWN0ID0gdGlzczEsIGRvLmxhYmVsID0gVCwgcHQuc2l6ZSA9IDEsIGxhYmVsLnNpemUgPSA0LCBncm91cC5ieT0iZnJlZV9hbm5vdGF0aW9uIikKCmBgYAoKCgojIyMjIyMjIyMjCkZpbmQgY2x1c3RlciBtYXJrZXJzIGZvciBsbmNSTkFzCmBgYHtyfQoKTUlOX0xPR0ZPTERfQ0hBTkdFID0gMSAjIHNldCB0byBtaW5pbXVtIHJlcXVpcmVkIGF2ZXJhZ2UgbG9nIGZvbGQgY2hhbmdlIGluIGdlbmUgZXhwcmVzc2lvbi4KTUlOX1BDVF9DRUxMU19FWFBSX0dFTkUgPSAwLjEKCmFsbC5tYXJrZXJzID0gRmluZEFsbE1hcmtlcnModGlzczEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnBjdCA9IE1JTl9QQ1RfQ0VMTFNfRVhQUl9HRU5FLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IE1JTl9MT0dGT0xEX0NIQU5HRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5LnBvcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdC51c2U9ImJpbW9kIikgIyBsaWtlbGlob29kIHJhdGlvIHRlc3QKbG5jX2FsbF9tYXJrZXJzIDwtIGdyZXAocGF0dGVybiA9ICJebmNSTkEiLCB4PSByb3duYW1lcyhhbGwubWFya2VycyksIHZhbHVlID0gVFJVRSkKbG5jX2FsbF9tYXJrZXJzCgojWzFdICJuY1JOQV9pbnRlcl9jaHIxMF85MjA4MSIgIm5jUk5BX2ludHJhX2NocjE2XzEzMzgzIiAibmNSTkFfaW50ZXJfY2hyMTdfMTM2MDUiICJuY1JOQV9pbnRlcl9jaHIxNF8xMTgxNSIKI1s1XSAibmNSTkFfaW50ZXJfY2hyMThfMTQzNDQiCgpGZWF0dXJlUGxvdChzdWJ0aXNzMSxjKGxuY19hbGxfbWFya2VycyksY29scy51c2UgPSBjKCJncmV5IiwgInJlZCIpLCBwdC5zaXplID0gMSwgbkNvbCA9IDQpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIGxuY1JOQSBtYXJrZXJzLSBDRUxMIFRZUEUgTUFSS0VSICMjIyMjIyMjIyMjIwptYXJrZXJzLmhlcCA8LSBGaW5kTWFya2VycyhvYmplY3QgPSB0aXNzMSwgaWRlbnQuMSA9IGMoMCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywyMCksIGlkZW50LjIgPSBjKDE4LDE5KSxvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjI1LCB0aHJlc2gudXNlID0gMC4yNSkKbG5jX21hcmtlcnNfaGVwIDwtIGdyZXAocGF0dGVybiA9ICJebmNSTkEiLCB4PSByb3duYW1lcyhtYXJrZXJzLmhlcCksIHZhbHVlID0gVFJVRSkKbG5jX21hcmtlcnNfaGVwCkZlYXR1cmVQbG90KHRpc3MxLGMobG5jX21hcmtlcnNfaGVwKSxjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCkKRG90UGxvdCh0aXNzMSxsbmNfbWFya2Vyc19oZXAsIHBsb3QubGVnZW5kID0gVCwgY29sLm1heCA9IDIuNSwgZG8ucmV0dXJuID0gVCkgKyBjb29yZF9mbGlwKCkKI1sxXSAibmNSTkFfYXNfY2hyMTFfOTQyMyIgICAgICJuY1JOQV9hc19jaHI3XzYxNjYiICAgICAgIm5jUk5BX2ludGVyX2NocjRfMzI5NSIgICAibmNSTkFfaW50ZXJfY2hyMTdfMTQwMjYiCiNbNV0gIm5jUk5BX2ludGVyX2NocjNfMjkxNSIgICAibmNSTkFfaW50ZXJfY2hyNV80NTQ3IiAgICJuY1JOQV9pbnRlcl9jaHIxNV8xMjY4NCIKCgptYXJrZXJzLmhlcC5NQVNUIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHRpc3MxLCBpZGVudC4xID0gYygwLDEsMiwzLDQsNSw2LDcsOCw5LDEwLDExLDEyLDEzLDE0LDE1LDE2LDE3LDIwKSwgaWRlbnQuMiA9IGMoMTgsMTkpLG9ubHkucG9zID0gVFJVRSwgdGVzdC51c2UgPSAiTUFTVCIpCmxuY19tYXJrZXJzX2hlcF9NQVNUX1RBQkxFIDwtIHN1YnNldChtYXJrZXJzLmhlcC5NQVNULCBncmVwbCgiXm5jUk5BIiwgcm93bmFtZXMobWFya2Vycy5oZXAuTUFTVCkpKQpsbmNfbWFya2Vyc19oZXBfTUFTVCA8LSBncmVwKHBhdHRlcm4gPSAiXm5jUk5BIiwgeD0gcm93bmFtZXMobWFya2Vycy5oZXAuTUFTVCksIHZhbHVlID0gVFJVRSkKbG5jX21hcmtlcnNfaGVwX01BU1QKCgoKbWFya2Vycy5lbmRvIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHRpc3MxLCBpZGVudC4xID0gYygxOCwxOSksICBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjI1LCB0aHJlc2gudXNlID0gMC41KQpsbmNfbWFya2Vyc19lbmRvIDwtIGdyZXAocGF0dGVybiA9ICJebmNSTkEiLCB4PSByb3duYW1lcyhtYXJrZXJzLmVuZG8pLCB2YWx1ZSA9IFRSVUUpCmxuY19tYXJrZXJzX2VuZG8KRmVhdHVyZVBsb3QodGlzczEsYyhsbmNfbWFya2Vyc19lbmRvKSxjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCkKRG90UGxvdCh0aXNzMSxsbmNfbWFya2Vyc19lbmRvLCBwbG90LmxlZ2VuZCA9IFQsIGNvbC5tYXggPSAyLjUsIGRvLnJldHVybiA9IFQpICsgY29vcmRfZmxpcCgpCgoKIyJuY1JOQV9pbnRlcl9jaHIxNV8xMjc3MCIsICJuY1JOQV9pbnRlcl9jaHIxMl8xMDgxNyIsICJuY1JOQV9hc19jaHIxM18xMTQ1MSIsCgoKbWFya2Vycy5lbmRvLk1BU1QgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gdGlzczEsIGlkZW50LjEgPSAxOSwgdGVzdC51c2UgPSAiTUFTVCIgLG9ubHkucG9zID0gVFJVRSkKbG5jX21hcmtlcnNfZW5kb19NQVNUX1RBQkxFIDwtIHN1YnNldChtYXJrZXJzLmVuZG8uTUFTVCwgZ3JlcGwoIl5uY1JOQSIsIHJvd25hbWVzKG1hcmtlcnMuZW5kby5NQVNUKSkpCmxuY19tYXJrZXJzX2VuZG9fTUFTVCA8LSBncmVwKHBhdHRlcm4gPSAiXm5jUk5BIiwgeD0gcm93bmFtZXMobWFya2Vycy5lbmRvLk1BU1QpLCB2YWx1ZSA9IFRSVUUpCmxuY19tYXJrZXJzX2VuZG9fTUFTVAoKCiMjIyMjIyMjIyMjIyMjIyMjIyBsbmNSTkEgZXhwcmVzc2lvbiAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMzCgojICJuY1JOQV9pbnRlcl9jaHIxN18xMzYwNSIgLCAibmNSTkFfaW50cmFfY2hyMTZfMTMzODMiCgojIyMjIyMjIyMjIFBlcmlwb3JhbCBtYXJrZXJzLSB6b25hdGlvbiBtYXJrZXJzICMjIyMjIyMjIyMjIwptYXJrZXJzLnBjIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHN1YnRpc3MxLCBpZGVudC4xID0gYygyLDUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBGQUxTRSwgbWluLnBjdCA9IDAuMDAxLCB0aHJlc2gudXNlID0gMC4wMDEsIHRlc3QudXNlID0gImJpbW9kIiApCgptYXJrZXJzLnBjLk1BU1QgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gc3VidGlzczEsIGlkZW50LjEgPSBjKDIsNSksIGlkZW50LjIgPSBjKDAsMyksIHRlc3QudXNlID0gIk1BU1QiICxvbmx5LnBvcyA9IFRSVUUpCmxuY19tYXJrZXJzX3BjIDwtIHN1YnNldChtYXJrZXJzLnBjLCBncmVwbCgiXm5jUk5BIiwgcm93bmFtZXMobWFya2Vycy5wYykpKQpsbmNfbWFya2Vyc19wYyA8LSBncmVwKHBhdHRlcm4gPSAiXm5jUk5BIiwgeD0gcm93bmFtZXMobWFya2Vycy5wYyksIHZhbHVlID0gVFJVRSkKbG5jX21hcmtlcnNfcGMgCgptYXJrZXJzLnBjLk1BU1QgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gc3VidGlzczEsIGlkZW50LjEgPSBjKDIsNSksIGlkZW50LjIgPSBjKDAsMyksIHRlc3QudXNlID0gIk1BU1QiICxvbmx5LnBvcyA9IFRSVUUpCmxuY19tYXJrZXJzX3BjX01BU1QgPC0gc3Vic2V0KG1hcmtlcnMucGMuTUFTVCwgZ3JlcGwoIl5uY1JOQSIsIHJvd25hbWVzKG1hcmtlcnMucGMuTUFTVCkpKQpsbmNfbWFya2Vyc19wY19NQVNUIDwtIGdyZXAocGF0dGVybiA9ICJebmNSTkEiLCB4PSByb3duYW1lcyhtYXJrZXJzLnBjLk1BU1QpLCB2YWx1ZSA9IFRSVUUpCmxuY19tYXJrZXJzX3BjX01BU1QKCkRvdFBsb3QodGlzczEsIGxuY19tYXJrZXJzX3BjLCBwbG90LmxlZ2VuZCA9IFQsIGNvbC5tYXggPSAyLjUsIGRvLnJldHVybiA9IFQsIGdyb3VwLmJ5PSJmcmVlX2Fubm90YXRpb24iKSArIGNvb3JkX2ZsaXAoKQpGZWF0dXJlUGxvdChzdWJ0aXNzMSxjKGxuY19tYXJrZXJzX3BjKSxjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCkKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIG1pZGxvYnVsYXIgZ2VuZXMgIyMjIyMjIyMjIyMjIwoKbWFya2Vycy5taWQgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gc3VidGlzczEsIGlkZW50LjEgPSBjKDEsNCw2LDcpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBGQUxTRSwgbWluLnBjdCA9IDAuMDAxLCB0aHJlc2gudXNlID0gMC4wNSkKCmxuY19tYXJrZXJzX21pZCA8LSBzdWJzZXQobWFya2Vycy5taWQsIGdyZXBsKCJebmNSTkEiLCByb3duYW1lcyhtYXJrZXJzLm1pZCkpKQpsbmNfbWFya2Vyc19taWQgPC0gZ3JlcChwYXR0ZXJuID0gIl5uY1JOQSIsIHg9IHJvd25hbWVzKG1hcmtlcnMubWlkKSwgdmFsdWUgPSBUUlVFKQpsbmNfbWFya2Vyc19taWQKRG90UGxvdCh0aXNzMSwgbG5jX21hcmtlcnNfbWlkLCBwbG90LmxlZ2VuZCA9IFQsIGNvbC5tYXggPSAyLjUsIGRvLnJldHVybiA9IFQsIGdyb3VwLmJ5PSJmcmVlX2Fubm90YXRpb24iKSArIGNvb3JkX2ZsaXAoKQoKCkZlYXR1cmVQbG90KHN1YnRpc3MxLGMobG5jX21hcmtlcnNfbWlkKSxjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCkKCgoKbWFya2Vycy5taWQuTUFTVCA8LSBGaW5kTWFya2VycyhvYmplY3QgPSBzdWJ0aXNzMSwgaWRlbnQuMSA9IGMoMSw0LDYsNyksdGVzdC51c2UgPSAiTUFTVCIsb25seS5wb3MgPSBUUlVFICkKCmxuY19tYXJrZXJzX21pZF9NQVNUX1RBQkxFIDwtIHN1YnNldChtYXJrZXJzLm1pZC5NQVNULCBncmVwbCgiXm5jUk5BIiwgcm93bmFtZXMobWFya2Vycy5taWQuTUFTVCkpKQpsbmNfbWFya2Vyc19taWRfTUFTVCA8LSBncmVwKHBhdHRlcm4gPSAiXm5jUk5BIiwgeD0gcm93bmFtZXMobWFya2Vycy5taWQuTUFTVCksIHZhbHVlID0gVFJVRSkKbG5jX21hcmtlcnNfbWlkX01BU1QKCgojIyMjIzMgcGVyaXBvcnRhbG1hcmtlciBnZW5lcyMjIyMjIyMjIyMjIzMKCm1hcmtlcnMucHAgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gc3VidGlzczEsIGlkZW50LjEgPSBjKDAsMyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gRkFMU0UsIG1pbi5wY3QgPSAwLjAwMSwgdGhyZXNoLnVzZSA9IDAuMDUpCgoKbG5jX21hcmtlcnNfcHAgPC0gc3Vic2V0KG1hcmtlcnMucHAsIGdyZXBsKCJebmNSTkEiLCByb3duYW1lcyhtYXJrZXJzLnBwKSkpCmxuY19tYXJrZXJzX3BwIDwtIGdyZXAocGF0dGVybiA9ICJebmNSTkEiLCB4PSByb3duYW1lcyhtYXJrZXJzLnBwKSwgdmFsdWUgPSBUUlVFKQpsbmNfbWFya2Vyc19wcAoKbWFya2Vycy5wcC5NQVNUIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHN1YnRpc3MxLCBpZGVudC4xID0gYygwLDMpLCBpZGVudC4yID0gYygyLDUpLHRlc3QudXNlID0gIk1BU1QiLG9ubHkucG9zID0gVFJVRSApCgpsbmNfbWFya2Vyc19wcF9NQVNUX1RBQkxFIDwtIHN1YnNldChtYXJrZXJzLnBwLk1BU1QsIGdyZXBsKCJebmNSTkEiLCByb3duYW1lcyhtYXJrZXJzLnBwLk1BU1QpKSkKbG5jX21hcmtlcnNfcHBfTUFTVCA8LSBncmVwKHBhdHRlcm4gPSAiXm5jUk5BIiwgeD0gcm93bmFtZXMobWFya2Vycy5wcC5NQVNUKSwgdmFsdWUgPSBUUlVFKQpsbmNfbWFya2Vyc19wcF9NQVNUCgpGZWF0dXJlUGxvdChzdWJ0aXNzMSxjKGxuY19tYXJrZXJzX3BwKSxjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCwgbWF4LmN1dG9mZiA9IDEpCkRvdFBsb3QodGlzczEsIGMobG5jX21hcmtlcnNfcHAsIkN5cDJlMSIsIkN5cDJmMiIpLCBwbG90LmxlZ2VuZCA9IFQsIGNvbC5tYXggPSAyLjUsIGRvLnJldHVybiA9IFQsIGdyb3VwLmJ5PSAiZnJlZV9hbm5vdGF0aW9uIikgKyBjb29yZF9mbGlwKCkKCgojIyMjIyMjIyMjIyMjIyMjIyMgYW1sZSBhbmQgZmVtYWxlIHNwZWNpZmljICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCm1hcmtlcnMuZmVtYWxlIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHN1YnRpc3MxLCBpZGVudC4xID0gYygwLDIsNiw3KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4xLCBsb2dmYy50aHJlc2hvbGQgPSAxKQoKbG5jX21hcmtlcnNfZmVtYWxlIDwtIHN1YnNldChtYXJrZXJzLmZlbWFsZSwgZ3JlcGwoIl5uY1JOQSIsIHJvd25hbWVzKG1hcmtlcnMuZmVtYWxlKSkpCmxuY19tYXJrZXJzX2ZlbWFsZSA8LSBncmVwKHBhdHRlcm4gPSAiXm5jUk5BIiwgeD0gcm93bmFtZXMobWFya2Vycy5mZW1hbGUpLCB2YWx1ZSA9IFRSVUUpCmxuY19tYXJrZXJzX2ZlbWFsZQoKRmVhdHVyZVBsb3Qoc3VidGlzczEsYyhsbmNfbWFya2Vyc19mZW1hbGUpLGNvbHMudXNlID0gYygiZ3JleSIsICJyZWQiKSwgcHQuc2l6ZSA9IDEsIG5Db2wgPSA0LCBtYXguY3V0b2ZmID0gMSkKRG90UGxvdCh0aXNzMSwgYyhsbmNfbWFya2Vyc19mZW1hbGUsIkN5cDJlMSIsIkN5cDJmMiIpLCBwbG90LmxlZ2VuZCA9IFQsIGNvbC5tYXggPSAyLjUsIGRvLnJldHVybiA9IFQsIGdyb3VwLmJ5PSAiZnJlZV9hbm5vdGF0aW9uIikgKyBjb29yZF9mbGlwKCkKCgoKbWFya2Vycy5tYWxlIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHN1YnRpc3MxLCBpZGVudC4xID0gYygxLDMsNCw1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4wMDEsIHRocmVzaC51c2UgPSAwLjA1KQoKbG5jX21hcmtlcnNfbWFsZSA8LSBzdWJzZXQobWFya2Vycy5tYWxlLCBncmVwbCgiXm5jUk5BIiwgcm93bmFtZXMobWFya2Vycy5tYWxlKSkpCmxuY19tYXJrZXJzX21hbGUgPC0gZ3JlcChwYXR0ZXJuID0gIl5uY1JOQSIsIHg9IHJvd25hbWVzKG1hcmtlcnMubWFsZSksIHZhbHVlID0gVFJVRSkKbG5jX21hcmtlcnNfbWFsZQoKRmVhdHVyZVBsb3Qoc3VidGlzczEsYyhsbmNfbWFya2Vyc19tYWxlKSxjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCwgbWF4LmN1dG9mZiA9IDEpCkRvdFBsb3QodGlzczEsIGMobG5jX21hcmtlcnNfbWFsZSwiQ3lwMmUxIiwiQ3lwMmYyIiksIHBsb3QubGVnZW5kID0gVCwgY29sLm1heCA9IDIuNSwgZG8ucmV0dXJuID0gVCwgZ3JvdXAuYnk9ICJmcmVlX2Fubm90YXRpb24iKSArIGNvb3JkX2ZsaXAoKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgRmVtYWxlIHpvbmF0ZSBzcGVjaWZpYyBnZW5lcyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbWFya2Vycy5wZXJpY2VudHJhbC5mZW1hbGUgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gdGlzczEsIGlkZW50LjEgPSBjKDYsMTEsMTQsMjApLCB0ZXN0LnVzZSA9ICJNQVNUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMSwgaWRlbnQuMiA9IGMoMiwzLDE1LDEyLDEzLDgsNSwxNiksIGxvZ2ZjLnRocmVzaG9sZCA9IDEpCgptYXJrZXJzLnBlcmlwb3J0YWwuZmVtYWxlIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHRpc3MxLCBpZGVudC4xID0gYygyLDMsMTUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4xLCBpZGVudC4yID0gYyg2LDExLDE0LDIwLDEyLDEzLDgsNSwxNiksIGxvZ2ZjLnRocmVzaG9sZCA9IDEpCgoKbWFya2Vycy5wZXJpY2VudHJhbC5tYWxlIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHRpc3MxLCBpZGVudC4xID0gYygxMywxMiksIHRlc3QudXNlID0gIk1BU1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4xLCBpZGVudC4yID0gYygyLDMsMTUsOCw1LDE2LDYsMTEsMTQsMjApLCBsb2dmYy50aHJlc2hvbGQgPSAxKQoKCm1hcmtlcnMucGVyaXBvcnRhbC5tYWxlIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHRpc3MxLCBpZGVudC4xID0gYyg4LDUsMTYpLCB0ZXN0LnVzZSA9ICJNQVNUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMSwgaWRlbnQuMiA9IGMoMiwzLDE1LDEzLDEyLDYsMTEsMTQsMjApLCBsb2dmYy50aHJlc2hvbGQgPSAxKQoKCgoKIyMjIyMjIyMjIyMjIyMjIHhlbm8tbG5jcyBDQVI/UlhSICMjIyMjIyMjIyMjIyMjIyMjIwoKRmVhdHVyZVBsb3QodGlzczEsYygibmNSTkFfaW50ZXJfY2hyMTVfMTI2ODQiLCJuY1JOQV9pbnRlcl9jaHI4Xzc0MzAiLCJuY1JOQV9pbnRlcl9jaHI3XzYyMjIiKSxjb2xzLnVzZSA9IGMoImdyZXkiLCAicmVkIiksIHB0LnNpemUgPSAxLCBuQ29sID0gNCwgbWF4LmN1dG9mZiA9IDEpCgoKCgoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgptYXJrZXJzLmVuZG8uMiA8LSBGaW5kTWFya2VycyhvYmplY3QgPSBzZXVyYXRfZHJvcCwgbG9nZmMudGhyZXNob2xkID0gMixpZGVudC4xID0gIkVuZG90aGVsaWFsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMjUsIHRocmVzaC51c2UgPSAwLjI1KQoKbG5jLmVuZG8uMiA8LSBncmVwKHBhdHRlcm4gPSAiXm5jUk5BIiwgeD0gcm93bmFtZXMobWFya2Vycy5lbmRvLjIpLCB2YWx1ZSA9IFRSVUUpCmxuYy5lbmRvLjIKCgoKCgpgYGAKCgoKWm9uYXRlZCBsbmNSTkFzIAoKYGBge3J9CnBwX3pvbnRhZWQgPC0gYygnbmNSTkFfaW50ZXJfY2hyMTRfMTIwMTYnLCduY1JOQV9hc19jaHIxOV8xNTA5MCcsJ25jUk5BX2ludGVyX2NocjEwXzkzNTEnLCduY1JOQV9pbnRlcl9jaHIxNl8xMzE3MCcsCiduY1JOQV9pbnRlcl9jaHIzXzI2OTcnLCduY1JOQV9pbnRlcl9jaHIxXzI3NCcsJ25jUk5BX2FzX2NocjZfNTUxOCcsJ25jUk5BX2ludGVyX2NocjE0XzEyMDY2JywnbmNSTkFfaW50cmFfY2hyMTJfMTA4NzEnLAonbmNSTkFfaW50ZXJfY2hyMTZfMTM1MTAnLCduY1JOQV9pbnRlcl9jaHIzXzIzMTQnLCduY1JOQV9pbnRlcl9jaHIxMF85MjY0LCduY1JOQV9pbnRlcl9jaHI5XzgxMjInKQoKCgoKCmBgYAoKIyMgQ2hlY2tpbmcgZm9yIGJhdGNoIGVmZmVjdHMKCkNvbG9yIGJ5IG1ldGFkYXRhLCBsaWtlIHBsYXRlIGJhcmNvZGUsIHRvIGNoZWNrIGZvciBiYXRjaCBlZmZlY3RzLgpgYGB7cn0KVFNORVBsb3Qob2JqZWN0ID0gc3VidGlzczEsIGRvLnJldHVybiA9IFRSVUUsIGdyb3VwLmJ5ID0gIm1vdXNlLnNleCIpCgpgYGAKCiMgRmluYWwgY29sb3JpbmcKCkNvbG9yIGJ5IGNlbGwgb250b2xvZ3kgY2xhc3Mgb24gdGhlIG9yaWdpbmFsIFRTTkUuCgpgYGB7cn0KVFNORVBsb3Qob2JqZWN0ID0gdGlzczEsIGRvLnJldHVybiA9IFRSVUUsIGdyb3VwLmJ5ID0gImNlbGxfb250b2xvZ3lfY2xhc3MiKQpgYGAKCiMgU2F2ZSB0aGUgUm9iamVjdCBmb3IgbGF0ZXIKCmBgYHtyfQpmaWxlbmFtZSA9IGhlcmUoJzAwX2RhdGFfaW5nZXN0JywgJzA0X3Rpc3MxdWVfcm9ial9nZW5lcmF0ZWQnLCAKICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJkcm9wbGV0XyIsIHRpc3MxdWVfb2ZfaW50ZXJlc3QsICJyZWZpbmVkY2VsbHNfc2V1cmF0X3Rpc3MxLlJvYmoiKSkKcHJpbnQoZmlsZW5hbWUpCnNhdmUodGlzczEsIGZpbGU9ZmlsZW5hbWUpCmBgYAoKYGBge3J9CiMgVG8gcmVsb2FkIGEgc2F2ZWQgb2JqZWN0CmZpbGVuYW1lID0gaGVyZSgnMDBfZGF0YV9pbmdlc3QnLCAnMDRfdGlzczF1ZV9yb2JqX2dlbmVyYXRlZCcsCiAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImRyb3BsZXRfIiwgdGlzc3VlX29mX2ludGVyZXN0LCAic2V1cmF0X3NtYXJ0ZHJvcC1pbnRlZ3JhdGVkLTgyNzIwMTkuUm9iaiIpKQpsb2FkKGZpbGU9ZmlsZW5hbWUpCmBgYAoKCiMgRXhwb3J0IHRoZSBmaW5hbCBtZXRhZGF0YQoKCmBgYHtyfQpzYXZlX2Fubm90YXRpb25fY3N2KHRpc3MxLCB0aXNzMXVlX29mX2ludGVyZXN0LCAiZHJvcGxldCIpCmBgYAo=