I) Filter the data based on the genotype likelihood (GL).
Based on our discussion of last week, we decided to apply the following filters:
1: The maf = 0.05
2: Call a SNP is it’s p-value is below \({10^{-6}}\)
3: Discard reads with mapping quality below 15 (minMapQ)
4: Discard bases with base quality below (minQ)
5: Keep SNPs present in at least 70% of the individuals i.e. 95 * 0.7 \(\approx\) 67 individuals
6: Sites with coverage ranging from 2 to 30 => -setMinDepth 190 -setMaxDepth 2850
This script was applied to each scaffold as a separate job using the following command:
“for i in $SCAFFOLD_NAME; do sbatch ./GL_per_scaffolds.sh $i; done”
The scaffold names can be retrieved from the fasta reference genome file (ajConsensus.fasta) using the following command: “SCAFFOLD_NAME=$(cat ajConsensus.fasta | grep”>“| awk ‘{print $1}’| sed ‘s/>//g’)”
#! /bin/bash
#SBATCH --job-name=GL_EcoHP
#SBATCH --partition=short
#SBATCH --mem=8GB
#SBATCH --cpus-per-task=1
#SBATCH --time=0-00:30:00
# Script name: GL_per_scaffolds.sh
################
# Define paths #
################
BAM_LIST="~/list_bam.txt" # List of path to the 95 bam files
PATH_RESULTS="~/GFL/All/$1"
GFL_File="Wasp_Pina_LD_Prunning_$1.glf"
REF="~/ajConsensus.fasta"
ANGSD="/data/p274105/tool_genomics/angsd"
mkdir -p $PATH_RESULTS
$ANGSD/angsd -GL 2 \
-doGlf 2 \
-doMaf 1 \
-doMajorMinor 1 \
-doPost 1 -doGeno 8 \
-ref ${REF} \
-r $1 \
-bam ${BAM_LIST} \
-uniqueOnly 1 -remove_bads 1 -only_proper_pairs 1 \
-trim 0 -C 50 -baq 1 \
-out ${mkdir -p $PATH_RESULTS}/${GFL_File} \
-nThreads 1 \
-minMapQ 15 \
-minQ 15 \
-SNP_pval 1e-6 \
-skipTriallelic 1 \
-minMaf 0.1 \
-minInd 67 \
-setMinDepth 190 -setMaxDepth 2850 -doCounts 1
=> This script generates 2 important type of files: “glf.geno.gz” and “glf.mafs.gz” for each scaffold.
Then I gathered the results of each scaffold in a single glf.mafs and glf.geno files using the commands below:
PATH_RESULTS="/data/p274105/Wasp_Pina/Data/GFL/All/
cd $PATH_RESULTS
(for i in 000000F; do cd $i; zcat *glf.mafs.gz | head -n1 ; cd ..; done ; \
for i in $(ls| grep -v -E "All|txt"); do cd $i; zcat *glf.mafs.gz | awk 'NR>1' ; cd ..; done)> All_Chr.glf.mafs
(for i in $(ls| grep -v -E "All|txt"); do cd $i; zcat *glf.geno.gz ; cd ..; done) > All_Chr.glf.geno
After filtering 130571 SNPs were kept.
IV) Phylogenetic relationships
The phylogenetic relationship were inferred using:
1: NgsDIST to calculate the distance matrix between the taxa and the bootstrap distances matrices.
2: FASTME to infer the resulting phylogenetic tree.
3: RAXML to replace the bootstrap support value on the main tree.
4: The tree was mid-point rooted using R package ape.
5: The tree was plotted using the R package ggtree.
#! /bin/bash
#SBATCH --job-name=ngsDistWasp
#SBATCH --partition=regular
#SBATCH --mem=6GB
#SBATCH --cpus-per-task=1
#SBATCH --time=2-22:30:00
# To calculate the distance matrix :
# https://github.com/mfumagalli/ngsTools/blob/master/TUTORIAL.md
# https://github.com/fgvieira/ngsDist
################
# Define paths #
################
PATH_RESULTS="~/Results/NJtree"
PATH_INPUTS="~/Results"
cat /data/p274105/Wasp_Pina/Data/New_pop.txt|cut -f 1 > $PATH_INPUTS/Labels.txt
FASTME="/data/p274105/tool_genomics/fastme-2.1.5/binaries/fastme-2.1.5-linux64"
##############
# NGSDIST
##############
# Programs needed
NGSDIST="/data/p274105/tool_genomics/ngsTools/ngsDist"
cd $PATH_INPUTS
GFL="Wasp_Pina_subset.glf.beagle.gz"
NSITES=`zcat $GFL| awk '{print $1}'| awk 'NR>1'|wc -l`
echo $NSITES
# 1000 boot, block 100 kb for whole genome SNPs (LD prunned)
module load GSL/2.5-GCC-8.2.0-2.31.1
$NGSDIST/ngsDist \
--n_threads 1 \
--verbose 1 \
--geno $PATH_INPUTS/$GFL \
--probs \
--n_ind 95 \
--pairwise_del \
--n_sites ${NSITES} \
--n_boot_rep 1000 \
--boot_block_size 5000 \
--labels $PATH_INPUTS/Labels.txt \
--out $PATH_RESULTS/output.dist
#--avg_nuc_dist \
echo "NGSDIST RAN"
##############
# FASTME
##############
$FASTME -i $PATH_RESULTS/output.dist \
-D 1001 \
-s \
-o $PATH_RESULTS/output.nwk
head -n 1 $PATH_RESULTS/output.nwk > $PATH_RESULTS/output.main.nwk
tail -n +2 $PATH_RESULTS/output.nwk | awk 'NF' > $PATH_RESULTS/output.boot.nwk
echo "FASTME ran"
#########
# RAXML #
#########
cd $PATH_RESULTS
module load RAxML/8.2.11-foss-2018a-mt-avx2
raxmlHPC -f b \
-t $PATH_RESULTS/output.main.nwk \
-z $PATH_RESULTS/output.boot.nwk \d
-m GTRCAT \
-T 1 \
-n output_final.tree
echo "RAXML ran"
The resulting phylogenetic tree was mid rooted and then plotted in R using the libraries ggtree and ape.
library(ggplot2)
library(ggtree)
library(ape)
library(phytools)
library(RColorBrewer)
setwd("/Users/yacinebenchehida/Desktop/Metadata/Results/NJtree/")
tree <- ggtree::read.tree("RAxML_bipartitions.output_final.tree") # There are different read.tree() functions that executes different commands. To avoid any ambiguity here I add ape:: before to say that I want to use the read.tree() fonction from the ape package. This tree is a maximum likelihood tree of the 6 species of the porpoises family (Phoconidae) reconstructed with PhyML.
#tree = ape::root(tree,node=109, resolve.root=TRUE)
#######################################
# Transform the tree to a ggtree tree #
#######################################
tree2 <- tree
tree2 <- midpoint.root(tree)
tree2 <- ggtree(tree2)
########################################
# Define a color code for each species #
########################################
data_color = cbind(tree2$data$label[1:95], rep("test")) # Combine the column with individual names with a new column that will contain the color associated to each individual in an objected called data_color
colnames(data_color) =c("species","color") # Give a name to the column of data_color
data_color = as.data.frame(data_color) # Transform data_color to a data frame
data_color[,2] = as.character(data_color[,2]) # Transform the color column into characters (insstead of factor)
pop = read.table("../../bam_location_correspondance.txt")
pop = data.frame(inds=pop$V1, locality = pop$V2)
pop$color = "NAN"
data_color$locality = "NAN"
species = c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Tokyo","Sendai") # Create a vector with the unique species name
color = brewer.pal(10,"Paired") # Create a vector with the color associated to each unique species name
info = cbind(species,color) # Combine the unique species and the color in an object called info
# Loop to attribute to each individuals its color
for (i in 1:95){
for (j in 1:10) {
if(grepl(info[j,1],pop[i,2])){
pop[i,3] = info[j,2]
}
}
}
# Loop to attribute to each individuals its color
for (i in 1:95){
for (j in 1:95) {
if(pop[j,1]==data_color[i,1]){
data_color[i,2] = pop[j,3]
data_color[i,3] = pop[j,2]
}
}
}
############################
# Define bootstraps groups #
############################
tree2$data$col <- cut(as.numeric(tree2$data$label),
breaks = c(0, 90, 100),
labels = c("<=90", ">90"))
for (i in 1:189){
for (j in 1:95) {
if(data_color[j,1]==tree2$data[i,4]){
tree2$data[i,4] = paste(tree2$data[i,4], data_color[j,3], sep = " ")
}
}
}
tree2$data$label = gsub("(\\w+) (\\w+)", "\\2",tree2$data$label, perl = TRUE)
#############################################
# Plot full tree to have the final topology #
#############################################
p <- ggtree(tree2$data) + geom_tiplab(size =4, color= data_color$color) + geom_point2(aes(subset=(!isTip), fill= tree2$data$col, size = tree2$data$col, shape=tree2$data$col)) + theme(legend.position="right") + scale_fill_manual(breaks = c("<=90", ">90") , values = c("green","red")) +
scale_size_manual(breaks = c("<=90", ">90") , values = c(1.2,2.5)) + scale_shape_manual(breaks = c("<=90", ">90") , values = c(21,21)) + guides(size = FALSE, shape=FALSE, fill = guide_legend(override.aes=list(shape=21))) + labs(fill="Bootstraps")
p = p + geom_treescale(x=0.30) + theme(legend.title = element_text(color = "black", size = 16),legend.text = element_text(color = "black", size = 14)) + theme(legend.background = element_rect(size=1, color = "black", linetype="solid"))
p = p + ggplot2::xlim(0, 0.40) + theme(legend.position = c(0.1, 0.9))
p

=> The tree is congruent with the analyses based on SNP calling.
V) PCA
1) Run PCangsd
PCangsd was used to get the PCA as well as to estimate the admixture proportion (option -admix). It was ran with different value of K ranging from 1 to 8. It was ran using the following command:
for i in {1..5}; do sbatch ./PCangsd.sh $i; done
#! /bin/bash
#SBATCH --job-name=PCAngsd
#SBATCH --partition=short
#SBATCH --mem=2GB
#SBATCH --cpus-per-task=1
#SBATCH --time=00:25:00
#Script name: PCangsd.sh
################
# Define paths #
################
PATH_GFL="~/Results"
OUTPUT_PATH="~/Results/PCangsd"
OUTPUT="PCAangsd_results_K$1"
PATH_SCRIPT="~/Script/PCangsd"
module load PCAngsd/0.98-foss-2018a-Python-3.6.4
cd $PATH_GFL
pcangsd.py -beagle ${PATH_GFL}/Wasp_Pina_subset.glf.beagle.gz \
-admix \ # estimate admixture proportion
-admix_K $1 \ # Estimate admixture proportion with K = $1 groups.
-admix_iter 500 \ # Perform 500 EM iterations for admixture estimations.
-admix_alpha 50 \
-o ${OUTPUT_PATH}/${OUTPUT} \
-threads 1 \
-e 5 \ # Use 5 eigenvalues to model individual allele frequencies
-admix_tole 1e-7 \ # Tolerance value for update in admixture estimations
-minMaf 0.10 # Make sure the maf it at 0.
2) PC1 and PC2
The raw PCA was plotted using this R commands
setwd("/Users/yacinebenchehida/Desktop/Metadata/Results/PCangsd/maf_0.05/")
library(RcppCNPy)
library(ggplot2)
library(RColorBrewer)
library(grid)
library(gridExtra)
library(ggplot2)
library(reshape2)
library(ggtree)
library(cowplot)
library(tidyverse)
library(patchwork)
library(plyr)
library(ggforce)
D <- npyLoad("PCAangsd_results_K5.cov.npy")
e <- eigen(D)
pop<-read.table("../../../bam_location_correspondance.txt")
colnames(pop)= c("ID","Location")
cumulative_variance = sum(unlist(e$values)) # Estimate the total variance of the data set
eigenvalues = as.data.frame(cbind(seq(1:length(e$values)),e$values,(e$values*100/(cumulative_variance))))
colnames(eigenvalues) = c("PC","Eigenvalue","Contributions")
population=unique(pop$Location)
myColors <- brewer.pal(10,"Paired")
All_data = cbind(e$vectors,pop)
data = cbind(All_data[,c(1,2)],pop)
colnames(data) = c("PC1","PC2","ID","Location")
data$Location <- factor(data$Location, levels= c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Tokyo","Sendai"))
data$reproduction = "ToBeAdded"
data[data$Location=="Iriomote" | data$Location=="Okinawa" | data$Location=="Amami",5] = c("Sexual")
data[data$reproduction!="Sexual",5] = "Asexual"
repro = data[,5]
ggplot(data, aes(x=-PC1, y=PC2,color=Location,shape=reproduction)) + geom_point(size=2) + theme_bw() +
xlab(paste("PC1 (",round(eigenvalues[1,3],digits=2)," %)",sep="")) +
ylab(paste("PC2 (",round(eigenvalues[2,3],digits=2)," %)",sep="")) +
scale_color_manual(values = myColors) + stat_ellipse(level = 0.95, size = 1)

=> The tree is consistent with the analyses based on SNP calling.
##################################
# Plot eigenvalues contributions #
##################################
ggplot(data=eigenvalues[1:10,], aes(x=PC, y=Contributions)) +
geom_bar(stat="identity", fill="steelblue") +
theme_bw() +
scale_x_continuous(breaks = unique(sort(eigenvalues$PC))) +
ylab("Contribution (%)") +
xlab("Principal Component")

scree<-eigenvalues[1:10,c(1,3)] %>%
ggplot(aes(x=PC,y=Contributions))+theme_bw()+
geom_col()+
labs(title="Scree plot",y="",x="")+theme(
title=element_text(size=8))
The contribution in % of each PC to the total variance of the PC is plot in the barplot above. 5 first PCs stand out and explain 61.8% of the total variance.
Below is the same scatter plot as before but trying to make it look more like the one of the manuscript:

3) Plot additional PCs (PC1 to PC5)
plots = list()
counter = 1
for (i in 1:5){
for (j in 1:5){
if(j>i){
if(j<5 || i<4){
data = cbind(All_data[,c(i,j)],pop,repro)
colnames(data) = c("PC1","PC2","ID","Location","Reproduction")
data$Location <- factor(data$Location, levels= c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Tokyo","Sendai"))
plot = ggplot(data, aes(x=-PC1, y=PC2,color=Location,shape=Reproduction)) + geom_point(size=2) + theme_bw() +
xlab(paste("PC",i," (",round(eigenvalues[i,3],digits=2)," %)",sep="")) +
ylab(paste("PC",j," (",round(eigenvalues[j,3],digits=2)," %)",sep="")) +
scale_color_manual(values = myColors) + theme(legend.position="none") #+ stat_ellipse(level = 0.95, size = 1)
plots[[counter]] = plot
counter = counter + 1
}
else{
data = cbind(All_data[,c(i,j)],pop,repro)
colnames(data) = c("PC1","PC2","ID","Location","Reproduction")
data$Location <- factor(data$Location, levels= c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Tokyo","Sendai","Missing"))
plot = ggplot(data, aes(x=-PC1, y=PC2,color=Location,shape=Reproduction)) + geom_point(size=2) + theme_bw() +
xlab(paste("PC",i," (",round(eigenvalues[i,3],digits=2)," %)",sep="")) +
ylab(paste("PC",j," (",round(eigenvalues[j,3],digits=2)," %)",sep="")) +
scale_color_manual(values = myColors) +
theme(legend.key.size = unit(0.5, 'cm')) #+ stat_ellipse(level = 0.95, size = 1)
plots[[counter]] = plot
counter = counter + 1
}
}
}
}
grid.arrange(grobs=plots, layout_matrix=rbind(c(1, 1, 1, 2, 2, 2, 3, 3, 3,4, 4, 4),
c( 5, 5, 5, 6, 6, 6,7, 7, 7, 8, 8 ,8),c(9,9,9, 10,10,10,10,NA,NA,NA,NA,NA)))

PC1: Separate asexual, Iriomote and the 2 other sexual.
PC2: Separate asexual, Iriomote and the 2 other sexual (so same as PC1)
PC3: Separate the asexual one into 2 groups with 3 individuals from Kobe being an hybrid between the two groups.
PC4: Separate Okinawa from Amami (the two sexual one excluding Iriomote)
PC5: No more group could be delineate after PC4. So no really new cluster is observed in PC5.
4) Plot admixture proportion estimated by pCangsd for K=1 to K=8
setwd("/Users/yacinebenchehida/Desktop/Metadata/Results/PCangsd/maf_0.05/")
New_col = c("darkcyan","#A52A2A", "#8A2BE2", "darkolivegreen4","darkorchid4","black","orange","grey")
par(mfrow=c(4,2), mai = c(0.3, 0.3, 0.3, 0.4))
for (i in 1:8){
D <- npyLoad(paste("PCAangsd_results_K",i,".cov.npy",sep=""))
e <- eigen(D)
pop<-read.table("../../../bam_location_correspondance.txt")
colnames(pop)= c("ID","Location")
cumulative_variance = sum(unlist(e$values)) # Estimate the total variance of the data set
eigenvalues = as.data.frame(cbind(seq(1:length(e$values)),e$values,(e$values*100/(cumulative_variance))))
colnames(eigenvalues) = c("PC","Eigenvalue","Contributions")
population=unique(pop$Location)
myColors <- brewer.pal(10,"Paired")
All_data = cbind(e$vectors,pop)
data = cbind(All_data[,c(1,2)],pop)
colnames(data) = c("PC1","PC2","ID","Location")
D2 <- npyLoad(paste("PCAangsd_results_K",i,".admix.Q.npy",sep=""))
data = cbind(data,D2)
data$Location <- factor(data$Location, levels= c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Tokyo","Sendai"))
data = with(data, data[order(Location),])
to_plot = t(data[,5:(4+i)])
colnames(to_plot) = NULL
s = replace(rep(0, 95), !duplicated(as.character(data$Location)), 0.5)
g <- table(data$Location)
barplot(to_plot,col=New_col,main=paste("K",i,sep=""), border = NA, space = s)
text(g / 2 + c(0, cumsum(g[-length(g)])), -0.15, names(g), srt = 25, xpd = NA)
}

The results echo to PCA. The admixture proportion obtain at K=5 are very similar to the admixture proportion obtained with the software admixture. Here is a zoom at K=5.
setwd("/Users/yacinebenchehida/Desktop/Metadata/Results/PCangsd/maf_0.05/")
for (i in 5){
D <- npyLoad(paste("PCAangsd_results_K",i,".cov.npy",sep=""))
e <- eigen(D)
pop<-read.table("../../../bam_location_correspondance.txt")
colnames(pop)= c("ID","Location")
cumulative_variance = sum(unlist(e$values)) # Estimate the total variance of the data set
eigenvalues = as.data.frame(cbind(seq(1:length(e$values)),e$values,(e$values*100/(cumulative_variance))))
colnames(eigenvalues) = c("PC","Eigenvalue","Contributions")
population=unique(pop$Location)
myColors <- brewer.pal(10,"Paired")
All_data = cbind(e$vectors,pop)
data = cbind(All_data[,c(1,2)],pop)
colnames(data) = c("PC1","PC2","ID","Location")
D2 <- npyLoad(paste("PCAangsd_results_K",i,".admix.Q.npy",sep=""))
data = cbind(data,D2)
data$Location <- factor(data$Location, levels= c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Tokyo","Sendai"))
data = with(data, data[order(Location),])
to_plot = t(data[,5:(4+i)])
colnames(to_plot) = NULL
s = replace(rep(0, 95), !duplicated(as.character(data$Location)), 0.5)
g <- table(data$Location)
barplot(to_plot,col=New_col[1:5],main=paste("K",i,sep=""), border = NA, space = s)
text(g / 2 + c(0, cumsum(g[-length(g)])), -0.15, names(g), srt = 25, xpd = NA)
}

VII) FST
1) Compute GL for each population and Site frequency spectrum for each population
The calculation of the FST with angsd rely on the SFS. The SFS must be estimated separately for each population of interest. The FST were computed between:
1) 5 (Ks) population inferred with the clustering approaches (NGSadmix, Admixture, PCangsd).
2) The 10 geographical locations.
The SFS for each population were estimated using the following command:
#! /bin/bash
#SBATCH --job-name=GL
#SBATCH --partition=regular
#SBATCH --mem=10GB
#SBATCH --cpus-per-task=1
#SBATCH --time=0-8:00:00
################
# Define paths #
################
module load HTSlib/1.12-GCC-10.2.0
BAM1_LIST="~/Data/BAM_FILES/BAM_$1.txt"
PATH_RESULTS="~/FST/GFL/$1"
GFL_File1="$1_intersect.glf"
REF="~/Data/ref_genome/ajConsensus.fasta"
ANGSD="/data/p274105/tool_genomics/angsd"
mkdir -p $PATH_RESULTS
#######################################################
# COMPUTE MIN SAMPLE SIZE AND MIN/MAX DEPTH for POP 1 #
#######################################################
NB_SAMPLES=$(cat $BAM1_LIST|wc -l)
MINDEPTH=$((NB_SAMPLES*2)) # Min depth 2X
MAXDEPTH=$((NB_SAMPLES*30)) # Max depth 30 X
MIN_sample=$((($NB_SAMPLES * 80 ) / 100)) # Min number of individuals 80%
echo $MINDEPTH
echo $MAXDEPTH
echo $MIN_sample
#########################################
# COMPUTE GENOTYPE LIKELIHOODS FOR POP1 #
#########################################
$ANGSD/angsd -GL 2 \
-ref ${REF} \
-anc ${REF} \
-bam $BAM1_LIST \
-uniqueOnly 1 -remove_bads 1 -only_proper_pairs 1 \
-trim 0 -C 50 -baq 1 \
-out ${PATH_RESULTS}/${GFL_File1} \
-nThreads 1 \
-minMapQ 20 \
-minQ 20 \
-doSaf 1 \ # To estimate the site frequency spectrum
-minInd $MIN_sample \
-doCounts 1 \
-setMinDepth $MINDEPTH -setMaxDepth $MAXDEPTH
Because we are not using an outgroup to get the ancestral state, the SFS must be folded. HOWEVER, the folding of the SFS should be performed in a later step This is why:

