Setup

Libraries etc.

library(tidyr)
Warning: package ‘tidyr’ was built under R version 3.2.5

Functions

#confluent hypergeometric
f1<-function(a,b,z){ return(log(hyperg_1F1(a,b,z))) }
#prochhammer symbol
proch<-function(x,n){return(lgamma(x+n)-lgamma(x))}
#Proposal Distribution
normalProposal <- function(theta,std){
  theta.prime = rnorm(n=1,mean=theta,sd=std)
  return(abs(theta.prime))
}
#likelihood function
like<-function(conditional,k,Ne,mu,nu,s,my_sfs){
  #Generate SFS for these values
  alpha=4*mu*Ne
  beta=4*nu*Ne
  gamma=4*s*Ne
  n=max(k)
  #unfolded SFS with fixed/absent, equation 16b from Charlesworh & Jain 2014 
  prob_SFS <- sapply(k,function(K){
    log(choose(n,K))+(f1(beta+K,alpha+beta+n,gamma)+proch(beta,K)+proch(alpha,n-K))-(f1(beta,alpha+beta,gamma)+proch(alpha+beta,n))
  })
  #normalize
  prob_SFS=prob_SFS-max(prob_SFS)
  prob_SFS=exp(prob_SFS)/sum(exp(prob_SFS))
  #Make conditional
  if(conditional==TRUE){
    cond_SFS <- sapply(1:length(prob_SFS), function(x) prob_SFS[x]/sum(prob_SFS[2:(length(prob_SFS)-1)]))  #divide by total p(seg)
    prob_SFS <- cond_SFS[-c(1,length(cond_SFS))]
  }
  return((dmultinom(my_sfs,prob=prob_SFS))+1E-300)
}

Set some values

conditional=FALSE #use only conditional likelihood
Ne=150000 #replace with estimated Ne from SNP data
ngen = 50000 # Set the number of generations.
sample.freq = 100 # Set the sample frequency.
l.samples = rep(NA,ngen/sample.freq) # Initialize a likelihood vector with length equal to the number of samples.
p.samples = vector("list", ngen/sample.freq)  # Initialize a prior list with length equal to the number of samples.
mu.samples=rep(NA,ngen/sample.freq) # Initialize a posterior vector for each param 
nu.samples = rep(NA,ngen/sample.freq)  #
s.samples = rep(NA,ngen/sample.freq)  #
acceptances = vector("list", ngen)  # List of when param changes were accepted.

Priors and proposal distribution. \(\mu\) is mutation \(A_2 \rightarrow A_1\), \(\nu\) is \(A_1 \rightarrow A_2\), \(s\) is advantage of \(A_2\) over \(A_1\) under simple \(1-s\), \(1-s/2\), \(1\) model for \(A_1A_1,A_1A_2\) and \(A_2A_2\) genotypes

rates=c(1E6,1E6,1E6) # rates for mu, nu, s (in that order)
#prior_means=c(-6,-6,-6)
#prior_sd=c(1,1,1)
sd=c(1E-8,1E-9,1E-7) # sd for proposal dist for mu, nu, s (in that order)
#sd=c(3E-9,7E-10,7E-8) # sd for proposal dist for mu, nu, s (in that order)

Data

Either Fake, Assuming sample size 20 chromosomes (10 diploid dudes) with 10K SNPs, or real

fake=FALSE
if(fake==TRUE){
  snps=10000 # only variant sites, used for conditional model only
  sites=100000 # total number of variant and invariant sites, used for complete model (conditional==FALSE) only
  k=0:40
  n=max(k)
  #fake.alpha=rexp(1,rates[1])*4*Ne
  #fake.beta=rexp(1,rates[2])*4*Ne
  #fake.gamma=rexp(1,rates[3])*4*Ne
  #neutral
  #my_sfs=(rmultinom(1,theta,(theta/1:(length(k)-2)))) 
  #Some params of interest for Jinliang's plots
  fake.alpha= 0.4
  fake.beta = 0.04
  fake.gamma = 0.2
 # fake.params=10^rnorm(3,prior_means,prior_sd)
  #fake.alpha=fake.params[1]*4*Ne
  #fake.beta=fake.params[2]*4*Ne
  #fake.gamma=fake.params[3]*4*Ne
  
  my_sfs <- sapply(k,function(K){
    log(choose(n,K))+(f1(fake.beta+K,fake.alpha+fake.beta+n,fake.gamma)+proch(fake.beta,K)+proch(fake.alpha,n-K))-(f1(fake.beta,fake.alpha+fake.beta,fake.gamma)+proch(fake.alpha+fake.beta,n))
  })
  my_sfs=my_sfs-max(my_sfs)
  my_sfs=exp(my_sfs)/sum(exp(my_sfs))
  if(conditional==TRUE){
    c_csfs <- sapply(1:length(my_sfs), function(x) my_sfs[x]/sum(my_sfs[2:(length(my_sfs)-1)]))  #divide by 
    my_sfs <- round(c_csfs[-c(1,length(c_csfs))]*snps)
  } else{
    my_sfs <- round(my_sfs*sites)
  }
} else{
  download.file("https://gist.githubusercontent.com/rossibarra/71d0d22bcb6a7c4a786fd99fdf42fcab/raw/47ecd73ec50a92258044618c322d2e83ea5370cb/sfsPC","PCsfs.csv")
  sfs_data<-read.table("PCsfs.csv",header=T)
  my_sfs=sfs_data$Freq
}
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100   331  100   331    0     0    418      0 --:--:-- --:--:-- --:--:--   418
k=0:(length(my_sfs)-1)
plot(my_sfs~k,pch=19,cex=2,ylab="counts",xlab="number of chromosomes",cex.lab=1.5)

Trying initial params

  #Some params of interest for Jinliang's plots
  fake.alpha= 0.4
  fake.beta = 0.04
  fake.gamma = 2
  my_sfs <- sapply(k,function(K){
    log(choose(n,K))+(f1(fake.beta+K,fake.alpha+fake.beta+n,fake.gamma)+proch(fake.beta,K)+proch(fake.alpha,n-K))-(f1(fake.beta,fake.alpha+fake.beta,fake.gamma)+proch(fake.alpha+fake.beta,n))
  })
  my_sfs=my_sfs-max(my_sfs)
  my_sfs=exp(my_sfs)/sum(exp(my_sfs))
  real_sfs=sfs_data$Freq
  my_sfs=my_sfs*sum(real_sfs)
k=0:40
crap=data.frame(real_sfs,my_sfs,k) %>% gather(kind,value,c(real_sfs,my_sfs))
ggplot(crap)+geom_point(aes(y=value,x=k,color=kind),size=3)

MCMCBC

Initial Values

#params<-rexp(3,rates) # initial values of mu,nu,s (in that order) from exponential
#params<-10^rnorm(3,prior_means,prior_sd) #from lognormal
fake.alpha= 0.05
fake.beta = 0.04
fake.gamma = .1
params<-c(fake.alpha/(4*Ne),fake.beta/(4*Ne),fake.gamma/(4*Ne))
priors<-dexp(params,rates) # Get the initial prior values of mu,nu,s (in that order) from exponential
#priors<-dnorm(log10(params),prior_means,prior_sd)
l=like(conditional,c(0:(length(my_sfs)-1)),Ne,params[1],params[2],params[3],my_sfs) # initial likelihood

