I) Filter the data based on the genotype likelihood (GL).

Based on our discussion of last week, we decided to apply the following filters:

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.

II) Linkage desequilibrium pruning

LD pruning was applied using the software NGSLD (https://github.com/fgvieira/ngsLD). First let’s check how LD decays with genetic distance.

The script below:

#! /bin/bash

#SBATCH --job-name=LD
#SBATCH --partition=regular
#SBATCH --mem=15GB
#SBATCH --cpus-per-task=1
#SBATCH --time=0-20:00:00

 #Script name: NgsLD_per_scaffolds.sh
 
################
# Define paths #
################
PATH_RESULTS="/data/p274105/Wasp_Pina/Data/GFL/All/"
NGSLD="/data/p274105/tool_genomics/ngsTools/ngsLD/"

##################
# Load libraries #
##################
module load R/4.0.0-foss-2020a 
module load Perl/5.26.1-foss-2018a
#cpanm --local-lib=~/perl5 local::lib
#eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)
#cpanm Graph::Easy

######################################
# Count the number of available SNPs #
#######################################
cd $PATH_RESULTS
cat  All_Chr.glf.mafs | cut -f 1,2 | tail -n +2 > Test_pos.txt
NS=`cat Test_pos* | wc -l` 

######################################################
# Estimate LD between every pair of SNPs up to 50 kb #
######################################################
$NGSLD/ngsLD --geno All_Chr.glf.geno --n_ind 95 --max_kb_dist 50 --n_sites $NS --out NGSLDOUTPUT.txt --pos Test_pos* --verbose 1

#########################################
# Plot the graph of LD decay over 50 kb #
#########################################
Rscript --vanilla --slave $NGSLD/scripts/fit_LDdecay.R --ld_file file_name.txt --out plot_LD.pdf --fit_level 2 --ld r2 --n_ind 95 --plot_y_lim 0.5 --plot_x_lim 50


The results show the LD (measured as \(r^2\)) goes below 0.1 after around 20 Kb.

Therefore, subsequently LD was estimated between every pair of SNPs up to a distance of 20 Kb using the following command:

cd $PATH_RESULTS

module load Perl/5.26.1-foss-2018a

perl $NGSLD/scripts/prune_graph.pl --in_file NGSLDOUTPUT.txt --max_kb_dist 20 --min_weight 0.6 --out LD_unlinked.id


88485 SNPs remained after LD pruning.

III) GL on the LD pruned data


It’s basically the same as for I) just the analysis was performed on a subset of LD pruned SNPs. The subset of LD pruned SNPs are provided as argument to angsd using the option -sites.

#! /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_subset_per_scaffolds.sh

################
# Define paths #
################
BAM_LIST="~/list_bam.txt"
PATH_INPUT="~/GFL/All/"
PATH_OUTPUT="~/GFL/LD_prunned/$1"
GFL_File="Wasp_Pina_subset_$1.glf"
REF="~/ajConsensus.fasta"
ANGSD="/data/p274105/tool_genomics/angsd"

cd $PATH_INPUT
mkdir -p $PATH_OUTPUT

$ANGSD/angsd sites index ${PATH_RESULTS}/LD_unlinked_to_sort.id

$ANGSD/angsd -GL 2 \
-doGlf 2 \
-doMaf 1  \
-doMajorMinor 1 \
-doPost 1 -doGeno 8 \
-ref ${REF} \
-r $1 \
-bam ${BAM_LIST} \
-sites ${PATH_INPUT}/LD_unlinked_to_sort.id \
-uniqueOnly 1 -remove_bads 1 -only_proper_pairs 1 \
-trim 0 -C 50 -baq 1 \
-out ${PATH_OUTPUT}/${GFL_File} \
-nThreads 1 \
-minMapQ 15 \
-minQ 15 \
-SNP_pval 1e-6 \
-skipTriallelic 1 \
-minMaf 0.10 \
-minInd 67 \
-setMinDepth 190  -setMaxDepth 2850 -doCounts 1

This script generated the *glf.beagle.gz file that is the base for most of the later analyses.

IV) Phylogenetic relationships

The phylogenetic relationship were inferred using:

#! /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)
}



VI) NGSadmix

1) Run NGsadmix

NGSadmix was also used to estimate individual admixture proportion while taking into account incertainty in genotype. Due to the instability of the results using the default setting, many of the default parameters were changed to perform a more stringent analyses. The explanations of all options is available here: http://www.popgen.dk/software/index.php/NgsAdmix.

The analyses was formed for K=1 to K=8 using 50 replicates for each K value. The script was ran using this command:

For K in {8..1}; do for replicate in {1..50}; do sbatch ./NgsAdmix_script.sh $K $replicate; done

#! /bin/bash