Also because the analyses is based on the SFS, monomorphic and rare variants must be kept to infer an unbiaised SFS. Therefore the flags on the minor allele frequency and on SNP P-value are not used here.

Applying these filters I retained between 16,231,719 and 202,084,034 sites (not SNPs) depending on the population.
2) Estimate 2DSFS + compute FST based on as many sites as possible
1: Compute the folded 2D SFS each pair of population.
2: Index the SFS.
3: Get the global estimate of the FST for each pair of population.
4: Compute the weighted FST over 10kb windows.
#! /bin/bash
#SBATCH --job-name=SFS_FST
#SBATCH --partition=gelifes
#SBATCH --mem=35GB
#SBATCH --cpus-per-task=8
#SBATCH --time=0-20:00:00
# cript name: SFS_FST.sh
################
# Define paths #
################
module load HTSlib/1.12-GCC-10.2.0
BAM1_LIST="~/Data/BAM_FILES/BAM_$1.txt"
BAM2_LIST="~/Data/BAM_FILES/BAM_$2.txt"
PATH_RESULTS="~/Results/FST/GFL/$1_$2"
GFL_File1="Wasp_Pina_$1.glf"
GFL_File2="Wasp_Pina_$2.glf"
REF="~/Data/ref_genome/ajConsensus.fasta"
ANGSD="/data/p274105/tool_genomics/angsd"
mkdir -p $PATH_RESULTS
##########
# ML SFS #
##########
#calculate the 2dsfs prior
$ANGSD/misc/realSFS $PATH_RESULTS/*"$1".glf.saf.idx $PATH_RESULTS/*"$2".glf.saf.idx -fold 1 -P 8 > $PATH_RESULTS/"$1"_"$2".ml
echo "2DSFS READY"
#prepare the fst for easy window analysis
$ANGSD/misc/realSFS fst index $PATH_RESULTS/*"$1".glf.saf.idx $PATH_RESULTS/*"$2".glf.saf.idx -sfs $PATH_RESULTS/"$1"_"$2".ml -fold 1 -whichFst 1 -fstout $PATH_RESULTS/"$1"_"$2" -P 8
echo "DATA READY FOR SFS CALCULATION"
#get the global estimate
echo -e Unweighted"\t"Weighted > $PATH_RESULTS/"$1"_"$2"_results.fst
$ANGSD/misc/realSFS fst stats $PATH_RESULTS/"$1"_"$2".fst.idx -fold 1 -whichFst 1 >> $PATH_RESULTS/"$1"_"$2"_results.fst
echo "GLOBAL FST COMPUTED"
# FST in sliding windows of 50 kb
$ANGSD/misc/realSFS fst stats2 $PATH_RESULTS/"$1"_"$2".fst.idx -win 10000 -step 10000 -fold 1 -whichFst 1 > $PATH_RESULTS/Sliding_windows_FST_"$1"_"$2".txt
echo "SLIDING WINDOW FST COMPUTED"
rm $PATH_RESULTS/*gz
# GL for everything. Including monomorphic sites. GL estimate for each pop separately. Then used to estimate SFS within pop. Then used to estimate the 2D SFs. Finally 2Dsfs is used to estimate the FST. FST were computed globally + within windows of 50kB.
The -whichFST option was used because it performs better with low sample sizes.
To avoid double comparisons (e.g. K1 with K2 and K2 with K1) this script was run as follow:
“for i in K{1..5}; do for j in K{1..5}; do if [ "\(i" \< "\)j" ]; then sbatch ./SFS_FST $i $j; fi; done; done”
3) Average FST values + 95% confidence interval (95CI)
The following R command was used to estimate the 95CI. It used the R package Rmisc which assume that the data are t-distributed (number of window-1 degree of freedom)
suppressPackageStartupMessages(library(Rmisc))
library(Rmisc)
args = commandArgs(trailingOnly=TRUE)
fst = read.table(args)
CI(unlist(fst), ci=0.95)
The previous R command was ran inside the following bash script:
module load R
cd GFL
(echo -e Pop1"\t"Pop2"\t"Global_FST"\t"Mean_FST"\t"Low_CI_FST"\t"High_CI_FST
for i in $(ls| grep -P "K\d_K\d")
do
POP=$(echo $i| perl -pe 's/_/\t/g')
cd $i
GLOBAL_FST=$(cat *fst|awk '{print $2}'|awk 'NR>1 {print $0}')
cat Sliding*|awk '{print $5}' > bizbizfaitlamouche.txt
MEAN_FST=$(Rscript --vanilla ../../FST_CI.R bizbizfaitlamouche.txt|awk 'NR>1 {print $0}'| cut -f 2 -d " ")
LOW_CI=$(Rscript --vanilla ../../FST_CI.R bizbizfaitlamouche.txt|awk 'NR>1 {print $0}'| cut -f 3 -d " ")
HIGH_CI=$(Rscript --vanilla ../../FST_CI.R bizbizfaitlamouche.txt|awk 'NR>1 {print $0}'| cut -f 1 -d " ")
echo -e $POP"\t"$GLOBAL_FST"\t"$MEAN_FST"\t"$LOW_CI"\t"$HIGH_CI
rm bizbizfaitlamouche.txt
cd ..
done) > ../FST_between_NGSadmix_GROUP.txt
(echo -e Pop1"\t"Pop2"\t"Global_FST"\t"Mean_FST"\t"Low_CI_FST"\t"High_CI_FST
for i in $(ls| grep -v -P "K\d|maf")
do
POP=$(echo $i| perl -pe 's/_/\t/g')
cd $i
GLOBAL_FST=$(cat *fst|awk '{print $2}'|awk 'NR>1 {print $0}')
cat Sliding*|awk '{print $5}' > bizbizfaitlamouche.txt
MEAN_FST=$(Rscript --vanilla ../../FST_CI.R bizbizfaitlamouche.txt|awk 'NR>1 {print $0}'| cut -f 2 -d " ")
LOW_CI=$(Rscript --vanilla ../../FST_CI.R bizbizfaitlamouche.txt|awk 'NR>1 {print $0}'| cut -f 3 -d " ")
HIGH_CI=$(Rscript --vanilla ../../FST_CI.R bizbizfaitlamouche.txt|awk 'NR>1 {print $0}'| cut -f 1 -d " ")
echo -e $POP"\t"$GLOBAL_FST"\t"$MEAN_FST"\t"$LOW_CI"\t"$HIGH_CI
rm bizbizfaitlamouche.txt
cd ..
done) > ../FST_between_GEO_GROUP.txt
a) FST based on NGSadmix groups
b) FST based on Geographic groups
4) Visualization with heatmaps (based on the Mean_FST)
a) FST based on NGSadmix groups
library(viridis)
library(ggtext)
FST = read.table("FST_between_NGSadmix_GROUP.txt",header =T)
FST = FST[,c(1,2,4,5,6)]
FST$To_plot = paste0('**', round(FST$Mean_FST,3),'**<br />[',round(FST$Low_CI_FST,3),'-',round(FST$High_CI_FST,3),']' , sep = "")
p = ggplot(data =FST, aes(x=Pop1, y=Pop2, fill=Mean_FST)) +
geom_tile(colour = "black") + scale_fill_gradientn(colours = viridis(10000)) + theme(plot.title = element_text(hjust = 0.5)) + labs(fill = "FST") + xlab("Population 1") + ylab("Population 2") + theme_bw() + geom_textbox(label = FST$To_plot, box.size = 0, fill = NA, halign = 0.5, colour = ifelse(FST$Mean_FST < 0.20,'white','black'),size=4) + guides(colour = FALSE) + scale_color_manual(values=c('black','white')) +
theme(axis.text.x = element_text(colour = New_col[1:4], size = 11,face="bold")) +
theme(axis.text.y = element_text(colour = New_col[2:5], size = 11,face="bold")) +
theme(legend.title = element_text(size=16, face = "bold")) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
p

NA
NA
=> The results are overall consistent with SNP calling results. There are still three differences:
a: The FST values are lower.
b: The FST values between K4 and K5 (between asexual pop.) tend to be larger (relatively speaking compared to the rest)
c: The FST values between the sexual population Iriomote (K1) vs K2/K3 (Amami/Okinawa) show lower FSt values than comparisons between Iriomote and K4/5 (asexual). The difference was less pronounced in the analysis performed by Pina:

b) FST based on Geographic groups
library(reshape2)
library(dplyr)
FST = read.table("FST_between_GEO_GROUP.txt",header =T)
FST = FST[,c(1,2,4,5,6)]
FST$To_plot = paste0('**', round(FST$Mean_FST,3),'**<br />[',round(FST$Low_CI_FST,3),'-',round(FST$High_CI_FST,3),']' , sep = "")
for (row in 1:dim(FST)[1]){
if(FST[row,c(1)]!=FST[row,c(2)]){
FST = rbind(FST,FST[row,])
tmp_pop1 = FST[nrow(FST),c(1)]
tmp_pop2 = FST[nrow(FST),c(2)]
FST[nrow(FST),c(2)] = tmp_pop1
FST[nrow(FST),c(1)] = tmp_pop2
}
}
FST$Pop1 <- factor(FST$Pop1, levels= c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Sendai","Tokyo"))
FST$Pop2 <- factor(FST$Pop2, levels= c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Sendai","Tokyo"))
FST = arrange(FST, Pop1, Pop2)
list = list()
a = 1
for (i in 1:dim(FST)[1]){
for (j in 1:dim(FST)[1]){
if(j > i){
if(FST[i,1]==FST[j,2] && FST[i,2]==FST[j,1]){
list[[a]] = FST[i,]
a = a + 1
}
}
}
}
FST = do.call(rbind,list)
p = ggplot(data =FST, aes(x=Pop1, y=Pop2, fill=Mean_FST)) +
geom_tile(colour = "black") + scale_fill_gradientn(colours = viridis(10000)) + theme(plot.title = element_text(hjust = 0.5)) + labs(fill ="FST") + xlab("Population 1") + ylab("Population 2") + theme_bw() + geom_textbox(label = FST$To_plot, box.size = 0, fill = NA, halign = 0.5, colour = ifelse(FST$Mean_FST < 0.20,'white','black'),size=5) + guides(colour = FALSE) + scale_color_manual(values=c('black','white')) +
theme(axis.text.x = element_text(colour = myColors[1:9], size = 16,face="bold")) +
theme(axis.text.y = element_text(colour = myColors[2:10], size = 16,face="bold")) +
theme(legend.title = element_text(size=16, face = "bold")) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
p

=> Consistent with all the analyses.
IX) Kinship (for subset of 54 individuals)
PCangsd was used to estimate the relatedness between each pair of individuals while taking into account the genetic structure (option -kinship). The method implemented in ANGSD is based on the PCrelate approach (https://www.cell.com/ajhg/fulltext/S0002-9297(15)00493-0).
For the sake of simplicity, I first ran PCrelate for the 95 available individuals. Then afterward I sub-sampled the resulting relatedness matrix to have the relatedness coefficient just for the 54 individuals for which microbiome data are available.
PCrelate was ran on the LD pruned data set using the below script:
#! /bin/bash
#SBATCH --job-name=PCrel
#SBATCH --partition=short
#SBATCH --mem=1GB
#SBATCH --cpus-per-task=1
#SBATCH --mail-type=BEGIN,END,FAIL
#SBATCH --mail-user=h.y.ben.chehida@rug.nl
#SBATCH --time=00:30:00
# PCAngsd needs and accepts are genotype likelihoods in Beagle format.
# We generated it already for the NGSadmix analysis. So we can reuse it here.
################
# Define paths #
################
GFL="/data/p274105/Wasp_Pina/Results/Wasp_Pina_subset.glf.beagle.gz"
OUTPUT="/data/p274105/Wasp_Pina/Results/PCrelate"
module load PCAngsd/0.98-foss-2018a-Python-3.6.4
module load R/4.0.0-foss-2020a
#################################
# Estimate pairwise relatedness #
#################################
pcangsd.py \
-beagle $GFL \
-o $OUTPUT/inbreeding_1/Res_1 \
-threads 1 \
-inbreed 1 \
-inbreed_iter 1000 \
-kinship \
-inbreed_tole 1e-6
The R code below:
1: Extract the pairwise relatedness coefficients for the 54 individuals of interests.
2: Set the negative relatedness values to 0.
3: Visually summarize the results using a default heat map (I can modify it so it looks like the FST one if needed).
#!/usr/bin/env Rscript
library(RcppCNPy) # library to read numpy files
options(scipen=999) # use full number instead of scientific notation
library(pheatmap)
library(dplyr)
kinship <- npyLoad(filename="PCrelate/inbreeding_1/Res_1.kinship.npy") #Import kinship matrix in numpy format
kinship = as.data.frame(kinship) # transform the matrix to a data frame
individuals <- read.table("../bam_location_correspondance.txt") # individuals name and pop of each individual
colnames(kinship) =unlist(individuals[,c(1)]) # add individuals name to each column
rownames(kinship) =unlist(individuals[,c(1)]) # add inviduals name to each row
kinship = kinship[rownames(kinship) %in% unlist(ind_to_subset),colnames(kinship )%in% unlist(ind_to_subset)]
kinship[kinship < 0] = 0 # kinship values inferior to 0 are not biologically meaningful. They are set to 0.
#kinship[kinship > 0.25] = 1
individuals = individuals[individuals$V1 %in% unlist(ind_to_subset),]
geography = as.data.frame(individuals[,2],stringsAsFactors=TRUE) # store information on geography in a separate object
rownames(geography) = individuals[,1]
colnames(geography) = "Geography"
geography$Geography <- factor(geography$Geography, levels= c("Iriomote","Okinawa","Amami","Kagoshima","Fukuoka","Kobe","Kyoto","Kanazawa","Tokyo","Sendai"))
geography$Geography = sort(geography$Geography)
# Block to assign desired colors to the desired populations.
mycolors = cbind(unique(geography),myColors)
geo = geography
geography$colors = "empty"
for (i in 1:dim(geography)[1]){
for (j in 1:dim(mycolors)[1]){
if(geography[i,1]==mycolors[j,1])
geography[i,2] = mycolors[j,2]
}
}
# Prepare the colors in the format desired by pheatmap
mat_colors <- list(Geography = myColors)
names(mat_colors$Geography) <- unique(unlist(geo))
# Pheatmap plot
pheatmap(t(kinship),na_col="white",annotation_col=geo,annotation_row=geo,cluster_rows=F,cluster_cols=F,annotation_colors = mat_colors)

max(unlist(kinship[kinship < 0.25]))
[1] 0.0407
The maximal relatedness values is equal to 0.0407
If we look at the distribution of the estimated relatedness values
plot(density(unlist(kinship[kinship < 0.25])))

Basically none of the individuals are related.
This result strongly contrasts with the results obtained using SNPrelate:

With SNPrelate many of the values are very high.
The following R code:
1: Label individuals according to their geographic ID (not the one in the bam files starting with Sx)
2: Create and display the matrix values that I will share with Pina (for correlation with the microbiome)
# Reverse distance matrix names to use the real geographical ID
for (i in 1:dim(test1)[1]){
for (j in 1:dim(ind_to_subset)[1]){
if (colnames(kinship)[j]==test1[i,2]){
colnames(kinship)[j]=test1[i,1]
rownames(kinship)[j]=test1[i,1]
}
}
}
# order matrix geographically
order_matrix = read.table("ind_for_matrix.txt")
kinship = kinship[unlist(order_matrix),unlist(order_matrix)]
kinship = round(kinship,digits=4)
# Create a text file with the relatedness matrix
write.table(kinship, file="matrix_relatedness_54_ind.txt",sep="\t",quote=FALSE,row.names = TRUE,
col.names = TRUE)
# Display matrix
kinship
Write final matrix of relatedness (handle missing tab in the beginning)
cat matrix_relatedness_54_ind.txt | head -n 1 | perl -pe 's/I_4/\tI_4/g' > matrix_relatedness.txt
cat matrix_relatedness_54_ind.txt| awk 'NR>1' >> matrix_relatedness.txt
rm matrix_relatedness_54_ind.txt
mv matrix_relatedness.txt matrix_relatedness_54_ind.txt
LS0tCnRpdGxlOiAiR2Vub3R5cGUgbGlrZWxpaG9vZCBiYXNlZCBhbmFseXNlcy4gUGluYSBldCBhbC4gMjAyMi4iCnN1YnRpdGxlOiAiUG9wdWxhdGlvbiBzdHJ1Y3R1cmUgYW5kIGRpZmZlcmVudGlhdGlvbiBvZiB0aGUgd2FzcCAqQXNvYmFyYSBqYXBvbmljYSoiCmF1dGhvcjogIllhY2luZSBCZW4gQ2hlaGlkYSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogeWVzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCd5aWh1aS9rbml0cicpCmxpYnJhcnkoa25pdHIpCm9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCgojIEkpIEZpbHRlciB0aGUgZGF0YSBiYXNlZCBvbiB0aGUgZ2Vub3R5cGUgbGlrZWxpaG9vZCAoR0wpLiAKQmFzZWQgb24gb3VyIGRpc2N1c3Npb24gb2YgbGFzdCB3ZWVrLCB3ZSBkZWNpZGVkIHRvIGFwcGx5IHRoZSBmb2xsb3dpbmcgZmlsdGVyczoKCisgKioxOioqICBUaGUgbWFmID0gMC4wNQoKKyAqKjI6KiogQ2FsbCBhIFNOUCBpcyBpdCdzIHAtdmFsdWUgaXMgYmVsb3cgJHsxMF57LTZ9fSQKCisgKiozOioqIERpc2NhcmQgcmVhZHMgd2l0aCBtYXBwaW5nIHF1YWxpdHkgYmVsb3cgMTUgKG1pbk1hcFEpCgorICoqNDoqKiBEaXNjYXJkIGJhc2VzIHdpdGggYmFzZSBxdWFsaXR5IGJlbG93IChtaW5RKQoKKyAqKjU6KiogS2VlcCBTTlBzIHByZXNlbnQgaW4gYXQgbGVhc3QgNzAlIG9mIHRoZSBpbmRpdmlkdWFscyBpLmUuIDk1ICogMC43ICRcYXBwcm94JCA2NyBpbmRpdmlkdWFscwoKKyAqKjY6KiogU2l0ZXMgd2l0aCBjb3ZlcmFnZSByYW5naW5nIGZyb20gMiB0byAzMCA9PiAtc2V0TWluRGVwdGggMTkwICAtc2V0TWF4RGVwdGggMjg1MAoKVGhpcyBzY3JpcHQgd2FzIGFwcGxpZWQgdG8gZWFjaCBzY2FmZm9sZCBhcyBhIHNlcGFyYXRlIGpvYiB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6CgoiZm9yIGkgaW4gJFNDQUZGT0xEX05BTUU7IGRvIHNiYXRjaCAuL0dMX3Blcl9zY2FmZm9sZHMuc2ggJGk7ICBkb25lIgoKVGhlIHNjYWZmb2xkIG5hbWVzIGNhbiBiZSByZXRyaWV2ZWQgZnJvbSB0aGUgZmFzdGEgcmVmZXJlbmNlIGdlbm9tZSBmaWxlIChhakNvbnNlbnN1cy5mYXN0YSkgdXNpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kOgoiU0NBRkZPTERfTkFNRT0kKGNhdCBhakNvbnNlbnN1cy5mYXN0YSB8IGdyZXAgIj4ifCBhd2sgJ3twcmludCAkMX0nfCBzZWQgJ3MvPi8vZycpIgpgYGB7YmFzaH0KIyEgL2Jpbi9iYXNoCgojU0JBVENIIC0tam9iLW5hbWU9R0xfRWNvSFAKI1NCQVRDSCAtLXBhcnRpdGlvbj1zaG9ydAojU0JBVENIIC0tbWVtPThHQgojU0JBVENIIC0tY3B1cy1wZXItdGFzaz0xCiNTQkFUQ0ggLS10aW1lPTAtMDA6MzA6MDAKCiMgU2NyaXB0IG5hbWU6IEdMX3Blcl9zY2FmZm9sZHMuc2gKCiMjIyMjIyMjIyMjIyMjIyMKIyBEZWZpbmUgcGF0aHMgIwojIyMjIyMjIyMjIyMjIyMjCkJBTV9MSVNUPSJ+L2xpc3RfYmFtLnR4dCIgIyBMaXN0IG9mIHBhdGggdG8gdGhlIDk1IGJhbSBmaWxlcwpQQVRIX1JFU1VMVFM9In4vR0ZML0FsbC8kMSIKR0ZMX0ZpbGU9Ildhc3BfUGluYV9MRF9QcnVubmluZ18kMS5nbGYiClJFRj0ifi9hakNvbnNlbnN1cy5mYXN0YSIKQU5HU0Q9Ii9kYXRhL3AyNzQxMDUvdG9vbF9nZW5vbWljcy9hbmdzZCIKCm1rZGlyIC1wICRQQVRIX1JFU1VMVFMKCiRBTkdTRC9hbmdzZCAtR0wgMiBcCi1kb0dsZiAyIFwKLWRvTWFmIDEgIFwKLWRvTWFqb3JNaW5vciAxIFwKLWRvUG9zdCAxIC1kb0dlbm8gOCBcCi1yZWYgJHtSRUZ9IFwKLXIgJDEgXAotYmFtICR7QkFNX0xJU1R9IFwKLXVuaXF1ZU9ubHkgMSAtcmVtb3ZlX2JhZHMgMSAtb25seV9wcm9wZXJfcGFpcnMgMSBcCi10cmltIDAgLUMgNTAgLWJhcSAxIFwKLW91dCAke21rZGlyIC1wICRQQVRIX1JFU1VMVFN9LyR7R0ZMX0ZpbGV9IFwKLW5UaHJlYWRzIDEgXAotbWluTWFwUSAxNSBcCi1taW5RIDE1IFwKLVNOUF9wdmFsIDFlLTYgXAotc2tpcFRyaWFsbGVsaWMgMSBcCi1taW5NYWYgMC4xIFwKLW1pbkluZCA2NyBcCi1zZXRNaW5EZXB0aCAxOTAgIC1zZXRNYXhEZXB0aCAyODUwIC1kb0NvdW50cyAxCgpgYGAKPGJyLz4KPT4gVGhpcyBzY3JpcHQgZ2VuZXJhdGVzIDIgaW1wb3J0YW50IHR5cGUgb2YgZmlsZXM6ICJnbGYuZ2Vuby5neiIgYW5kICJnbGYubWFmcy5neiIgZm9yIGVhY2ggc2NhZmZvbGQuCgpUaGVuIEkgZ2F0aGVyZWQgdGhlIHJlc3VsdHMgb2YgZWFjaCBzY2FmZm9sZCBpbiBhIHNpbmdsZSBnbGYubWFmcyBhbmQgZ2xmLmdlbm8gZmlsZXMgdXNpbmcgdGhlIGNvbW1hbmRzIGJlbG93OgpgYGB7YmFzaH0KYGBgCgoKYGBge2Jhc2h9ClBBVEhfUkVTVUxUUz0iL2RhdGEvcDI3NDEwNS9XYXNwX1BpbmEvRGF0YS9HRkwvQWxsLwoKY2QgJFBBVEhfUkVTVUxUUwoKKGZvciBpIGluIDAwMDAwMEY7IGRvIGNkICRpOyB6Y2F0ICpnbGYubWFmcy5neiB8IGhlYWQgLW4xICA7IGNkIC4uOyBkb25lIDsgXApmb3IgaSBpbiAkKGxzfCBncmVwIC12IC1FICJBbGx8dHh0Iik7IGRvIGNkICRpOyB6Y2F0ICpnbGYubWFmcy5neiB8IGF3ayAnTlI+MScgIDsgY2QgLi47IGRvbmUpPiBBbGxfQ2hyLmdsZi5tYWZzCgooZm9yIGkgaW4gJChsc3wgZ3JlcCAtdiAtRSAiQWxsfHR4dCIpOyBkbyBjZCAkaTsgemNhdCAqZ2xmLmdlbm8uZ3ogIDsgY2QgLi47IGRvbmUpID4gQWxsX0Noci5nbGYuZ2VubwpgYGAKCgpBZnRlciBmaWx0ZXJpbmcgKioxMzA1NzEqKiBTTlBzIHdlcmUga2VwdC4KPGJyLz48YnIvPgoKIyBJSSkgTGlua2FnZSBkZXNlcXVpbGlicml1bSBwcnVuaW5nCkxEIHBydW5pbmcgd2FzIGFwcGxpZWQgdXNpbmcgdGhlIHNvZnR3YXJlIE5HU0xEIChodHRwczovL2dpdGh1Yi5jb20vZmd2aWVpcmEvbmdzTEQpLgpGaXJzdCBsZXQncyBjaGVjayBob3cgTEQgZGVjYXlzIHdpdGggZ2VuZXRpYyBkaXN0YW5jZS4KClRoZSBzY3JpcHQgYmVsb3c6CgorICoqMSkqKiAgRXN0aW1hdGUgTEQgYmV0d2VlbiBwYWlyIG9mIFNOUCB1cCB0byBhIGRpc3RhbmNlIG9mIDUwIEtiCgorICoqMikqKiBQbG90IExEIGRlY2F5IAogCmBgYHtiYXNofQojISAvYmluL2Jhc2gKCiNTQkFUQ0ggLS1qb2ItbmFtZT1MRAojU0JBVENIIC0tcGFydGl0aW9uPXJlZ3VsYXIKI1NCQVRDSCAtLW1lbT0xNUdCCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTEKI1NCQVRDSCAtLXRpbWU9MC0yMDowMDowMAoKICNTY3JpcHQgbmFtZTogTmdzTERfcGVyX3NjYWZmb2xkcy5zaAogCiMjIyMjIyMjIyMjIyMjIyMKIyBEZWZpbmUgcGF0aHMgIwojIyMjIyMjIyMjIyMjIyMjClBBVEhfUkVTVUxUUz0iL2RhdGEvcDI3NDEwNS9XYXNwX1BpbmEvRGF0YS9HRkwvQWxsLyIKTkdTTEQ9Ii9kYXRhL3AyNzQxMDUvdG9vbF9nZW5vbWljcy9uZ3NUb29scy9uZ3NMRC8iCgojIyMjIyMjIyMjIyMjIyMjIyMKIyBMb2FkIGxpYnJhcmllcyAjCiMjIyMjIyMjIyMjIyMjIyMjIwptb2R1bGUgbG9hZCBSLzQuMC4wLWZvc3MtMjAyMGEgCm1vZHVsZSBsb2FkIFBlcmwvNS4yNi4xLWZvc3MtMjAxOGEKI2NwYW5tIC0tbG9jYWwtbGliPX4vcGVybDUgbG9jYWw6OmxpYgojZXZhbCAkKHBlcmwgLUkgfi9wZXJsNS9saWIvcGVybDUvIC1NbG9jYWw6OmxpYikKI2NwYW5tIEdyYXBoOjpFYXN5CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENvdW50IHRoZSBudW1iZXIgb2YgYXZhaWxhYmxlIFNOUHMgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKY2QgJFBBVEhfUkVTVUxUUwpjYXQgIEFsbF9DaHIuZ2xmLm1hZnMgfCBjdXQgLWYgMSwyIHwgdGFpbCAtbiArMiA+IFRlc3RfcG9zLnR4dApOUz1gY2F0IFRlc3RfcG9zKiB8IHdjIC1sYCAKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEVzdGltYXRlIExEIGJldHdlZW4gZXZlcnkgcGFpciBvZiBTTlBzIHVwIHRvIDUwIGtiICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiROR1NMRC9uZ3NMRCAtLWdlbm8gQWxsX0Noci5nbGYuZ2VubyAtLW5faW5kIDk1IC0tbWF4X2tiX2Rpc3QgNTAgLS1uX3NpdGVzICROUyAtLW91dCBOR1NMRE9VVFBVVC50eHQgLS1wb3MgVGVzdF9wb3MqIC0tdmVyYm9zZSAxCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFBsb3QgdGhlIGdyYXBoIG9mIExEIGRlY2F5IG92ZXIgNTAga2IgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpSc2NyaXB0IC0tdmFuaWxsYSAtLXNsYXZlICROR1NMRC9zY3JpcHRzL2ZpdF9MRGRlY2F5LlIgLS1sZF9maWxlIGZpbGVfbmFtZS50eHQgLS1vdXQgcGxvdF9MRC5wZGYgLS1maXRfbGV2ZWwgMiAtLWxkIHIyIC0tbl9pbmQgOTUgLS1wbG90X3lfbGltIDAuNSAtLXBsb3RfeF9saW0gNTAKYGBgCjxici8+ClRoZSByZXN1bHRzIHNob3cgdGhlIExEIChtZWFzdXJlZCBhcyAkcl4yJCkgZ29lcyBiZWxvdyAwLjEgYWZ0ZXIgYXJvdW5kICoqMjAgS2IqKi4gCjxici8+CmBgYHtyIGVjaG89RkFMU0UsICBpbmNsdWRlPVRSVUUsIG91dC53aWR0aD0iNzUlIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGF0aD0iU2NyZWVuc2hvdCAyMDIyLTA3LTE5IGF0IDA5LjEwLjM5LnBuZyIpCmBgYAoKVGhlcmVmb3JlLCBzdWJzZXF1ZW50bHkgTEQgd2FzIGVzdGltYXRlZCBiZXR3ZWVuIGV2ZXJ5IHBhaXIgb2YgU05QcyB1cCB0byBhIGRpc3RhbmNlIG9mICAqKjIwIEtiKiogdXNpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kOgpgYGB7YmFzaH0KY2QgJFBBVEhfUkVTVUxUUwoKbW9kdWxlIGxvYWQgUGVybC81LjI2LjEtZm9zcy0yMDE4YQoKcGVybCAkTkdTTEQvc2NyaXB0cy9wcnVuZV9ncmFwaC5wbCAtLWluX2ZpbGUgTkdTTERPVVRQVVQudHh0IC0tbWF4X2tiX2Rpc3QgMjAgLS1taW5fd2VpZ2h0IDAuNiAtLW91dCBMRF91bmxpbmtlZC5pZApgYGAKPGJyLz4KKio4ODQ4NSoqIFNOUHMgcmVtYWluZWQgYWZ0ZXIgTEQgcHJ1bmluZy4gCjxici8+PGJyLz4KCiMgSUlJKSBHTCBvbiB0aGUgTEQgcHJ1bmVkIGRhdGEKPGJyLz4KSXQncyBiYXNpY2FsbHkgdGhlIHNhbWUgYXMgZm9yICoqSSkqKiBqdXN0IHRoZSBhbmFseXNpcyB3YXMgcGVyZm9ybWVkIG9uIGEgc3Vic2V0IG9mIExEIHBydW5lZCBTTlBzLiAKVGhlIHN1YnNldCBvZiBMRCBwcnVuZWQgU05QcyBhcmUgcHJvdmlkZWQgYXMgYXJndW1lbnQgdG8gYW5nc2QgdXNpbmcgdGhlIG9wdGlvbiAqKi1zaXRlcyoqLgoKYGBge2Jhc2h9CiMhIC9iaW4vYmFzaAoKI1NCQVRDSCAtLWpvYi1uYW1lPUdMX0Vjb0hQCiNTQkFUQ0ggLS1wYXJ0aXRpb249c2hvcnQKI1NCQVRDSCAtLW1lbT04R0IKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9MQojU0JBVENIIC0tdGltZT0wLTAwOjMwOjAwCgojU2NyaXB0IG5hbWU6IEdMX3N1YnNldF9wZXJfc2NhZmZvbGRzLnNoCgojIyMjIyMjIyMjIyMjIyMjCiMgRGVmaW5lIHBhdGhzICMKIyMjIyMjIyMjIyMjIyMjIwpCQU1fTElTVD0ifi9saXN0X2JhbS50eHQiClBBVEhfSU5QVVQ9In4vR0ZML0FsbC8iClBBVEhfT1VUUFVUPSJ+L0dGTC9MRF9wcnVubmVkLyQxIgpHRkxfRmlsZT0iV2FzcF9QaW5hX3N1YnNldF8kMS5nbGYiClJFRj0ifi9hakNvbnNlbnN1cy5mYXN0YSIKQU5HU0Q9Ii9kYXRhL3AyNzQxMDUvdG9vbF9nZW5vbWljcy9hbmdzZCIKCmNkICRQQVRIX0lOUFVUCm1rZGlyIC1wICRQQVRIX09VVFBVVAoKJEFOR1NEL2FuZ3NkIHNpdGVzIGluZGV4ICR7UEFUSF9SRVNVTFRTfS9MRF91bmxpbmtlZF90b19zb3J0LmlkCgokQU5HU0QvYW5nc2QgLUdMIDIgXAotZG9HbGYgMiBcCi1kb01hZiAxICBcCi1kb01ham9yTWlub3IgMSBcCi1kb1Bvc3QgMSAtZG9HZW5vIDggXAotcmVmICR7UkVGfSBcCi1yICQxIFwKLWJhbSAke0JBTV9MSVNUfSBcCi1zaXRlcyAke1BBVEhfSU5QVVR9L0xEX3VubGlua2VkX3RvX3NvcnQuaWQgXAotdW5pcXVlT25seSAxIC1yZW1vdmVfYmFkcyAxIC1vbmx5X3Byb3Blcl9wYWlycyAxIFwKLXRyaW0gMCAtQyA1MCAtYmFxIDEgXAotb3V0ICR7UEFUSF9PVVRQVVR9LyR7R0ZMX0ZpbGV9IFwKLW5UaHJlYWRzIDEgXAotbWluTWFwUSAxNSBcCi1taW5RIDE1IFwKLVNOUF9wdmFsIDFlLTYgXAotc2tpcFRyaWFsbGVsaWMgMSBcCi1taW5NYWYgMC4xMCBcCi1taW5JbmQgNjcgXAotc2V0TWluRGVwdGggMTkwICAtc2V0TWF4RGVwdGggMjg1MCAtZG9Db3VudHMgMQpgYGAKClRoaXMgc2NyaXB0IGdlbmVyYXRlZCB0aGUgKmdsZi5iZWFnbGUuZ3ogZmlsZSB0aGF0IGlzIHRoZSBiYXNlIGZvciBtb3N0IG9mIHRoZSBsYXRlciBhbmFseXNlcy4gCjxici8+PGJyLz4KCiMgSVYpIFBoeWxvZ2VuZXRpYyByZWxhdGlvbnNoaXBzClRoZSBwaHlsb2dlbmV0aWMgcmVsYXRpb25zaGlwIHdlcmUgaW5mZXJyZWQgdXNpbmc6IAoKKyAqKjE6KiogTmdzRElTVCB0byBjYWxjdWxhdGUgdGhlIGRpc3RhbmNlIG1hdHJpeCBiZXR3ZWVuIHRoZSB0YXhhIGFuZCB0aGUgYm9vdHN0cmFwIGRpc3RhbmNlcyBtYXRyaWNlcy4KCisgKioyOioqIEZBU1RNRSB0byBpbmZlciB0aGUgcmVzdWx0aW5nIHBoeWxvZ2VuZXRpYyB0cmVlLgoKKyAqKjM6KiogUkFYTUwgdG8gcmVwbGFjZSB0aGUgYm9vdHN0cmFwIHN1cHBvcnQgdmFsdWUgb24gdGhlIG1haW4gdHJlZS4KCisgKio0OioqIFRoZSB0cmVlIHdhcyBtaWQtcG9pbnQgcm9vdGVkIHVzaW5nIFIgcGFja2FnZSBhcGUuCgorICoqNToqKiBUaGUgdHJlZSB3YXMgcGxvdHRlZCB1c2luZyB0aGUgUiBwYWNrYWdlIGdndHJlZS4KCmBgYHtiYXNofQojISAvYmluL2Jhc2gKCiNTQkFUQ0ggLS1qb2ItbmFtZT1uZ3NEaXN0V2FzcAojU0JBVENIIC0tcGFydGl0aW9uPXJlZ3VsYXIKI1NCQVRDSCAtLW1lbT02R0IKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9MQojU0JBVENIIC0tdGltZT0yLTIyOjMwOjAwCgojIFRvIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgbWF0cml4IDogCiMgaHR0cHM6Ly9naXRodWIuY29tL21mdW1hZ2FsbGkvbmdzVG9vbHMvYmxvYi9tYXN0ZXIvVFVUT1JJQUwubWQKIyBodHRwczovL2dpdGh1Yi5jb20vZmd2aWVpcmEvbmdzRGlzdAoKIyMjIyMjIyMjIyMjIyMjIwojIERlZmluZSBwYXRocyAjCiMjIyMjIyMjIyMjIyMjIyMKUEFUSF9SRVNVTFRTPSJ+L1Jlc3VsdHMvTkp0cmVlIgpQQVRIX0lOUFVUUz0ifi9SZXN1bHRzIgpjYXQgL2RhdGEvcDI3NDEwNS9XYXNwX1BpbmEvRGF0YS9OZXdfcG9wLnR4dHxjdXQgLWYgMSA+ICRQQVRIX0lOUFVUUy9MYWJlbHMudHh0CkZBU1RNRT0iL2RhdGEvcDI3NDEwNS90b29sX2dlbm9taWNzL2Zhc3RtZS0yLjEuNS9iaW5hcmllcy9mYXN0bWUtMi4xLjUtbGludXg2NCIKCQojIyMjIyMjIyMjIyMjIwojIE5HU0RJU1QKIyMjIyMjIyMjIyMjIyMKIyBQcm9ncmFtcyBuZWVkZWQKTkdTRElTVD0iL2RhdGEvcDI3NDEwNS90b29sX2dlbm9taWNzL25nc1Rvb2xzL25nc0Rpc3QiCgpjZCAkUEFUSF9JTlBVVFMKCkdGTD0iV2FzcF9QaW5hX3N1YnNldC5nbGYuYmVhZ2xlLmd6IgoKTlNJVEVTPWB6Y2F0ICRHRkx8IGF3ayAne3ByaW50ICQxfSd8IGF3ayAnTlI+MSd8d2MgLWxgCmVjaG8gJE5TSVRFUwoKIyAxMDAwIGJvb3QsIGJsb2NrIDEwMCBrYiBmb3Igd2hvbGUgZ2Vub21lIFNOUHMgKExEIHBydW5uZWQpCm1vZHVsZSBsb2FkIEdTTC8yLjUtR0NDLTguMi4wLTIuMzEuMQokTkdTRElTVC9uZ3NEaXN0IFwKLS1uX3RocmVhZHMgMSBcCi0tdmVyYm9zZSAxIFwKLS1nZW5vICRQQVRIX0lOUFVUUy8kR0ZMIFwKLS1wcm9icyBcCi0tbl9pbmQgIDk1IFwKLS1wYWlyd2lzZV9kZWwgXAotLW5fc2l0ZXMgJHtOU0lURVN9IFwKLS1uX2Jvb3RfcmVwIDEwMDAgXAotLWJvb3RfYmxvY2tfc2l6ZSA1MDAwIFwKLS1sYWJlbHMgJFBBVEhfSU5QVVRTL0xhYmVscy50eHQgXAotLW91dCAkUEFUSF9SRVNVTFRTL291dHB1dC5kaXN0CgojLS1hdmdfbnVjX2Rpc3QgXAplY2hvICJOR1NESVNUIFJBTiIKCiMjIyMjIyMjIyMjIyMjCiMgRkFTVE1FCiMjIyMjIyMjIyMjIyMjCiRGQVNUTUUgLWkgJFBBVEhfUkVTVUxUUy9vdXRwdXQuZGlzdCBcCi1EIDEwMDEgXAotcyBcCi1vICAkUEFUSF9SRVNVTFRTL291dHB1dC5ud2sKCmhlYWQgLW4gMSAkUEFUSF9SRVNVTFRTL291dHB1dC5ud2sgPiAkUEFUSF9SRVNVTFRTL291dHB1dC5tYWluLm53awp0YWlsIC1uICsyICRQQVRIX1JFU1VMVFMvb3V0cHV0Lm53ayB8IGF3ayAnTkYnID4gJFBBVEhfUkVTVUxUUy9vdXRwdXQuYm9vdC5ud2sKCmVjaG8gIkZBU1RNRSByYW4iCgojIyMjIyMjIyMKIyBSQVhNTCAjCiMjIyMjIyMjIwpjZCAkUEFUSF9SRVNVTFRTCm1vZHVsZSBsb2FkIFJBeE1MLzguMi4xMS1mb3NzLTIwMThhLW10LWF2eDIKcmF4bWxIUEMgLWYgYiBcCi10ICRQQVRIX1JFU1VMVFMvb3V0cHV0Lm1haW4ubndrIFwKLXogJFBBVEhfUkVTVUxUUy9vdXRwdXQuYm9vdC5ud2sgXGQKLW0gR1RSQ0FUIFwKLVQgMSBcCi1uIG91dHB1dF9maW5hbC50cmVlCgplY2hvICJSQVhNTCByYW4iCmBgYAoKVGhlIHJlc3VsdGluZyAgcGh5bG9nZW5ldGljIHRyZWUgd2FzIG1pZCByb290ZWQgYW5kICB0aGVuIHBsb3R0ZWQgaW4gUiB1c2luZyB0aGUgbGlicmFyaWVzIGdndHJlZSBhbmQgYXBlLgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTE0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2d0cmVlKQpsaWJyYXJ5KGFwZSkKbGlicmFyeShwaHl0b29scykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCgpzZXR3ZCgiL1VzZXJzL3lhY2luZWJlbmNoZWhpZGEvRGVza3RvcC9NZXRhZGF0YS9SZXN1bHRzL05KdHJlZS8iKQp0cmVlIDwtIGdndHJlZTo6cmVhZC50cmVlKCJSQXhNTF9iaXBhcnRpdGlvbnMub3V0cHV0X2ZpbmFsLnRyZWUiKSAjIFRoZXJlIGFyZSBkaWZmZXJlbnQgcmVhZC50cmVlKCkgZnVuY3Rpb25zIHRoYXQgZXhlY3V0ZXMgZGlmZmVyZW50IGNvbW1hbmRzLiBUbyBhdm9pZCBhbnkgYW1iaWd1aXR5IGhlcmUgSSBhZGQgYXBlOjogYmVmb3JlIHRvIHNheSB0aGF0IEkgd2FudCB0byB1c2UgdGhlIHJlYWQudHJlZSgpIGZvbmN0aW9uIGZyb20gdGhlIGFwZSBwYWNrYWdlLiBUaGlzIHRyZWUgaXMgYSBtYXhpbXVtIGxpa2VsaWhvb2QgdHJlZSBvZiB0aGUgNiBzcGVjaWVzIG9mIHRoZSBwb3Jwb2lzZXMgZmFtaWx5IChQaG9jb25pZGFlKSByZWNvbnN0cnVjdGVkIHdpdGggUGh5TUwuCiN0cmVlID0gYXBlOjpyb290KHRyZWUsbm9kZT0xMDksIHJlc29sdmUucm9vdD1UUlVFKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgVHJhbnNmb3JtIHRoZSB0cmVlIHRvIGEgZ2d0cmVlIHRyZWUgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKdHJlZTIgPC0gdHJlZQp0cmVlMiA8LSBtaWRwb2ludC5yb290KHRyZWUpCnRyZWUyIDwtIGdndHJlZSh0cmVlMikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBEZWZpbmUgYSBjb2xvciBjb2RlIGZvciBlYWNoIHNwZWNpZXMgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmRhdGFfY29sb3IgPSBjYmluZCh0cmVlMiRkYXRhJGxhYmVsWzE6OTVdLCByZXAoInRlc3QiKSkgIyBDb21iaW5lIHRoZSBjb2x1bW4gd2l0aCBpbmRpdmlkdWFsIG5hbWVzIHdpdGggYSBuZXcgY29sdW1uIHRoYXQgd2lsbCBjb250YWluIHRoZSBjb2xvciBhc3NvY2lhdGVkIHRvIGVhY2ggaW5kaXZpZHVhbCBpbiBhbiBvYmplY3RlZCBjYWxsZWQgZGF0YV9jb2xvcgpjb2xuYW1lcyhkYXRhX2NvbG9yKSA9Yygic3BlY2llcyIsImNvbG9yIikgICMgR2l2ZSBhIG5hbWUgdG8gdGhlIGNvbHVtbiBvZiBkYXRhX2NvbG9yCmRhdGFfY29sb3IgPSBhcy5kYXRhLmZyYW1lKGRhdGFfY29sb3IpICMgVHJhbnNmb3JtIGRhdGFfY29sb3IgdG8gYSBkYXRhIGZyYW1lCmRhdGFfY29sb3JbLDJdID0gYXMuY2hhcmFjdGVyKGRhdGFfY29sb3JbLDJdKSAgIyBUcmFuc2Zvcm0gdGhlIGNvbG9yIGNvbHVtbiBpbnRvIGNoYXJhY3RlcnMgKGluc3N0ZWFkIG9mIGZhY3RvcikKcG9wID0gcmVhZC50YWJsZSgiLi4vLi4vYmFtX2xvY2F0aW9uX2NvcnJlc3BvbmRhbmNlLnR4dCIpCnBvcCA9ICBkYXRhLmZyYW1lKGluZHM9cG9wJFYxLCBsb2NhbGl0eSA9IHBvcCRWMikKcG9wJGNvbG9yID0gIk5BTiIKZGF0YV9jb2xvciRsb2NhbGl0eSA9ICJOQU4iCgpzcGVjaWVzID0gYygiSXJpb21vdGUiLCJPa2luYXdhIiwiQW1hbWkiLCJLYWdvc2hpbWEiLCJGdWt1b2thIiwiS29iZSIsIkt5b3RvIiwiS2FuYXphd2EiLCJUb2t5byIsIlNlbmRhaSIpICAjIENyZWF0ZSBhIHZlY3RvciB3aXRoIHRoZSB1bmlxdWUgc3BlY2llcyBuYW1lCmNvbG9yID0gYnJld2VyLnBhbCgxMCwiUGFpcmVkIikgICMgQ3JlYXRlIGEgdmVjdG9yIHdpdGggdGhlIGNvbG9yIGFzc29jaWF0ZWQgdG8gZWFjaCB1bmlxdWUgc3BlY2llcyBuYW1lCmluZm8gPSBjYmluZChzcGVjaWVzLGNvbG9yKSAgIyBDb21iaW5lIHRoZSB1bmlxdWUgc3BlY2llcyBhbmQgdGhlIGNvbG9yIGluIGFuIG9iamVjdCBjYWxsZWQgaW5mbwoKCiMgTG9vcCB0byBhdHRyaWJ1dGUgdG8gZWFjaCBpbmRpdmlkdWFscyBpdHMgY29sb3IKZm9yIChpIGluIDE6OTUpeyAgIAogIGZvciAoaiBpbiAxOjEwKSB7ICAgCiAgICBpZihncmVwbChpbmZvW2osMV0scG9wW2ksMl0pKXsgICAKICAgICAgcG9wW2ksM10gPSBpbmZvW2osMl0KICAgIH0KICB9Cn0KCiMgTG9vcCB0byBhdHRyaWJ1dGUgdG8gZWFjaCBpbmRpdmlkdWFscyBpdHMgY29sb3IKZm9yIChpIGluIDE6OTUpeyAgIAogIGZvciAoaiBpbiAxOjk1KSB7ICAgCiAgICBpZihwb3BbaiwxXT09ZGF0YV9jb2xvcltpLDFdKXsgICAKICAgICAgZGF0YV9jb2xvcltpLDJdID0gcG9wW2osM10KICAgICAgZGF0YV9jb2xvcltpLDNdID0gcG9wW2osMl0KICAgIH0KICB9Cn0KCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRGVmaW5lIGJvb3RzdHJhcHMgZ3JvdXBzICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwp0cmVlMiRkYXRhJGNvbCA8LSBjdXQoYXMubnVtZXJpYyh0cmVlMiRkYXRhJGxhYmVsKSwKICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMCwgOTAsIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCI8PTkwIiwgIj45MCIpKQoKZm9yIChpIGluIDE6MTg5KXsgICAKICBmb3IgKGogaW4gMTo5NSkgeyAgIAogICAgaWYoZGF0YV9jb2xvcltqLDFdPT10cmVlMiRkYXRhW2ksNF0peyAgCiAgICAgIHRyZWUyJGRhdGFbaSw0XSA9IHBhc3RlKHRyZWUyJGRhdGFbaSw0XSwgZGF0YV9jb2xvcltqLDNdLCBzZXAgPSAiICIpCiAgICB9CiAgfQp9Cgp0cmVlMiRkYXRhJGxhYmVsID0gZ3N1YigiKFxcdyspIChcXHcrKSIsICJcXDIiLHRyZWUyJGRhdGEkbGFiZWwsIHBlcmwgPSBUUlVFKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgUGxvdCBmdWxsIHRyZWUgdG8gaGF2ZSB0aGUgZmluYWwgdG9wb2xvZ3kgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKcCA8LSBnZ3RyZWUodHJlZTIkZGF0YSkgKyBnZW9tX3RpcGxhYihzaXplID00LCBjb2xvcj0gZGF0YV9jb2xvciRjb2xvcikgKyBnZW9tX3BvaW50MihhZXMoc3Vic2V0PSghaXNUaXApLCBmaWxsPSB0cmVlMiRkYXRhJGNvbCwgc2l6ZSA9IHRyZWUyJGRhdGEkY29sLCBzaGFwZT10cmVlMiRkYXRhJGNvbCkpICArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSAgKyBzY2FsZV9maWxsX21hbnVhbChicmVha3MgPSBjKCI8PTkwIiwgIj45MCIpICwgdmFsdWVzID0gYygiZ3JlZW4iLCJyZWQiKSkgKyAKICBzY2FsZV9zaXplX21hbnVhbChicmVha3MgPSBjKCI8PTkwIiwgIj45MCIpICwgdmFsdWVzID0gYygxLjIsMi41KSkgKyBzY2FsZV9zaGFwZV9tYW51YWwoYnJlYWtzID0gYygiPD05MCIsICI+OTAiKSAsIHZhbHVlcyA9IGMoMjEsMjEpKSArIGd1aWRlcyhzaXplID0gRkFMU0UsIHNoYXBlPUZBTFNFLCBmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcz1saXN0KHNoYXBlPTIxKSkpICsgbGFicyhmaWxsPSJCb290c3RyYXBzIikgCnAgPSBwICsgZ2VvbV90cmVlc2NhbGUoeD0wLjMwKSArIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNiksbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpKSAgKyB0aGVtZShsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChzaXplPTEsIGNvbG9yID0gImJsYWNrIiwgbGluZXR5cGU9InNvbGlkIikpIApwID0gcCArIGdncGxvdDI6OnhsaW0oMCwgMC40MCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMSwgMC45KSkKcCAKYGBgCj0+IFRoZSB0cmVlIGlzIGNvbmdydWVudCB3aXRoIHRoZSBhbmFseXNlcyBiYXNlZCBvbiBTTlAgY2FsbGluZy4KPGJyLz48YnIvPgoKIyBWKSBQQ0EKIyMgMSkgUnVuIFBDYW5nc2QKUENhbmdzZCB3YXMgdXNlZCB0byBnZXQgdGhlIFBDQSBhcyB3ZWxsIGFzIHRvIGVzdGltYXRlIHRoZSBhZG1peHR1cmUgcHJvcG9ydGlvbiAob3B0aW9uIC1hZG1peCkuIApJdCB3YXMgcmFuIHdpdGggZGlmZmVyZW50IHZhbHVlIG9mIEsgcmFuZ2luZyBmcm9tIDEgdG8gOC4KSXQgd2FzIHJhbiB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6IAoKZm9yIGkgaW4gezEuLjV9OyBkbyBzYmF0Y2ggLi9QQ2FuZ3NkLnNoICRpOyBkb25lCgpgYGB7YmFzaH0KIyEgL2Jpbi9iYXNoCgojU0JBVENIIC0tam9iLW5hbWU9UENBbmdzZAojU0JBVENIIC0tcGFydGl0aW9uPXNob3J0CiNTQkFUQ0ggLS1tZW09MkdCCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTEKI1NCQVRDSCAtLXRpbWU9MDA6MjU6MDAKCiNTY3JpcHQgbmFtZTogUENhbmdzZC5zaAoKIyMjIyMjIyMjIyMjIyMjIwojIERlZmluZSBwYXRocyAjCiMjIyMjIyMjIyMjIyMjIyMKClBBVEhfR0ZMPSJ+L1Jlc3VsdHMiCk9VVFBVVF9QQVRIPSJ+L1Jlc3VsdHMvUENhbmdzZCIKT1VUUFVUPSJQQ0FhbmdzZF9yZXN1bHRzX0skMSIKUEFUSF9TQ1JJUFQ9In4vU2NyaXB0L1BDYW5nc2QiCgptb2R1bGUgbG9hZCBQQ0FuZ3NkLzAuOTgtZm9zcy0yMDE4YS1QeXRob24tMy42LjQKCmNkICRQQVRIX0dGTAoKcGNhbmdzZC5weSAtYmVhZ2xlICR7UEFUSF9HRkx9L1dhc3BfUGluYV9zdWJzZXQuZ2xmLmJlYWdsZS5neiBcCi1hZG1peCBcICMgZXN0aW1hdGUgYWRtaXh0dXJlIHByb3BvcnRpb24KLWFkbWl4X0sgJDEgXCAjIEVzdGltYXRlIGFkbWl4dHVyZSBwcm9wb3J0aW9uIHdpdGggSyA9ICQxIGdyb3Vwcy4KLWFkbWl4X2l0ZXIgNTAwIFwgIyBQZXJmb3JtIDUwMCBFTSBpdGVyYXRpb25zIGZvciBhZG1peHR1cmUgZXN0aW1hdGlvbnMuCi1hZG1peF9hbHBoYSA1MCBcIAotbyAke09VVFBVVF9QQVRIfS8ke09VVFBVVH0gXAotdGhyZWFkcyAxIFwKLWUgNSBcICMgVXNlIDUgZWlnZW52YWx1ZXMgdG8gbW9kZWwgaW5kaXZpZHVhbCBhbGxlbGUgZnJlcXVlbmNpZXMKLWFkbWl4X3RvbGUgMWUtNyBcICMgVG9sZXJhbmNlIHZhbHVlIGZvciB1cGRhdGUgaW4gYWRtaXh0dXJlIGVzdGltYXRpb25zCi1taW5NYWYgMC4xMCAjIE1ha2Ugc3VyZSB0aGUgbWFmIGl0IGF0IDAuCmBgYAo8YnIvPjxici8+CgojIyAyKSBQQzEgYW5kIFBDMgpUaGUgcmF3IFBDQSB3YXMgcGxvdHRlZCB1c2luZyB0aGlzIFIgY29tbWFuZHMKCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTYsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNldHdkKCIvVXNlcnMveWFjaW5lYmVuY2hlaGlkYS9EZXNrdG9wL01ldGFkYXRhL1Jlc3VsdHMvUENhbmdzZC9tYWZfMC4wNS8iKQpsaWJyYXJ5KFJjcHBDTlB5KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoZ2d0cmVlKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBhdGNod29yaykgCmxpYnJhcnkocGx5cikKbGlicmFyeShnZ2ZvcmNlKQoKRCA8LSBucHlMb2FkKCJQQ0FhbmdzZF9yZXN1bHRzX0s1LmNvdi5ucHkiKQplIDwtIGVpZ2VuKEQpCnBvcDwtcmVhZC50YWJsZSgiLi4vLi4vLi4vYmFtX2xvY2F0aW9uX2NvcnJlc3BvbmRhbmNlLnR4dCIpCmNvbG5hbWVzKHBvcCk9IGMoIklEIiwiTG9jYXRpb24iKQpjdW11bGF0aXZlX3ZhcmlhbmNlID0gc3VtKHVubGlzdChlJHZhbHVlcykpICMgRXN0aW1hdGUgdGhlIHRvdGFsIHZhcmlhbmNlIG9mIHRoZSBkYXRhIHNldAplaWdlbnZhbHVlcyA9IGFzLmRhdGEuZnJhbWUoY2JpbmQoc2VxKDE6bGVuZ3RoKGUkdmFsdWVzKSksZSR2YWx1ZXMsKGUkdmFsdWVzKjEwMC8oY3VtdWxhdGl2ZV92YXJpYW5jZSkpKSkKY29sbmFtZXMoZWlnZW52YWx1ZXMpID0gYygiUEMiLCJFaWdlbnZhbHVlIiwiQ29udHJpYnV0aW9ucyIpCnBvcHVsYXRpb249dW5pcXVlKHBvcCRMb2NhdGlvbikKbXlDb2xvcnMgPC0gYnJld2VyLnBhbCgxMCwiUGFpcmVkIikKQWxsX2RhdGEgPSBjYmluZChlJHZlY3RvcnMscG9wKQpkYXRhID0gY2JpbmQoQWxsX2RhdGFbLGMoMSwyKV0scG9wKQpjb2xuYW1lcyhkYXRhKSA9IGMoIlBDMSIsIlBDMiIsIklEIiwiTG9jYXRpb24iKQoKZGF0YSRMb2NhdGlvbiA8LSBmYWN0b3IoZGF0YSRMb2NhdGlvbiwgbGV2ZWxzPSBjKCJJcmlvbW90ZSIsIk9raW5hd2EiLCJBbWFtaSIsIkthZ29zaGltYSIsIkZ1a3Vva2EiLCJLb2JlIiwiS3lvdG8iLCJLYW5hemF3YSIsIlRva3lvIiwiU2VuZGFpIikpCmRhdGEkcmVwcm9kdWN0aW9uID0gIlRvQmVBZGRlZCIKZGF0YVtkYXRhJExvY2F0aW9uPT0iSXJpb21vdGUiIHwgZGF0YSRMb2NhdGlvbj09Ik9raW5hd2EiIHwgZGF0YSRMb2NhdGlvbj09IkFtYW1pIiw1XSA9IGMoIlNleHVhbCIpCmRhdGFbZGF0YSRyZXByb2R1Y3Rpb24hPSJTZXh1YWwiLDVdID0gIkFzZXh1YWwiCnJlcHJvID0gZGF0YVssNV0KZ2dwbG90KGRhdGEsIGFlcyh4PS1QQzEsIHk9UEMyLGNvbG9yPUxvY2F0aW9uLHNoYXBlPXJlcHJvZHVjdGlvbikpICsgZ2VvbV9wb2ludChzaXplPTIpICsgdGhlbWVfYncoKSArIAogIHhsYWIocGFzdGUoIlBDMSAoIixyb3VuZChlaWdlbnZhbHVlc1sxLDNdLGRpZ2l0cz0yKSwiICUpIixzZXA9IiIpKSArIAogIHlsYWIocGFzdGUoIlBDMiAoIixyb3VuZChlaWdlbnZhbHVlc1syLDNdLGRpZ2l0cz0yKSwiICUpIixzZXA9IiIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IG15Q29sb3JzKSArIHN0YXRfZWxsaXBzZShsZXZlbCA9IDAuOTUsIHNpemUgPSAxKQpgYGAKPT4gVGhlIHRyZWUgaXMgY29uc2lzdGVudCB3aXRoIHRoZSBhbmFseXNlcyBiYXNlZCBvbiBTTlAgY2FsbGluZy4KCgpgYGB7cn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFBsb3QgZWlnZW52YWx1ZXMgY29udHJpYnV0aW9ucyAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZ2dwbG90KGRhdGE9ZWlnZW52YWx1ZXNbMToxMCxdLCBhZXMoeD1QQywgeT1Db250cmlidXRpb25zKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0ic3RlZWxibHVlIikgKyAKICB0aGVtZV9idygpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHVuaXF1ZShzb3J0KGVpZ2VudmFsdWVzJFBDKSkpICsKICB5bGFiKCJDb250cmlidXRpb24gKCUpIikgKwogIHhsYWIoIlByaW5jaXBhbCBDb21wb25lbnQiKQoKc2NyZWU8LWVpZ2VudmFsdWVzWzE6MTAsYygxLDMpXSAlPiUKICBnZ3Bsb3QoYWVzKHg9UEMseT1Db250cmlidXRpb25zKSkrdGhlbWVfYncoKSsKICBnZW9tX2NvbCgpKwogIGxhYnModGl0bGU9IlNjcmVlIHBsb3QiLHk9IiIseD0iIikrdGhlbWUoCiAgICB0aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSkKYGBgCgpUaGUgY29udHJpYnV0aW9uIGluICUgb2YgZWFjaCBQQyB0byB0aGUgdG90YWwgdmFyaWFuY2Ugb2YgdGhlIFBDIGlzIHBsb3QgaW4gdGhlIGJhcnBsb3QgYWJvdmUuIDUgZmlyc3QgUENzIHN0YW5kIG91dCBhbmQgZXhwbGFpbiA2MS44JSBvZiB0aGUgdG90YWwgdmFyaWFuY2UuCgpCZWxvdyBpcyB0aGUgc2FtZSBzY2F0dGVyIHBsb3QgYXMgYmVmb3JlIGJ1dCB0cnlpbmcgdG8gbWFrZSBpdCBsb29rIG1vcmUgbGlrZSB0aGUgb25lIG9mIHRoZSBtYW51c2NyaXB0OgoKYGBge3IgZWNobz1GQUxTRSwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YSA9IGNiaW5kKEFsbF9kYXRhWyxjKDEsMildLHBvcCkKY29sbmFtZXMoZGF0YSkgPSBjKCJQQzEiLCJQQzIiLCJJRCIsIkxvY2F0aW9uIikKZGF0YSRMb2NhdGlvbiA8LSBmYWN0b3IoZGF0YSRMb2NhdGlvbiwgbGV2ZWxzPSBjKCJJcmlvbW90ZSIsIk9raW5hd2EiLCJBbWFtaSIsIkthZ29zaGltYSIsIkZ1a3Vva2EiLCJLb2JlIiwiS3lvdG8iLCJLYW5hemF3YSIsIlRva3lvIiwiU2VuZGFpIikpCmRhdGEkcmVwcm9kdWN0aW9uID0gIlRvQmVBZGRlZCIKZGF0YVtkYXRhJExvY2F0aW9uPT0iSXJpb21vdGUiIHwgZGF0YSRMb2NhdGlvbj09Ik9raW5hd2EiIHwgZGF0YSRMb2NhdGlvbj09IkFtYW1pIiw1XSA9IGMoIlNleHVhbCIpCmRhdGFbZGF0YSRyZXByb2R1Y3Rpb24hPSJTZXh1YWwiLDVdID0gIkFzZXh1YWwiCnJlcHJvID0gZGF0YVssNV0KCnBhaiA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHg9LVBDMSwgeT1QQzIsY29sb3I9TG9jYXRpb24sc2hhcGU9cmVwcm9kdWN0aW9uKSkgCnBhaiA8LSBwYWogKyBnZW9tX3BvaW50KHNpemU9MikKcGFqIDwtIHBhaiArIHN0YXRfZWxsaXBzZShsZXZlbCA9IDAuOTUsIHNpemUgPSAxKQpwYWogPC0gcGFqICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IG15Q29sb3JzKSAKcGFqIDwtIHBhaiArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApIApwYWogPC0gcGFqICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkgCnBhaiA8LSBwYWogKyB0aGVtZV9idygpICtsYWJzKHg9cGFzdGUoIlBDMSAoIixyb3VuZChlaWdlbnZhbHVlc1sxLDNdLGRpZ2l0cz0yKSwiICUpIixzZXA9IiIpLHk9cGFzdGUoIlBDMiAoIixyb3VuZChlaWdlbnZhbHVlc1syLDNdLGRpZ2l0cz0yKSwiICUpIixzZXA9IiIpKSArdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpwYWogICsgcGF0Y2h3b3JrOjppbnNldF9lbGVtZW50KHNjcmVlLDAuMDUsIDAuMDUsIDAuMzUsIDAuMzUpCgpgYGAKPGJyLz48YnIvPgoKIyMgMykgUGxvdCBhZGRpdGlvbmFsIFBDcyAoUEMxIHRvIFBDNSkKCmBgYHtyIGZpZy5oZWlnaHQ9OSwgZmlnLndpZHRoPTE0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwbG90cyA9IGxpc3QoKQpjb3VudGVyID0gMQpmb3IgKGkgaW4gMTo1KXsKICBmb3IgKGogaW4gMTo1KXsKICAgIGlmKGo+aSl7CiAgICAgIGlmKGo8NSB8fCBpPDQpewogIGRhdGEgPSBjYmluZChBbGxfZGF0YVssYyhpLGopXSxwb3AscmVwcm8pCiAgY29sbmFtZXMoZGF0YSkgPSBjKCJQQzEiLCJQQzIiLCJJRCIsIkxvY2F0aW9uIiwiUmVwcm9kdWN0aW9uIikKICBkYXRhJExvY2F0aW9uIDwtIGZhY3RvcihkYXRhJExvY2F0aW9uLCBsZXZlbHM9IGMoIklyaW9tb3RlIiwiT2tpbmF3YSIsIkFtYW1pIiwiS2Fnb3NoaW1hIiwiRnVrdW9rYSIsIktvYmUiLCJLeW90byIsIkthbmF6YXdhIiwiVG9reW8iLCJTZW5kYWkiKSkKICBwbG90ID0gZ2dwbG90KGRhdGEsIGFlcyh4PS1QQzEsIHk9UEMyLGNvbG9yPUxvY2F0aW9uLHNoYXBlPVJlcHJvZHVjdGlvbikpICsgZ2VvbV9wb2ludChzaXplPTIpICsgdGhlbWVfYncoKSArIAogICAgeGxhYihwYXN0ZSgiUEMiLGksIiAoIixyb3VuZChlaWdlbnZhbHVlc1tpLDNdLGRpZ2l0cz0yKSwiICUpIixzZXA9IiIpKSArIAogICAgeWxhYihwYXN0ZSgiUEMiLGosIiAoIixyb3VuZChlaWdlbnZhbHVlc1tqLDNdLGRpZ2l0cz0yKSwiICUpIixzZXA9IiIpKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXlDb2xvcnMpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgIysgc3RhdF9lbGxpcHNlKGxldmVsID0gMC45NSwgc2l6ZSA9IDEpCiAgcGxvdHNbW2NvdW50ZXJdXSA9IHBsb3QKICBjb3VudGVyID0gY291bnRlciArIDEKICAgICAgfQogICAgIGVsc2V7CiAgICAgICBkYXRhID0gY2JpbmQoQWxsX2RhdGFbLGMoaSxqKV0scG9wLHJlcHJvKQogICAgICAgY29sbmFtZXMoZGF0YSkgPSBjKCJQQzEiLCJQQzIiLCJJRCIsIkxvY2F0aW9uIiwiUmVwcm9kdWN0aW9uIikKICAgICAgIGRhdGEkTG9jYXRpb24gPC0gZmFjdG9yKGRhdGEkTG9jYXRpb24sIGxldmVscz0gYygiSXJpb21vdGUiLCJPa2luYXdhIiwiQW1hbWkiLCJLYWdvc2hpbWEiLCJGdWt1b2thIiwiS29iZSIsIkt5b3RvIiwiS2FuYXphd2EiLCJUb2t5byIsIlNlbmRhaSIsIk1pc3NpbmciKSkKICAgICAgIHBsb3QgPSBnZ3Bsb3QoZGF0YSwgYWVzKHg9LVBDMSwgeT1QQzIsY29sb3I9TG9jYXRpb24sc2hhcGU9UmVwcm9kdWN0aW9uKSkgKyBnZW9tX3BvaW50KHNpemU9MikgKyB0aGVtZV9idygpICsgCiAgICAgICAgIHhsYWIocGFzdGUoIlBDIixpLCIgKCIscm91bmQoZWlnZW52YWx1ZXNbaSwzXSxkaWdpdHM9MiksIiAlKSIsc2VwPSIiKSkgKyAKICAgICAgICAgeWxhYihwYXN0ZSgiUEMiLGosIiAoIixyb3VuZChlaWdlbnZhbHVlc1tqLDNdLGRpZ2l0cz0yKSwiICUpIixzZXA9IiIpKSArCiAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteUNvbG9ycykgKwogICAgICAgICB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNSwgJ2NtJykpICMrIHN0YXRfZWxsaXBzZShsZXZlbCA9IDAuOTUsIHNpemUgPSAxKQogICAgICAgcGxvdHNbW2NvdW50ZXJdXSA9IHBsb3QKICAgICAgIGNvdW50ZXIgPSBjb3VudGVyICsgMQogICAgIH0gIAogICAgfQogIH0KfQoKZ3JpZC5hcnJhbmdlKGdyb2JzPXBsb3RzLCBsYXlvdXRfbWF0cml4PXJiaW5kKGMoMSwgMSwgMSwgMiwgMiwgMiwgMywgMywgMyw0LCA0LCA0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIDUsIDUsIDUsIDYsIDYsIDYsNywgNywgNywgOCwgOCAsOCksYyg5LDksOSwgMTAsMTAsMTAsMTAsTkEsTkEsTkEsTkEsTkEpKSkKCmBgYAo8YnIvPgoKKyAqKlBDMToqKiBTZXBhcmF0ZSBhc2V4dWFsLCBJcmlvbW90ZSBhbmQgdGhlIDIgb3RoZXIgc2V4dWFsLgoKKyAqKlBDMjoqKiBTZXBhcmF0ZSBhc2V4dWFsLCBJcmlvbW90ZSBhbmQgdGhlIDIgb3RoZXIgc2V4dWFsIChzbyBzYW1lIGFzIFBDMSkKCisgKipQQzM6KiogU2VwYXJhdGUgdGhlIGFzZXh1YWwgb25lIGludG8gMiBncm91cHMgd2l0aCAzIGluZGl2aWR1YWxzIGZyb20gS29iZSBiZWluZyBhbiBoeWJyaWQgYmV0d2VlbiB0aGUgdHdvIGdyb3Vwcy4KCisgKipQQzQ6KiogU2VwYXJhdGUgT2tpbmF3YSBmcm9tIEFtYW1pICh0aGUgdHdvIHNleHVhbCBvbmUgZXhjbHVkaW5nIElyaW9tb3RlKQoKKyAqKlBDNToqKiBObyBtb3JlIGdyb3VwIGNvdWxkIGJlIGRlbGluZWF0ZSBhZnRlciBQQzQuIFNvIG5vIHJlYWxseSBuZXcgY2x1c3RlciBpcyBvYnNlcnZlZCBpbiBQQzUuCgo8YnIvPjxici8+CgojIyA0KSBQbG90IGFkbWl4dHVyZSBwcm9wb3J0aW9uIGVzdGltYXRlZCBieSBwQ2FuZ3NkIGZvciBLPTEgdG8gSz04IAoKYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpzZXR3ZCgiL1VzZXJzL3lhY2luZWJlbmNoZWhpZGEvRGVza3RvcC9NZXRhZGF0YS9SZXN1bHRzL1BDYW5nc2QvbWFmXzAuMDUvIikKTmV3X2NvbCA9ICBjKCJkYXJrY3lhbiIsIiNBNTJBMkEiLCAiIzhBMkJFMiIsICAiZGFya29saXZlZ3JlZW40IiwiZGFya29yY2hpZDQiLCJibGFjayIsIm9yYW5nZSIsImdyZXkiKQoKcGFyKG1mcm93PWMoNCwyKSwgbWFpID0gYygwLjMsIDAuMywgMC4zLCAwLjQpKQpmb3IgKGkgaW4gMTo4KXsKICBEIDwtIG5weUxvYWQocGFzdGUoIlBDQWFuZ3NkX3Jlc3VsdHNfSyIsaSwiLmNvdi5ucHkiLHNlcD0iIikpCiAgZSA8LSBlaWdlbihEKQogIHBvcDwtcmVhZC50YWJsZSgiLi4vLi4vLi4vYmFtX2xvY2F0aW9uX2NvcnJlc3BvbmRhbmNlLnR4dCIpCiAgY29sbmFtZXMocG9wKT0gYygiSUQiLCJMb2NhdGlvbiIpCiAgY3VtdWxhdGl2ZV92YXJpYW5jZSA9IHN1bSh1bmxpc3QoZSR2YWx1ZXMpKSAjIEVzdGltYXRlIHRoZSB0b3RhbCB2YXJpYW5jZSBvZiB0aGUgZGF0YSBzZXQKICBlaWdlbnZhbHVlcyA9IGFzLmRhdGEuZnJhbWUoY2JpbmQoc2VxKDE6bGVuZ3RoKGUkdmFsdWVzKSksZSR2YWx1ZXMsKGUkdmFsdWVzKjEwMC8oY3VtdWxhdGl2ZV92YXJpYW5jZSkpKSkKICBjb2xuYW1lcyhlaWdlbnZhbHVlcykgPSBjKCJQQyIsIkVpZ2VudmFsdWUiLCJDb250cmlidXRpb25zIikKICBwb3B1bGF0aW9uPXVuaXF1ZShwb3AkTG9jYXRpb24pCiAgbXlDb2xvcnMgPC0gYnJld2VyLnBhbCgxMCwiUGFpcmVkIikKICBBbGxfZGF0YSA9IGNiaW5kKGUkdmVjdG9ycyxwb3ApCiAgZGF0YSA9IGNiaW5kKEFsbF9kYXRhWyxjKDEsMildLHBvcCkKICBjb2xuYW1lcyhkYXRhKSA9IGMoIlBDMSIsIlBDMiIsIklEIiwiTG9jYXRpb24iKQogIEQyIDwtIG5weUxvYWQocGFzdGUoIlBDQWFuZ3NkX3Jlc3VsdHNfSyIsaSwiLmFkbWl4LlEubnB5IixzZXA9IiIpKQogIGRhdGEgPSBjYmluZChkYXRhLEQyKQogIGRhdGEkTG9jYXRpb24gPC0gZmFjdG9yKGRhdGEkTG9jYXRpb24sIGxldmVscz0gYygiSXJpb21vdGUiLCJPa2luYXdhIiwiQW1hbWkiLCJLYWdvc2hpbWEiLCJGdWt1b2thIiwiS29iZSIsIkt5b3RvIiwiS2FuYXphd2EiLCJUb2t5byIsIlNlbmRhaSIpKQogIGRhdGEgPSB3aXRoKGRhdGEsIGRhdGFbb3JkZXIoTG9jYXRpb24pLF0pCiAgdG9fcGxvdCA9IHQoZGF0YVssNTooNCtpKV0pCiAgY29sbmFtZXModG9fcGxvdCkgPSBOVUxMCiAgcyA9IHJlcGxhY2UocmVwKDAsIDk1KSwgIWR1cGxpY2F0ZWQoYXMuY2hhcmFjdGVyKGRhdGEkTG9jYXRpb24pKSwgMC41KQogIGcgPC0gdGFibGUoZGF0YSRMb2NhdGlvbikKICBiYXJwbG90KHRvX3Bsb3QsY29sPU5ld19jb2wsbWFpbj1wYXN0ZSgiSyIsaSxzZXA9IiIpLCBib3JkZXIgPSBOQSwgc3BhY2UgPSBzKQogIHRleHQoZyAvIDIgKyBjKDAsIGN1bXN1bShnWy1sZW5ndGgoZyldKSksIC0wLjE1LCBuYW1lcyhnKSwgc3J0ID0gMjUsIHhwZCA9IE5BKQp9CmBgYAo8YnIvPgpUaGUgcmVzdWx0cyBlY2hvIHRvIFBDQS4gVGhlIGFkbWl4dHVyZSBwcm9wb3J0aW9uIG9idGFpbiBhdCBLPTUgYXJlIHZlcnkgc2ltaWxhciB0byB0aGUgYWRtaXh0dXJlIHByb3BvcnRpb24gb2J0YWluZWQgd2l0aCB0aGUgc29mdHdhcmUgYWRtaXh0dXJlLiBIZXJlIGlzIGEgem9vbSBhdCBLPTUuCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD05LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKc2V0d2QoIi9Vc2Vycy95YWNpbmViZW5jaGVoaWRhL0Rlc2t0b3AvTWV0YWRhdGEvUmVzdWx0cy9QQ2FuZ3NkL21hZl8wLjA1LyIpCgpmb3IgKGkgaW4gNSl7CiAgRCA8LSBucHlMb2FkKHBhc3RlKCJQQ0FhbmdzZF9yZXN1bHRzX0siLGksIi5jb3YubnB5IixzZXA9IiIpKQogIGUgPC0gZWlnZW4oRCkKICBwb3A8LXJlYWQudGFibGUoIi4uLy4uLy4uL2JhbV9sb2NhdGlvbl9jb3JyZXNwb25kYW5jZS50eHQiKQogIGNvbG5hbWVzKHBvcCk9IGMoIklEIiwiTG9jYXRpb24iKQogIGN1bXVsYXRpdmVfdmFyaWFuY2UgPSBzdW0odW5saXN0KGUkdmFsdWVzKSkgIyBFc3RpbWF0ZSB0aGUgdG90YWwgdmFyaWFuY2Ugb2YgdGhlIGRhdGEgc2V0CiAgZWlnZW52YWx1ZXMgPSBhcy5kYXRhLmZyYW1lKGNiaW5kKHNlcSgxOmxlbmd0aChlJHZhbHVlcykpLGUkdmFsdWVzLChlJHZhbHVlcyoxMDAvKGN1bXVsYXRpdmVfdmFyaWFuY2UpKSkpCiAgY29sbmFtZXMoZWlnZW52YWx1ZXMpID0gYygiUEMiLCJFaWdlbnZhbHVlIiwiQ29udHJpYnV0aW9ucyIpCiAgcG9wdWxhdGlvbj11bmlxdWUocG9wJExvY2F0aW9uKQogIG15Q29sb3JzIDwtIGJyZXdlci5wYWwoMTAsIlBhaXJlZCIpCiAgQWxsX2RhdGEgPSBjYmluZChlJHZlY3RvcnMscG9wKQogIGRhdGEgPSBjYmluZChBbGxfZGF0YVssYygxLDIpXSxwb3ApCiAgY29sbmFtZXMoZGF0YSkgPSBjKCJQQzEiLCJQQzIiLCJJRCIsIkxvY2F0aW9uIikKICBEMiA8LSBucHlMb2FkKHBhc3RlKCJQQ0FhbmdzZF9yZXN1bHRzX0siLGksIi5hZG1peC5RLm5weSIsc2VwPSIiKSkKICBkYXRhID0gY2JpbmQoZGF0YSxEMikKICBkYXRhJExvY2F0aW9uIDwtIGZhY3RvcihkYXRhJExvY2F0aW9uLCBsZXZlbHM9IGMoIklyaW9tb3RlIiwiT2tpbmF3YSIsIkFtYW1pIiwiS2Fnb3NoaW1hIiwiRnVrdW9rYSIsIktvYmUiLCJLeW90byIsIkthbmF6YXdhIiwiVG9reW8iLCJTZW5kYWkiKSkKICBkYXRhID0gd2l0aChkYXRhLCBkYXRhW29yZGVyKExvY2F0aW9uKSxdKQogIHRvX3Bsb3QgPSB0KGRhdGFbLDU6KDQraSldKQogIGNvbG5hbWVzKHRvX3Bsb3QpID0gTlVMTAogIHMgPSByZXBsYWNlKHJlcCgwLCA5NSksICFkdXBsaWNhdGVkKGFzLmNoYXJhY3RlcihkYXRhJExvY2F0aW9uKSksIDAuNSkKICBnIDwtIHRhYmxlKGRhdGEkTG9jYXRpb24pCiAgYmFycGxvdCh0b19wbG90LGNvbD1OZXdfY29sWzE6NV0sbWFpbj1wYXN0ZSgiSyIsaSxzZXA9IiIpLCBib3JkZXIgPSBOQSwgc3BhY2UgPSBzKQogIHRleHQoZyAvIDIgKyBjKDAsIGN1bXN1bShnWy1sZW5ndGgoZyldKSksIC0wLjE1LCBuYW1lcyhnKSwgc3J0ID0gMjUsIHhwZCA9IE5BKQp9CmBgYAo8YnIvPjxici8+CgojIFZJKSBOR1NhZG1peAojIyAxKSBSdW4gTkdzYWRtaXgKCk5HU2FkbWl4IHdhcyBhbHNvIHVzZWQgdG8gZXN0aW1hdGUgaW5kaXZpZHVhbCBhZG1peHR1cmUgcHJvcG9ydGlvbiB3aGlsZSB0YWtpbmcgaW50byBhY2NvdW50IGluY2VydGFpbnR5IGluIGdlbm90eXBlLgpEdWUgdG8gdGhlIGluc3RhYmlsaXR5IG9mIHRoZSByZXN1bHRzIHVzaW5nIHRoZSBkZWZhdWx0IHNldHRpbmcsIG1hbnkgb2YgdGhlIGRlZmF1bHQgcGFyYW1ldGVycyB3ZXJlIGNoYW5nZWQgdG8gcGVyZm9ybSBhIG1vcmUgc3RyaW5nZW50IGFuYWx5c2VzLiBUaGUgZXhwbGFuYXRpb25zIG9mIGFsbCBvcHRpb25zIGlzIGF2YWlsYWJsZSBoZXJlOiBodHRwOi8vd3d3LnBvcGdlbi5kay9zb2Z0d2FyZS9pbmRleC5waHAvTmdzQWRtaXguCgpUaGUgYW5hbHlzZXMgd2FzIGZvcm1lZCBmb3IgSz0xIHRvIEs9OCB1c2luZyA1MCByZXBsaWNhdGVzIGZvciBlYWNoIEsgdmFsdWUuIApUaGUgc2NyaXB0IHdhcyByYW4gdXNpbmcgdGhpcyBjb21tYW5kOgoKRm9yIEsgaW4gezguLjF9OyBkbyBmb3IgcmVwbGljYXRlIGluIHsxLi41MH07IGRvIHNiYXRjaCAuL05nc0FkbWl4X3NjcmlwdC5zaCAkSyAkcmVwbGljYXRlOyBkb25lCmBgYHtiYXNofQojISAvYmluL2Jhc2gKCiNTQkFUQ0ggLS1qb2ItbmFtZT1OR1NhZG0KI1NCQVRDSCAtLXBhcnRpdGlvbj1zaG9ydAojU0JBVENIIC0tbWVtPTdHQgojU0JBVENIIC0tY3B1cy1wZXItdGFzaz0xCiNTQkFUQ0ggLS10aW1lPTAtMDA6MzA6MDAKCiNTY3JpcHQgbmFtZTogTmdzQWRtaXhfc2NyaXB0LnNoCgojIyMjIyMjIyMjIyMjIyMjCiMgRGVmaW5lIHBhdGhzICMKIyMjIyMjIyMjIyMjIyMjIwpQQVRIX1JFU1VMVFM9In4vTkdTYWRtaXgiClBBVEhfSU5QVVQ9In4vUmVzdWx0cyIKbW9kdWxlIGxvYWQgYW5nc2QvMC45MjUtZm9zcy0yMDE4YQoKTkdTYWRtaXggLWxpa2VzICRQQVRIX0lOUFVUL1dhc3BfUGluYV9zdWJzZXQuZ2xmLmJlYWdsZS5neiBcCi1LICQxIFwgIyB2YWx1ZSBvZiBLIHRvIHRlc3QgKG51bWJlciBvZiBjbHVzdGVycykKLW1pc1RvbCAwLjkgXCAjIFRvbGVyYW5jZSBmb3IgY29uc2lkZXJpbmcgYSBzaXRlIGFzIG1pc3NpbmcuCi10b2wgMWUtOCBcICMgVG9sZXJhbmNlIGZvciBjb252ZXJnZW5jZS4gVGhlIHNtYWxsZXIgdGhlIG1vcmUgc3RyaW5nZW50LgotbyAke1BBVEhfUkVTVUxUU30vSyIkMSJfcmVwbGljYXRlXyIkMiIgXCAKLVAgMSBcCi1taW5JbmQgNjcgXAotbWF4aXRlciA1MDAwIFwgIyBNYXhpbXVtIG51bWJlciBvZiBFTSBpdGVyYXRpb25zLiAKLW1pbk1hZiAwLjEwIFwKLXRvbExpa2U1MCAwLjMgIyBMb2dsaWtlbGlob29kIGRpZmZlcmVuY2UgaW4gNTAgaXRlcmF0aW9ucy4gVGhlIGxhcmdlciB0aGUgbW9yZSBzdHJpbmdlbnQuCgpgYGAKPGJyLz48YnIvPgoKIyMgMikgUnVuIGNsdW1wYWsKVGhlIHJhdyByZXN1bHRzIGZyb20gTkdTYWRtaXggKCpvcHQgZmlsZXMpIHdlcmUgc3VtbWFyaXplZCB1c2luZyB0aGUgc29mdHdhcmUgQ2x1bXBhay4gCkNsdW1wYWsgZXN0aW1hdGVzIGZvciBlYWNoIEsgdmFsdWUgb3ZlciB0aGUgNTAgcmVwbGljYXRlcyB0aGUgZGlmZmVyZW50IHBhcnRpdGlvbiAoaW4gdGVybSBvZiBpbmRpdmlkdWFsIGFkbWl4dHVyZSBwcm9wb3J0aW9uKSBpbmZlcnJlZCBieSBOR1NhZG1peC4KYGBge2Jhc2h9CiMhIC9iaW4vYmFzaAoKI1NCQVRDSCAtLWpvYi1uYW1lPWNsdW1wYWsKI1NCQVRDSCAtLXBhcnRpdGlvbj1zaG9ydAojU0JBVENIIC0tbWVtPTJHQgojU0JBVENIIC0tY3B1cy1wZXItdGFzaz0xCiNTQkFUQ0ggLS10aW1lPS0wMDozMDowMAoKCiNTY3JpcHQgbmFtZTogY2x1bXBhay5zaAoKbW9kdWxlIGxvYWQgQ0xVTVBBSy8xLjEtR0NDY29yZS02LjQuMC1QZXJsLTUuMjYuMQoKUGF0aD0ifi9SZXN1bHRzL05HU2FkbWl4IgpEYXRhPSJ+L0RhdGEiCmNkICRQYXRoCgp6aXAgbXl6aXAgKm9wdAoKQ0xVTVBBSy5wbCBcCi0taWQgJCgoUkFORE9NJTUwMCkpIFwKLS1kaXIgICRQYXRoICAgIFwKLS1maWxlICAkUGF0aC9teXppcC56aXAgICBcCi0taW5wdXR0eXBlIGFkbWl4dHVyZSBcCi0taW5kdG9wb3AgJERhdGEvaW5kLnR4dCBcCi0tbGFiZWxzICREYXRhL2dlb19ncm91cC50eHQKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUUsIG91dC53aWR0aD0iMTUwJSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHBhdGg9IlNjcmVlbnNob3QgMjAyMi0wNy0xMSBhdCAxMi4wNC41MS5wbmciKQpgYGAKCk91dCBvZiB0aGUgNTAgcmVwbGljYXRlcyBJIG9idGFpbmVkOgoKKyAqKks9MioqIDUwLzUwCgorICoqSz0zKiogNTAvNTAKCisgKipLPTQqKiA1MC81MAoKKyAqKks9NSoqIDMyLzUwIChtYWpvciBwYXJ0aXRpb24pIDE4LzUwIChtaW5vciBwYXJ0aXRpb246IG5vdCBzaG93bikuIAoKPT4gVGhlIGNsdXN0ZXJpbmcgb2J0YWluZWQgYXQgSz01IChtYWpvciBwYXJ0aXRpb24pIGlzIHRoZSBzYW1lIGFzIHRoZSBvbmUgb2J0YWluZWQgYXQgdXNpbmcgQWRtaXh0dXJlIGFuZCB0aGUgUENBLgo8YnIvPjxici8+CgojIyAzKSBCZXN0IEs/CgpDbHVtcGFrIGltcGxlbWVudHMgYWxzbyB0d28gYXBwcm9hY2hlcyB0byBlc3RpbWF0ZSB0aGUgYmVzdCBLOgoKKyAqKlRoZSBtZXRob2Qgb2YgRXZhbm5vKiogCgorICoqbG4oUHIoWHxLKSBvciB0aGUgUHJpY2hhcmQgSyoqIAoKVGhleSB3ZXJlIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIGZvbGxvd2luZyBzY3JpcHQ6CkJlY2F1c2Ugc29tZSByZXBsaWNhdGVzIGhhZCBleGFjdGx5IHRoZSBzYW1lIGxpa2VsaWhvb2QgdGhhdCBjcmVhdGVzIGEgY29uZmxpY3Qgd2l0aCB0aGUgd2F5IHRoaXMgbWV0aG9kIHdvcmsuIFRoZXJlIEkgdXNlZCBhIGhvbWUgbWFkZSBiYXNoIGNvbW1hbmQgKGxpbmUgNzYzKSB0byBqaXR0ZXIgKGFkZCBzb21lIG5vaXNlIHRvKSB0aGUgZGVjaW1hbCB2YWx1ZSBvZiB0aGUgZXN0aW1hdGVkIGxpa2VsaWhvb2QuCmBgYHtiYXNofQptb2R1bGUgbG9hZCBDTFVNUEFLLzEuMS1HQ0Njb3JlLTYuNC4wLVBlcmwtNS4yNi4xCgpQYXRoPSJ+L1Jlc3VsdHMvTkdTYWRtaXgiCmNkICRQYXRoCgpjYXQgKksqbG9nIHxncmVwICJiZXN0Inxhd2sgLUYiICIgJ3twcmludCAkMn0nfGN1dCAtYyA2LTI0ID4gbGlrZWxpaG9vZC50eHQKbHMgKksqbG9nIHxjdXQgLWQgXyAtZiAxIHwgc2VkICdzL0svL2cnID4ga3ZhbHVlLnR4dApwYXN0ZSBrdmFsdWUudHh0IGxpa2VsaWhvb2QudHh0ID4gbGlrZWxpaG9vZF9iZXN0S19pbnB1dC50eHQKY2F0IGxpa2VsaWhvb2RfYmVzdEtfaW5wdXQudHh0IHwgd2hpbGUgcmVhZCBsaW5lOyBkbyBlY2hvICRsaW5lfHBlcmwgLXBlICdzLyhcLikoXGQrKSQvJDEiJyRSQU5ET00nIi9nJ3wgc2VkICdzLyIvL2cnOyBkb25lID4gbmV3X2xpa2VsaWhvb2RfYmVzdEtfaW5wdXQudHh0CgoKcGVybCAvc29mdHdhcmUvc29mdHdhcmUvQ0xVTVBBSy8xLjEtR0NDY29yZS02LjQuMC1QZXJsLTUuMjYuMS9CZXN0S0J5RXZhbm5vLnBsIC0taWQgMTIxIC0tZCAkUGF0aCAtLWYgJFBhdGgvbmV3X2xpa2VsaWhvb2RfYmVzdEtfaW5wdXQudHh0IC0taW5wdXR0eXBlIGxucHJvYmJ5awpgYGAKPGJyLz4KQWNjb3JkaW5nIHRvIHRoZSBFdmFubm8gbWV0aG9kICoqSz00KiogaXMgdGhlIGJlc3QgSyB2YWx1ZQo8YnIvPjxici8+CkFjY29yZGluZyB0byB0aGUgUHJpdGNoYXJkIEsgbWV0aG9kICoqSz04KiogaXMgdGhlIGJlc3QgSyB2YWx1ZQo8YnIvPjxici8+ClRoaXMgcmVzdWx0cyBtdXN0IGJlIHRha2VuIHdpdGggYSBwaW5jaCBvZiBzYWx0LiBFdmFubm8gRGVsdGEoSykgaGFzIGJlZW4gc2hvd24gdG8gYmUgcXVpdGUgdW5yZWxpYWJsZS4gVGhlIHByaXRjaGFyZCBLIHdhcyBkZXNpZ25lZCB0byB3b3JrIHdlbGwgd2l0aCB0aGUgcmVzdWx0cyBvZiB0aGUgc29mdHdhcmUgU1RSVUNUVVJFLiBXaXRoIG90aGVyIHNvdGZ3YXJlLCBteSBleHBlcmllbmNlIHNob3cgdGhhdCBpdCBhbHdheXMgcGlja3MgdGhlIGxhcmdlciBudW1iZXIgb2YgSy4gVGhlcmUgaXMgY2xlYXJseSBhIGRhdGEgb3ZlcmZpdHRpbmcgcHJvYmxlbS4gCjxici8+PGJyLz4KCiMgVklJKSBGU1QKIyMgMSkgQ29tcHV0ZSBHTCBmb3IgZWFjaCBwb3B1bGF0aW9uIGFuZCBTaXRlIGZyZXF1ZW5jeSBzcGVjdHJ1bSBmb3IgZWFjaCBwb3B1bGF0aW9uCjxici8+ClRoZSBjYWxjdWxhdGlvbiBvZiB0aGUgRlNUICB3aXRoIGFuZ3NkIHJlbHkgb24gdGhlIFNGUy4gVGhlIFNGUyBtdXN0IGJlIGVzdGltYXRlZCBzZXBhcmF0ZWx5IGZvciBlYWNoIHBvcHVsYXRpb24gb2YgaW50ZXJlc3QuClRoZSBGU1Qgd2VyZSBjb21wdXRlZCBiZXR3ZWVuOgoKKyAqKjEpKiogNSAoS3MpIHBvcHVsYXRpb24gaW5mZXJyZWQgd2l0aCB0aGUgY2x1c3RlcmluZyBhcHByb2FjaGVzIChOR1NhZG1peCwgQWRtaXh0dXJlLCBQQ2FuZ3NkKS4gCgorICoqMikqKiBUaGUgMTAgZ2VvZ3JhcGhpY2FsIGxvY2F0aW9ucy4KClRoZSBTRlMgZm9yIGVhY2ggcG9wdWxhdGlvbiB3ZXJlIGVzdGltYXRlZCB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6CmBgYHtiYXNofQojISAvYmluL2Jhc2gKCiNTQkFUQ0ggLS1qb2ItbmFtZT1HTAojU0JBVENIIC0tcGFydGl0aW9uPXJlZ3VsYXIKI1NCQVRDSCAtLW1lbT0xMEdCCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTEKI1NCQVRDSCAtLXRpbWU9MC04OjAwOjAwCgojIyMjIyMjIyMjIyMjIyMjCiMgRGVmaW5lIHBhdGhzICMKIyMjIyMjIyMjIyMjIyMjIwptb2R1bGUgbG9hZCBIVFNsaWIvMS4xMi1HQ0MtMTAuMi4wCgpCQU0xX0xJU1Q9In4vRGF0YS9CQU1fRklMRVMvQkFNXyQxLnR4dCIKUEFUSF9SRVNVTFRTPSJ+L0ZTVC9HRkwvJDEiCkdGTF9GaWxlMT0iJDFfaW50ZXJzZWN0LmdsZiIKUkVGPSJ+L0RhdGEvcmVmX2dlbm9tZS9hakNvbnNlbnN1cy5mYXN0YSIKQU5HU0Q9Ii9kYXRhL3AyNzQxMDUvdG9vbF9nZW5vbWljcy9hbmdzZCIKCm1rZGlyIC1wICAkUEFUSF9SRVNVTFRTCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ09NUFVURSBNSU4gU0FNUExFIFNJWkUgQU5EIE1JTi9NQVggREVQVEggZm9yIFBPUCAxICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAgCk5CX1NBTVBMRVM9JChjYXQgJEJBTTFfTElTVHx3YyAtbCkgCk1JTkRFUFRIPSQoKE5CX1NBTVBMRVMqMikpICMgTWluIGRlcHRoIDJYICAKTUFYREVQVEg9JCgoTkJfU0FNUExFUyozMCkpICMgTWF4IGRlcHRoIDMwIFgKTUlOX3NhbXBsZT0kKCgoJE5CX1NBTVBMRVMgKiA4MCApIC8gMTAwKSkgIyBNaW4gbnVtYmVyIG9mIGluZGl2aWR1YWxzIDgwJSAKCmVjaG8gJE1JTkRFUFRICmVjaG8gJE1BWERFUFRICmVjaG8gJE1JTl9zYW1wbGUKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQ09NUFVURSBHRU5PVFlQRSBMSUtFTElIT09EUyBGT1IgUE9QMSAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiRBTkdTRC9hbmdzZCAtR0wgMiBcCi1yZWYgJHtSRUZ9IFwKLWFuYyAke1JFRn0gXAotYmFtICRCQU0xX0xJU1QgXAotdW5pcXVlT25seSAxIC1yZW1vdmVfYmFkcyAxIC1vbmx5X3Byb3Blcl9wYWlycyAxIFwKLXRyaW0gMCAtQyA1MCAtYmFxIDEgXAotb3V0ICR7UEFUSF9SRVNVTFRTfS8ke0dGTF9GaWxlMX0gXAotblRocmVhZHMgMSBcCi1taW5NYXBRIDIwIFwKLW1pblEgMjAgXAotZG9TYWYgMSBcICMgVG8gZXN0aW1hdGUgdGhlIHNpdGUgZnJlcXVlbmN5IHNwZWN0cnVtCi1taW5JbmQgJE1JTl9zYW1wbGUgXAotZG9Db3VudHMgMSBcCi1zZXRNaW5EZXB0aCAkTUlOREVQVEggLXNldE1heERlcHRoICRNQVhERVBUSCAKYGBgCgpCZWNhdXNlIHdlIGFyZSBub3QgdXNpbmcgYW4gb3V0Z3JvdXAgdG8gZ2V0IHRoZSBhbmNlc3RyYWwgc3RhdGUsIHRoZSBTRlMgbXVzdCBiZSBmb2xkZWQuIEhPV0VWRVIsIHRoZSBmb2xkaW5nIG9mIHRoZSBTRlMgc2hvdWxkIGJlIHBlcmZvcm1lZCBpbiBhIGxhdGVyIHN0ZXAgVGhpcyBpcyB3aHk6CgpgYGB7ciBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUUsIG91dC53aWR0aD0iMTUwJSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHBhdGg9IlNjcmVlbnNob3QgMjAyMi0wNy0xMCBhdCAyMC4wNC4zNC5wbmciKQpgYGAKCkFsc28gYmVjYXVzZSB0aGUgYW5hbHlzZXMgaXMgYmFzZWQgb24gdGhlIFNGUywgbW9ub21vcnBoaWMgYW5kIHJhcmUgdmFyaWFudHMgbXVzdCBiZSBrZXB0IHRvIGluZmVyIGFuIHVuYmlhaXNlZCBTRlMuIFRoZXJlZm9yZSB0aGUgZmxhZ3Mgb24gdGhlIG1pbm9yIGFsbGVsZSBmcmVxdWVuY3kgYW5kIG9uIFNOUCBQLXZhbHVlIGFyZSBub3QgdXNlZCBoZXJlLiAKCmBgYHtyIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRSwgb3V0LndpZHRoPSIxNTAlIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGF0aD0iU2NyZWVuc2hvdCAyMDIyLTA3LTEwIGF0IDIwLjEzLjEwLnBuZyIpCmBgYAoKCkFwcGx5aW5nIHRoZXNlIGZpbHRlcnMgSSByZXRhaW5lZCBiZXR3ZWVuICoqMTYsMjMxLDcxOSBhbmQgMjAyLDA4NCwwMzQgc2l0ZXMqKiAobm90IFNOUHMpIGRlcGVuZGluZyBvbiB0aGUgcG9wdWxhdGlvbi4gCjxici8+PGJyLz4KCiMjIDIpIEVzdGltYXRlIDJEU0ZTICsgY29tcHV0ZSBGU1QgYmFzZWQgb24gYXMgbWFueSBzaXRlcyBhcyBwb3NzaWJsZQorICoqMToqKiBDb21wdXRlIHRoZSBmb2xkZWQgMkQgU0ZTIGVhY2ggcGFpciBvZiBwb3B1bGF0aW9uLgoKKyAqKjI6KiogSW5kZXggdGhlIFNGUy4KCisgKiozOioqIEdldCB0aGUgZ2xvYmFsIGVzdGltYXRlIG9mIHRoZSBGU1QgZm9yIGVhY2ggcGFpciBvZiBwb3B1bGF0aW9uLgoKKyAqKjQ6KiogQ29tcHV0ZSB0aGUgd2VpZ2h0ZWQgRlNUIG92ZXIgMTBrYiB3aW5kb3dzLgoKYGBge2Jhc2h9CiMhIC9iaW4vYmFzaAoKI1NCQVRDSCAtLWpvYi1uYW1lPVNGU19GU1QKI1NCQVRDSCAtLXBhcnRpdGlvbj1nZWxpZmVzCiNTQkFUQ0ggLS1tZW09MzVHQgojU0JBVENIIC0tY3B1cy1wZXItdGFzaz04CiNTQkFUQ0ggLS10aW1lPTAtMjA6MDA6MDAKCiMgY3JpcHQgbmFtZTogU0ZTX0ZTVC5zaAoKIyMjIyMjIyMjIyMjIyMjIwojIERlZmluZSBwYXRocyAjCiMjIyMjIyMjIyMjIyMjIyMKbW9kdWxlIGxvYWQgSFRTbGliLzEuMTItR0NDLTEwLjIuMAoKQkFNMV9MSVNUPSJ+L0RhdGEvQkFNX0ZJTEVTL0JBTV8kMS50eHQiCkJBTTJfTElTVD0ifi9EYXRhL0JBTV9GSUxFUy9CQU1fJDIudHh0IgpQQVRIX1JFU1VMVFM9In4vUmVzdWx0cy9GU1QvR0ZMLyQxXyQyIgpHRkxfRmlsZTE9Ildhc3BfUGluYV8kMS5nbGYiCkdGTF9GaWxlMj0iV2FzcF9QaW5hXyQyLmdsZiIKUkVGPSJ+L0RhdGEvcmVmX2dlbm9tZS9hakNvbnNlbnN1cy5mYXN0YSIKQU5HU0Q9Ii9kYXRhL3AyNzQxMDUvdG9vbF9nZW5vbWljcy9hbmdzZCIKCm1rZGlyIC1wICAkUEFUSF9SRVNVTFRTCgojIyMjIyMjIyMjCiMgTUwgU0ZTICMKIyMjIyMjIyMjIwojY2FsY3VsYXRlIHRoZSAyZHNmcyBwcmlvcgokQU5HU0QvbWlzYy9yZWFsU0ZTICAkUEFUSF9SRVNVTFRTLyoiJDEiLmdsZi5zYWYuaWR4ICRQQVRIX1JFU1VMVFMvKiIkMiIuZ2xmLnNhZi5pZHggLWZvbGQgMSAtUCA4ID4gJFBBVEhfUkVTVUxUUy8iJDEiXyIkMiIubWwKZWNobyAiMkRTRlMgUkVBRFkiCgojcHJlcGFyZSB0aGUgZnN0IGZvciBlYXN5IHdpbmRvdyBhbmFseXNpcwokQU5HU0QvbWlzYy9yZWFsU0ZTIGZzdCBpbmRleCAkUEFUSF9SRVNVTFRTLyoiJDEiLmdsZi5zYWYuaWR4ICRQQVRIX1JFU1VMVFMvKiIkMiIuZ2xmLnNhZi5pZHggLXNmcyAkUEFUSF9SRVNVTFRTLyIkMSJfIiQyIi5tbCAtZm9sZCAxIC13aGljaEZzdCAxIC1mc3RvdXQgJFBBVEhfUkVTVUxUUy8iJDEiXyIkMiIgLVAgOAplY2hvICJEQVRBIFJFQURZIEZPUiBTRlMgQ0FMQ1VMQVRJT04iCgojZ2V0IHRoZSBnbG9iYWwgZXN0aW1hdGUKZWNobyAtZSBVbndlaWdodGVkIlx0IldlaWdodGVkID4gJFBBVEhfUkVTVUxUUy8iJDEiXyIkMiJfcmVzdWx0cy5mc3QKJEFOR1NEL21pc2MvcmVhbFNGUyBmc3Qgc3RhdHMgJFBBVEhfUkVTVUxUUy8iJDEiXyIkMiIuZnN0LmlkeCAtZm9sZCAxIC13aGljaEZzdCAxID4+ICRQQVRIX1JFU1VMVFMvIiQxIl8iJDIiX3Jlc3VsdHMuZnN0CmVjaG8gIkdMT0JBTCBGU1QgQ09NUFVURUQiCgojIEZTVCBpbiBzbGlkaW5nIHdpbmRvd3Mgb2YgNTAga2IKJEFOR1NEL21pc2MvcmVhbFNGUyBmc3Qgc3RhdHMyICRQQVRIX1JFU1VMVFMvIiQxIl8iJDIiLmZzdC5pZHggLXdpbiAxMDAwMCAtc3RlcCAxMDAwMCAtZm9sZCAxIC13aGljaEZzdCAxID4gJFBBVEhfUkVTVUxUUy9TbGlkaW5nX3dpbmRvd3NfRlNUXyIkMSJfIiQyIi50eHQKZWNobyAiU0xJRElORyBXSU5ET1cgRlNUIENPTVBVVEVEIgoKcm0gJFBBVEhfUkVTVUxUUy8qZ3oKIyBHTCBmb3IgZXZlcnl0aGluZy4gSW5jbHVkaW5nIG1vbm9tb3JwaGljIHNpdGVzLiBHTCBlc3RpbWF0ZSBmb3IgZWFjaCBwb3Agc2VwYXJhdGVseS4gVGhlbiB1c2VkIHRvIGVzdGltYXRlIFNGUyB3aXRoaW4gcG9wLiBUaGVuIHVzZWQgdG8gZXN0aW1hdGUgdGhlIDJEIFNGcy4gRmluYWxseSAyRHNmcyBpcyB1c2VkIHRvIGVzdGltYXRlIHRoZSBGU1QuIEZTVCB3ZXJlIGNvbXB1dGVkIGdsb2JhbGx5ICsgd2l0aGluIHdpbmRvd3Mgb2YgNTBrQi4gCgpgYGAKClRoZSAtd2hpY2hGU1Qgb3B0aW9uIHdhcyB1c2VkIGJlY2F1c2UgaXQgcGVyZm9ybXMgYmV0dGVyIHdpdGggbG93IHNhbXBsZSBzaXplcy4KClRvIGF2b2lkIGRvdWJsZSBjb21wYXJpc29ucyAoZS5nLiBLMSB3aXRoIEsyIGFuZCBLMiB3aXRoIEsxKSB0aGlzIHNjcmlwdCB3YXMgcnVuIGFzIGZvbGxvdzoKCiJmb3IgaSBpbiBLezEuLjV9OyBkbyBmb3IgaiBpbiBLezEuLjV9OyBkbyBpZiBbICIkaSIgIFw8ICAiJGoiIF07IHRoZW4gc2JhdGNoIC4vU0ZTX0ZTVCAkaSAkajsgZmk7IGRvbmU7IGRvbmUiCgo8YnIvPjxici8+CgojIyAzKSBBdmVyYWdlIEZTVCB2YWx1ZXMgKyA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCAoOTVDSSkKClRoZSBmb2xsb3dpbmcgUiBjb21tYW5kIHdhcyB1c2VkIHRvIGVzdGltYXRlIHRoZSA5NUNJLiBJdCB1c2VkIHRoZSBSIHBhY2thZ2UgUm1pc2Mgd2hpY2ggYXNzdW1lIHRoYXQgdGhlIGRhdGEgYXJlIHQtZGlzdHJpYnV0ZWQgKG51bWJlciBvZiB3aW5kb3ctMSBkZWdyZWUgb2YgZnJlZWRvbSkKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KFJtaXNjKSkKbGlicmFyeShSbWlzYykKYXJncyA9IGNvbW1hbmRBcmdzKHRyYWlsaW5nT25seT1UUlVFKQpmc3QgPSByZWFkLnRhYmxlKGFyZ3MpCkNJKHVubGlzdChmc3QpLCBjaT0wLjk1KQpgYGAKClRoZSBwcmV2aW91cyBSIGNvbW1hbmQgd2FzIHJhbiBpbnNpZGUgdGhlIGZvbGxvd2luZyBiYXNoIHNjcmlwdDoKCmBgYHtiYXNofQptb2R1bGUgbG9hZCBSCmNkIEdGTAoKKGVjaG8gLWUgUG9wMSJcdCJQb3AyIlx0Ikdsb2JhbF9GU1QiXHQiTWVhbl9GU1QiXHQiTG93X0NJX0ZTVCJcdCJIaWdoX0NJX0ZTVAoKZm9yIGkgaW4gJChsc3wgZ3JlcCAtUCAiS1xkX0tcZCIpCglkbyAKCQlQT1A9JChlY2hvICRpfCBwZXJsIC1wZSAncy9fL1x0L2cnKQoJCWNkICRpCgkJR0xPQkFMX0ZTVD0kKGNhdCAqZnN0fGF3ayAne3ByaW50ICQyfSd8YXdrICdOUj4xIHtwcmludCAkMH0nKQoJCWNhdCBTbGlkaW5nKnxhd2sgJ3twcmludCAkNX0nID4gYml6Yml6ZmFpdGxhbW91Y2hlLnR4dAoJCU1FQU5fRlNUPSQoUnNjcmlwdCAtLXZhbmlsbGEgLi4vLi4vRlNUX0NJLlIgYml6Yml6ZmFpdGxhbW91Y2hlLnR4dHxhd2sgJ05SPjEge3ByaW50ICQwfSd8IGN1dCAtZiAyIC1kICIgIikKCQlMT1dfQ0k9JChSc2NyaXB0IC0tdmFuaWxsYSAuLi8uLi9GU1RfQ0kuUiBiaXpiaXpmYWl0bGFtb3VjaGUudHh0fGF3ayAnTlI+MSB7cHJpbnQgJDB9J3wgY3V0IC1mIDMgLWQgIiAiKQoJCUhJR0hfQ0k9JChSc2NyaXB0IC0tdmFuaWxsYSAuLi8uLi9GU1RfQ0kuUiBiaXpiaXpmYWl0bGFtb3VjaGUudHh0fGF3ayAnTlI+MSB7cHJpbnQgJDB9J3wgY3V0IC1mIDEgLWQgIiAiKSAgCgkJZWNobyAtZSAkUE9QIlx0IiRHTE9CQUxfRlNUIlx0IiRNRUFOX0ZTVCJcdCIkTE9XX0NJIlx0IiRISUdIX0NJCgkJcm0gYml6Yml6ZmFpdGxhbW91Y2hlLnR4dCAKCQljZCAuLgoJIGRvbmUpID4gLi4vRlNUX2JldHdlZW5fTkdTYWRtaXhfR1JPVVAudHh0CgoKCihlY2hvIC1lIFBvcDEiXHQiUG9wMiJcdCJHbG9iYWxfRlNUIlx0Ik1lYW5fRlNUIlx0Ikxvd19DSV9GU1QiXHQiSGlnaF9DSV9GU1QKCmZvciBpIGluICQobHN8IGdyZXAgLXYgLVAgIktcZHxtYWYiKQogICAgICAgIGRvCiAgICAgICAgICAJUE9QPSQoZWNobyAkaXwgcGVybCAtcGUgJ3MvXy9cdC9nJykKICAgICAgICAgICAgICAgIGNkICRpCiAgICAgICAgICAgICAgICBHTE9CQUxfRlNUPSQoY2F0ICpmc3R8YXdrICd7cHJpbnQgJDJ9J3xhd2sgJ05SPjEge3ByaW50ICQwfScpCiAgICAgICAgICAgICAgICBjYXQgU2xpZGluZyp8YXdrICd7cHJpbnQgJDV9JyA+IGJpemJpemZhaXRsYW1vdWNoZS50eHQKICAgICAgICAgICAgICAgIE1FQU5fRlNUPSQoUnNjcmlwdCAtLXZhbmlsbGEgLi4vLi4vRlNUX0NJLlIgYml6Yml6ZmFpdGxhbW91Y2hlLnR4dHxhd2sgJ05SPjEge3ByaW50ICQwfSd8IGN1dCAtZiAyIC1kICIgIikKICAgICAgICAgICAgICAgIExPV19DST0kKFJzY3JpcHQgLS12YW5pbGxhIC4uLy4uL0ZTVF9DSS5SIGJpemJpemZhaXRsYW1vdWNoZS50eHR8YXdrICdOUj4xIHtwcmludCAkMH0nfCBjdXQgLWYgMyAtZCAiICIpCiAgICAgICAgICAgICAgICBISUdIX0NJPSQoUnNjcmlwdCAtLXZhbmlsbGEgLi4vLi4vRlNUX0NJLlIgYml6Yml6ZmFpdGxhbW91Y2hlLnR4dHxhd2sgJ05SPjEge3ByaW50ICQwfSd8IGN1dCAtZiAxIC1kICIgIikKICAgICAgICAgICAgICAgIGVjaG8gLWUgJFBPUCJcdCIkR0xPQkFMX0ZTVCJcdCIkTUVBTl9GU1QiXHQiJExPV19DSSJcdCIkSElHSF9DSQogICAgICAgICAgICAgICAgcm0gYml6Yml6ZmFpdGxhbW91Y2hlLnR4dAogICAgICAgICAgICAgICAgY2QgLi4KICAgICAgICAgZG9uZSkgPiAuLi9GU1RfYmV0d2Vlbl9HRU9fR1JPVVAudHh0CmBgYAoKIyMjIGEpIEZTVCBiYXNlZCBvbiBOR1NhZG1peCBncm91cHMKYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmVhZC50YWJsZSgiRlNUX2JldHdlZW5fTkdTYWRtaXhfR1JPVVAudHh0IixoZWFkZXIgPVQpCmBgYAojIyMgYikgRlNUIGJhc2VkIG9uIEdlb2dyYXBoaWMgZ3JvdXBzCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJlYWQudGFibGUoIkZTVF9iZXR3ZWVuX0dFT19HUk9VUC50eHQiLGhlYWRlciA9VCkKYGBgCjxici8+PGJyLz4KCiMjIDQpIFZpc3VhbGl6YXRpb24gd2l0aCBoZWF0bWFwcyAoYmFzZWQgb24gdGhlIE1lYW5fRlNUKQojIyMgYSkgRlNUIGJhc2VkIG9uIE5HU2FkbWl4IGdyb3VwcwpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2d0ZXh0KQpGU1QgPSByZWFkLnRhYmxlKCJGU1RfYmV0d2Vlbl9OR1NhZG1peF9HUk9VUC50eHQiLGhlYWRlciA9VCkKRlNUID0gRlNUWyxjKDEsMiw0LDUsNildCkZTVCRUb19wbG90ID0gcGFzdGUwKCcqKicsIHJvdW5kKEZTVCRNZWFuX0ZTVCwzKSwnKio8YnIgLz5bJyxyb3VuZChGU1QkTG93X0NJX0ZTVCwzKSwnLScscm91bmQoRlNUJEhpZ2hfQ0lfRlNULDMpLCddJyAsIHNlcCA9ICIiKQoKcCA9IGdncGxvdChkYXRhID1GU1QsIGFlcyh4PVBvcDEsIHk9UG9wMiwgZmlsbD1NZWFuX0ZTVCkpICsgCmdlb21fdGlsZShjb2xvdXIgPSAiYmxhY2siKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSB2aXJpZGlzKDEwMDAwKSkgKyAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgbGFicyhmaWxsID0gIkZTVCIpICsgeGxhYigiUG9wdWxhdGlvbiAxIikgKyB5bGFiKCJQb3B1bGF0aW9uIDIiKSArIHRoZW1lX2J3KCkgKyAgIGdlb21fdGV4dGJveChsYWJlbCA9IEZTVCRUb19wbG90LCBib3guc2l6ZSA9IDAsIGZpbGwgPSBOQSwgaGFsaWduID0gMC41LCAgY29sb3VyID0gaWZlbHNlKEZTVCRNZWFuX0ZTVCA8IDAuMjAsJ3doaXRlJywnYmxhY2snKSxzaXplPTQpICsgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnYmxhY2snLCd3aGl0ZScpKSArCnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9IE5ld19jb2xbMTo0XSwgc2l6ZSA9IDExLGZhY2U9ImJvbGQiKSkgKwp0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSBOZXdfY29sWzI6NV0sIHNpemUgPSAxMSxmYWNlPSJib2xkIikpICsgCnRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE2LCBmYWNlID0gImJvbGQiKSkgKwp0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCAgaGp1c3QgPSAxKSkgCgpwCgoKYGBgCgo9PiBUaGUgcmVzdWx0cyBhcmUgb3ZlcmFsbCBjb25zaXN0ZW50IHdpdGggU05QIGNhbGxpbmcgcmVzdWx0cy4gVGhlcmUgYXJlIHN0aWxsIHRocmVlIGRpZmZlcmVuY2VzOgoKKyAqKmE6KiogVGhlIEZTVCB2YWx1ZXMgYXJlIGxvd2VyLiAKCisgKipiOioqIFRoZSBGU1QgdmFsdWVzIGJldHdlZW4gSzQgYW5kIEs1IChiZXR3ZWVuIGFzZXh1YWwgcG9wLikgdGVuZCB0byBiZSBsYXJnZXIgKHJlbGF0aXZlbHkgc3BlYWtpbmcgY29tcGFyZWQgdG8gdGhlIHJlc3QpCgorICoqYzoqKiBUaGUgRlNUIHZhbHVlcyBiZXR3ZWVuIHRoZSBzZXh1YWwgcG9wdWxhdGlvbiBJcmlvbW90ZSAoSzEpIHZzIEsyL0szIChBbWFtaS9Pa2luYXdhKSBzaG93IGxvd2VyIEZTdCB2YWx1ZXMgdGhhbiBjb21wYXJpc29ucyBiZXR3ZWVuIElyaW9tb3RlIGFuZCBLNC81IChhc2V4dWFsKS4gVGhlIGRpZmZlcmVuY2Ugd2FzIGxlc3MgcHJvbm91bmNlZCBpbiB0aGUgYW5hbHlzaXMgcGVyZm9ybWVkIGJ5IFBpbmE6Cjxici8+Cgo8Y2VudGVyPgoKYGBge3IgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFLCBvdXQud2lkdGg9IjE1MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGF0aD0iU2NyZWVuc2hvdCAyMDIyLTA3LTExIGF0IDE3LjI4LjAxLnBuZyIpCmBgYAo8Y2VudGVyPgo8YnIvPjxici8+CgojIyMgYikgRlNUIGJhc2VkIG9uIEdlb2dyYXBoaWMgZ3JvdXBzCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoZHBseXIpCgpGU1QgPSByZWFkLnRhYmxlKCJGU1RfYmV0d2Vlbl9HRU9fR1JPVVAudHh0IixoZWFkZXIgPVQpCkZTVCA9IEZTVFssYygxLDIsNCw1LDYpXQpGU1QkVG9fcGxvdCA9IHBhc3RlMCgnKionLCByb3VuZChGU1QkTWVhbl9GU1QsMyksJyoqPGJyIC8+Wycscm91bmQoRlNUJExvd19DSV9GU1QsMyksJy0nLHJvdW5kKEZTVCRIaWdoX0NJX0ZTVCwzKSwnXScgLCBzZXAgPSAiIikKCmZvciAocm93IGluIDE6ZGltKEZTVClbMV0pewogIGlmKEZTVFtyb3csYygxKV0hPUZTVFtyb3csYygyKV0pewogICBGU1QgPSByYmluZChGU1QsRlNUW3JvdyxdKQogICB0bXBfcG9wMSA9IEZTVFtucm93KEZTVCksYygxKV0KICAgdG1wX3BvcDIgPSBGU1RbbnJvdyhGU1QpLGMoMildCiAgIEZTVFtucm93KEZTVCksYygyKV0gPSB0bXBfcG9wMQogICBGU1RbbnJvdyhGU1QpLGMoMSldID0gdG1wX3BvcDIKICAgfQp9CgoKRlNUJFBvcDEgPC0gZmFjdG9yKEZTVCRQb3AxLCBsZXZlbHM9IGMoIklyaW9tb3RlIiwiT2tpbmF3YSIsIkFtYW1pIiwiS2Fnb3NoaW1hIiwiRnVrdW9rYSIsIktvYmUiLCJLeW90byIsIkthbmF6YXdhIiwiU2VuZGFpIiwiVG9reW8iKSkKRlNUJFBvcDIgPC0gZmFjdG9yKEZTVCRQb3AyLCBsZXZlbHM9IGMoIklyaW9tb3RlIiwiT2tpbmF3YSIsIkFtYW1pIiwiS2Fnb3NoaW1hIiwiRnVrdW9rYSIsIktvYmUiLCJLeW90byIsIkthbmF6YXdhIiwiU2VuZGFpIiwiVG9reW8iKSkKRlNUID0gYXJyYW5nZShGU1QsIFBvcDEsIFBvcDIpCgoKbGlzdCA9IGxpc3QoKQphID0gMQpmb3IgKGkgaW4gMTpkaW0oRlNUKVsxXSl7CiAgZm9yIChqIGluIDE6ZGltKEZTVClbMV0pewogICAgaWYoaiA+IGkpewogICAgICBpZihGU1RbaSwxXT09RlNUW2osMl0gJiYgRlNUW2ksMl09PUZTVFtqLDFdKXsKICAgICAgICBsaXN0W1thXV0gPSBGU1RbaSxdCiAgICAgICAgYSA9IGEgKyAxCiAgICAgIH0KICAgIH0KICB9Cn0KCkZTVCA9IGRvLmNhbGwocmJpbmQsbGlzdCkKCgpwID0gZ2dwbG90KGRhdGEgPUZTVCwgYWVzKHg9UG9wMSwgeT1Qb3AyLCBmaWxsPU1lYW5fRlNUKSkgKyAKZ2VvbV90aWxlKGNvbG91ciA9ICJibGFjayIpICsgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IHZpcmlkaXMoMTAwMDApKSArICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKyBsYWJzKGZpbGwgPSJGU1QiKSArIHhsYWIoIlBvcHVsYXRpb24gMSIpICsgeWxhYigiUG9wdWxhdGlvbiAyIikgKyB0aGVtZV9idygpICsgIGdlb21fdGV4dGJveChsYWJlbCA9IEZTVCRUb19wbG90LCBib3guc2l6ZSA9IDAsIGZpbGwgPSBOQSwgaGFsaWduID0gMC41LCBjb2xvdXIgPSBpZmVsc2UoRlNUJE1lYW5fRlNUIDwgMC4yMCwnd2hpdGUnLCdibGFjaycpLHNpemU9NSkgKyBndWlkZXMoY29sb3VyID0gRkFMU0UpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdibGFjaycsJ3doaXRlJykpICsKdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoY29sb3VyID0gbXlDb2xvcnNbMTo5XSwgc2l6ZSA9IDE2LGZhY2U9ImJvbGQiKSkgKwp0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSBteUNvbG9yc1syOjEwXSwgc2l6ZSA9IDE2LGZhY2U9ImJvbGQiKSkgKyAKdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTYsIGZhY2UgPSAiYm9sZCIpKSArCnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsICBoanVzdCA9IDEpKSAKCnAKCmBgYAo9PiBDb25zaXN0ZW50IHdpdGggYWxsIHRoZSBhbmFseXNlcy4gCjxici8+PGJyLz4KCiMgVklJSSkgR2VudGljIGRpc3RhbmNlIG1hdHJpeCAoZm9yIHN1YnNldCBvZiA1NCBpbmRpdmlkdWFscykKVGhpcyBwYXJ0IGFsbG93IHRvIHN1YnNldCB0aGUgbWF0cml4IG9mIGdlbmV0aWMgZGlzdGFuY2UgY2FsY3VsYXRlZCBieSBOR1NkaXN0IDU0IGluZGl2aWR1YWxzLiAKQXMgcmVxdWVzdGVkIGJ5IHRoZSByZXZpZXdlcnMsIHRoaXMgbWF0cml4IGlzIGdvaW5nIHRvIGJlIGNvcnJlbGF0ZWQgd2l0aCB0aGUgbWljcm9iaW9tZSBjb21wb3NpdGlvbi4gClRoZSBhaW0gaXMgdG8gaW52ZXN0aWdhdGUgaWYgbW9yZSBnZW5ldGljYWxseSBkaXN0YW50IHBvcHVsYXRpb25zIHRlbmQgdG8gaGF2ZSBhIG1vcmUgZGlmZmVyZW50aWF0ZWQgbWljcm9iaW9tZXMgdG9vLiAKClRoaXMgZmlyc3QgYmFzaCBibG9jayBzZXBhcmF0ZSB0aGUgZ2VuZXRpYyBkaXN0YW5jZSBtYXRyaXggZXN0aW1hdGVkIGJ5IE5HU2Rpc3QgZnJvbSB0aGUgYm9vdHN0cmFwZWQgb25lIGFuZCBwcmVwYXJlIChhZGp1c3QgbGFiZWwgbmFtZXMpIHRoZSB0aGUgbGlzdCBvZiBpbmRpdmlkdWFscyB0byBiZSBzdWJzZXR0ZWQgbGF0ZXIuIApgYGB7YmFzaCBlY2hvPVRSVUV9CiMgRXh0cmFjdCB0aGUgZ2VuZXRpYyBkaXN0YW5jZSBtYXRyaXggKGZyb20gdGhlIGJvb3RzdHJhcHBlZCBvbmVzKQpjYXQgTkp0cmVlL291dHB1dC5kaXN0IHwgYXdrIC12ICdSUz1cblxuJyAnMTt7ZXhpdH0nfGF3ayAnTlI+Mid8cGVybCAtcGUgJ3MvXlxuLy9nJyA+IGRpc3RhbmNlX21hdHJpeC50eHQKCiMgUmVtb3ZlIHRoZSBzb3J0ZWQgaW4gdGhlIGxhYmVsIG5hbWVzCmNhdCBzdWJzZXQqfHBlcmwgLXBlICdzL15cbi8vZyd8cGVybCAtcGUgJ3MvX3NvcnRlZC8vZycgPiBpbmRfZm9yX21hdHJpeC50eHQKYGBgCjxici8+CkhlcmUgaXMgdGhlIGxpc3Qgb2YgNTQgaW5kaXZpZHVhbHMgdGhhdCBhcmUgZ29pbmcgdG8gYmUgZXh0cmFjdGVkOgpgYGB7cn0KIyBJbXBvcnQgdGhlIHJlYWwgSUQgKHdpdGggZ2VvIG5hbWVzKQppbmRfdG9fc3Vic2V0ID0gcmVhZC50YWJsZSgiaW5kX2Zvcl9tYXRyaXgudHh0IikKbmFtZXMoaW5kX3RvX3N1YnNldCkgPSAiSUQiCmluZF90b19zdWJzZXQKYGBgCjxici8+ClRoaXMgUiBjb2RlIGJsb2NrIGV4dHJhY3QgdGhlIGdlbmV0aWMgZGlzdGFuY2VzIGVzdGltYXRlZCBieSBOR1NEaXN0IGZvciBhIHN1YnNldCA1NCBpbmRpdmlkdWFscyAod2l0aCBtaWNyb2Jpb21lIGRhdGEpLgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEltcG9ydCB0aGUgZGlzdGFuY2UgbWF0cml4IGluIFIKZGlzdF9tYXRyaXggPSByZWFkLnRhYmxlKCJkaXN0YW5jZV9tYXRyaXgudHh0IikKZGlzdF9tYXRyaXhfbmFtZXMgPSBkaXN0X21hdHJpeFssYygxKV0KZGlzdF9tYXRyaXggPSBkaXN0X21hdHJpeFssYygtMSldCmNvbG5hbWVzKGRpc3RfbWF0cml4KSA9IGRpc3RfbWF0cml4X25hbWVzCnJvd25hbWVzKGRpc3RfbWF0cml4KSA9IGRpc3RfbWF0cml4X25hbWVzCgoKIyBUcmFuc2Zvcm0gdGhlIGJhbSBJRCB0byB0aGUgcmVhbCBJRAp0ZXN0MSA9IHJlYWQudGFibGUoIi4uL2NvcnJlc3BvbmRhbmNlX2JhbV9JRF9sb2NhdGlvbi50eHQiKQp0ZXN0MSA9IGNiaW5kKHBhc3RlKHRlc3QxJFYxLHRlc3QxJFYyLHNlcD0iXyIpLCB0ZXN0MSRWMykKdGVzdDEgPSBhcy5kYXRhLmZyYW1lKHRlc3QxKQp0ZXN0MSA9IHRlc3QxWy1jKDEpLF0KCgpmb3IgKGkgaW4gMTpkaW0odGVzdDEpWzFdKXsKICBmb3IgKGogaW4gMTpkaW0oaW5kX3RvX3N1YnNldClbMV0pewogICAgaWYgKHRlc3QxW2ksMV09PWluZF90b19zdWJzZXRbaixdKXsKICAgICAgaW5kX3RvX3N1YnNldFtqLF09dGVzdDFbaSwyXQogICAgfQogIH0KfQoKIyBTdWJzZXQgdGhlIGRpc3RhbmNlIG1hdHJpeCBmb3IgdGhlIGluZGl2aWR1YWxzIGZvciB3aGljaCBtaWNyb2Jpb21lcyBkYXRhIGFyZSBhdmFpbGFibGUKZGlzdF9tYXRyaXggPSBkaXN0X21hdHJpeFtyb3duYW1lcyhkaXN0X21hdHJpeCkgJWluJSB1bmxpc3QoaW5kX3RvX3N1YnNldCksY29sbmFtZXMoZGlzdF9tYXRyaXggKSVpbiUgdW5saXN0KGluZF90b19zdWJzZXQpXQoKIyBSZXZlcnNlIGRpc3RhbmNlIG1hdHJpeCBuYW1lcyB0byB1c2UgdGhlIHJlYWwgZ2VvZ3JhcGhpY2FsIElECmZvciAoaSBpbiAxOmRpbSh0ZXN0MSlbMV0pewogIGZvciAoaiBpbiAxOmRpbShpbmRfdG9fc3Vic2V0KVsxXSl7CiAgICBpZiAoY29sbmFtZXMoZGlzdF9tYXRyaXgpW2pdPT10ZXN0MVtpLDJdKXsKICAgICAgY29sbmFtZXMoZGlzdF9tYXRyaXgpW2pdPXRlc3QxW2ksMV0KICAgICAgcm93bmFtZXMoZGlzdF9tYXRyaXgpW2pdPXRlc3QxW2ksMV0KICAgIH0KICB9Cn0KCm9yZGVyX21hdHJpeCA9IHJlYWQudGFibGUoImluZF9mb3JfbWF0cml4LnR4dCIpCmRpc3RfbWF0cml4ID0gZGlzdF9tYXRyaXhbdW5saXN0KG9yZGVyX21hdHJpeCksdW5saXN0KG9yZGVyX21hdHJpeCldCmRpc3RfbWF0cml4CgpgYGAKCjxici8+PGJyLz4KCiMgSVgpIEtpbnNoaXAgKGZvciBzdWJzZXQgb2YgNTQgaW5kaXZpZHVhbHMpCjxici8+ClBDYW5nc2Qgd2FzIHVzZWQgdG8gZXN0aW1hdGUgdGhlIHJlbGF0ZWRuZXNzIGJldHdlZW4gZWFjaCBwYWlyIG9mIGluZGl2aWR1YWxzIHdoaWxlIHRha2luZyBpbnRvIGFjY291bnQgdGhlIGdlbmV0aWMgc3RydWN0dXJlIChvcHRpb24gLWtpbnNoaXApLiBUaGUgbWV0aG9kIGltcGxlbWVudGVkIGluIEFOR1NEIGlzIGJhc2VkIG9uIHRoZSBQQ3JlbGF0ZSBhcHByb2FjaCAoaHR0cHM6Ly93d3cuY2VsbC5jb20vYWpoZy9mdWxsdGV4dC9TMDAwMi05Mjk3KDE1KTAwNDkzLTApLgoKRm9yIHRoZSBzYWtlIG9mIHNpbXBsaWNpdHksIEkgZmlyc3QgcmFuIFBDcmVsYXRlIGZvciB0aGUgOTUgYXZhaWxhYmxlIGluZGl2aWR1YWxzLiBUaGVuIGFmdGVyd2FyZCBJIHN1Yi1zYW1wbGVkIHRoZSByZXN1bHRpbmcgcmVsYXRlZG5lc3MgbWF0cml4IHRvIGhhdmUgdGhlIHJlbGF0ZWRuZXNzIGNvZWZmaWNpZW50IGp1c3QgZm9yIHRoZSA1NCBpbmRpdmlkdWFscyBmb3Igd2hpY2ggbWljcm9iaW9tZSBkYXRhIGFyZSBhdmFpbGFibGUuCgpQQ3JlbGF0ZSB3YXMgcmFuIG9uIHRoZSBMRCBwcnVuZWQgZGF0YSBzZXQgdXNpbmcgdGhlIGJlbG93IHNjcmlwdDoKYGBge2Jhc2h9CiMhIC9iaW4vYmFzaAoKI1NCQVRDSCAtLWpvYi1uYW1lPVBDcmVsCiNTQkFUQ0ggLS1wYXJ0aXRpb249c2hvcnQKI1NCQVRDSCAtLW1lbT0xR0IKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9MQojU0JBVENIIC0tbWFpbC10eXBlPUJFR0lOLEVORCxGQUlMCiNTQkFUQ0ggLS1tYWlsLXVzZXI9aC55LmJlbi5jaGVoaWRhQHJ1Zy5ubAojU0JBVENIIC0tdGltZT0wMDozMDowMAoKCiMgUENBbmdzZCBuZWVkcyBhbmQgYWNjZXB0cyBhcmUgZ2Vub3R5cGUgbGlrZWxpaG9vZHMgaW4gQmVhZ2xlIGZvcm1hdC4KIyBXZSBnZW5lcmF0ZWQgaXQgYWxyZWFkeSBmb3IgdGhlIE5HU2FkbWl4IGFuYWx5c2lzLiBTbyB3ZSBjYW4gcmV1c2UgaXQgaGVyZS4KCiMjIyMjIyMjIyMjIyMjIyMKIyBEZWZpbmUgcGF0aHMgIwojIyMjIyMjIyMjIyMjIyMjCkdGTD0iL2RhdGEvcDI3NDEwNS9XYXNwX1BpbmEvUmVzdWx0cy9XYXNwX1BpbmFfc3Vic2V0LmdsZi5iZWFnbGUuZ3oiCk9VVFBVVD0iL2RhdGEvcDI3NDEwNS9XYXNwX1BpbmEvUmVzdWx0cy9QQ3JlbGF0ZSIKCm1vZHVsZSBsb2FkIFBDQW5nc2QvMC45OC1mb3NzLTIwMThhLVB5dGhvbi0zLjYuNAptb2R1bGUgbG9hZCBSLzQuMC4wLWZvc3MtMjAyMGEgICAKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIEVzdGltYXRlIHBhaXJ3aXNlIHJlbGF0ZWRuZXNzICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnBjYW5nc2QucHkgXAotYmVhZ2xlICRHRkwgIFwKLW8gJE9VVFBVVC9pbmJyZWVkaW5nXzEvUmVzXzEgXAotdGhyZWFkcyAxIFwKLWluYnJlZWQgMSBcCi1pbmJyZWVkX2l0ZXIgMTAwMCBcCi1raW5zaGlwIFwKLWluYnJlZWRfdG9sZSAxZS02CmBgYAoKPGJyLz4gCgpUaGUgUiBjb2RlIGJlbG93OgoKKyAqKjE6KiogRXh0cmFjdCB0aGUgcGFpcndpc2UgcmVsYXRlZG5lc3MgY29lZmZpY2llbnRzIGZvciB0aGUgNTQgaW5kaXZpZHVhbHMgb2YgaW50ZXJlc3RzLgoKKyAqKjI6KiogU2V0IHRoZSBuZWdhdGl2ZSByZWxhdGVkbmVzcyB2YWx1ZXMgdG8gMC4KCisgKiozOioqIFZpc3VhbGx5IHN1bW1hcml6ZSB0aGUgcmVzdWx0cyB1c2luZyBhIGRlZmF1bHQgaGVhdCBtYXAgKEkgY2FuIG1vZGlmeSBpdCBzbyBpdCBsb29rcyBsaWtlIHRoZSBGU1Qgb25lIGlmIG5lZWRlZCkuCgoKYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEyfQojIS91c3IvYmluL2VudiBSc2NyaXB0CmxpYnJhcnkoUmNwcENOUHkpICMgbGlicmFyeSB0byByZWFkIG51bXB5IGZpbGVzCm9wdGlvbnMoc2NpcGVuPTk5OSkgIyB1c2UgZnVsbCBudW1iZXIgaW5zdGVhZCBvZiBzY2llbnRpZmljIG5vdGF0aW9uCmxpYnJhcnkocGhlYXRtYXApIApsaWJyYXJ5KGRwbHlyKQoKCmtpbnNoaXAgPC0gbnB5TG9hZChmaWxlbmFtZT0iUENyZWxhdGUvaW5icmVlZGluZ18xL1Jlc18xLmtpbnNoaXAubnB5IikgI0ltcG9ydCBraW5zaGlwIG1hdHJpeCBpbiBudW1weSBmb3JtYXQKa2luc2hpcCA9IGFzLmRhdGEuZnJhbWUoa2luc2hpcCkgIyB0cmFuc2Zvcm0gdGhlIG1hdHJpeCB0byBhIGRhdGEgZnJhbWUKaW5kaXZpZHVhbHMgPC0gcmVhZC50YWJsZSgiLi4vYmFtX2xvY2F0aW9uX2NvcnJlc3BvbmRhbmNlLnR4dCIpICMgaW5kaXZpZHVhbHMgbmFtZSBhbmQgcG9wIG9mIGVhY2ggaW5kaXZpZHVhbApjb2xuYW1lcyhraW5zaGlwKSA9dW5saXN0KGluZGl2aWR1YWxzWyxjKDEpXSkgIyBhZGQgaW5kaXZpZHVhbHMgbmFtZSB0byBlYWNoIGNvbHVtbiAKcm93bmFtZXMoa2luc2hpcCkgPXVubGlzdChpbmRpdmlkdWFsc1ssYygxKV0pICMgYWRkIGludmlkdWFscyBuYW1lIHRvIGVhY2ggcm93CmtpbnNoaXAgPSBraW5zaGlwW3Jvd25hbWVzKGtpbnNoaXApICVpbiUgdW5saXN0KGluZF90b19zdWJzZXQpLGNvbG5hbWVzKGtpbnNoaXAgKSVpbiUgdW5saXN0KGluZF90b19zdWJzZXQpXQpraW5zaGlwW2tpbnNoaXAgPCAwXSA9IDAgIyBraW5zaGlwIHZhbHVlcyBpbmZlcmlvciB0byAwIGFyZSBub3QgYmlvbG9naWNhbGx5IG1lYW5pbmdmdWwuIFRoZXkgYXJlIHNldCB0byAwLgoja2luc2hpcFtraW5zaGlwID4gMC4yNV0gPSAxCmluZGl2aWR1YWxzID0gaW5kaXZpZHVhbHNbaW5kaXZpZHVhbHMkVjEgICVpbiUgdW5saXN0KGluZF90b19zdWJzZXQpLF0KCgpnZW9ncmFwaHkgPSBhcy5kYXRhLmZyYW1lKGluZGl2aWR1YWxzWywyXSxzdHJpbmdzQXNGYWN0b3JzPVRSVUUpICMgc3RvcmUgaW5mb3JtYXRpb24gb24gZ2VvZ3JhcGh5IGluIGEgc2VwYXJhdGUgb2JqZWN0CnJvd25hbWVzKGdlb2dyYXBoeSkgPSBpbmRpdmlkdWFsc1ssMV0KY29sbmFtZXMoZ2VvZ3JhcGh5KSA9ICJHZW9ncmFwaHkiCmdlb2dyYXBoeSRHZW9ncmFwaHkgPC0gZmFjdG9yKGdlb2dyYXBoeSRHZW9ncmFwaHksIGxldmVscz0gYygiSXJpb21vdGUiLCJPa2luYXdhIiwiQW1hbWkiLCJLYWdvc2hpbWEiLCJGdWt1b2thIiwiS29iZSIsIkt5b3RvIiwiS2FuYXphd2EiLCJUb2t5byIsIlNlbmRhaSIpKQpnZW9ncmFwaHkkR2VvZ3JhcGh5ID0gc29ydChnZW9ncmFwaHkkR2VvZ3JhcGh5KQoKIyBCbG9jayB0byBhc3NpZ24gZGVzaXJlZCBjb2xvcnMgdG8gdGhlIGRlc2lyZWQgcG9wdWxhdGlvbnMuIApteWNvbG9ycyA9IGNiaW5kKHVuaXF1ZShnZW9ncmFwaHkpLG15Q29sb3JzKSAKZ2VvID0gZ2VvZ3JhcGh5CgpnZW9ncmFwaHkkY29sb3JzID0gImVtcHR5Igpmb3IgKGkgaW4gMTpkaW0oZ2VvZ3JhcGh5KVsxXSl7CiAgZm9yIChqIGluIDE6ZGltKG15Y29sb3JzKVsxXSl7CiAgICBpZihnZW9ncmFwaHlbaSwxXT09bXljb2xvcnNbaiwxXSkKICAgICAgZ2VvZ3JhcGh5W2ksMl0gPSBteWNvbG9yc1tqLDJdCiAgfQp9CgojIFByZXBhcmUgdGhlIGNvbG9ycyBpbiB0aGUgZm9ybWF0IGRlc2lyZWQgYnkgcGhlYXRtYXAKbWF0X2NvbG9ycyA8LSBsaXN0KEdlb2dyYXBoeSA9IG15Q29sb3JzKQpuYW1lcyhtYXRfY29sb3JzJEdlb2dyYXBoeSkgPC0gdW5pcXVlKHVubGlzdChnZW8pKQoKIyBQaGVhdG1hcCBwbG90CnBoZWF0bWFwKHQoa2luc2hpcCksbmFfY29sPSJ3aGl0ZSIsYW5ub3RhdGlvbl9jb2w9Z2VvLGFubm90YXRpb25fcm93PWdlbyxjbHVzdGVyX3Jvd3M9RixjbHVzdGVyX2NvbHM9Rixhbm5vdGF0aW9uX2NvbG9ycyA9IG1hdF9jb2xvcnMpCgpgYGAKCmBgYHtyfQptYXgodW5saXN0KGtpbnNoaXBba2luc2hpcCA8IDAuMjVdKSkKYGBgClRoZSBtYXhpbWFsIHJlbGF0ZWRuZXNzIHZhbHVlcyBpcyBlcXVhbCB0byAqKjAuMDQwNyoqCgo8YnIvPiAKCgpJZiB3ZSBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGVzdGltYXRlZCByZWxhdGVkbmVzcyB2YWx1ZXMKYGBge3J9CnBsb3QoZGVuc2l0eSh1bmxpc3Qoa2luc2hpcFtraW5zaGlwIDwgMC4yNV0pKSkKCmBgYApCYXNpY2FsbHkgbm9uZSBvZiB0aGUgaW5kaXZpZHVhbHMgYXJlIHJlbGF0ZWQuIAoKVGhpcyByZXN1bHQgc3Ryb25nbHkgY29udHJhc3RzIHdpdGggdGhlIHJlc3VsdHMgb2J0YWluZWQgdXNpbmcgU05QcmVsYXRlOgpgYGB7ciBlY2hvPUZBTFNFLCAgaW5jbHVkZT1UUlVFLCBvdXQud2lkdGg9Ijc1JSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHBhdGg9IlNjcmVlbnNob3QgMjAyMi0wNy0yMCBhdCAxMS41Ni4yNS5wbmciKQpgYGAKCldpdGggU05QcmVsYXRlIG1hbnkgb2YgdGhlIHZhbHVlcyBhcmUgdmVyeSBoaWdoLiAKCjxici8+IAoKVGhlIGZvbGxvd2luZyBSIGNvZGU6CgorICoqMToqKiBMYWJlbCBpbmRpdmlkdWFscyBhY2NvcmRpbmcgdG8gdGhlaXIgZ2VvZ3JhcGhpYyBJRCAobm90IHRoZSBvbmUgaW4gdGhlIGJhbSBmaWxlcyBzdGFydGluZyB3aXRoIFN4KQoKKyAqKjI6KiogQ3JlYXRlIGFuZCBkaXNwbGF5IHRoZSBtYXRyaXggdmFsdWVzIHRoYXQgSSB3aWxsIHNoYXJlIHdpdGggUGluYSAoZm9yIGNvcnJlbGF0aW9uIHdpdGggdGhlIG1pY3JvYmlvbWUpCgoKYGBge3J9CiMgUmV2ZXJzZSBkaXN0YW5jZSBtYXRyaXggbmFtZXMgdG8gdXNlIHRoZSByZWFsIGdlb2dyYXBoaWNhbCBJRApmb3IgKGkgaW4gMTpkaW0odGVzdDEpWzFdKXsKICBmb3IgKGogaW4gMTpkaW0oaW5kX3RvX3N1YnNldClbMV0pewogICAgaWYgKGNvbG5hbWVzKGtpbnNoaXApW2pdPT10ZXN0MVtpLDJdKXsKICAgICAgY29sbmFtZXMoa2luc2hpcClbal09dGVzdDFbaSwxXQogICAgICByb3duYW1lcyhraW5zaGlwKVtqXT10ZXN0MVtpLDFdCiAgICB9CiAgfQp9CgojIG9yZGVyIG1hdHJpeCBnZW9ncmFwaGljYWxseQpvcmRlcl9tYXRyaXggPSByZWFkLnRhYmxlKCJpbmRfZm9yX21hdHJpeC50eHQiKQpraW5zaGlwID0ga2luc2hpcFt1bmxpc3Qob3JkZXJfbWF0cml4KSx1bmxpc3Qob3JkZXJfbWF0cml4KV0KCmtpbnNoaXAgPSByb3VuZChraW5zaGlwLGRpZ2l0cz00KQoKIyBDcmVhdGUgYSB0ZXh0IGZpbGUgd2l0aCB0aGUgcmVsYXRlZG5lc3MgbWF0cml4CndyaXRlLnRhYmxlKGtpbnNoaXAsIGZpbGU9Im1hdHJpeF9yZWxhdGVkbmVzc181NF9pbmQudHh0IixzZXA9Ilx0IixxdW90ZT1GQUxTRSxyb3cubmFtZXMgPSBUUlVFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBUUlVFKQoKIyBEaXNwbGF5IG1hdHJpeApraW5zaGlwCmBgYAoKV3JpdGUgZmluYWwgbWF0cml4IG9mIHJlbGF0ZWRuZXNzIChoYW5kbGUgbWlzc2luZyB0YWIgaW4gdGhlIGJlZ2lubmluZykKYGBge2Jhc2h9CmNhdCBtYXRyaXhfcmVsYXRlZG5lc3NfNTRfaW5kLnR4dCB8IGhlYWQgLW4gMSB8IHBlcmwgLXBlICdzL0lfNC9cdElfNC9nJyA+IG1hdHJpeF9yZWxhdGVkbmVzcy50eHQKY2F0IG1hdHJpeF9yZWxhdGVkbmVzc181NF9pbmQudHh0fCBhd2sgJ05SPjEnID4+IG1hdHJpeF9yZWxhdGVkbmVzcy50eHQKcm0gbWF0cml4X3JlbGF0ZWRuZXNzXzU0X2luZC50eHQKbXYgbWF0cml4X3JlbGF0ZWRuZXNzLnR4dCBtYXRyaXhfcmVsYXRlZG5lc3NfNTRfaW5kLnR4dApgYGAKCg==