Run the MCMC

textbar = txtProgressBar(style=ifelse(interactive(),1,3),width=50, file = stderr())
for(i in 1:ngen){ # For each generation...
  #choose which param
  params.prime = params
  random.param=sample(c(1:3),1)
  acceptances[[i]]=c(NA,NA,NA)
  
  # Propose a value based on the previous values.
  params.prime[random.param]=normalProposal(params[random.param],sd[random.param]) 
  
  # Calculate the proposed likelihood.
  l.prime = like(conditional,k,Ne,params.prime[1],params.prime[2],params.prime[3],my_sfs) 
  
  
  # Calculate the proposed prior probability.
  priors.prime=dexp(params.prime,rates) # from exponential
  #priors.prime=dnorm(log10(params.prime),prior_means,prior_sd) # from lognormal
  
  # Calculate the acceptance probability.
  R = (l.prime/l)*(priors.prime[random.param]/priors[random.param]) 
  
  # If r < R, accept the proposed parameters.
  r = runif(1)
  acceptances[[i]][random.param]=0
  if(r < R){ 
    acceptances[[i]][random.param]=1
    params[random.param] = params.prime[random.param] # Set the current value to the proposed value.
    l = l.prime # Set current likelihood to  proposed likelihood.
    priors = priors.prime # Set current prior probability to  proposed prior probability.
  }
  
  # Sample from posterior
  if(i %% sample.freq == 0){ 
    mu.samples[i/sample.freq] = params[1] # Save the current param values.
    nu.samples[i/sample.freq] = params[2]
    s.samples[i/sample.freq] = params[3]
    l.samples[i/sample.freq] = l # Save the current likelihood value.
    p.samples[[i/sample.freq]] = priors # Save the current prior value.
    setTxtProgressBar(textbar,(i/ngen)) # Progress bar.
  }
}
==================================================

Calculate acceptances to evaluate sd=c(1E-6,1E-6,1E-5). If acceptance too high, increase these values to explore wider space. If acceptance too low, decrease.

#Acceptance rate
mu.acc=round(mean(sapply(acceptances, function (x) x[1]),na.rm=T),3)
nu.acc=round(mean(sapply(acceptances, function (x) x[2]),na.rm=T),3)
s.acc=round(mean(sapply(acceptances, function (x) x[3]),na.rm=T),3)

Traces: tos first 25% as burnin

s.samples=s.samples[(length(s.samples)*0.25):length(s.samples)]
strace=ggplot(data=data.frame(s.samples),aes(y=s.samples,x=1:(length(s.samples))))+
  geom_line(color=cbPalette[4])+
  ylab("s")+
  xlab(paste("generations\n(",round(effectiveSize(s.samples))," eff. samples)\n(acc. rate ",s.acc,")",sep="")) +
  theme(axis.text=element_text(size=10),axis.title=element_text(size=10))
nu.samples=nu.samples[(length(nu.samples)*0.25):length(nu.samples)]
ntrace=ggplot(data=data.frame(nu.samples),aes(y=nu.samples,x=1:(length(nu.samples))))+
  geom_line(color=cbPalette[3])+
  ylab(expression(nu))+
  xlab(paste("generations\n(",round(effectiveSize(nu.samples))," eff. samples)\n(acc. rate ",nu.acc,")",sep="")) +
  theme(axis.text=element_text(size=10),axis.title=element_text(size=10))
mu.samples=mu.samples[(length(mu.samples)*0.25):length(mu.samples)]
mtrace=ggplot(data=data.frame(mu.samples),aes(y=mu.samples,x=1:(length(mu.samples))))+
  geom_line(color=cbPalette[2])+
  ylab(expression(mu))+
  xlab(paste("generations\n(",round(effectiveSize(mu.samples))," eff. samples)\n(acc. rate ",mu.acc,")",sep="")) +
  theme(axis.text=element_text(size=10),axis.title=element_text(size=10))
l.samples=l.samples[(length(l.samples)*0.25):length(l.samples)]
ltrace=ggplot(data=data.frame(log(l.samples)),aes(y=log.l.samples.,x=1:(length(l.samples))))+
  geom_line(color=cbPalette[1])+
  ylab("Likelihood")+
  xlab("generations") +
  theme(axis.text=element_text(size=10),axis.title=element_text(size=10) )

Posterior vs Prior

#ALPHA
prior.mu=rexp(length(mu.samples[-c(1:(0.1*ngen/sample.freq))]),rates[1])
post.mu=mu.samples[-c(1:(0.1*ngen/sample.freq))]
mode.mu=density(post.mu)$x[which(density(post.mu)$y==max(density(post.mu)$y))]
muplot<-ggplot(data.frame(post.mu,prior.mu)) +
  geom_histogram(aes(post.mu),fill=cbPalette[2],bins=30) + 
  geom_histogram(aes(prior.mu),bins=30,alpha=0.2,fill=cbPalette[1])+
  scale_x_log10()+
  xlab(expression(mu))
if(fake==TRUE){ muplot=muplot+geom_vline(xintercept = fake.alpha/(4*Ne))} else{ muplot=muplot+geom_vline(xintercept = mode.mu) }
muplotzoom<-ggplot(data.frame(post.mu)) +
  geom_histogram(aes(post.mu),fill=cbPalette[2],bins=30)+
  xlab(expression(mu))+  
  theme(axis.text=element_text(size=6))
if(fake==TRUE){ muplotzoom=muplotzoom+geom_vline(xintercept = fake.alpha/(4*Ne))} else{ muplotzoom=muplotzoom+geom_vline(xintercept = mode.mu) }
#BETA
prior.nu=rexp(length(nu.samples[-c(1:(0.1*ngen/sample.freq))]),rates[2])
post.nu=nu.samples[-c(1:(0.1*ngen/sample.freq))]
mode.nu=density(post.nu)$x[which(density(post.nu)$y==max(density(post.nu)$y))]
nuplot<-ggplot(data.frame(post.nu,prior.nu)) +
  geom_histogram(aes(post.nu),fill=cbPalette[3],bins=30) + 
  geom_histogram(aes(prior.nu),bins=30,alpha=0.2,fill=cbPalette[1])+
  scale_x_log10()+
  xlab(expression(nu))
if(fake==TRUE){ nuplot=nuplot+geom_vline(xintercept = fake.beta/(4*Ne))} else{ 
  nuplot=nuplot+geom_vline(xintercept = mode.nu) }
nuplotzoom<-ggplot(data.frame(post.nu)) +
  geom_histogram(aes(post.nu),fill=cbPalette[3],bins=30)+
  xlab(expression(nu))+ 
  theme(axis.text=element_text(size=6))