#SBATCH --job-name=NGSadm
#SBATCH --partition=short
#SBATCH --mem=7GB
#SBATCH --cpus-per-task=1
#SBATCH --time=0-00:30:00

#Script name: NgsAdmix_script.sh

################
# Define paths #
################
PATH_RESULTS="~/NGSadmix"
PATH_INPUT="~/Results"
module load angsd/0.925-foss-2018a

NGSadmix -likes $PATH_INPUT/Wasp_Pina_subset.glf.beagle.gz \
-K $1 \ # value of K to test (number of clusters)
-misTol 0.9 \ # Tolerance for considering a site as missing.
-tol 1e-8 \ # Tolerance for convergence. The smaller the more stringent.
-o ${PATH_RESULTS}/K"$1"_replicate_"$2" \ 
-P 1 \
-minInd 67 \
-maxiter 5000 \ # Maximum number of EM iterations. 
-minMaf 0.10 \
-tolLike50 0.3 # Loglikelihood difference in 50 iterations. The larger the more stringent.



2) Run clumpak

The raw results from NGSadmix (*opt files) were summarized using the software Clumpak. Clumpak estimates for each K value over the 50 replicates the different partition (in term of individual admixture proportion) inferred by NGSadmix.

#! /bin/bash

#SBATCH --job-name=clumpak
#SBATCH --partition=short
#SBATCH --mem=2GB
#SBATCH --cpus-per-task=1
#SBATCH --time=-00:30:00


#Script name: clumpak.sh

module load CLUMPAK/1.1-GCCcore-6.4.0-Perl-5.26.1

Path="~/Results/NGSadmix"
Data="~/Data"
cd $Path

zip myzip *opt

CLUMPAK.pl \
--id $((RANDOM%500)) \
--dir  $Path    \
--file  $Path/myzip.zip   \
--inputtype admixture \
--indtopop $Data/ind.txt \
--labels $Data/geo_group.txt

Out of the 50 replicates I obtained:

  • K=2 50/50

  • K=3 50/50

  • K=4 50/50

  • K=5 32/50 (major partition) 18/50 (minor partition: not shown).

=> The clustering obtained at K=5 (major partition) is the same as the one obtained at using Admixture and the PCA.

3) Best K?

Clumpak implements also two approaches to estimate the best K:

  • The method of Evanno

  • ln(Pr(X|K) or the Prichard K

They were calculated using the following script: Because some replicates had exactly the same likelihood that creates a conflict with the way this method work. There I used a home made bash command (line 763) to jitter (add some noise to) the decimal value of the estimated likelihood.

module load CLUMPAK/1.1-GCCcore-6.4.0-Perl-5.26.1

Path="~/Results/NGSadmix"
cd $Path

cat *K*log |grep "best"|awk -F" " '{print $2}'|cut -c 6-24 > likelihood.txt
ls *K*log |cut -d _ -f 1 | sed 's/K//g' > kvalue.txt
paste kvalue.txt likelihood.txt > likelihood_bestK_input.txt
cat likelihood_bestK_input.txt | while read line; do echo $line|perl -pe 's/(\.)(\d+)$/$1"'$RANDOM'"/g'| sed 's/"//g'; done > new_likelihood_bestK_input.txt


perl /software/software/CLUMPAK/1.1-GCCcore-6.4.0-Perl-5.26.1/BestKByEvanno.pl --id 121 --d $Path --f $Path/new_likelihood_bestK_input.txt --inputtype lnprobbyk


According to the Evanno method K=4 is the best K value

According to the Pritchard K method K=8 is the best K value

This results must be taken with a pinch of salt. Evanno Delta(K) has been shown to be quite unreliable. The pritchard K was designed to work well with the results of the software STRUCTURE. With other sotfware, my experience show that it always picks the larger number of K. There is clearly a data overfitting problem.

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.

VIII) Gentic distance matrix (for subset of 54 individuals)

This part allow to subset the matrix of genetic distance calculated by NGSdist 54 individuals. As requested by the reviewers, this matrix is going to be correlated with the microbiome composition. The aim is to investigate if more genetically distant populations tend to have a more differentiated microbiomes too.

This first bash block separate the genetic distance matrix estimated by NGSdist from the bootstraped one and prepare (adjust label names) the the list of individuals to be subsetted later.

# Extract the genetic distance matrix (from the bootstrapped ones)
cat NJtree/output.dist | awk -v 'RS=\n\n' '1;{exit}'|awk 'NR>2'|perl -pe 's/^\n//g' > distance_matrix.txt

# Remove the sorted in the label names
cat subset*|perl -pe 's/^\n//g'|perl -pe 's/_sorted//g' > ind_for_matrix.txt


Here is the list of 54 individuals that are going to be extracted:


This R code block extract the genetic distances estimated by NGSDist for a subset 54 individuals (with microbiome data).



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:

#!/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:

# 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==