if(fake==TRUE){ nuplotzoom=nuplotzoom+geom_vline(xintercept = fake.beta/(4*Ne))} else{ nuplotzoom=nuplotzoom+geom_vline(xintercept = mode.nu) }
#GAMMA
prior.s=rexp(length(s.samples[-c(1:(0.1*ngen/sample.freq))]),rates[3])
post.s=s.samples[-c(1:(0.1*ngen/sample.freq))]
mode.s=density(post.s)$x[which(density(post.s)$y==max(density(post.s)$y))]
splot<-ggplot(data.frame(post.s,prior.s)) + 
  geom_histogram(aes(post.s),fill=cbPalette[4],bins=30) + 
  geom_histogram(aes(prior.s),bins=30,alpha=0.2,fill=cbPalette[1])+
  scale_x_log10()+
  xlab("s")
if(fake==TRUE){ splot=splot+geom_vline(xintercept = fake.gamma/(4*Ne))} else{ splot=splot+geom_vline(xintercept = mode.s) }
splotzoom<-ggplot(data.frame(post.s)) + 
  geom_histogram(aes(post.s),fill=cbPalette[4],bins=30)+
  xlab("s")+
  theme(axis.text=element_text(size=6))
if(fake==TRUE){ splotzoom=splotzoom+geom_vline(xintercept = fake.gamma/(4*Ne))}  else{ splotzoom=splotzoom+geom_vline(xintercept = mode.s) }
#PLOT
plot_grid(mtrace,ntrace,strace,muplot,nuplot,splot,muplotzoom,nuplotzoom,splotzoom,ncol=3,rel_heights=c(1.5,1,1),align="v")
plot(ltrace)

Plot observed data and estimate from mean and MAP:

#plot mean
plot(my_sfs~(c(0:max(k))),pch=19,cex=2,ylab="counts",xlab="number of chromosomes",cex.lab=1.5)
 post_sfs=sapply(k,function(K){
     log(choose(n,K))+(f1(mean(post.nu)*4*Ne+K,mean(post.mu)*4*Ne+mean(post.nu)*4*Ne+n,mean(post.s)*4*Ne)+proch(mean(post.nu)*4*Ne,K)+proch(mean(post.mu)*4*Ne,n-K))-(f1(mean(post.nu)*4*Ne,mean(post.mu)*4*Ne+mean(post.nu)*4*Ne,mean(post.s)*4*Ne)+proch(mean(post.mu)*4*Ne+mean(post.nu)*4*Ne,n))})
post_sfs=post_sfs-max(post_sfs)
post_sfs=exp(post_sfs)/sum(exp(post_sfs))*sum(my_sfs)
points(post_sfs~c(0:max(k)),cex=1,col=cbPalette[2],pch=19)
legend("top",legend=c("observed","mean of posterior"),fill=c("black",cbPalette[2]))
#Plot maximum a posteriori (mode)
plot(my_sfs~(c(0:max(k))),pch=19,cex=2,ylab="counts",xlab="number of chromosomes",cex.lab=1.5)

post_sfs=sapply(k,function(K){
    log(choose(n,K))+(f1(mode.nu*4*Ne+K,mode.mu*4*Ne+mode.nu*4*Ne+n,mode.s*4*Ne)+proch(mode.nu*4*Ne,K)+proch(mode.mu*4*Ne,n-K))-(f1(mode.nu*4*Ne,mode.mu*4*Ne+mode.nu*4*Ne,mode.s*4*Ne)+proch(mode.mu*4*Ne+mode.nu*4*Ne,n))})
post_sfs=post_sfs-max(post_sfs)
post_sfs=exp(post_sfs)/sum(exp(post_sfs))*sum(my_sfs)
points(post_sfs~c(0:max(k)),cex=1,col=cbPalette[2],pch=19)
legend("top",legend=c("observed","mode of posterior"),fill=c("black",cbPalette[2]))

LS0tCnRpdGxlOiAiTUNNQ0JDIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCiNTZXR1cAojIyMgTGlicmFyaWVzIGV0Yy4KYGBge3IsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnc2wpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoY29kYSkKbGlicmFyeSh1dGlscykKbGlicmFyeShjb3dwbG90KQpjYlBhbGV0dGUgPC0gYygiIzk5OTk5OSIsICIjRTY5RjAwIiwgIiM1NkI0RTkiLCAiIzAwOUU3MyIsICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIsICIjQ0M3OUE3IikKYGBgCgojIyMgRnVuY3Rpb25zCmBgYHtyfQojY29uZmx1ZW50IGh5cGVyZ2VvbWV0cmljCmYxPC1mdW5jdGlvbihhLGIseil7IHJldHVybihsb2coaHlwZXJnXzFGMShhLGIseikpKSB9CgojcHJvY2hoYW1tZXIgc3ltYm9sCnByb2NoPC1mdW5jdGlvbih4LG4pe3JldHVybihsZ2FtbWEoeCtuKS1sZ2FtbWEoeCkpfQoKI1Byb3Bvc2FsIERpc3RyaWJ1dGlvbgpub3JtYWxQcm9wb3NhbCA8LSBmdW5jdGlvbih0aGV0YSxzdGQpewogIHRoZXRhLnByaW1lID0gcm5vcm0obj0xLG1lYW49dGhldGEsc2Q9c3RkKQogIHJldHVybihhYnModGhldGEucHJpbWUpKQp9CgojbGlrZWxpaG9vZCBmdW5jdGlvbgpsaWtlPC1mdW5jdGlvbihjb25kaXRpb25hbCxrLE5lLG11LG51LHMsbXlfc2ZzKXsKICAjR2VuZXJhdGUgU0ZTIGZvciB0aGVzZSB2YWx1ZXMKICBhbHBoYT00Km11Kk5lCiAgYmV0YT00Km51Kk5lCiAgZ2FtbWE9NCpzKk5lCiAgbj1tYXgoaykKICAjdW5mb2xkZWQgU0ZTIHdpdGggZml4ZWQvYWJzZW50LCBlcXVhdGlvbiAxNmIgZnJvbSBDaGFybGVzd29yaCAmIEphaW4gMjAxNCAKICBwcm9iX1NGUyA8LSBzYXBwbHkoayxmdW5jdGlvbihLKXsKICAgIGxvZyhjaG9vc2UobixLKSkrKGYxKGJldGErSyxhbHBoYStiZXRhK24sZ2FtbWEpK3Byb2NoKGJldGEsSykrcHJvY2goYWxwaGEsbi1LKSktKGYxKGJldGEsYWxwaGErYmV0YSxnYW1tYSkrcHJvY2goYWxwaGErYmV0YSxuKSkKICB9KQoKICAjbm9ybWFsaXplCiAgcHJvYl9TRlM9cHJvYl9TRlMtbWF4KHByb2JfU0ZTKQogIHByb2JfU0ZTPWV4cChwcm9iX1NGUykvc3VtKGV4cChwcm9iX1NGUykpCgogICNNYWtlIGNvbmRpdGlvbmFsCiAgaWYoY29uZGl0aW9uYWw9PVRSVUUpewogICAgY29uZF9TRlMgPC0gc2FwcGx5KDE6bGVuZ3RoKHByb2JfU0ZTKSwgZnVuY3Rpb24oeCkgcHJvYl9TRlNbeF0vc3VtKHByb2JfU0ZTWzI6KGxlbmd0aChwcm9iX1NGUyktMSldKSkgICNkaXZpZGUgYnkgdG90YWwgcChzZWcpCiAgICBwcm9iX1NGUyA8LSBjb25kX1NGU1stYygxLGxlbmd0aChjb25kX1NGUykpXQogIH0KICByZXR1cm4oKGRtdWx0aW5vbShteV9zZnMscHJvYj1wcm9iX1NGUykpKzFFLTMwMCkKfQpgYGAKCiMjIyBTZXQgc29tZSB2YWx1ZXMgCmBgYHtyfQpjb25kaXRpb25hbD1GQUxTRSAjdXNlIG9ubHkgY29uZGl0aW9uYWwgbGlrZWxpaG9vZApOZT0xNTAwMDAgI3JlcGxhY2Ugd2l0aCBlc3RpbWF0ZWQgTmUgZnJvbSBTTlAgZGF0YQpuZ2VuID0gNTAwMDAgIyBTZXQgdGhlIG51bWJlciBvZiBnZW5lcmF0aW9ucy4Kc2FtcGxlLmZyZXEgPSAxMDAgIyBTZXQgdGhlIHNhbXBsZSBmcmVxdWVuY3kuCmwuc2FtcGxlcyA9IHJlcChOQSxuZ2VuL3NhbXBsZS5mcmVxKSAjIEluaXRpYWxpemUgYSBsaWtlbGlob29kIHZlY3RvciB3aXRoIGxlbmd0aCBlcXVhbCB0byB0aGUgbnVtYmVyIG9mIHNhbXBsZXMuCnAuc2FtcGxlcyA9IHZlY3RvcigibGlzdCIsIG5nZW4vc2FtcGxlLmZyZXEpICAjIEluaXRpYWxpemUgYSBwcmlvciBsaXN0IHdpdGggbGVuZ3RoIGVxdWFsIHRvIHRoZSBudW1iZXIgb2Ygc2FtcGxlcy4KbXUuc2FtcGxlcz1yZXAoTkEsbmdlbi9zYW1wbGUuZnJlcSkgIyBJbml0aWFsaXplIGEgcG9zdGVyaW9yIHZlY3RvciBmb3IgZWFjaCBwYXJhbSAKbnUuc2FtcGxlcyA9IHJlcChOQSxuZ2VuL3NhbXBsZS5mcmVxKSAgIwpzLnNhbXBsZXMgPSByZXAoTkEsbmdlbi9zYW1wbGUuZnJlcSkgICMKYWNjZXB0YW5jZXMgPSB2ZWN0b3IoImxpc3QiLCBuZ2VuKSAgIyBMaXN0IG9mIHdoZW4gcGFyYW0gY2hhbmdlcyB3ZXJlIGFjY2VwdGVkLgpgYGAKCiMjIyBQcmlvcnMgYW5kIHByb3Bvc2FsIGRpc3RyaWJ1dGlvbi4gJFxtdSQgaXMgbXV0YXRpb24gJEFfMiBccmlnaHRhcnJvdyBBXzEkLCAkXG51JCBpcyAkQV8xIFxyaWdodGFycm93IEFfMiQsICRzJCBpcyBhZHZhbnRhZ2Ugb2YgJEFfMiQgb3ZlciAkQV8xJCB1bmRlciBzaW1wbGUgJDEtcyQsICQxLXMvMiQsICQxJCBtb2RlbCBmb3IgJEFfMUFfMSxBXzFBXzIkIGFuZCAkQV8yQV8yJCBnZW5vdHlwZXMKCmBgYHtyfQpyYXRlcz1jKDFFNiwxRTYsMUU2KSAjIHJhdGVzIGZvciBtdSwgbnUsIHMgKGluIHRoYXQgb3JkZXIpCiNwcmlvcl9tZWFucz1jKC02LC02LC02KQojcHJpb3Jfc2Q9YygxLDEsMSkKc2Q9YygxRS04LDFFLTksMUUtNykgIyBzZCBmb3IgcHJvcG9zYWwgZGlzdCBmb3IgbXUsIG51LCBzIChpbiB0aGF0IG9yZGVyKQojc2Q9YygzRS05LDdFLTEwLDdFLTgpICMgc2QgZm9yIHByb3Bvc2FsIGRpc3QgZm9yIG11LCBudSwgcyAoaW4gdGhhdCBvcmRlcikKCmBgYAoKIyBEYXRhCkVpdGhlciBGYWtlLCBBc3N1bWluZyBzYW1wbGUgc2l6ZSAyMCBjaHJvbW9zb21lcyAoMTAgZGlwbG9pZCBkdWRlcykgd2l0aCAxMEsgU05Qcywgb3IgcmVhbApgYGB7cn0KZmFrZT1GQUxTRQoKaWYoZmFrZT09VFJVRSl7CiAgc25wcz0xMDAwMCAjIG9ubHkgdmFyaWFudCBzaXRlcywgdXNlZCBmb3IgY29uZGl0aW9uYWwgbW9kZWwgb25seQogIHNpdGVzPTEwMDAwMCAjIHRvdGFsIG51bWJlciBvZiB2YXJpYW50IGFuZCBpbnZhcmlhbnQgc2l0ZXMsIHVzZWQgZm9yIGNvbXBsZXRlIG1vZGVsIChjb25kaXRpb25hbD09RkFMU0UpIG9ubHkKICBrPTA6NDAKICBuPW1heChrKQogICNmYWtlLmFscGhhPXJleHAoMSxyYXRlc1sxXSkqNCpOZQogICNmYWtlLmJldGE9cmV4cCgxLHJhdGVzWzJdKSo0Kk5lCiAgI2Zha2UuZ2FtbWE9cmV4cCgxLHJhdGVzWzNdKSo0Kk5lCgogICNuZXV0cmFsCiAgI215X3Nmcz0ocm11bHRpbm9tKDEsdGhldGEsKHRoZXRhLzE6KGxlbmd0aChrKS0yKSkpKSAKCiAgI1NvbWUgcGFyYW1zIG9mIGludGVyZXN0IGZvciBKaW5saWFuZydzIHBsb3RzCiAgZmFrZS5hbHBoYT0gMC40CiAgZmFrZS5iZXRhID0gMC4wNAogIGZha2UuZ2FtbWEgPSAwLjIKICMgZmFrZS5wYXJhbXM9MTBecm5vcm0oMyxwcmlvcl9tZWFucyxwcmlvcl9zZCkKICAjZmFrZS5hbHBoYT1mYWtlLnBhcmFtc1sxXSo0Kk5lCiAgI2Zha2UuYmV0YT1mYWtlLnBhcmFtc1syXSo0Kk5lCiAgI2Zha2UuZ2FtbWE9ZmFrZS5wYXJhbXNbM10qNCpOZQogIAogIG15X3NmcyA8LSBzYXBwbHkoayxmdW5jdGlvbihLKXsKICAgIGxvZyhjaG9vc2UobixLKSkrKGYxKGZha2UuYmV0YStLLGZha2UuYWxwaGErZmFrZS5iZXRhK24sZmFrZS5nYW1tYSkrcHJvY2goZmFrZS5iZXRhLEspK3Byb2NoKGZha2UuYWxwaGEsbi1LKSktKGYxKGZha2UuYmV0YSxmYWtlLmFscGhhK2Zha2UuYmV0YSxmYWtlLmdhbW1hKStwcm9jaChmYWtlLmFscGhhK2Zha2UuYmV0YSxuKSkKICB9KQogIG15X3Nmcz1teV9zZnMtbWF4KG15X3NmcykKICBteV9zZnM9ZXhwKG15X3Nmcykvc3VtKGV4cChteV9zZnMpKQoKICBpZihjb25kaXRpb25hbD09VFJVRSl7CiAgICBjX2NzZnMgPC0gc2FwcGx5KDE6bGVuZ3RoKG15X3NmcyksIGZ1bmN0aW9uKHgpIG15X3Nmc1t4XS9zdW0obXlfc2ZzWzI6KGxlbmd0aChteV9zZnMpLTEpXSkpICAjZGl2aWRlIGJ5IAogICAgbXlfc2ZzIDwtIHJvdW5kKGNfY3Nmc1stYygxLGxlbmd0aChjX2NzZnMpKV0qc25wcykKICB9IGVsc2V7CiAgICBteV9zZnMgPC0gcm91bmQobXlfc2ZzKnNpdGVzKQogIH0KfSBlbHNlewogIGRvd25sb2FkLmZpbGUoImh0dHBzOi8vZ2lzdC5naXRodWJ1c2VyY29udGVudC5jb20vcm9zc2liYXJyYS83MWQwZDIyYmNiNmE3YzRhNzg2ZmQ5OWZkZjQyZmNhYi9yYXcvNDdlY2Q3M2VjNTBhOTIyNTgwNDQ2MThjMzIyZDJlODNlYTUzNzBjYi9zZnNQQyIsIlBDc2ZzLmNzdiIpCiAgc2ZzX2RhdGE8LXJlYWQudGFibGUoIlBDc2ZzLmNzdiIsaGVhZGVyPVQpCiAgbXlfc2ZzPXNmc19kYXRhJEZyZXEKfQprPTA6KGxlbmd0aChteV9zZnMpLTEpCnBsb3QobXlfc2ZzfmsscGNoPTE5LGNleD0yLHlsYWI9ImNvdW50cyIseGxhYj0ibnVtYmVyIG9mIGNocm9tb3NvbWVzIixjZXgubGFiPTEuNSkKYGBgCgpUcnlpbmcgaW5pdGlhbCBwYXJhbXMKYGBge3J9CiAgI1NvbWUgcGFyYW1zIG9mIGludGVyZXN0IGZvciBKaW5saWFuZydzIHBsb3RzCiAgZmFrZS5hbHBoYT0gMC40CiAgZmFrZS5iZXRhID0gMC4wNAogIGZha2UuZ2FtbWEgPSAyCgogIG15X3NmcyA8LSBzYXBwbHkoayxmdW5jdGlvbihLKXsKICAgIGxvZyhjaG9vc2UobixLKSkrKGYxKGZha2UuYmV0YStLLGZha2UuYWxwaGErZmFrZS5iZXRhK24sZmFrZS5nYW1tYSkrcHJvY2goZmFrZS5iZXRhLEspK3Byb2NoKGZha2UuYWxwaGEsbi1LKSktKGYxKGZha2UuYmV0YSxmYWtlLmFscGhhK2Zha2UuYmV0YSxmYWtlLmdhbW1hKStwcm9jaChmYWtlLmFscGhhK2Zha2UuYmV0YSxuKSkKICB9KQogIG15X3Nmcz1teV9zZnMtbWF4KG15X3NmcykKICBteV9zZnM9ZXhwKG15X3Nmcykvc3VtKGV4cChteV9zZnMpKQogIHJlYWxfc2ZzPXNmc19kYXRhJEZyZXEKICBteV9zZnM9bXlfc2ZzKnN1bShyZWFsX3NmcykKCms9MDo0MAoKY3JhcD1kYXRhLmZyYW1lKHJlYWxfc2ZzLG15X3NmcyxrKSAlPiUgZ2F0aGVyKGtpbmQsdmFsdWUsYyhyZWFsX3NmcyxteV9zZnMpKQpnZ3Bsb3QoY3JhcCkrZ2VvbV9wb2ludChhZXMoeT12YWx1ZSx4PWssY29sb3I9a2luZCksc2l6ZT0zKQpgYGAKCiMgTUNNQ0JDCiMjIyBJbml0aWFsIFZhbHVlcwpgYGB7cn0KI3BhcmFtczwtcmV4cCgzLHJhdGVzKSAjIGluaXRpYWwgdmFsdWVzIG9mIG11LG51LHMgKGluIHRoYXQgb3JkZXIpIGZyb20gZXhwb25lbnRpYWwKI3BhcmFtczwtMTBecm5vcm0oMyxwcmlvcl9tZWFucyxwcmlvcl9zZCkgI2Zyb20gbG9nbm9ybWFsCgpmYWtlLmFscGhhPSAwLjA1CmZha2UuYmV0YSA9IDAuMDQKZmFrZS5nYW1tYSA9IC4xCnBhcmFtczwtYyhmYWtlLmFscGhhLyg0Kk5lKSxmYWtlLmJldGEvKDQqTmUpLGZha2UuZ2FtbWEvKDQqTmUpKQpwcmlvcnM8LWRleHAocGFyYW1zLHJhdGVzKSAjIEdldCB0aGUgaW5pdGlhbCBwcmlvciB2YWx1ZXMgb2YgbXUsbnUscyAoaW4gdGhhdCBvcmRlcikgZnJvbSBleHBvbmVudGlhbAojcHJpb3JzPC1kbm9ybShsb2cxMChwYXJhbXMpLHByaW9yX21lYW5zLHByaW9yX3NkKQpsPWxpa2UoY29uZGl0aW9uYWwsYygwOihsZW5ndGgobXlfc2ZzKS0xKSksTmUscGFyYW1zWzFdLHBhcmFtc1syXSxwYXJhbXNbM10sbXlfc2ZzKSAjIGluaXRpYWwgbGlrZWxpaG9vZApgYGAKCiMjIyBSdW4gdGhlIE1DTUMKYGBge3J9CnRleHRiYXIgPSB0eHRQcm9ncmVzc0JhcihzdHlsZT1pZmVsc2UoaW50ZXJhY3RpdmUoKSwxLDMpLHdpZHRoPTUwLCBmaWxlID0gc3RkZXJyKCkpCgpmb3IoaSBpbiAxOm5nZW4peyAjIEZvciBlYWNoIGdlbmVyYXRpb24uLi4KICAjY2hvb3NlIHdoaWNoIHBhcmFtCiAgcGFyYW1zLnByaW1lID0gcGFyYW1zCiAgcmFuZG9tLnBhcmFtPXNhbXBsZShjKDE6MyksMSkKICBhY2NlcHRhbmNlc1tbaV1dPWMoTkEsTkEsTkEpCiAgCiAgIyBQcm9wb3NlIGEgdmFsdWUgYmFzZWQgb24gdGhlIHByZXZpb3VzIHZhbHVlcy4KICBwYXJhbXMucHJpbWVbcmFuZG9tLnBhcmFtXT1ub3JtYWxQcm9wb3NhbChwYXJhbXNbcmFuZG9tLnBhcmFtXSxzZFtyYW5kb20ucGFyYW1dKSAKICAKICAjIENhbGN1bGF0ZSB0aGUgcHJvcG9zZWQgbGlrZWxpaG9vZC4KICBsLnByaW1lID0gbGlrZShjb25kaXRpb25hbCxrLE5lLHBhcmFtcy5wcmltZVsxXSxwYXJhbXMucHJpbWVbMl0scGFyYW1zLnByaW1lWzNdLG15X3NmcykgCiAgCiAgCiAgIyBDYWxjdWxhdGUgdGhlIHByb3Bvc2VkIHByaW9yIHByb2JhYmlsaXR5LgogIHByaW9ycy5wcmltZT1kZXhwKHBhcmFtcy5wcmltZSxyYXRlcykgIyBmcm9tIGV4cG9uZW50aWFsCiAgI3ByaW9ycy5wcmltZT1kbm9ybShsb2cxMChwYXJhbXMucHJpbWUpLHByaW9yX21lYW5zLHByaW9yX3NkKSAjIGZyb20gbG9nbm9ybWFsCiAgCiAgIyBDYWxjdWxhdGUgdGhlIGFjY2VwdGFuY2UgcHJvYmFiaWxpdHkuCiAgUiA9IChsLnByaW1lL2wpKihwcmlvcnMucHJpbWVbcmFuZG9tLnBhcmFtXS9wcmlvcnNbcmFuZG9tLnBhcmFtXSkgCiAgCiAgIyBJZiByIDwgUiwgYWNjZXB0IHRoZSBwcm9wb3NlZCBwYXJhbWV0ZXJzLgogIHIgPSBydW5pZigxKQogIGFjY2VwdGFuY2VzW1tpXV1bcmFuZG9tLnBhcmFtXT0wCiAgaWYociA8IFIpeyAKICAgIGFjY2VwdGFuY2VzW1tpXV1bcmFuZG9tLnBhcmFtXT0xCiAgICBwYXJhbXNbcmFuZG9tLnBhcmFtXSA9IHBhcmFtcy5wcmltZVtyYW5kb20ucGFyYW1dICMgU2V0IHRoZSBjdXJyZW50IHZhbHVlIHRvIHRoZSBwcm9wb3NlZCB2YWx1ZS4KICAgIGwgPSBsLnByaW1lICMgU2V0IGN1cnJlbnQgbGlrZWxpaG9vZCB0byAgcHJvcG9zZWQgbGlrZWxpaG9vZC4KICAgIHByaW9ycyA9IHByaW9ycy5wcmltZSAjIFNldCBjdXJyZW50IHByaW9yIHByb2JhYmlsaXR5IHRvICBwcm9wb3NlZCBwcmlvciBwcm9iYWJpbGl0eS4KICB9CiAgCiAgIyBTYW1wbGUgZnJvbSBwb3N0ZXJpb3IKICBpZihpICUlIHNhbXBsZS5mcmVxID09IDApeyAKICAgIG11LnNhbXBsZXNbaS9zYW1wbGUuZnJlcV0gPSBwYXJhbXNbMV0gIyBTYXZlIHRoZSBjdXJyZW50IHBhcmFtIHZhbHVlcy4KICAgIG51LnNhbXBsZXNbaS9zYW1wbGUuZnJlcV0gPSBwYXJhbXNbMl0KICAgIHMuc2FtcGxlc1tpL3NhbXBsZS5mcmVxXSA9IHBhcmFtc1szXQogICAgbC5zYW1wbGVzW2kvc2FtcGxlLmZyZXFdID0gbCAjIFNhdmUgdGhlIGN1cnJlbnQgbGlrZWxpaG9vZCB2YWx1ZS4KICAgIHAuc2FtcGxlc1tbaS9zYW1wbGUuZnJlcV1dID0gcHJpb3JzICMgU2F2ZSB0aGUgY3VycmVudCBwcmlvciB2YWx1ZS4KICAgIHNldFR4dFByb2dyZXNzQmFyKHRleHRiYXIsKGkvbmdlbikpICMgUHJvZ3Jlc3MgYmFyLgogIH0KfQpgYGAKCkNhbGN1bGF0ZSBhY2NlcHRhbmNlcyB0byBldmFsdWF0ZSBgYGBzZD1jKDFFLTYsMUUtNiwxRS01KWBgYC4gSWYgYWNjZXB0YW5jZSB0b28gaGlnaCwgaW5jcmVhc2UgdGhlc2UgdmFsdWVzIHRvIGV4cGxvcmUgd2lkZXIgc3BhY2UuIElmIGFjY2VwdGFuY2UgdG9vIGxvdywgZGVjcmVhc2UuCmBgYHtyfQojQWNjZXB0YW5jZSByYXRlCm11LmFjYz1yb3VuZChtZWFuKHNhcHBseShhY2NlcHRhbmNlcywgZnVuY3Rpb24gKHgpIHhbMV0pLG5hLnJtPVQpLDMpCm51LmFjYz1yb3VuZChtZWFuKHNhcHBseShhY2NlcHRhbmNlcywgZnVuY3Rpb24gKHgpIHhbMl0pLG5hLnJtPVQpLDMpCnMuYWNjPXJvdW5kKG1lYW4oc2FwcGx5KGFjY2VwdGFuY2VzLCBmdW5jdGlvbiAoeCkgeFszXSksbmEucm09VCksMykKYGBgCgpUcmFjZXM6IHRvcyBmaXJzdCAyNSUgYXMgYnVybmluCmBgYHtyfQpzLnNhbXBsZXM9cy5zYW1wbGVzWyhsZW5ndGgocy5zYW1wbGVzKSowLjI1KTpsZW5ndGgocy5zYW1wbGVzKV0Kc3RyYWNlPWdncGxvdChkYXRhPWRhdGEuZnJhbWUocy5zYW1wbGVzKSxhZXMoeT1zLnNhbXBsZXMseD0xOihsZW5ndGgocy5zYW1wbGVzKSkpKSsKICBnZW9tX2xpbmUoY29sb3I9Y2JQYWxldHRlWzRdKSsKICB5bGFiKCJzIikrCiAgeGxhYihwYXN0ZSgiZ2VuZXJhdGlvbnNcbigiLHJvdW5kKGVmZmVjdGl2ZVNpemUocy5zYW1wbGVzKSksIiBlZmYuIHNhbXBsZXMpXG4oYWNjLiByYXRlICIscy5hY2MsIikiLHNlcD0iIikpICsKICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApKQpudS5zYW1wbGVzPW51LnNhbXBsZXNbKGxlbmd0aChudS5zYW1wbGVzKSowLjI1KTpsZW5ndGgobnUuc2FtcGxlcyldCm50cmFjZT1nZ3Bsb3QoZGF0YT1kYXRhLmZyYW1lKG51LnNhbXBsZXMpLGFlcyh5PW51LnNhbXBsZXMseD0xOihsZW5ndGgobnUuc2FtcGxlcykpKSkrCiAgZ2VvbV9saW5lKGNvbG9yPWNiUGFsZXR0ZVszXSkrCiAgeWxhYihleHByZXNzaW9uKG51KSkrCiAgeGxhYihwYXN0ZSgiZ2VuZXJhdGlvbnNcbigiLHJvdW5kKGVmZmVjdGl2ZVNpemUobnUuc2FtcGxlcykpLCIgZWZmLiBzYW1wbGVzKVxuKGFjYy4gcmF0ZSAiLG51LmFjYywiKSIsc2VwPSIiKSkgKwogIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCksYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCkpCm11LnNhbXBsZXM9bXUuc2FtcGxlc1sobGVuZ3RoKG11LnNhbXBsZXMpKjAuMjUpOmxlbmd0aChtdS5zYW1wbGVzKV0KbXRyYWNlPWdncGxvdChkYXRhPWRhdGEuZnJhbWUobXUuc2FtcGxlcyksYWVzKHk9bXUuc2FtcGxlcyx4PTE6KGxlbmd0aChtdS5zYW1wbGVzKSkpKSsKICBnZW9tX2xpbmUoY29sb3I9Y2JQYWxldHRlWzJdKSsKICB5bGFiKGV4cHJlc3Npb24obXUpKSsKICB4bGFiKHBhc3RlKCJnZW5lcmF0aW9uc1xuKCIscm91bmQoZWZmZWN0aXZlU2l6ZShtdS5zYW1wbGVzKSksIiBlZmYuIHNhbXBsZXMpXG4oYWNjLiByYXRlICIsbXUuYWNjLCIpIixzZXA9IiIpKSArCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSxheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSkKbC5zYW1wbGVzPWwuc2FtcGxlc1sobGVuZ3RoKGwuc2FtcGxlcykqMC4yNSk6bGVuZ3RoKGwuc2FtcGxlcyldCmx0cmFjZT1nZ3Bsb3QoZGF0YT1kYXRhLmZyYW1lKGxvZyhsLnNhbXBsZXMpKSxhZXMoeT1sb2cubC5zYW1wbGVzLix4PTE6KGxlbmd0aChsLnNhbXBsZXMpKSkpKwogIGdlb21fbGluZShjb2xvcj1jYlBhbGV0dGVbMV0pKwogIHlsYWIoIkxpa2VsaWhvb2QiKSsKICB4bGFiKCJnZW5lcmF0aW9ucyIpICsKICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApICkKCmBgYAoKClBvc3RlcmlvciB2cyBQcmlvcgpgYGB7cn0KI0FMUEhBCnByaW9yLm11PXJleHAobGVuZ3RoKG11LnNhbXBsZXNbLWMoMTooMC4xKm5nZW4vc2FtcGxlLmZyZXEpKV0pLHJhdGVzWzFdKQpwb3N0Lm11PW11LnNhbXBsZXNbLWMoMTooMC4xKm5nZW4vc2FtcGxlLmZyZXEpKV0KbW9kZS5tdT1kZW5zaXR5KHBvc3QubXUpJHhbd2hpY2goZGVuc2l0eShwb3N0Lm11KSR5PT1tYXgoZGVuc2l0eShwb3N0Lm11KSR5KSldCm11cGxvdDwtZ2dwbG90KGRhdGEuZnJhbWUocG9zdC5tdSxwcmlvci5tdSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMocG9zdC5tdSksZmlsbD1jYlBhbGV0dGVbMl0sYmlucz0zMCkgKyAKICBnZW9tX2hpc3RvZ3JhbShhZXMocHJpb3IubXUpLGJpbnM9MzAsYWxwaGE9MC4yLGZpbGw9Y2JQYWxldHRlWzFdKSsKICBzY2FsZV94X2xvZzEwKCkrCiAgeGxhYihleHByZXNzaW9uKG11KSkKaWYoZmFrZT09VFJVRSl7IG11cGxvdD1tdXBsb3QrZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZmFrZS5hbHBoYS8oNCpOZSkpfSBlbHNleyBtdXBsb3Q9bXVwbG90K2dlb21fdmxpbmUoeGludGVyY2VwdCA9IG1vZGUubXUpIH0KCm11cGxvdHpvb208LWdncGxvdChkYXRhLmZyYW1lKHBvc3QubXUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHBvc3QubXUpLGZpbGw9Y2JQYWxldHRlWzJdLGJpbnM9MzApKwogIHhsYWIoZXhwcmVzc2lvbihtdSkpKyAgCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpKQppZihmYWtlPT1UUlVFKXsgbXVwbG90em9vbT1tdXBsb3R6b29tK2dlb21fdmxpbmUoeGludGVyY2VwdCA9IGZha2UuYWxwaGEvKDQqTmUpKX0gZWxzZXsgbXVwbG90em9vbT1tdXBsb3R6b29tK2dlb21fdmxpbmUoeGludGVyY2VwdCA9IG1vZGUubXUpIH0KCiNCRVRBCnByaW9yLm51PXJleHAobGVuZ3RoKG51LnNhbXBsZXNbLWMoMTooMC4xKm5nZW4vc2FtcGxlLmZyZXEpKV0pLHJhdGVzWzJdKQpwb3N0Lm51PW51LnNhbXBsZXNbLWMoMTooMC4xKm5nZW4vc2FtcGxlLmZyZXEpKV0KbW9kZS5udT1kZW5zaXR5KHBvc3QubnUpJHhbd2hpY2goZGVuc2l0eShwb3N0Lm51KSR5PT1tYXgoZGVuc2l0eShwb3N0Lm51KSR5KSldCm51cGxvdDwtZ2dwbG90KGRhdGEuZnJhbWUocG9zdC5udSxwcmlvci5udSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMocG9zdC5udSksZmlsbD1jYlBhbGV0dGVbM10sYmlucz0zMCkgKyAKICBnZW9tX2hpc3RvZ3JhbShhZXMocHJpb3IubnUpLGJpbnM9MzAsYWxwaGE9MC4yLGZpbGw9Y2JQYWxldHRlWzFdKSsKICBzY2FsZV94X2xvZzEwKCkrCiAgeGxhYihleHByZXNzaW9uKG51KSkKaWYoZmFrZT09VFJVRSl7IG51cGxvdD1udXBsb3QrZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZmFrZS5iZXRhLyg0Kk5lKSl9IGVsc2V7IAogIG51cGxvdD1udXBsb3QrZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbW9kZS5udSkgfQoKbnVwbG90em9vbTwtZ2dwbG90KGRhdGEuZnJhbWUocG9zdC5udSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMocG9zdC5udSksZmlsbD1jYlBhbGV0dGVbM10sYmlucz0zMCkrCiAgeGxhYihleHByZXNzaW9uKG51KSkrIAogIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT02KSkKaWYoZmFrZT09VFJVRSl7IG51cGxvdHpvb209bnVwbG90em9vbStnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBmYWtlLmJldGEvKDQqTmUpKX0gZWxzZXsgbnVwbG90em9vbT1udXBsb3R6b29tK2dlb21fdmxpbmUoeGludGVyY2VwdCA9IG1vZGUubnUpIH0KCiNHQU1NQQpwcmlvci5zPXJleHAobGVuZ3RoKHMuc2FtcGxlc1stYygxOigwLjEqbmdlbi9zYW1wbGUuZnJlcSkpXSkscmF0ZXNbM10pCnBvc3Qucz1zLnNhbXBsZXNbLWMoMTooMC4xKm5nZW4vc2FtcGxlLmZyZXEpKV0KbW9kZS5zPWRlbnNpdHkocG9zdC5zKSR4W3doaWNoKGRlbnNpdHkocG9zdC5zKSR5PT1tYXgoZGVuc2l0eShwb3N0LnMpJHkpKV0Kc3Bsb3Q8LWdncGxvdChkYXRhLmZyYW1lKHBvc3Qucyxwcmlvci5zKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShhZXMocG9zdC5zKSxmaWxsPWNiUGFsZXR0ZVs0XSxiaW5zPTMwKSArIAogIGdlb21faGlzdG9ncmFtKGFlcyhwcmlvci5zKSxiaW5zPTMwLGFscGhhPTAuMixmaWxsPWNiUGFsZXR0ZVsxXSkrCiAgc2NhbGVfeF9sb2cxMCgpKwogIHhsYWIoInMiKQppZihmYWtlPT1UUlVFKXsgc3Bsb3Q9c3Bsb3QrZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZmFrZS5nYW1tYS8oNCpOZSkpfSBlbHNleyBzcGxvdD1zcGxvdCtnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtb2RlLnMpIH0KCnNwbG90em9vbTwtZ2dwbG90KGRhdGEuZnJhbWUocG9zdC5zKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShhZXMocG9zdC5zKSxmaWxsPWNiUGFsZXR0ZVs0XSxiaW5zPTMwKSsKICB4bGFiKCJzIikrCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpKQppZihmYWtlPT1UUlVFKXsgc3Bsb3R6b29tPXNwbG90em9vbStnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBmYWtlLmdhbW1hLyg0Kk5lKSl9ICBlbHNleyBzcGxvdHpvb209c3Bsb3R6b29tK2dlb21fdmxpbmUoeGludGVyY2VwdCA9IG1vZGUucykgfQoKI1BMT1QKcGxvdF9ncmlkKG10cmFjZSxudHJhY2Usc3RyYWNlLG11cGxvdCxudXBsb3Qsc3Bsb3QsbXVwbG90em9vbSxudXBsb3R6b29tLHNwbG90em9vbSxuY29sPTMscmVsX2hlaWdodHM9YygxLjUsMSwxKSxhbGlnbj0idiIpCnBsb3QobHRyYWNlKQpgYGAKClBsb3Qgb2JzZXJ2ZWQgZGF0YSBhbmQgZXN0aW1hdGUgZnJvbSBtZWFuIGFuZCBNQVA6CmBgYHtyfQojcGxvdCBtZWFuCnBsb3QobXlfc2ZzfihjKDA6bWF4KGspKSkscGNoPTE5LGNleD0yLHlsYWI9ImNvdW50cyIseGxhYj0ibnVtYmVyIG9mIGNocm9tb3NvbWVzIixjZXgubGFiPTEuNSkKCiBwb3N0X3Nmcz1zYXBwbHkoayxmdW5jdGlvbihLKXsKICAgICBsb2coY2hvb3NlKG4sSykpKyhmMShtZWFuKHBvc3QubnUpKjQqTmUrSyxtZWFuKHBvc3QubXUpKjQqTmUrbWVhbihwb3N0Lm51KSo0Kk5lK24sbWVhbihwb3N0LnMpKjQqTmUpK3Byb2NoKG1lYW4ocG9zdC5udSkqNCpOZSxLKStwcm9jaChtZWFuKHBvc3QubXUpKjQqTmUsbi1LKSktKGYxKG1lYW4ocG9zdC5udSkqNCpOZSxtZWFuKHBvc3QubXUpKjQqTmUrbWVhbihwb3N0Lm51KSo0Kk5lLG1lYW4ocG9zdC5zKSo0Kk5lKStwcm9jaChtZWFuKHBvc3QubXUpKjQqTmUrbWVhbihwb3N0Lm51KSo0Kk5lLG4pKX0pCgpwb3N0X3Nmcz1wb3N0X3Nmcy1tYXgocG9zdF9zZnMpCnBvc3Rfc2ZzPWV4cChwb3N0X3Nmcykvc3VtKGV4cChwb3N0X3NmcykpKnN1bShteV9zZnMpCnBvaW50cyhwb3N0X3Nmc35jKDA6bWF4KGspKSxjZXg9MSxjb2w9Y2JQYWxldHRlWzJdLHBjaD0xOSkKbGVnZW5kKCJ0b3AiLGxlZ2VuZD1jKCJvYnNlcnZlZCIsIm1lYW4gb2YgcG9zdGVyaW9yIiksZmlsbD1jKCJibGFjayIsY2JQYWxldHRlWzJdKSkKCiNQbG90IG1heGltdW0gYSBwb3N0ZXJpb3JpIChtb2RlKQpwbG90KG15X3Nmc34oYygwOm1heChrKSkpLHBjaD0xOSxjZXg9Mix5bGFiPSJjb3VudHMiLHhsYWI9Im51bWJlciBvZiBjaHJvbW9zb21lcyIsY2V4LmxhYj0xLjUpCnBvc3Rfc2ZzPXNhcHBseShrLGZ1bmN0aW9uKEspewogICAgbG9nKGNob29zZShuLEspKSsoZjEobW9kZS5udSo0Kk5lK0ssbW9kZS5tdSo0Kk5lK21vZGUubnUqNCpOZStuLG1vZGUucyo0Kk5lKStwcm9jaChtb2RlLm51KjQqTmUsSykrcHJvY2gobW9kZS5tdSo0Kk5lLG4tSykpLShmMShtb2RlLm51KjQqTmUsbW9kZS5tdSo0Kk5lK21vZGUubnUqNCpOZSxtb2RlLnMqNCpOZSkrcHJvY2gobW9kZS5tdSo0Kk5lK21vZGUubnUqNCpOZSxuKSl9KQoKcG9zdF9zZnM9cG9zdF9zZnMtbWF4KHBvc3Rfc2ZzKQpwb3N0X3Nmcz1leHAocG9zdF9zZnMpL3N1bShleHAocG9zdF9zZnMpKSpzdW0obXlfc2ZzKQpwb2ludHMocG9zdF9zZnN+YygwOm1heChrKSksY2V4PTEsY29sPWNiUGFsZXR0ZVsyXSxwY2g9MTkpCmxlZ2VuZCgidG9wIixsZWdlbmQ9Yygib2JzZXJ2ZWQiLCJtb2RlIG9mIHBvc3RlcmlvciIpLGZpbGw9YygiYmxhY2siLGNiUGFsZXR0ZVsyXSkpCmBgYAoK